sudojs-rails 0.3.8 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,5 +9,5 @@ _.namespace('<%= @path_to_object %>');
9
9
 
10
10
  <%= @path_to_object %>.<%= @a_pascal %>.prototype = $.extend(Object.create(<%= @parent %>.prototype), {
11
11
  // optional init method for your new viewController, delete if unused
12
- init: function init() {};
12
+ init: function init() {}
13
13
  });
@@ -1,3 +1,3 @@
1
1
  module Sudojs
2
- VERSION = '0.3.8'
2
+ VERSION = '0.3.9'
3
3
  end
@@ -425,10 +425,11 @@ sudo.Container.prototype.removeChild = function removeChild(arg) {
425
425
  };
426
426
 
427
427
  // ###removeChildren
428
- // Remove all children, removing the name references and index
429
- // This method calls removeFromParent on each child. If it's a DataView also removes the child's DOM.
430
- // `returns` {Object} `this`
431
- sudo.Container.prototype.removeChildren = function removeChildren(arg) {
428
+ // Remove all children, name references and adjust indexes accordingly.
429
+ // This method calls removeFromParent as each child may have overridden logic there.
430
+ //
431
+ // `returns` {object} `this`
432
+ sudo.Container.prototype.removeChildren = function removeChildren() {
432
433
  while(this.children.length) {
433
434
  this.children.shift().removeFromParent();
434
435
  }
@@ -452,6 +453,9 @@ sudo.Container.prototype.role = 'container';
452
453
  // What this does is allow children of a `sudo.Container` to simply pass
453
454
  // events upward, delegating the responsibility of deciding what to do to the parent.
454
455
  //
456
+ // TODO Currently, only the first target method found is called, then the
457
+ // bubbling is stopped. Should bubbling continue all the way up the 'chain'?
458
+ //
455
459
  // `param` {*} Any number of arguments is supported, but the first is the only one searched for info.
456
460
  // A sendMethod will be located by:
457
461
  // 1. using the first argument if it is a string
@@ -855,6 +859,7 @@ sudo.DataView = function(el, data) {
855
859
  if((t = d.template)) {
856
860
  if(typeof t === 'string') this.model.data.template = sudo.template(t);
857
861
  }
862
+ this.build();
858
863
  this.bindEvents();
859
864
  if(this.role === 'dataview') this.init();
860
865
  };
@@ -866,6 +871,15 @@ sudo.inherit(sudo.View, sudo.DataView);
866
871
  sudo.DataView.prototype.addedToParent = function(parent) {
867
872
  return this.render();
868
873
  };
874
+ // ###build
875
+ // Construct the innerHTML of the $el here so that the behavior of the
876
+ // DataView, that the markup is ready after a subclass calls `this.construct`,
877
+ // is the same as other View classes
878
+ sudo.DataView.prototype.build = function build() {
879
+ this.$el.html(this.model.data.template(this.model.data));
880
+ this.built = true;
881
+ return this;
882
+ };
869
883
  // ###removeFromParent
870
884
  // Remove this object from the DOM and its parent's list of children.
871
885
  // Overrides `sudo.View.removeFromParent` to actually remove the DOM as well
@@ -892,7 +906,10 @@ sudo.DataView.prototype.render = function render(change) {
892
906
  // return early if a `blacklisted` key is set to my model
893
907
  if(change && this.autoRenderBlacklist[change.name]) return this;
894
908
  d = this.model.data;
895
- this.$el.html(d.template(d));
909
+ // has `build` been called already? If not:
910
+ if(!this.built) this.$el.html(d.template(d));
911
+ // if so erase the flag
912
+ else this.built = false;
896
913
  if(d.renderTarget) {
897
914
  this._normalizedEl_(d.renderTarget)[d.renderMethod || 'append'](this.$el);
898
915
  delete d.renderTarget;
@@ -1526,6 +1543,13 @@ sudo.extensions.listener = {
1526
1543
  this.$el.off(e.name, e.sel);
1527
1544
  }
1528
1545
  },
1546
+ // ###rebindEvents
1547
+ // Convenience method for `this.unbindEvents().bindEvents()`
1548
+ //
1549
+ // 'returns' {object} 'this'
1550
+ rebindEvents: function rebindEvents() {
1551
+ return this.unbindEvents().bindEvents();
1552
+ },
1529
1553
  // ###unbindEvents
1530
1554
  // Unbind the events in the data store from this object's $el
1531
1555
  //
@@ -1574,7 +1598,7 @@ sudo.extensions.persistable = {
1574
1598
  //
1575
1599
  // `param` {object} `params` Optional hash of options for the XHR
1576
1600
  // `returns` {object} jqXhr
1577
- destroy: function _delete(params) {
1601
+ destroy: function destroy(params) {
1578
1602
  return this._sendData_('DELETE', params);
1579
1603
  },
1580
1604
  // ###_normalizeParams_
@@ -1610,7 +1634,7 @@ sudo.extensions.persistable = {
1610
1634
  // `param` {object} `params`. Optional info for the XHR call. If
1611
1635
  // present will override any set in this model's `ajax` options object.
1612
1636
  // `returns` {object} The jQuery XHR object
1613
- read: function post(params) {
1637
+ read: function read(params) {
1614
1638
  return $.ajax(this._normalizeParams_('GET', null, params));
1615
1639
  },
1616
1640
  // ###save
@@ -1794,4 +1818,4 @@ sudo.delegates.Data.prototype.role = 'data';
1794
1818
  sudo.version = "0.9.4";
1795
1819
  window.sudo = sudo;
1796
1820
  if(typeof window._ === "undefined") window._ = sudo;
1797
- }).call(this, this);
1821
+ }).call(this, this);
@@ -1,817 +0,0 @@
1
- (function(window) {
2
- // #Sudo Namespace
3
- var sudo = {
4
- // Namespace for `Delegate` Class Objects used to delegate functionality
5
- // from a `delegator`
6
- //
7
- // `namespace`
8
- delegates: {},
9
- // The sudo.extensions namespace holds the objects that are stand alone `modules` which
10
- // can be `implemented` (mixed-in) in sudo Class Objects
11
- //
12
- // `namespace`
13
- extensions: {},
14
- // ###getPath
15
- // Extract a value located at `path` relative to the passed in object
16
- //
17
- // `param` {String} `path`. The key in the form of a dot-delimited path.
18
- // `param` {object} `obj`. An object literal to operate on.
19
- //
20
- // `returns` {*|undefined}. The value at keypath or undefined if not found.
21
- getPath: function getPath(path, obj) {
22
- var key, p;
23
- p = path.split('.');
24
- for (key; p.length && (key = p.shift());) {
25
- if(!p.length) {
26
- return obj[key];
27
- } else {
28
- obj = obj[key] || {};
29
- }
30
- }
31
- return obj;
32
- },
33
- // ###inherit
34
- // Inherit the prototype from a parent to a child.
35
- // Set the childs constructor for subclasses of child.
36
- // Subclasses of the library base classes will not
37
- // want to use this function in *most* use-cases. Why? User Sudo Class Objects
38
- // possess their own constructors and any call back to a `superclass` constructor
39
- // will generally be looking for the library Object's constructor.
40
- //
41
- // `param` {function} `parent`
42
- // `param` {function} `child`
43
- inherit: function inherit(parent, child) {
44
- child.prototype = Object.create(parent.prototype);
45
- child.prototype.constructor = child;
46
- },
47
- // ###makeMeASandwich
48
- // Notice there is no need to extrinsically instruct *how* to
49
- // make the sandwich, just the elegant single command.
50
- //
51
- // `returns` {string}
52
- makeMeASandwich: function makeMeASandwich() {return 'Okay.';},
53
- // ###namespace
54
- // Method for assuring a Namespace is defined.
55
- //
56
- // `param` {string} `path`. The path that leads to a blank Object.
57
- namespace: function namespace(path) {
58
- if (!this.getPath(path, window)) {
59
- this.setPath(path, {}, window);
60
- }
61
- },
62
- // ###premier
63
- // The premier object takes precedence over all others so define it at the topmost level.
64
- //
65
- // `type` {Object}
66
- premier: null,
67
- // ###setPath
68
- // Traverse the keypath and get each object
69
- // (or make blank ones) eventually setting the value
70
- // at the end of the path
71
- //
72
- // `param` {string} `path`. The path to traverse when setting a value.
73
- // `param` {*} `value`. What to set.
74
- // `param` {Object} `obj`. The object literal to operate on.
75
- setPath: function setPath(path, value, obj) {
76
- var p = path.split('.'), key;
77
- for (key; p.length && (key = p.shift());) {
78
- if(!p.length) {
79
- obj[key] = value;
80
- } else if (obj[key]) {
81
- obj = obj[key];
82
- } else {
83
- obj = obj[key] = {};
84
- }
85
- }
86
- },
87
- // ####uid
88
- // Some sudo Objects use a unique integer as a `tag` for identification.
89
- // (Views for example). This ensures they are indeed unique.
90
- uid: 0,
91
- // ####unique
92
- // An integer used as 'tags' by some sudo Objects as well
93
- // as a unique string for views when needed
94
- //
95
- // `param` {string} prefix. Optional string identifier
96
- unique: function unique(prefix) {
97
- return prefix ? prefix + this.uid++ : this.uid++;
98
- },
99
- // ###unsetPath
100
- // Remove a key:value pair from this object's data store
101
- // located at <path>
102
- //
103
- // `param` {String} `path`
104
- // `param` {Object} `obj` The object to operate on.
105
- unsetPath: function unsetPath(path, obj) {
106
- var p = path.split('.'), key;
107
- for (key; p.length && (key = p.shift());) {
108
- if(!p.length) {
109
- delete obj[key];
110
- } else {
111
- // this can fail if a faulty path is passed.
112
- // using getPath beforehand can prevent that
113
- obj = obj[key];
114
- }
115
- }
116
- }
117
- };
118
- // ##Base Class Object
119
- //
120
- // All sudo.js objects inherit base, giving the ability
121
- // to utilize delegation, the `base` function and the
122
- // `construct` convenience method.
123
- //
124
- // `constructor`
125
- sudo.Base = function() {
126
- // can delegate
127
- this.delegates = [];
128
- // a beautiful and unique snowflake
129
- this.uid = sudo.unique();
130
- };
131
- // ###addDelegate
132
- // Push an instance of a Class Object into this object's `_delegates_` list.
133
- //
134
- // `param` {Object} an instance of a sudo.delegates Class Object
135
- // `returns` {Object} `this`
136
- sudo.Base.prototype.addDelegate = function addDelegate(del) {
137
- del.delegator = this;
138
- this.delegates.push(del);
139
- return this;
140
- };
141
- // ###base
142
- // Lookup the function matching the name passed in and call it with
143
- // any passed in argumets scoped to the calling object.
144
- // This method will avoid the recursive-loop problem by making sure
145
- // that the first match is not the function that called `base`.
146
- //
147
- // `params` {*} any other number of arguments to be passed to the looked up method
148
- // along with the initial method name
149
- sudo.Base.prototype.base = function base() {
150
- var args = Array.prototype.slice.call(arguments),
151
- name = args.shift(),
152
- found = false,
153
- obj = this,
154
- curr;
155
- // find method on the prototype, excluding the caller
156
- while(!found) {
157
- curr = Object.getPrototypeOf(obj);
158
- if(curr[name] && curr[name] !== this[name]) found = true;
159
- // keep digging
160
- else obj = curr;
161
- }
162
- return curr[name].apply(this, args);
163
- };
164
- // ###construct
165
- // A convenience method that alleviates the need to place:
166
- // `Object.getPrototypeOf(this).consturctor.apply(this, arguments)`
167
- // in every constructor
168
- sudo.Base.prototype.construct = function construct() {
169
- Object.getPrototypeOf(this).constructor.apply(this, arguments || []);
170
- };
171
- // ###delegate
172
- // From this object's list of delegates find the object whose `_role_` matches
173
- // the passed `name` and:
174
- // 1. if `meth` is falsy return the delegate.
175
- // 2 if `meth` is truthy bind its method (to the delegate) and return the method
176
- //
177
- // `param` {String} `role` The role property to match in this object's delegates list
178
- // `param` {String} `meth` Optional method to bind to the action this delegate is being used for
179
- // `returns`
180
- sudo.Base.prototype.delegate = function delegate(role, meth) {
181
- var del = this.delegates, i;
182
- for(i = 0; i < del.length; i++) {
183
- if(del[i].role === role) {
184
- if(!meth) return del[i];
185
- return del[i][meth].bind(del[i]);
186
- }
187
- }
188
- };
189
- // ###getDelegate
190
- // Fetch a delegate whose role property matches the passed in argument.
191
- // Uses the `delegate` method in its 'single argument' form, included for
192
- // API consistency
193
- //
194
- // `param` {String} `role`
195
- // 'returns' {Object|undefined}
196
- sudo.Base.prototype.getDelegate = function getDelegate(role) {
197
- return this.delegate(role);
198
- };
199
- // ###removeDelegate
200
- // From this objects `delegates` list remove the object (there should only ever be 1)
201
- // whose role matches the passed in argument
202
- //
203
- // `param` {String} `role`
204
- // `returns` {Object} `this`
205
- sudo.Base.prototype.removeDelegate = function removeDelegate(role) {
206
- var del = this.delegates, i;
207
- for(i = 0; i < del.length; i++) {
208
- if(del[i].role === role) {
209
- // no _delegator_ for you
210
- del[i].delegator = void 0;
211
- del.splice(i, 1);
212
- return this;
213
- }
214
- }
215
- return this;
216
- };
217
- // `private`
218
- sudo.Base.prototype.role = 'base';
219
- // ##Model Class Object
220
- //
221
- // Model Objects expose methods for setting and getting data, and
222
- // can be observed if implementing the `Observable Extension`
223
- //
224
- // `param` {object} data. An initial state for this model.
225
- //
226
- // `constructor`
227
- sudo.Model = function(data) {
228
- sudo.Base.call(this);
229
- this.data = data || {};
230
- // only models are `observable`
231
- this.callbacks = [];
232
- this.changeRecords = [];
233
- };
234
- // Model inherits from sudo.Base
235
- // `private`
236
- sudo.inherit(sudo.Base, sudo.Model);
237
- // ###get
238
- // Returns the value associated with a key.
239
- //
240
- // `param` {String} `key`. The name of the key
241
- // `returns` {*}. The value associated with the key or false if not found.
242
- sudo.Model.prototype.get = function get(key) {
243
- return this.data[key];
244
- };
245
- // ###getPath
246
- //
247
- // Uses the sudo namespace's getpath function operating on the model's
248
- // data hash.
249
- //
250
- // `returns` {*|undefined}. The value at keypath or undefined if not found.
251
- sudo.Model.prototype.getPath = function getPath(path) {
252
- return sudo.getPath(path, this.data);
253
- };
254
- // ###gets
255
- // Assembles and returns an object of key:value pairs for each key
256
- // contained in the passed in Array.
257
- //
258
- // `param` {array} `ary`. An array of keys.
259
- // `returns` {object}
260
- sudo.Model.prototype.gets = function gets(ary) {
261
- var i, obj = {};
262
- for (i = 0; i < ary.length; i++) {
263
- obj[ary[i]] = ary[i].indexOf('.') === -1 ? this.data[ary[i]] :
264
- this.getPath(ary[i]);
265
- }
266
- return obj;
267
- };
268
- // `private`
269
- sudo.Model.prototype.role = 'model';
270
- // ###set
271
- // Set a key:value pair.
272
- //
273
- // `param` {String} `key`. The name of the key.
274
- // `param` {*} `value`. The value associated with the key.
275
- // `returns` {Object} `this`
276
- sudo.Model.prototype.set = function set(key, value) {
277
- // _NOTE: intentional possibilty of setting a falsy value_
278
- this.data[key] = value;
279
- return this;
280
- };
281
- // ###setPath
282
- //
283
- // Uses the sudo namespace's setpath function operating on the model's
284
- // data hash.
285
- //
286
- // `param` {String} `path`
287
- // `param` {*} `value`
288
- // `returns` {Object} this.
289
- sudo.Model.prototype.setPath = function setPath(path, value) {
290
- sudo.setPath(path, value, this.data);
291
- return this;
292
- };
293
- // ###sets
294
- // Invokes `set()` or `setPath()` for each key value pair in `obj`.
295
- // Any listeners for those keys or paths will be called.
296
- //
297
- // `param` {Object} `obj`. The keys and values to set.
298
- // `returns` {Object} `this`
299
- sudo.Model.prototype.sets = function sets(obj) {
300
- var i, k = Object.keys(obj);
301
- for(i = 0; i < k.length; i++) {
302
- k[i].indexOf('.') === -1 ? this.set(k[i], obj[k[i]]) :
303
- this.setPath(k[i], obj[k[i]]);
304
- }
305
- return this;
306
- };
307
- // ###unset
308
- // Remove a key:value pair from this object's data store
309
- //
310
- // `param` {String} key
311
- // `returns` {Object} `this`
312
- sudo.Model.prototype.unset = function unset(key) {
313
- delete this.data[key];
314
- return this;
315
- };
316
- // ###unsetPath
317
- // Uses `sudo.unsetPath` operating on this models data hash
318
- //
319
- // `param` {String} path
320
- // `returns` {Object} `this`
321
- sudo.Model.prototype.unsetPath = function unsetPath(path) {
322
- sudo.unsetPath(path, this.data);
323
- return this;
324
- };
325
- // ###unsets
326
- // Deletes a number of keys or paths from this object's data store
327
- //
328
- // `param` {array} `ary`. An array of keys or paths.
329
- // `returns` {Objaect} `this`
330
- sudo.Model.prototype.unsets = function unsets(ary) {
331
- var i;
332
- for(i = 0; i < ary.length; i++) {
333
- ary[i].indexOf('.') === -1 ? this.unset(ary[i]) :
334
- this.unsetPath(ary[i]);
335
- }
336
- return this;
337
- };
338
- // ##Container Class Object
339
- //
340
- // A container is any object that can both contain other objects and
341
- // itself be contained
342
- //
343
- // `constructor`
344
- sudo.Container = function() {
345
- sudo.Base.call(this);
346
- this.children = [];
347
- this.childNames = {};
348
- };
349
- // Container is a subclass of sudo.Base
350
- sudo.inherit(sudo.Base, sudo.Container);
351
- // ###addChild
352
- // Adds a View to this container's list of children.
353
- // Also adds an 'index' property and an entry in the childNames hash.
354
- // If `addedToParent` if found on the child, call it, sending `this` as an argument.
355
- //
356
- // `param` {Object} `child`. View (or View subclass) instance.
357
- // `param` {String} `name`. An optional name for the child that will go in the childNames hash.
358
- // `returns` {Object} `this`
359
- sudo.Container.prototype.addChild = function addChild(child, name) {
360
- var c = this.children;
361
- child.parent = this;
362
- child.index = c.length;
363
- if(name) {
364
- child.name = name;
365
- this.childNames[name] = child.index;
366
- }
367
- c.push(child);
368
- if('addedToParent' in child) child.addedToParent(this);
369
- return this;
370
- };
371
- // ###bubble
372
- // By default, `bubble` returns the current view's parent (if it has one)
373
- //
374
- // `returns` {Object|undefined}
375
- sudo.Container.prototype.bubble = function bubble() {return this.parent;};
376
- // ###getChild
377
- // If a child was added with a name, via `addChild`,
378
- // that object can be fetched by name. This prevents us from having to reference a
379
- // containers children by index. That is possible however, though not preferred.
380
- //
381
- // `param` {String|Number} `id`. The string `name` or numeric `index` of the child to fetch.
382
- // `returns` {Object|undefined} The found child
383
- sudo.Container.prototype.getChild = function getChild(id) {
384
- return typeof id === 'string' ? this.children[this.childNames[id]] :
385
- this.children[id];
386
- };
387
- // ###_indexChildren_
388
- // Method is called with the `index` property of a subview that is being removed.
389
- // Beginning at <i> decrement subview indices.
390
- // `param` {Number} `i`
391
- // `private`
392
- sudo.Container.prototype._indexChildren_ = function _indexChildren_(i) {
393
- var c = this.children, obj = this.childNames, len;
394
- for (len = c.length; i < len; i++) {
395
- c[i].index--;
396
- // adjust any entries in childNames
397
- if(c[i].name in obj) obj[c[i].name] = c[i].index;
398
- }
399
- };
400
- // ###removeChild
401
- // Find the intended child from my list of children and remove it, removing the name reference and re-indexing
402
- // remaining children. This method does not remove the child's DOM.
403
- // Override this method, doing whatever you want to the child's DOM, then call `base('removeChild')` to do so.
404
- //
405
- // `param` {String|Number|Object} `arg`. Children will always have an `index` number, and optionally a `name`.
406
- // If passed a string `name` is assumed, so be sure to pass an actual number if expecting to use index.
407
- // An object will be assumed to be an actual sudo Class Object.
408
- // `returns` {Object} `this`
409
- sudo.Container.prototype.removeChild = function removeChild(arg) {
410
- var i, t = typeof arg, c;
411
- // normalize the input
412
- if(t === 'object') c = arg;
413
- else c = t === 'string' ? this.children[this.childNames[arg]] : this.children[arg];
414
- i = c.index;
415
- // remove from the children Array
416
- this.children.splice(i, 1);
417
- // remove from the named child hash if present
418
- delete this.childNames[c.name];
419
- // child is now an `orphan`
420
- delete c.parent;
421
- delete c.index;
422
- delete c.name;
423
- this._indexChildren_(i);
424
- return this;
425
- };
426
- // ###removeFromParent
427
- // Remove this object from its parents list of children.
428
- // Does not alter the dom - do that yourself by overriding this method
429
- // or chaining method calls
430
- sudo.Container.prototype.removeFromParent = function removeFromParent() {
431
- // will error without a parent, but that would be your fault...
432
- this.parent.removeChild(this);
433
- return this;
434
- };
435
- sudo.Container.prototype.role = 'container';
436
- // ###send
437
- // The call to the specific method on a (un)specified target happens here.
438
- // If this Object is part of a `sudo.Container` maintained hierarchy
439
- // the 'target' may be left out, causing the `bubble()` method to be called.
440
- // What this does is allow children of a `sudo.Container` to simply pass
441
- // events upward, delegating the responsibility of deciding what to do to the parent.
442
- //
443
- // `param` {*} Any number of arguments is supported, but the first is the only one searched for info.
444
- // A sendMethod will be located by:
445
- // 1. using the first argument if it is a string
446
- // 2. looking for a `sendMethod` property if it is an object
447
- // In the case a specified target exists at `this.model.get('sendTarget')` it will be used
448
- // Any other args will be passed to the sendMethod after `this`
449
- // `returns` {Object} `this`
450
- sudo.Container.prototype.send = function send(/*args*/) {
451
- var args = Array.prototype.slice.call(arguments),
452
- d = this.model && this.model.data, meth, targ, fn;
453
- // normalize the input, common use cases first
454
- if(d && 'sendMethod' in d) meth = d.sendMethod;
455
- else if(typeof args[0] === 'string') meth = args.shift();
456
- // less common but viable options
457
- if(!meth) {
458
- // passed as a jquery custom data attr bound in events
459
- meth = 'data' in args[0] ? args[0].data.sendMethod :
460
- // passed in a hash from something or not passed at all
461
- args[0].sendMethod || void 0;
462
- }
463
- // target is either specified or my parent
464
- targ = d && d.sendTarget || this.bubble();
465
- // obvious chance for errors here, don't be dumb
466
- fn = targ[meth];
467
- while(!fn && (targ = targ.bubble())) {
468
- fn = targ[meth];
469
- }
470
- // sendMethods expect a signature (sender, ...)
471
- if(fn) {
472
- args.unshift(this);
473
- fn.apply(targ, args);
474
- }
475
- return this;
476
- };
477
- // ##View Class Object
478
-
479
- // Create an instance of a sudo.View object. A view is any object
480
- // that maintains its own `el`, that being some type of DOM element.
481
- // Pass in a string selector or an actual dom node reference to have the object
482
- // set that as its `el`. If no `el` is specified one will be created upon instantiation
483
- // based on the `tagName` (`div` by default). Specify `className`, `id` (or other attributes if desired)
484
- // as an (optional) `attributes` object literal on the `data` arg.
485
- //
486
- // The view object uses jquery for dom manipulation
487
- // and event delegation etc... A jquerified `this` reference is located
488
- // at `this.$el` and `this.$` scopes queries to this objects `el`, i.e it's
489
- // a shortcut for `this.$el.find(selector)`
490
- //
491
- // `param` {string|element|jQuery} `el`. Otional el for the View instance.
492
- // `param` {Object} `data`. Optional data object-literal which becomes the initial state
493
- // of a new model located at `this.model`. Also can be a reference to an existing sudo.Model instance
494
- //
495
- // `constructor`
496
- sudo.View = function(el, data) {
497
- sudo.Container.call(this);
498
- // allow model instance to be passed in as well
499
- if(data) {
500
- this.model = data.role === 'model' ? data :
501
- this.model = new sudo.Model(data);
502
- }
503
- this.setEl(el);
504
- if(this.role === 'view') this.init();
505
- };
506
- // View inherits from Container
507
- // `private`
508
- sudo.inherit(sudo.Container, sudo.View);
509
- // ###becomePremier
510
- // Premier functionality provides hooks for behavioral differentiation
511
- // among elements or class objects.
512
- //
513
- // `returns` {Object} `this`
514
- sudo.View.prototype.becomePremier = function becomePremier() {
515
- var p, f = function() {
516
- this.isPremier = true;
517
- sudo.premier = this;
518
- }.bind(this);
519
- // is there an existing premier that isn't me?
520
- if((p = sudo.premier) && p.uid !== this.uid) {
521
- // ask it to resign and call the cb
522
- p.resignPremier(f);
523
- } else f(); // no existing premier
524
- return this;
525
- };
526
- // ###init
527
- // A 'contruction-time' hook to call for further initialization needs in
528
- // View objects (and their subclasses). A noop by default child classes should override.
529
- sudo.View.prototype.init = $.noop;
530
- // the el needs to be normalized before use
531
- // `private`
532
- sudo.View.prototype._normalizedEl_ = function _normalizedEl_(el) {
533
- if(typeof el === 'string') {
534
- return $(el);
535
- } else {
536
- // Passed an already `jquerified` Element?
537
- // It will have a length of 1 if so.
538
- return el.length ? el : $(el);
539
- }
540
- };
541
- // ### resignPremier
542
- // Resign premier status
543
- //
544
- // `param` {Function} `cb`. An optional callback to execute
545
- // after resigning premier status.
546
- // `returns` {Object} `this`
547
- sudo.View.prototype.resignPremier = function resignPremier(cb) {
548
- var p;
549
- this.isPremier = false;
550
- // only remove the global premier if it is me
551
- if((p = sudo.premier) && p.uid === this.uid) {
552
- sudo.premier = null;
553
- }
554
- // fire the cb if passed
555
- if(cb) cb();
556
- return this;
557
- };
558
- // `private`
559
- sudo.View.prototype.role = 'view';
560
- // ###setEl
561
- // A view must have an element, set that here.
562
- // Stores a jquerified object as `this.$el` the raw
563
- // node is always then available as `this.$el[0]`.
564
- //
565
- // `param` {string=|element} `el`
566
- // `returns` {Object} `this`
567
- sudo.View.prototype.setEl = function setEl(el) {
568
- var d = this.model && this.model.data, a, t;
569
- if(!el) {
570
- // normalize any relevant data
571
- t = d ? d.tagName || 'div': 'div';
572
- this.$el = $(document.createElement(t));
573
- if(d && (a = d.attributes)) this.$el.attr(a);
574
- } else {
575
- this.$el = this._normalizedEl_(el);
576
- }
577
- return this;
578
- };
579
- // ###this.$
580
- // Return a single Element matching `sel` scoped to this View's el.
581
- // This is an alias to `this.$el.find(sel)`.
582
- //
583
- // `param` {string} `sel`. A jQuery compatible selector
584
- // `returns` {jQuery} A 'jquerified' result matching the selector
585
- sudo.View.prototype.$ = function(sel) {
586
- return this.$el.find(sel);
587
- };
588
- // ## Observable Extension Object
589
- //
590
- // Implementaion of the ES6 Harmony Observer pattern.
591
- // Extend a `sudo.Model` class with this object if
592
- // data-mutation-observation is required
593
- sudo.extensions.observable = {
594
- // ###_deliver_
595
- // Called from deliverChangeRecords when ready to send
596
- // changeRecords to observers.
597
- //
598
- // `private`
599
- _deliver_: function _deliver_(obj) {
600
- var i, cb = this.callbacks;
601
- for(i = 0; i < cb.length; i++) {
602
- cb[i](obj);
603
- }
604
- },
605
- // ###deliverChangeRecords
606
- // Iterate through the changeRecords array(emptying it as you go), delivering them to the
607
- // observers. You can override this method to change the standard delivery behavior.
608
- //
609
- // `returns` {Object} `this`
610
- deliverChangeRecords: function deliverChangeRecords() {
611
- var rec, cr = this.changeRecords;
612
- // FIFO
613
- for(rec; cr.length && (rec = cr.shift());) {
614
- this._deliver_(rec);
615
- }
616
- return this;
617
- },
618
- // ###observe
619
- // In a quasi-ES6 Object.observe pattern, calling observe on an `observable` and
620
- // passing a callback will cause that callback to be called whenever any
621
- // property on the observable's data store is set, changed or deleted
622
- // via set, unset, setPath or unsetPath with an object containing:
623
- // {
624
- // type: <new, updated, deleted>,
625
- // object: <the object being observed>,
626
- // name: <the key that was modified>,
627
- // oldValue: <if a previous value existed for this key>
628
- // }
629
- // For ease of 'unobserving' the same Function passed in is returned.
630
- //
631
- // `param` {Function} `fn` The callback to be called with changeRecord(s)
632
- // `returns` {Function} the Function passed in as an argument
633
- observe: function observe(fn) {
634
- // this will fail if mixed-in and no `callbacks` created so don't do that.
635
- // Per the spec, do not allow the same callback to be added
636
- var d = this.callbacks;
637
- if(d.indexOf(fn) === -1) d.push(fn);
638
- return fn;
639
- },
640
- // ###observes
641
- // Allow an array of callbacks to be registered as changeRecord recipients
642
- //
643
- // `param` {Array} ary
644
- // `returns` {Array} the Array passed in to observe
645
- observes: function observes(ary) {
646
- var i;
647
- for(i = 0; i < ary.length; i++) {
648
- this.observe(ary[i]);
649
- }
650
- return ary;
651
- },
652
- // ###set
653
- // Overrides sudo.Base.set to check for observers
654
- //
655
- // `param` {String} `key`. The name of the key
656
- // `param` {*} `value`
657
- // `param` {Bool} `hold` Call _deliver_ (falsy) or store the change notification
658
- // to be delivered upon a call to deliverChangeRecords (truthy)
659
- //
660
- // `returns` {Object|*} `this` or calls deliverChangeRecords
661
- set: function set(key, value, hold) {
662
- var obj = {name: key, object: this.data};
663
- // did this key exist already
664
- if(key in this.data) {
665
- obj.type = 'updated';
666
- // then there is an oldValue
667
- obj.oldValue = this.data[key];
668
- } else obj.type = 'new';
669
- // now actually set the value
670
- this.data[key] = value;
671
- this.changeRecords.push(obj);
672
- // call the observers or not
673
- if(hold) return this;
674
- return this.deliverChangeRecords();
675
- },
676
- // ###setPath
677
- // Overrides sudo.Base.setPath to check for observers.
678
- // Change records originating from a `setPath` operation
679
- // send back the passed in `path` as `name` as well as the
680
- // top level object being observed (this observable's data).
681
- // this allows for easy filtering either manually or via a
682
- // `change delegate`
683
- //
684
- // `param` {String} `path`
685
- // `param` {*} `value`
686
- // `param` {Bool} `hold` Call _deliver_ (falsy) or store the change notification
687
- // to be delivered upon a call to deliverChangeRecords (truthy)
688
- // `returns` {Object|*} `this` or calls deliverChangeRecords
689
- setPath: function setPath(path, value, hold) {
690
- var curr = this.data, obj = {name: path, object: this.data},
691
- p = path.split('.'), key;
692
- for (key; p.length && (key = p.shift());) {
693
- if(!p.length) {
694
- // reached the last refinement, pre-existing?
695
- if (key in curr) {
696
- obj.type = 'updated';
697
- obj.oldValue = curr[key];
698
- } else obj.type = 'new';
699
- curr[key] = value;
700
- } else if (curr[key]) {
701
- curr = curr[key];
702
- } else {
703
- curr = curr[key] = {};
704
- }
705
- }
706
- this.changeRecords.push(obj);
707
- // call all observers or not
708
- if(hold) return this;
709
- return this.deliverChangeRecords();
710
- },
711
- // ###sets
712
- // Overrides Base.sets to hold the call to _deliver_ until
713
- // all operations are done
714
- //
715
- // `returns` {Object|*} `this` or calls deliverChangeRecords
716
- sets: function sets(obj, hold) {
717
- var i, k = Object.keys(obj);
718
- for(i = 0; i < k.length; i++) {
719
- k[i].indexOf('.') === -1 ? this.set(k[i], obj[k[i]], true) :
720
- this.setPath(k[i], obj[k[i]], true);
721
- }
722
- if(hold) return this;
723
- return this.deliverChangeRecords();
724
- },
725
- // ###unobserve
726
- // Remove a particular callback from this observable
727
- //
728
- // `param` {Function} the function passed in to `observe`
729
- // `returns` {Object} `this`
730
- unobserve: function unobserve(fn) {
731
- var cb = this.callbacks, i = cb.indexOf(fn);
732
- if(i !== -1) cb.splice(i, 1);
733
- return this;
734
- },
735
- // ###unobserves
736
- // Allow an array of callbacks to be unregistered as changeRecord recipients
737
- //
738
- // `param` {Array} ary
739
- // `returns` {Object} `this`
740
- unobserves: function unobserves(ary) {
741
- var i;
742
- for(i = 0; i < ary.length; i++) {
743
- this.unobserve(ary[i]);
744
- }
745
- return this;
746
- },
747
- // ###unset
748
- // Overrides sudo.Base.unset to check for observers
749
- //
750
- // `param` {String} `key`. The name of the key
751
- // `param` {Bool} `hold`
752
- //
753
- // `returns` {Object|*} `this` or calls deliverChangeRecords
754
- unset: function unset(key, hold) {
755
- var obj = {name: key, object: this.data, type: 'deleted'},
756
- val = !!this.data[key];
757
- delete this.data[key];
758
- // call the observers if there was a val to delete
759
- return this._unset_(obj, val, hold);
760
- },
761
- // ###_unset_
762
- // Helper for the unset functions
763
- //
764
- // `private`
765
- _unset_: function _unset_(o, v, h) {
766
- if(v) {
767
- this.changeRecords.push(o);
768
- if(h) return this;
769
- return this.deliverChangeRecords();
770
- }
771
- return this;
772
- },
773
- // ###setPath
774
- // Overrides sudo.Base.unsetPath to check for observers
775
- //
776
- // `param` {String} `path`
777
- // `param` {*} `value`
778
- // `param` {bool} `hold`
779
- //
780
- // `returns` {Object|*} `this` or calls deliverChangeRecords
781
- unsetPath: function unsetPath(path, hold) {
782
- var obj = {name: path, object: this.data, type: 'deleted'},
783
- curr = this.data, p = path.split('.'),
784
- key, val;
785
- for (key; p.length && (key = p.shift());) {
786
- if(!p.length) {
787
- // reached the last refinement
788
- val = !!curr[key];
789
- delete curr[key];
790
- } else {
791
- // this can obviously fail, but can be prevented by checking
792
- // with `getPath` first.
793
- curr = curr[key];
794
- }
795
- }
796
- return this._unset_(obj, val, hold);
797
- },
798
- // ###unsets
799
- // Override of Base.unsets to hold the call to _deliver_ until done
800
- //
801
- // `param` ary
802
- // `param` hold
803
- // `returns` {Object|*} `this` or calls deliverChangeRecords
804
- unsets: function unsets(ary, hold) {
805
- var i;
806
- for(i = 0; i < ary.length; i++) {
807
- ary[i].indexOf('.') === -1 ? this.unset(k[i], true) :
808
- this.unsetPath(k[i], true);
809
- }
810
- if(hold) return this;
811
- return this.deliverChangeRecords();
812
- }
813
- };
814
- sudo.version = "0.9.4";
815
- window.sudo = sudo;
816
- if(typeof window._ === "undefined") window._ = sudo;
817
- }).call(this, this);
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sudojs-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.3.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-08 00:00:00.000000000 Z
12
+ date: 2013-05-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -79,7 +79,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
79
79
  version: '0'
80
80
  segments:
81
81
  - 0
82
- hash: 67341650352489792
82
+ hash: -4367349649715488954
83
83
  required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
@@ -88,10 +88,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
88
  version: '0'
89
89
  segments:
90
90
  - 0
91
- hash: 67341650352489792
91
+ hash: -4367349649715488954
92
92
  requirements: []
93
93
  rubyforge_project:
94
- rubygems_version: 1.8.25
94
+ rubygems_version: 1.8.24
95
95
  signing_key:
96
96
  specification_version: 3
97
97
  summary: Install and use sudo.js quickly and easily with rails 3.2+.