sudojs-rails 0.1.7 → 0.1.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.
- data/README.md +83 -3
- data/Rakefile +5 -3
- data/lib/sudojs/version.rb +1 -1
- data/vendor/assets/javascripts/es5-sham +25 -0
- data/vendor/assets/javascripts/es5-shim +948 -0
- data/vendor/assets/javascripts/sudo-x.js +341 -262
- data/vendor/assets/javascripts/sudo.js +222 -191
- metadata +6 -4
@@ -11,19 +11,36 @@ var sudo = {
|
|
11
11
|
//
|
12
12
|
// `namespace`
|
13
13
|
ext: {},
|
14
|
-
// ###
|
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
|
15
34
|
// Inherit the prototype from a parent to a child.
|
16
35
|
// Set the childs constructor for subclasses of child.
|
17
|
-
//
|
36
|
+
// Subclasses of the library base classes will not
|
18
37
|
// want to use this function in *most* use-cases. Why? User Sudo Class Objects
|
19
38
|
// possess their own constructors and any call back to a `superclass` constructor
|
20
39
|
// will generally be looking for the library Object's constructor.
|
21
40
|
//
|
22
41
|
// `param` {function} `parent`
|
23
42
|
// `param` {function} `child`
|
24
|
-
|
25
|
-
// `private`
|
26
|
-
_inherit_: function _inherit_(parent, child) {
|
43
|
+
inherit: function inherit(parent, child) {
|
27
44
|
child.prototype = Object.create(parent.prototype);
|
28
45
|
child.prototype.constructor = child;
|
29
46
|
},
|
@@ -34,13 +51,12 @@ var sudo = {
|
|
34
51
|
// `returns` {string}
|
35
52
|
makeMeASandwich: function makeMeASandwich() {return 'Okay.';},
|
36
53
|
// ###namespace
|
37
|
-
//
|
38
|
-
// the optional `obj` arg with the Base objects `setPath` method.
|
54
|
+
// Method for assuring a Namespace is defined.
|
39
55
|
//
|
40
56
|
// `param` {string} `path`. The path that leads to a blank Object.
|
41
57
|
namespace: function namespace(path) {
|
42
|
-
if (!
|
43
|
-
|
58
|
+
if (!this.getPath(path, window)) {
|
59
|
+
this.setPath(path, {}, window);
|
44
60
|
}
|
45
61
|
},
|
46
62
|
// ###premier
|
@@ -48,39 +64,69 @@ var sudo = {
|
|
48
64
|
//
|
49
65
|
// `type` {Object}
|
50
66
|
premier: null,
|
51
|
-
//
|
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
|
52
88
|
// Some sudo Objects use a unique integer as a `tag` for identification.
|
53
89
|
// (Views for example). This ensures they are indeed unique.
|
54
|
-
|
55
|
-
//
|
56
|
-
_uid_: 0,
|
57
|
-
// ####_unique_
|
90
|
+
uid: 0,
|
91
|
+
// ####unique
|
58
92
|
// An integer used as 'tags' by some sudo Objects as well
|
59
93
|
// as a unique string for views when needed
|
60
94
|
//
|
61
95
|
// `param` {string} prefix. Optional string identifier
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
+
}
|
66
116
|
}
|
67
117
|
};
|
68
118
|
// ##Base Class Object
|
69
119
|
//
|
70
|
-
// All sudo.js objects inherit base
|
71
|
-
//
|
72
|
-
// `
|
120
|
+
// All sudo.js objects inherit base, giving the ability
|
121
|
+
// to utilize delegation, the `base` function and the
|
122
|
+
// `construct` convenience method.
|
73
123
|
//
|
74
124
|
// `constructor`
|
75
|
-
sudo.Base = function(
|
76
|
-
this._data_ = data || {};
|
125
|
+
sudo.Base = function() {
|
77
126
|
// can delegate
|
78
|
-
this.
|
79
|
-
// may implement `observable`
|
80
|
-
this._callbacks_ = [];
|
81
|
-
this._changeRecords_ = [];
|
127
|
+
this.delegates = [];
|
82
128
|
// a beautiful and unique snowflake
|
83
|
-
this.
|
129
|
+
this.uid = sudo.unique();
|
84
130
|
};
|
85
131
|
// ###addDelegate
|
86
132
|
// Push an instance of a Class Object into this object's `_delegates_` list.
|
@@ -88,8 +134,8 @@ sudo.Base = function(data) {
|
|
88
134
|
// `param` {Object} an instance of a sudo.delegates Class Object
|
89
135
|
// `returns` {Object} `this`
|
90
136
|
sudo.Base.prototype.addDelegate = function addDelegate(del) {
|
91
|
-
del.
|
92
|
-
this.
|
137
|
+
del.delegator = this;
|
138
|
+
this.delegates.push(del);
|
93
139
|
return this;
|
94
140
|
};
|
95
141
|
// ###base
|
@@ -99,6 +145,7 @@ sudo.Base.prototype.addDelegate = function addDelegate(del) {
|
|
99
145
|
// that the first match is not the function that called `base`.
|
100
146
|
//
|
101
147
|
// `params` {*} any other number of arguments to be passed to the looked up method
|
148
|
+
// along with the initial method name
|
102
149
|
sudo.Base.prototype.base = function base() {
|
103
150
|
var args = Array.prototype.slice.call(arguments),
|
104
151
|
name = args.shift(),
|
@@ -127,28 +174,20 @@ sudo.Base.prototype.construct = function construct() {
|
|
127
174
|
// 1. if `meth` is falsy return the delegate.
|
128
175
|
// 2 if `meth` is truthy bind its method (to the delegate) and return the method
|
129
176
|
//
|
130
|
-
// `param` {String} `role` The
|
177
|
+
// `param` {String} `role` The role property to match in this object's delegates list
|
131
178
|
// `param` {String} `meth` Optional method to bind to the action this delegate is being used for
|
132
179
|
// `returns`
|
133
180
|
sudo.Base.prototype.delegate = function delegate(role, meth) {
|
134
|
-
var del = this.
|
181
|
+
var del = this.delegates, i;
|
135
182
|
for(i = 0; i < del.length; i++) {
|
136
|
-
if(del[i].
|
183
|
+
if(del[i].role === role) {
|
137
184
|
if(!meth) return del[i];
|
138
185
|
return del[i][meth].bind(del[i]);
|
139
186
|
}
|
140
187
|
}
|
141
188
|
};
|
142
|
-
// ###get
|
143
|
-
// Returns the value associated with a key in this object's data store.
|
144
|
-
//
|
145
|
-
// `param` {String} `key`. The name of the key
|
146
|
-
// `returns` {*}. The value associated with the key or false if not found.
|
147
|
-
sudo.Base.prototype.get = function get(key) {
|
148
|
-
return this._data_[key];
|
149
|
-
};
|
150
189
|
// ###getDelegate
|
151
|
-
// Fetch a delegate whose
|
190
|
+
// Fetch a delegate whose role property matches the passed in argument.
|
152
191
|
// Uses the `delegate` method in its 'single argument' form, included for
|
153
192
|
// API consistency
|
154
193
|
//
|
@@ -157,24 +196,59 @@ sudo.Base.prototype.get = function get(key) {
|
|
157
196
|
sudo.Base.prototype.getDelegate = function getDelegate(role) {
|
158
197
|
return this.delegate(role);
|
159
198
|
};
|
160
|
-
// ###
|
161
|
-
//
|
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
|
162
202
|
//
|
163
|
-
// `param` {String} `
|
164
|
-
// `
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
return
|
173
|
-
} else {
|
174
|
-
curr = curr[key] || {};
|
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;
|
175
213
|
}
|
176
214
|
}
|
177
|
-
return
|
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
|
+
this.data = data || {};
|
229
|
+
// only models are `observable`
|
230
|
+
this.callbacks = [];
|
231
|
+
this.changeRecords = [];
|
232
|
+
};
|
233
|
+
// Model inherits from sudo.Base
|
234
|
+
// `private`
|
235
|
+
sudo.inherit(sudo.Base, sudo.Model);
|
236
|
+
// ###get
|
237
|
+
// Returns the value associated with a key.
|
238
|
+
//
|
239
|
+
// `param` {String} `key`. The name of the key
|
240
|
+
// `returns` {*}. The value associated with the key or false if not found.
|
241
|
+
sudo.Model.prototype.get = function get(key) {
|
242
|
+
return this.data[key];
|
243
|
+
};
|
244
|
+
// ###getPath
|
245
|
+
//
|
246
|
+
// Uses the sudo namespace's getpath function operating on the model's
|
247
|
+
// data hash.
|
248
|
+
//
|
249
|
+
// `returns` {*|undefined}. The value at keypath or undefined if not found.
|
250
|
+
sudo.Model.prototype.getPath = function getPath(path) {
|
251
|
+
return sudo.getPath(path, this.data);
|
178
252
|
};
|
179
253
|
// ###gets
|
180
254
|
// Assembles and returns an object of key:value pairs for each key
|
@@ -182,66 +256,38 @@ sudo.Base.prototype.getPath = function getPath(path, obj) {
|
|
182
256
|
//
|
183
257
|
// `param` {array} `ary`. An array of keys.
|
184
258
|
// `returns` {object}
|
185
|
-
sudo.
|
259
|
+
sudo.Model.prototype.gets = function gets(ary) {
|
186
260
|
var i, obj = {};
|
187
261
|
for (i = 0; i < ary.length; i++) {
|
188
|
-
obj[ary[i]] = ary[i].indexOf('.') === -1 ? this.
|
262
|
+
obj[ary[i]] = ary[i].indexOf('.') === -1 ? this.data[ary[i]] :
|
189
263
|
this.getPath(ary[i]);
|
190
264
|
}
|
191
265
|
return obj;
|
192
266
|
};
|
193
|
-
// ###removeDelegate
|
194
|
-
// From this objects `delegates` list remove the object (there should only ever be 1)
|
195
|
-
// whose _role_ matches the passed in argument
|
196
|
-
//
|
197
|
-
// `param` {String} `role`
|
198
|
-
// `returns` {Object} `this`
|
199
|
-
sudo.Base.prototype.removeDelegate = function removeDelegate(role) {
|
200
|
-
var del = this._delegates_, i;
|
201
|
-
for(i = 0; i < del.length; i++) {
|
202
|
-
if(del[i]._role_ === role) {
|
203
|
-
// no _delegator_ for you
|
204
|
-
del[i]._delegator_ = void 0;
|
205
|
-
del.splice(i, 1);
|
206
|
-
return this;
|
207
|
-
}
|
208
|
-
}
|
209
|
-
return this;
|
210
|
-
};
|
211
267
|
// `private`
|
212
|
-
sudo.
|
268
|
+
sudo.Model.prototype.role = 'model';
|
213
269
|
// ###set
|
214
|
-
// Set a key:value pair
|
270
|
+
// Set a key:value pair.
|
215
271
|
//
|
216
272
|
// `param` {String} `key`. The name of the key.
|
217
273
|
// `param` {*} `value`. The value associated with the key.
|
218
274
|
// `returns` {Object} `this`
|
219
|
-
sudo.
|
275
|
+
sudo.Model.prototype.set = function set(key, value) {
|
220
276
|
// _NOTE: intentional possibilty of setting a falsy value_
|
221
|
-
this.
|
277
|
+
this.data[key] = value;
|
222
278
|
return this;
|
223
279
|
};
|
224
280
|
// ###setPath
|
225
|
-
// Traverse the keypath and get each object
|
226
|
-
// (or make blank ones) eventually setting the value
|
227
|
-
// at the end of the path
|
228
281
|
//
|
229
|
-
//
|
230
|
-
//
|
231
|
-
//
|
232
|
-
// `
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
} else if (curr[key]) {
|
239
|
-
curr = curr[key];
|
240
|
-
} else {
|
241
|
-
curr = curr[key] = {};
|
242
|
-
}
|
243
|
-
}
|
244
|
-
return this;
|
282
|
+
// Uses the sudo namespace's setpath function operating on the model's
|
283
|
+
// data hash.
|
284
|
+
//
|
285
|
+
// `param` {String} `path`
|
286
|
+
// `param` {*} `value`
|
287
|
+
// `returns` {Object} this.
|
288
|
+
sudo.Model.prototype.setPath = function setPath(path, value) {
|
289
|
+
sudo.setPath(path, value, this.data);
|
290
|
+
return this;
|
245
291
|
};
|
246
292
|
// ###sets
|
247
293
|
// Invokes `set()` or `setPath()` for each key value pair in `obj`.
|
@@ -249,7 +295,7 @@ sudo.Base.prototype.setPath = function setPath(path, value, obj) {
|
|
249
295
|
//
|
250
296
|
// `param` {Object} `obj`. The keys and values to set.
|
251
297
|
// `returns` {Object} `this`
|
252
|
-
sudo.
|
298
|
+
sudo.Model.prototype.sets = function sets(obj) {
|
253
299
|
var i, k = Object.keys(obj);
|
254
300
|
for(i = 0; i < k.length; i++) {
|
255
301
|
k[i].indexOf('.') === -1 ? this.set(k[i], obj[k[i]]) :
|
@@ -262,35 +308,25 @@ sudo.Base.prototype.sets = function sets(obj) {
|
|
262
308
|
//
|
263
309
|
// `param` {String} key
|
264
310
|
// `returns` {Object} `this`
|
265
|
-
sudo.
|
266
|
-
delete this.
|
311
|
+
sudo.Model.prototype.unset = function unset(key) {
|
312
|
+
delete this.data[key];
|
267
313
|
return this;
|
268
314
|
};
|
269
315
|
// ###unsetPath
|
270
|
-
//
|
271
|
-
// located at <path>
|
316
|
+
// Uses `sudo.unsetPath` operating on this models data hash
|
272
317
|
//
|
273
318
|
// `param` {String} path
|
274
319
|
// `returns` {Object} `this`
|
275
|
-
sudo.
|
276
|
-
|
277
|
-
|
278
|
-
if(!p.length) {
|
279
|
-
delete curr[key];
|
280
|
-
} else {
|
281
|
-
// this can fail if a faulty path is passed.
|
282
|
-
// using getPath beforehand can prevent that
|
283
|
-
curr = curr[key];
|
284
|
-
}
|
285
|
-
}
|
286
|
-
return this;
|
320
|
+
sudo.Model.prototype.unsetPath = function unsetPath(path) {
|
321
|
+
sudo.unsetPath(path, this.data);
|
322
|
+
return this;
|
287
323
|
};
|
288
324
|
// ###unsets
|
289
325
|
// Deletes a number of keys or paths from this object's data store
|
290
326
|
//
|
291
327
|
// `param` {array} `ary`. An array of keys or paths.
|
292
328
|
// `returns` {Objaect} `this`
|
293
|
-
sudo.
|
329
|
+
sudo.Model.prototype.unsets = function unsets(ary) {
|
294
330
|
var i;
|
295
331
|
for(i = 0; i < ary.length; i++) {
|
296
332
|
ary[i].indexOf('.') === -1 ? this.unset(ary[i]) :
|
@@ -304,13 +340,13 @@ sudo.Base.prototype.unsets = function unsets(ary) {
|
|
304
340
|
// itself be contained
|
305
341
|
//
|
306
342
|
// `constructor`
|
307
|
-
sudo.Container = function(
|
308
|
-
sudo.Base.call(this
|
309
|
-
this.
|
310
|
-
this.
|
343
|
+
sudo.Container = function() {
|
344
|
+
sudo.Base.call(this);
|
345
|
+
this.children = [];
|
346
|
+
this.childNames = {};
|
311
347
|
};
|
312
|
-
//
|
313
|
-
sudo.
|
348
|
+
// Container is a subclass of sudo.Base
|
349
|
+
sudo.inherit(sudo.Base, sudo.Container);
|
314
350
|
// ###addChild
|
315
351
|
// Adds a View to this container's list of children.
|
316
352
|
// Also adds an 'index' property and an entry in the childNames hash.
|
@@ -320,12 +356,12 @@ sudo._inherit_(sudo.Base, sudo.Container);
|
|
320
356
|
// `param` {String} `name`. An optional name for the child that will go in the childNames hash.
|
321
357
|
// `returns` {Object} `this`
|
322
358
|
sudo.Container.prototype.addChild = function addChild(child, name) {
|
323
|
-
var c = this.
|
324
|
-
child.
|
325
|
-
child.
|
359
|
+
var c = this.children;
|
360
|
+
child.parent = this;
|
361
|
+
child.index = c.length;
|
326
362
|
if(name) {
|
327
|
-
child.
|
328
|
-
this.
|
363
|
+
child.name = name;
|
364
|
+
this.childNames[name] = child.index;
|
329
365
|
}
|
330
366
|
c.push(child);
|
331
367
|
if('addedToParent' in child) child.addedToParent(this);
|
@@ -335,7 +371,7 @@ sudo.Container.prototype.addChild = function addChild(child, name) {
|
|
335
371
|
// By default, `bubble` returns the current view's parent (if it has one)
|
336
372
|
//
|
337
373
|
// `returns` {Object|undefined}
|
338
|
-
sudo.Container.prototype.bubble = function bubble() {return this.
|
374
|
+
sudo.Container.prototype.bubble = function bubble() {return this.parent;};
|
339
375
|
// ###getChild
|
340
376
|
// If a child was added with a name, via `addChild`,
|
341
377
|
// that object can be fetched by name. This prevents us from having to reference a
|
@@ -344,8 +380,8 @@ sudo.Container.prototype.bubble = function bubble() {return this._parent_;};
|
|
344
380
|
// `param` {String|Number} `id`. The string `name` or numeric `index` of the child to fetch.
|
345
381
|
// `returns` {Object|undefined} The found child
|
346
382
|
sudo.Container.prototype.getChild = function getChild(id) {
|
347
|
-
return typeof id === 'string' ? this.
|
348
|
-
this.
|
383
|
+
return typeof id === 'string' ? this.children[this.childNames[id]] :
|
384
|
+
this.children[id];
|
349
385
|
};
|
350
386
|
// ###_indexChildren_
|
351
387
|
// Method is called with the `index` property of a subview that is being removed.
|
@@ -353,11 +389,11 @@ sudo.Container.prototype.getChild = function getChild(id) {
|
|
353
389
|
// `param` {Number} `i`
|
354
390
|
// `private`
|
355
391
|
sudo.Container.prototype._indexChildren_ = function _indexChildren_(i) {
|
356
|
-
var c = this.
|
392
|
+
var c = this.children, obj = this.childNames, len;
|
357
393
|
for (len = c.length; i < len; i++) {
|
358
|
-
c[i].
|
394
|
+
c[i].index--;
|
359
395
|
// adjust any entries in childNames
|
360
|
-
if(c[i].
|
396
|
+
if(c[i].name in obj) obj[c[i].name] = c[i].index;
|
361
397
|
}
|
362
398
|
};
|
363
399
|
// ###removeChild
|
@@ -370,28 +406,19 @@ sudo.Container.prototype._indexChildren_ = function _indexChildren_(i) {
|
|
370
406
|
// An object will be assumed to be an actual sudo Class Object.
|
371
407
|
// `returns` {Object} `this`
|
372
408
|
sudo.Container.prototype.removeChild = function removeChild(arg) {
|
373
|
-
var t = typeof arg, c;
|
374
|
-
//
|
375
|
-
if(t === 'object')
|
376
|
-
else
|
377
|
-
|
378
|
-
this._removeChild_(c);
|
379
|
-
}
|
380
|
-
return this;
|
381
|
-
};
|
382
|
-
// ###_removeChild_
|
383
|
-
// Helper method for 'public' removeChild
|
384
|
-
// `param` {Object} `child`. The view that is going to be removed.
|
385
|
-
// `private`
|
386
|
-
sudo.Container.prototype._removeChild_ = function _removeChild_(child) {
|
387
|
-
var i = child._index_;
|
409
|
+
var i, t = typeof arg, c;
|
410
|
+
// normalize the input
|
411
|
+
if(t === 'object') c = arg;
|
412
|
+
else c = t === 'string' ? this.children[this.childNames[arg]] : this.children[arg];
|
413
|
+
i = c.index;
|
388
414
|
// remove from the children Array
|
389
|
-
this.
|
415
|
+
this.children.splice(i, 1);
|
390
416
|
// remove from the named child hash if present
|
391
|
-
delete this.
|
417
|
+
delete this.childNames[c.name];
|
392
418
|
// child is now an `orphan`
|
393
|
-
delete
|
419
|
+
delete c.parent;
|
394
420
|
this._indexChildren_(i);
|
421
|
+
return this;
|
395
422
|
};
|
396
423
|
// ###removeFromParent
|
397
424
|
// Remove this object from its parents list of children.
|
@@ -399,10 +426,10 @@ sudo.Container.prototype._removeChild_ = function _removeChild_(child) {
|
|
399
426
|
// or chaining method calls
|
400
427
|
sudo.Container.prototype.removeFromParent = function removeFromParent() {
|
401
428
|
// will error without a parent, but that would be your fault...
|
402
|
-
this.
|
429
|
+
this.parent.removeChild(this);
|
403
430
|
return this;
|
404
431
|
};
|
405
|
-
sudo.Container.prototype.
|
432
|
+
sudo.Container.prototype.role = 'container';
|
406
433
|
// ###send
|
407
434
|
// The call to the specific method on a (un)specified target happens here.
|
408
435
|
// If this Object is part of a `sudo.ext.container` maintained hierarchy
|
@@ -414,14 +441,14 @@ sudo.Container.prototype._role_ = 'container';
|
|
414
441
|
// A sendMethod will be located by:
|
415
442
|
// 1. using the first argument if it is a string
|
416
443
|
// 2. looking for a `sendMethod` property if it is an object
|
417
|
-
// In the case a specified target exists at `this.get('sendTarget')` it will be used
|
444
|
+
// In the case a specified target exists at `this.model.get('sendTarget')` it will be used
|
418
445
|
// Any other args will be passed to the sendMethod after `this`
|
419
446
|
// `returns` {Object} `this`
|
420
447
|
sudo.Container.prototype.send = function send(/*args*/) {
|
421
448
|
var args = Array.prototype.slice.call(arguments),
|
422
|
-
meth, targ, fn;
|
449
|
+
d = this.model && this.model.data, meth, targ, fn;
|
423
450
|
// normalize the input, common use cases first
|
424
|
-
if('sendMethod' in
|
451
|
+
if(d && 'sendMethod' in d) meth = d.sendMethod;
|
425
452
|
else if(typeof args[0] === 'string') meth = args.shift();
|
426
453
|
// less common but viable options
|
427
454
|
if(!meth) {
|
@@ -431,7 +458,7 @@ sudo.Container.prototype.send = function send(/*args*/) {
|
|
431
458
|
args[0].sendMethod || void 0;
|
432
459
|
}
|
433
460
|
// target is either specified or my parent
|
434
|
-
targ =
|
461
|
+
targ = d && d.sendTarget || this.parent;
|
435
462
|
// obvious chance for errors here, don't be dumb
|
436
463
|
fn = targ[meth];
|
437
464
|
while(!fn && (targ = targ.bubble())) {
|
@@ -459,17 +486,19 @@ sudo.Container.prototype.send = function send(/*args*/) {
|
|
459
486
|
// a shortcut for `this.$el.find(selector)`
|
460
487
|
//
|
461
488
|
// `param` {string|element|jQuery} `el`. Otional el for the View instance.
|
462
|
-
// `param` {Object} `data`. Optional data object
|
489
|
+
// `param` {Object} `data`. Optional data object which becomes the initial state
|
490
|
+
// of a new model located at `this.model`.
|
463
491
|
//
|
464
492
|
// `constructor`
|
465
493
|
sudo.View = function(el, data) {
|
466
|
-
sudo.Container.call(this
|
494
|
+
sudo.Container.call(this);
|
495
|
+
if(data) this.model = new sudo.Model(data);
|
467
496
|
this.setEl(el);
|
468
|
-
if(this.
|
497
|
+
if(this.role === 'view') this.init();
|
469
498
|
};
|
470
499
|
// View inherits from Container
|
471
500
|
// `private`
|
472
|
-
sudo.
|
501
|
+
sudo.inherit(sudo.Container, sudo.View);
|
473
502
|
// ###becomePremier
|
474
503
|
// Premier functionality provides hooks for behavioral differentiation
|
475
504
|
// among elements or class objects.
|
@@ -477,11 +506,11 @@ sudo._inherit_(sudo.Container, sudo.View);
|
|
477
506
|
// `returns` {Object} `this`
|
478
507
|
sudo.View.prototype.becomePremier = function becomePremier() {
|
479
508
|
var p, f = function() {
|
480
|
-
this.
|
509
|
+
this.isPremier = true;
|
481
510
|
sudo.premier = this;
|
482
511
|
}.bind(this);
|
483
512
|
// is there an existing premier that isn't me?
|
484
|
-
if((p = sudo.premier) && p.
|
513
|
+
if((p = sudo.premier) && p.uid !== this.uid) {
|
485
514
|
// ask it to resign and call the cb
|
486
515
|
p.resignPremier(f);
|
487
516
|
} else f(); // no existing premier
|
@@ -510,9 +539,9 @@ sudo.View.prototype._normalizedEl_ = function _normalizedEl_(el) {
|
|
510
539
|
// `returns` {Object} `this`
|
511
540
|
sudo.View.prototype.resignPremier = function resignPremier(cb) {
|
512
541
|
var p;
|
513
|
-
this.
|
542
|
+
this.isPremier = false;
|
514
543
|
// only remove the global premier if it is me
|
515
|
-
if((p = sudo.premier) && p.
|
544
|
+
if((p = sudo.premier) && p.uid === this.uid) {
|
516
545
|
sudo.premier = null;
|
517
546
|
}
|
518
547
|
// fire the cb if passed
|
@@ -520,7 +549,7 @@ sudo.View.prototype.resignPremier = function resignPremier(cb) {
|
|
520
549
|
return this;
|
521
550
|
};
|
522
551
|
// `private`
|
523
|
-
sudo.View.prototype.
|
552
|
+
sudo.View.prototype.role = 'view';
|
524
553
|
// ###setEl
|
525
554
|
// A view must have an element, set that here.
|
526
555
|
// Stores a jquerified object as `this.$el` the raw
|
@@ -529,12 +558,12 @@ sudo.View.prototype._role_ = 'view';
|
|
529
558
|
// `param` {string=|element} `el`
|
530
559
|
// `returns` {Object} `this`
|
531
560
|
sudo.View.prototype.setEl = function setEl(el) {
|
532
|
-
var a, t;
|
561
|
+
var d = this.model && this.model.data, a, t;
|
533
562
|
if(!el) {
|
534
563
|
// normalize any relevant data
|
535
|
-
t =
|
564
|
+
t = d ? d.tagName || 'div': 'div';
|
536
565
|
this.$el = $(document.createElement(t));
|
537
|
-
if((a =
|
566
|
+
if(d && (a = d.attributes)) this.$el.attr(a);
|
538
567
|
} else {
|
539
568
|
this.$el = this._normalizedEl_(el);
|
540
569
|
}
|
@@ -587,14 +616,16 @@ sudo.ViewController = function(el, data) {
|
|
587
616
|
'ajax:aborted:file': 'onAjaxAbortedFile'
|
588
617
|
};
|
589
618
|
// can be called again if mapping changes...
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
619
|
+
if(data) {
|
620
|
+
this.doMapping();
|
621
|
+
if('descriptor' in data) this.instantiateChildren([data.descriptor]);
|
622
|
+
else if('descriptors' in data) this.instantiateChildren();
|
623
|
+
}
|
624
|
+
if(this.role === 'viewController') this.init();
|
594
625
|
};
|
595
626
|
// ViewController inherits from View.
|
596
627
|
// `private`
|
597
|
-
sudo.
|
628
|
+
sudo.inherit(sudo.View, sudo.ViewController);
|
598
629
|
// ###doMapping
|
599
630
|
//
|
600
631
|
// assign the proxy mapping for events. This can be called at any time
|
@@ -604,12 +635,12 @@ sudo._inherit_(sudo.View, sudo.ViewController);
|
|
604
635
|
sudo.ViewController.prototype.doMapping = function() {
|
605
636
|
// either a single event or an array of them
|
606
637
|
var i,
|
607
|
-
toMap = this.
|
638
|
+
toMap = this.model.data.ujsEvent || this.model.data.ujsEvents;
|
608
639
|
if(toMap) {
|
609
|
-
if(typeof toMap === 'string') this.
|
640
|
+
if(typeof toMap === 'string') this._mapEvent_(toMap);
|
610
641
|
else {
|
611
642
|
for(i = 0; i < toMap.length; i++) {
|
612
|
-
this.
|
643
|
+
this._mapEvent_(toMap[i]);
|
613
644
|
}
|
614
645
|
}
|
615
646
|
}
|
@@ -628,7 +659,7 @@ sudo.ViewController.prototype._handleObserve_ = function _handleObserve_(obs, c)
|
|
628
659
|
//
|
629
660
|
// `returns` {object} `this`
|
630
661
|
sudo.ViewController.prototype.instantiateChildren = function instantiateChildren(ary) {
|
631
|
-
var i, j, curr, c, d = ary || this.
|
662
|
+
var i, j, curr, c, d = ary || this.model.data.descriptors;
|
632
663
|
for(i = 0; i < d.length; i++) {
|
633
664
|
curr = d[i];
|
634
665
|
c = new curr.is_a(curr.el, curr.data);
|
@@ -645,10 +676,10 @@ sudo.ViewController.prototype.instantiateChildren = function instantiateChildren
|
|
645
676
|
}
|
646
677
|
return this;
|
647
678
|
};
|
648
|
-
// ###
|
679
|
+
// ###_mapEvent_
|
649
680
|
// Maps the ajax:event names to methods
|
650
681
|
// `private`
|
651
|
-
sudo.ViewController.prototype.
|
682
|
+
sudo.ViewController.prototype._mapEvent_ = function _mapEvent_(name) {
|
652
683
|
// because the signatures vary we need specific methods
|
653
684
|
this.$el.on(name, this[this.eventMap[name]].bind(this));
|
654
685
|
};
|
@@ -658,7 +689,7 @@ sudo.ViewController.prototype._mapEvent = function(name) {
|
|
658
689
|
// when viewController's are instantiated.
|
659
690
|
// `private`
|
660
691
|
sudo.ViewController.prototype._objectForPath_ = function _objectForPath_(path) {
|
661
|
-
return sudo.
|
692
|
+
return sudo.getPath(path, window);
|
662
693
|
};
|
663
694
|
// Virtual methods to override in your child classes for
|
664
695
|
// any events you chose to listen for
|
@@ -670,7 +701,7 @@ sudo.ViewController.prototype.onAjaxComplete = $.noop;
|
|
670
701
|
sudo.ViewController.prototype.onAjaxSuccess = $.noop;
|
671
702
|
sudo.ViewController.prototype.onAjaxError = $.noop;
|
672
703
|
// `private`
|
673
|
-
sudo.ViewController.prototype.
|
704
|
+
sudo.ViewController.prototype.role = 'viewController';
|
674
705
|
// ###Templating
|
675
706
|
|
676
707
|
// Allow the default {{ js code }}, {{= key }}, and {{- escape stuff }}
|
@@ -789,26 +820,29 @@ sudo.template = function template(str, data, scope) {
|
|
789
820
|
//
|
790
821
|
//`constructor`
|
791
822
|
sudo.Dataview = function(el, data) {
|
792
|
-
var d, t;
|
793
|
-
sudo.View.call(this, el,
|
823
|
+
var d = data || {}, t;
|
824
|
+
sudo.View.call(this, el, d);
|
794
825
|
// implements the listener extension
|
795
826
|
$.extend(this, sudo.ext.listener);
|
796
|
-
|
797
|
-
|
827
|
+
// dataview's models are observable
|
828
|
+
$.extend(this.model, sudo.ext.observable);
|
829
|
+
// dont autoRender on the setting of events,
|
798
830
|
// add to this to prevent others if needed
|
799
|
-
|
831
|
+
this.autoRenderBlacklist = {event: true, events: true};
|
832
|
+
// if autorendering, observe your own model
|
833
|
+
// use this ref to unobserve if desired
|
834
|
+
if(d.autoRender) this.observer = this.model.observe(this.render.bind(this));
|
800
835
|
// compile my template if not already done
|
801
836
|
if((t = d.template)) {
|
802
|
-
if(typeof t === 'string')
|
837
|
+
if(typeof t === 'string') this.model.data.template = sudo.template(t);
|
803
838
|
}
|
804
|
-
if(this.
|
805
|
-
// as all events are delegated to this.$el binding can take place here
|
839
|
+
if(this.role === 'dataview') {
|
806
840
|
this.bindEvents();
|
807
841
|
this.init();
|
808
842
|
}
|
809
843
|
};
|
810
844
|
// `private`
|
811
|
-
sudo.
|
845
|
+
sudo.inherit(sudo.View, sudo.Dataview);
|
812
846
|
// ###addedToParent
|
813
847
|
// Container's will check for the presence of this method and call it if it is present
|
814
848
|
// after adding a child - essentially, this will auto render the dataview when added to a parent
|
@@ -821,7 +855,7 @@ sudo.Dataview.prototype.addedToParent = function() {
|
|
821
855
|
//
|
822
856
|
// `returns` {Object} `this`
|
823
857
|
sudo.Dataview.prototype.removeFromParent = function removeFromParent() {
|
824
|
-
this.
|
858
|
+
this.parent.removeChild(this);
|
825
859
|
this.$el.remove();
|
826
860
|
return this;
|
827
861
|
};
|
@@ -833,9 +867,13 @@ sudo.Dataview.prototype.removeFromParent = function removeFromParent() {
|
|
833
867
|
// Event unbinding/rebinding is generally not necessary for the Objects innerHTML as all events from the
|
834
868
|
// Object's list of events (`this.get('event(s)'))` are delegated to the $el on instantiation.
|
835
869
|
//
|
870
|
+
// `param` {object} `change` dataviews may be observing their model if `autoRender: true`
|
871
|
+
//
|
836
872
|
// `returns` {Object} `this`
|
837
|
-
sudo.Dataview.prototype.render = function render() {
|
838
|
-
|
873
|
+
sudo.Dataview.prototype.render = function render(change) {
|
874
|
+
// return early if a `blacklisted` key is set to my model
|
875
|
+
if(change && this.autoRenderBlacklist[change.name]) return this;
|
876
|
+
var d = this.model.data;
|
839
877
|
this.$el.html(d.template(d));
|
840
878
|
if(d.renderTarget) {
|
841
879
|
this._normalizedEl_(d.renderTarget)[d.renderMethod || 'append'](this.$el);
|
@@ -844,48 +882,11 @@ sudo.Dataview.prototype.render = function render() {
|
|
844
882
|
return this;
|
845
883
|
};
|
846
884
|
// `private`
|
847
|
-
sudo.Dataview.prototype.
|
848
|
-
// ###set
|
849
|
-
// Override `sudo.Base.set` to provide auto rendering if desired
|
850
|
-
//
|
851
|
-
// `returns` {Object|*} `this` or call to `render`
|
852
|
-
sudo.Dataview.prototype.set = function set(key, value) {
|
853
|
-
this._data_[key] = value;
|
854
|
-
if(this._data_['autoRender'] && !this._data_.autoRenderBlacklist[key]) return this.render();
|
855
|
-
return this;
|
856
|
-
};
|
857
|
-
// ###setPath
|
858
|
-
// Override `sudo.Base.setPath` to provide auto rendering if desired
|
859
|
-
//
|
860
|
-
// `returns` {Object|*} `this` or call to `render`
|
861
|
-
sudo.Dataview.prototype.setPath = function setPath(path, value) {
|
862
|
-
sudo.Base.prototype.setPath.call(this, path, value);
|
863
|
-
// the blacklist has no paths
|
864
|
-
if(this._data_['autoRender']) return this.render();
|
865
|
-
return this;
|
866
|
-
};
|
867
|
-
// ###sets
|
868
|
-
// Override `sudo.Base.sets` in case of autoRender.being true
|
869
|
-
// Temporarily sets `autoRender` to false (if true) to avoid unnecessary
|
870
|
-
// rendering, calling `render` when finished with all `set` type operations,
|
871
|
-
// returning `autoRender` to true (if appropriate)
|
872
|
-
//
|
873
|
-
// `returns` {Object|*} `this` or call to `render`
|
874
|
-
sudo.Dataview.prototype.sets = function sets(obj) {
|
875
|
-
var a;
|
876
|
-
if(this._data_['autoRender']) {
|
877
|
-
this._data_['autoRender'] = false;
|
878
|
-
a = true;
|
879
|
-
}
|
880
|
-
sudo.Base.prototype.sets.call(this, obj);
|
881
|
-
if(a) {
|
882
|
-
this._data_['autoRender'] = true;
|
883
|
-
return this.render();
|
884
|
-
}
|
885
|
-
return this;
|
886
|
-
};
|
885
|
+
sudo.Dataview.prototype.role = 'dataview';
|
887
886
|
// ## Observable Extension Object
|
888
|
-
// Implementaion of the ES6 Harmony Observer pattern
|
887
|
+
// Implementaion of the ES6 Harmony Observer pattern.
|
888
|
+
// Extend a `sudo.Model` class with this object if
|
889
|
+
// data-mutation-observation is required
|
889
890
|
sudo.ext.observable = {
|
890
891
|
// ###_deliver_
|
891
892
|
// Called from deliverChangeRecords when ready to send
|
@@ -893,7 +894,7 @@ sudo.ext.observable = {
|
|
893
894
|
//
|
894
895
|
// `private`
|
895
896
|
_deliver_: function _deliver_(obj) {
|
896
|
-
var i, cb = this.
|
897
|
+
var i, cb = this.callbacks;
|
897
898
|
for(i = 0; i < cb.length; i++) {
|
898
899
|
cb[i](obj);
|
899
900
|
}
|
@@ -904,7 +905,7 @@ sudo.ext.observable = {
|
|
904
905
|
//
|
905
906
|
// `returns` {Object} `this`
|
906
907
|
deliverChangeRecords: function deliverChangeRecords() {
|
907
|
-
var rec, cr = this.
|
908
|
+
var rec, cr = this.changeRecords;
|
908
909
|
// FIFO
|
909
910
|
for(rec; cr.length && (rec = cr.shift());) {
|
910
911
|
this._deliver_(rec);
|
@@ -929,7 +930,7 @@ sudo.ext.observable = {
|
|
929
930
|
observe: function observe(fn) {
|
930
931
|
// this will fail if mixed-in and no `callbacks` created so don't do that.
|
931
932
|
// Per the spec, do not allow the same callback to be added
|
932
|
-
var d = this.
|
933
|
+
var d = this.callbacks;
|
933
934
|
if(d.indexOf(fn) === -1) d.push(fn);
|
934
935
|
return fn;
|
935
936
|
},
|
@@ -955,16 +956,16 @@ sudo.ext.observable = {
|
|
955
956
|
//
|
956
957
|
// `returns` {Object|*} `this` or calls deliverChangeRecords
|
957
958
|
set: function set(key, value, hold) {
|
958
|
-
var obj = {name: key, object: this.
|
959
|
+
var obj = {name: key, object: this.data};
|
959
960
|
// did this key exist already
|
960
|
-
if(key in this.
|
961
|
+
if(key in this.data) {
|
961
962
|
obj.type = 'updated';
|
962
963
|
// then there is an oldValue
|
963
|
-
obj.oldValue = this.
|
964
|
+
obj.oldValue = this.data[key];
|
964
965
|
} else obj.type = 'new';
|
965
966
|
// now actually set the value
|
966
|
-
this.
|
967
|
-
this.
|
967
|
+
this.data[key] = value;
|
968
|
+
this.changeRecords.push(obj);
|
968
969
|
// call the observers or not
|
969
970
|
if(hold) return this;
|
970
971
|
return this.deliverChangeRecords();
|
@@ -973,7 +974,7 @@ sudo.ext.observable = {
|
|
973
974
|
// Overrides sudo.Base.setPath to check for observers.
|
974
975
|
// Change records originating from a `setPath` operation
|
975
976
|
// send back the passed in `path` as `name` as well as the
|
976
|
-
// top level object being observed (this observable's
|
977
|
+
// top level object being observed (this observable's data).
|
977
978
|
// this allows for easy filtering either manually or via a
|
978
979
|
// `change delegate`
|
979
980
|
//
|
@@ -983,7 +984,7 @@ sudo.ext.observable = {
|
|
983
984
|
// to be delivered upon a call to deliverChangeRecords (truthy)
|
984
985
|
// `returns` {Object|*} `this` or calls deliverChangeRecords
|
985
986
|
setPath: function setPath(path, value, hold) {
|
986
|
-
var curr = this.
|
987
|
+
var curr = this.data, obj = {name: path, object: this.data},
|
987
988
|
p = path.split('.'), key;
|
988
989
|
for (key; p.length && (key = p.shift());) {
|
989
990
|
if(!p.length) {
|
@@ -999,7 +1000,7 @@ sudo.ext.observable = {
|
|
999
1000
|
curr = curr[key] = {};
|
1000
1001
|
}
|
1001
1002
|
}
|
1002
|
-
this.
|
1003
|
+
this.changeRecords.push(obj);
|
1003
1004
|
// call all observers or not
|
1004
1005
|
if(hold) return this;
|
1005
1006
|
return this.deliverChangeRecords();
|
@@ -1024,7 +1025,7 @@ sudo.ext.observable = {
|
|
1024
1025
|
// `param` {Function} the function passed in to `observe`
|
1025
1026
|
// `returns` {Object} `this`
|
1026
1027
|
unobserve: function unobserve(fn) {
|
1027
|
-
var cb = this.
|
1028
|
+
var cb = this.callbacks, i = cb.indexOf(fn);
|
1028
1029
|
if(i !== -1) cb.splice(i, 1);
|
1029
1030
|
return this;
|
1030
1031
|
},
|
@@ -1048,9 +1049,9 @@ sudo.ext.observable = {
|
|
1048
1049
|
//
|
1049
1050
|
// `returns` {Object|*} `this` or calls deliverChangeRecords
|
1050
1051
|
unset: function unset(key, hold) {
|
1051
|
-
var obj = {name: key, object: this.
|
1052
|
-
val = !!this.
|
1053
|
-
delete this.
|
1052
|
+
var obj = {name: key, object: this.data, type: 'deleted'},
|
1053
|
+
val = !!this.data[key];
|
1054
|
+
delete this.data[key];
|
1054
1055
|
// call the observers if there was a val to delete
|
1055
1056
|
return this._unset_(obj, val, hold);
|
1056
1057
|
},
|
@@ -1060,7 +1061,7 @@ sudo.ext.observable = {
|
|
1060
1061
|
// `private`
|
1061
1062
|
_unset_: function _unset_(o, v, h) {
|
1062
1063
|
if(v) {
|
1063
|
-
this.
|
1064
|
+
this.changeRecords.push(o);
|
1064
1065
|
if(h) return this;
|
1065
1066
|
return this.deliverChangeRecords();
|
1066
1067
|
}
|
@@ -1075,8 +1076,8 @@ sudo.ext.observable = {
|
|
1075
1076
|
//
|
1076
1077
|
// `returns` {Object|*} `this` or calls deliverChangeRecords
|
1077
1078
|
unsetPath: function unsetPath(path, hold) {
|
1078
|
-
var obj = {name: path, object: this.
|
1079
|
-
curr = this.
|
1079
|
+
var obj = {name: path, object: this.data, type: 'deleted'},
|
1080
|
+
curr = this.data, p = path.split('.'),
|
1080
1081
|
key, val;
|
1081
1082
|
for (key; p.length && (key = p.shift());) {
|
1082
1083
|
if(!p.length) {
|
@@ -1233,7 +1234,7 @@ sudo.ext.bindable = {
|
|
1233
1234
|
//
|
1234
1235
|
// `returns` {Object} `this`
|
1235
1236
|
setBinding: function setBinding() {
|
1236
|
-
return this._setBinding_(this.
|
1237
|
+
return this._setBinding_(this.model.data.binding);
|
1237
1238
|
},
|
1238
1239
|
// ###_setBinding_
|
1239
1240
|
// Given a single explicit binding, create it. Called from
|
@@ -1255,10 +1256,10 @@ sudo.ext.bindable = {
|
|
1255
1256
|
//
|
1256
1257
|
// `returns` {Object} `this`
|
1257
1258
|
setBindings: function setBindings() {
|
1258
|
-
var b, i;
|
1259
|
+
var d = this.model.data, b, i;
|
1259
1260
|
// handle the single binding use case
|
1260
|
-
if((b =
|
1261
|
-
if(!(b =
|
1261
|
+
if((b = d.binding)) return this._setBinding_(b);
|
1262
|
+
if(!(b = d.bindings)) return this;
|
1262
1263
|
for(i = 0; i < b.length; i++) {
|
1263
1264
|
this._setBinding_(b[i]);
|
1264
1265
|
}
|
@@ -1300,7 +1301,7 @@ sudo.ext.listener = {
|
|
1300
1301
|
// `returns` {Object} `this`
|
1301
1302
|
bindEvents: function bindEvents() {
|
1302
1303
|
var e;
|
1303
|
-
if((e = this.
|
1304
|
+
if((e = this.model.data.event || this.model.data.events)) this._handleEvents_(e, 1);
|
1304
1305
|
return this;
|
1305
1306
|
},
|
1306
1307
|
// Use the jQuery `on` or 'off' method, optionally delegating to a selector if present
|
@@ -1333,12 +1334,90 @@ sudo.ext.listener = {
|
|
1333
1334
|
// `returns` {Object} `this`
|
1334
1335
|
unbindEvents: function unbindEvents() {
|
1335
1336
|
var e;
|
1336
|
-
if((e = this.
|
1337
|
+
if((e = this.model.data.event || this.model.data.events)) this._handleEvents_(e);
|
1337
1338
|
return this;
|
1338
1339
|
}
|
1339
1340
|
};
|
1341
|
+
//##Change Delegate
|
1342
|
+
|
1343
|
+
// Delegates, if present, can override or extend the behavior
|
1344
|
+
// of objects. The change delegate is specifically designed to
|
1345
|
+
// filter change records from an Observable instance and only forward
|
1346
|
+
// the ones matching a given `filters` criteria (key or path).
|
1347
|
+
// The forwarded messages will be sent to the specified method
|
1348
|
+
// on the delegates `delegator` (bound to the _delegator_ scope)
|
1349
|
+
//
|
1350
|
+
// `param` {Object} data
|
1351
|
+
sudo.delegates.Change = function(data) {
|
1352
|
+
this.construct(data);
|
1353
|
+
};
|
1354
|
+
// Delegates inherit from Model
|
1355
|
+
sudo.delegates.Change.prototype = Object.create(sudo.Model.prototype);
|
1356
|
+
// Change records are delivered here and filtered, calling any matching
|
1357
|
+
// methods specified in `this.get('filters').
|
1358
|
+
//
|
1359
|
+
// `returns` {Object} a call to the specified _delegator_ method, passing
|
1360
|
+
// a hash containing:
|
1361
|
+
// 1. the `type` of Change
|
1362
|
+
// 2. the value located at the key/path
|
1363
|
+
// 3. the `oldValue` of the key if present
|
1364
|
+
sudo.delegates.Change.prototype.filter = function(change) {
|
1365
|
+
var filters = this.data.filters, name = change.name, obj = {};
|
1366
|
+
// does my delegator care about this?
|
1367
|
+
if(name in filters && filters.hasOwnProperty(name)) {
|
1368
|
+
// assemble the object to return to the method
|
1369
|
+
obj.type = change.type;
|
1370
|
+
obj.value = name.indexOf('.') === -1 ? change.object[change.name] :
|
1371
|
+
this.getPath(name, change.object);
|
1372
|
+
obj.oldValue = change.oldValue;
|
1373
|
+
return this.delegator[filters[name]].call(this.delegator, obj);
|
1374
|
+
}
|
1375
|
+
};
|
1376
|
+
// `private`
|
1377
|
+
sudo.delegates.Change.prototype.role = 'change';
|
1378
|
+
//##Data Delegate
|
1379
|
+
|
1380
|
+
// Delegates, if present, can extend the behavior
|
1381
|
+
// of objects, lessening the need for subclassing.
|
1382
|
+
// The data delegate is specifically designed to
|
1383
|
+
// filter through an object, looking for specified keys or paths
|
1384
|
+
// and returning values for those if found
|
1385
|
+
//
|
1386
|
+
// `param` {Object} data
|
1387
|
+
// `returns` {*} the value found at the specified key/path if found
|
1388
|
+
sudo.delegates.Data = function(data) {
|
1389
|
+
this.construct(data);
|
1390
|
+
};
|
1391
|
+
// inherits from Model
|
1392
|
+
sudo.delegates.Data.prototype = Object.create(sudo.Model.prototype);
|
1393
|
+
// ###filter
|
1394
|
+
// iterates over a given object literal and returns a value (if present)
|
1395
|
+
// located at a given key or path
|
1396
|
+
//
|
1397
|
+
// `param` {Object} `obj`
|
1398
|
+
sudo.delegates.Data.prototype.filter = function(obj) {
|
1399
|
+
var filters = this.data.filters, key, o, k;
|
1400
|
+
for(key in filters) {
|
1401
|
+
if(filters.hasOwnProperty(key)) {
|
1402
|
+
// keys and paths need different handling
|
1403
|
+
if(key.indexOf('.') === -1) {
|
1404
|
+
if(key in obj) this.delegator[filters[key]].call(
|
1405
|
+
this.delegator, obj[key]);
|
1406
|
+
} else {
|
1407
|
+
// the chars after the last refinement are the key we need to check for
|
1408
|
+
k = key.slice(key.lastIndexOf('.') + 1);
|
1409
|
+
// and the ones prior are the object
|
1410
|
+
o = sudo.getPath(key.slice(0, key.lastIndexOf('.')), obj);
|
1411
|
+
if(o && k in o) this.delegator[filters[key]].call(
|
1412
|
+
this.delegator, o[k]);
|
1413
|
+
}
|
1414
|
+
}
|
1415
|
+
}
|
1416
|
+
};
|
1417
|
+
// `private`
|
1418
|
+
sudo.delegates.Data.prototype.role = 'data';
|
1340
1419
|
|
1341
|
-
sudo.version = "0.
|
1420
|
+
sudo.version = "0.9.0";
|
1342
1421
|
window.sudo = sudo;
|
1343
1422
|
if(typeof window._ === "undefined") window._ = sudo;
|
1344
1423
|
}).call(this, this);
|