canjs-rails 0.1.0 → 1.1.2
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/CHANGELOG.md +5 -0
- data/canjs-rails.gemspec +1 -1
- data/lib/canjs/rails/version.rb +2 -2
- data/vendor/assets/javascripts/can.construct.proxy.js +27 -26
- data/vendor/assets/javascripts/can.construct.super.js +40 -36
- data/vendor/assets/javascripts/can.control.plugin.js +92 -229
- data/vendor/assets/javascripts/can.control.view.js +37 -31
- data/vendor/assets/javascripts/can.fixture.js +277 -570
- data/vendor/assets/javascripts/can.jquery.js +2396 -1792
- data/vendor/assets/javascripts/can.jquery.min.js +70 -52
- data/vendor/assets/javascripts/can.observe.attributes.js +137 -276
- data/vendor/assets/javascripts/can.observe.backup.js +137 -335
- data/vendor/assets/javascripts/can.observe.delegate.js +97 -250
- data/vendor/assets/javascripts/can.observe.setter.js +53 -52
- data/vendor/assets/javascripts/can.observe.validations.js +187 -362
- data/vendor/assets/javascripts/can.view.modifiers.js +57 -190
- data/vendor/assets/javascripts/can.view.mustache.js +758 -0
- data/vendor/assets/javascripts/download_canjs.sh +18 -13
- metadata +4 -3
@@ -1,42 +1,48 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
/*
|
2
|
+
* CanJS - 1.1.2 (2012-11-28)
|
3
|
+
* http://canjs.us/
|
4
|
+
* Copyright (c) 2012 Bitovi
|
5
|
+
* Licensed MIT
|
6
|
+
*/
|
7
|
+
(function (can, window, undefined) {
|
8
|
+
// ## can/observe/delegate/delegate.js
|
9
|
+
|
10
|
+
|
11
|
+
|
5
12
|
// ** - 'this' will be the deepest item changed
|
6
13
|
// * - 'this' will be any changes within *, but * will be the
|
7
14
|
// this returned
|
8
|
-
|
9
15
|
// tells if the parts part of a delegate matches the broken up props of the event
|
10
16
|
// gives the prop to use as 'this'
|
11
17
|
// - parts - the attribute name of the delegate split in parts ['foo','*']
|
12
18
|
// - props - the split props of the event that happened ['foo','bar','0']
|
13
19
|
// - returns - the attribute to delegate too ('foo.bar'), or null if not a match
|
14
|
-
var matches = function(parts, props){
|
20
|
+
var matches = function (parts, props) {
|
15
21
|
//check props parts are the same or
|
16
22
|
var len = parts.length,
|
17
|
-
i =0,
|
23
|
+
i = 0,
|
18
24
|
// keeps the matched props we will use
|
19
25
|
matchedProps = [],
|
20
26
|
prop;
|
21
|
-
|
27
|
+
|
22
28
|
// if the event matches
|
23
|
-
for(i; i< len; i++){
|
24
|
-
prop =
|
29
|
+
for (i; i < len; i++) {
|
30
|
+
prop = props[i]
|
25
31
|
// if no more props (but we should be matching them)
|
26
32
|
// return null
|
27
|
-
if(
|
33
|
+
if (typeof prop !== 'string') {
|
28
34
|
return null;
|
29
35
|
} else
|
30
36
|
// if we have a "**", match everything
|
31
|
-
if(
|
37
|
+
if (parts[i] == "**") {
|
32
38
|
return props.join(".");
|
33
|
-
} else
|
39
|
+
} else
|
34
40
|
// a match, but we want to delegate to "*"
|
35
|
-
if (parts[i] == "*"){
|
41
|
+
if (parts[i] == "*") {
|
36
42
|
// only do this if there is nothing after ...
|
37
43
|
matchedProps.push(prop);
|
38
44
|
}
|
39
|
-
else if(
|
45
|
+
else if (prop === parts[i]) {
|
40
46
|
matchedProps.push(prop);
|
41
47
|
} else {
|
42
48
|
return null;
|
@@ -46,299 +52,139 @@
|
|
46
52
|
},
|
47
53
|
// gets a change event and tries to figure out which
|
48
54
|
// delegates to call
|
49
|
-
delegate = function(event, prop, how, newVal, oldVal){
|
55
|
+
delegate = function (event, prop, how, newVal, oldVal) {
|
50
56
|
// pre-split properties to save some regexp time
|
51
57
|
var props = prop.split("."),
|
52
58
|
delegates = (this._observe_delegates || []).slice(0),
|
53
|
-
delegate,
|
54
|
-
attr,
|
55
|
-
matchedAttr,
|
56
|
-
hasMatch,
|
57
|
-
valuesEqual;
|
59
|
+
delegate, attr, matchedAttr, hasMatch, valuesEqual;
|
58
60
|
event.attr = prop;
|
59
|
-
event.lastAttr = props[props.length -1
|
60
|
-
|
61
|
+
event.lastAttr = props[props.length - 1];
|
62
|
+
|
61
63
|
// for each delegate
|
62
|
-
for(var i =0; delegate = delegates[i++];){
|
63
|
-
|
64
|
+
for (var i = 0; delegate = delegates[i++];) {
|
65
|
+
|
64
66
|
// if there is a batchNum, this means that this
|
65
67
|
// event is part of a series of events caused by a single
|
66
68
|
// attrs call. We don't want to issue the same event
|
67
69
|
// multiple times
|
68
70
|
// setting the batchNum happens later
|
69
|
-
if((event.batchNum && delegate.batchNum === event.batchNum) || delegate.undelegated
|
71
|
+
if ((event.batchNum && delegate.batchNum === event.batchNum) || delegate.undelegated) {
|
70
72
|
continue;
|
71
73
|
}
|
72
|
-
|
74
|
+
|
73
75
|
// reset match and values tests
|
74
76
|
hasMatch = undefined;
|
75
77
|
valuesEqual = true;
|
76
|
-
|
78
|
+
|
79
|
+
// yeah, all this under here has to be redone v
|
77
80
|
// for each attr in a delegate
|
78
|
-
for(var a =0
|
79
|
-
|
81
|
+
for (var a = 0; a < delegate.attrs.length; a++) {
|
82
|
+
|
80
83
|
attr = delegate.attrs[a];
|
81
|
-
|
84
|
+
|
82
85
|
// check if it is a match
|
83
|
-
if(matchedAttr = matches(attr.parts, props)){
|
86
|
+
if (matchedAttr = matches(attr.parts, props)) {
|
84
87
|
hasMatch = matchedAttr;
|
85
88
|
}
|
86
89
|
// if it has a value, make sure it's the right value
|
87
90
|
// if it's set, we should probably check that it has a
|
88
91
|
// value no matter what
|
89
|
-
if(attr.value && valuesEqual
|
90
|
-
valuesEqual = attr.value === ""+this.attr(attr.attr)
|
91
|
-
} else if (valuesEqual && delegate.attrs.length > 1){
|
92
|
+
if (attr.value && valuesEqual) {
|
93
|
+
valuesEqual = attr.value === "" + this.attr(attr.attr)
|
94
|
+
} else if (valuesEqual && delegate.attrs.length > 1) {
|
92
95
|
// if there are multiple attributes, each has to at
|
93
96
|
// least have some value
|
94
97
|
valuesEqual = this.attr(attr.attr) !== undefined
|
95
98
|
}
|
96
99
|
}
|
97
|
-
|
98
|
-
// if there is a match and valuesEqual ... call back
|
99
100
|
|
100
|
-
|
101
|
+
|
102
|
+
// if there is a match and valuesEqual ... call back
|
103
|
+
if (hasMatch && valuesEqual) {
|
101
104
|
// how to get to the changed property from the delegate
|
102
|
-
var from = prop.replace(hasMatch+".","");
|
103
|
-
|
105
|
+
var from = prop.replace(hasMatch + ".", "");
|
106
|
+
|
104
107
|
// if this event is part of a batch, set it on the delegate
|
105
108
|
// to only send one event
|
106
|
-
if(event.batchNum
|
109
|
+
if (event.batchNum) {
|
107
110
|
delegate.batchNum = event.batchNum
|
108
111
|
}
|
109
|
-
|
112
|
+
|
110
113
|
// if we listen to change, fire those with the same attrs
|
111
114
|
// TODO: the attrs should probably be using from
|
112
|
-
if(
|
115
|
+
if (delegate.event === 'change') {
|
113
116
|
arguments[1] = from;
|
114
117
|
event.curAttr = hasMatch;
|
115
|
-
delegate.callback.apply(this.attr(hasMatch), can.makeArray(
|
116
|
-
} else if(delegate.event === how
|
117
|
-
|
118
|
+
delegate.callback.apply(this.attr(hasMatch), can.makeArray(arguments));
|
119
|
+
} else if (delegate.event === how) {
|
120
|
+
|
118
121
|
// if it's a match, callback with the location of the match
|
119
|
-
delegate.callback.apply(this.attr(hasMatch), [event,newVal, oldVal, from]);
|
120
|
-
} else if(delegate.event === 'set' &&
|
121
|
-
how == 'add' ) {
|
122
|
+
delegate.callback.apply(this.attr(hasMatch), [event, newVal, oldVal, from]);
|
123
|
+
} else if (delegate.event === 'set' && how == 'add') {
|
122
124
|
// if we are listening to set, we should also listen to add
|
123
|
-
delegate.callback.apply(this.attr(hasMatch), [event,newVal, oldVal, from]);
|
125
|
+
delegate.callback.apply(this.attr(hasMatch), [event, newVal, oldVal, from]);
|
124
126
|
}
|
125
127
|
}
|
126
|
-
|
128
|
+
|
127
129
|
}
|
128
130
|
};
|
129
|
-
|
130
|
-
can.extend(can.Observe.prototype,{
|
131
|
-
|
132
|
-
|
133
|
-
* @parent can.Observe.delegate
|
134
|
-
* @plugin can/observe/delegate
|
135
|
-
*
|
136
|
-
* `delegate( selector, event, handler(ev,newVal,oldVal,from) )` listen for changes
|
137
|
-
* in a child attribute from the parent. The child attribute
|
138
|
-
* does not have to exist.
|
139
|
-
*
|
140
|
-
*
|
141
|
-
* // create an observable
|
142
|
-
* var observe = can.Observe({
|
143
|
-
* foo : {
|
144
|
-
* bar : "Hello World"
|
145
|
-
* }
|
146
|
-
* })
|
147
|
-
*
|
148
|
-
* //listen to changes on a property
|
149
|
-
* observe.delegate("foo.bar","change", function(ev, prop, how, newVal, oldVal){
|
150
|
-
* // foo.bar has been added, set, or removed
|
151
|
-
* this //->
|
152
|
-
* });
|
153
|
-
*
|
154
|
-
* // change the property
|
155
|
-
* observe.attr('foo.bar',"Goodbye Cruel World")
|
156
|
-
*
|
157
|
-
* ## Types of events
|
158
|
-
*
|
159
|
-
* Delegate lets you listen to add, set, remove, and change events on property.
|
160
|
-
*
|
161
|
-
* __add__
|
162
|
-
*
|
163
|
-
* An add event is fired when a new property has been added.
|
164
|
-
*
|
165
|
-
* var o = new can.Control({});
|
166
|
-
* o.delegate("name","add", function(ev, value){
|
167
|
-
* // called once
|
168
|
-
* can.$('#name').show()
|
169
|
-
* })
|
170
|
-
* o.attr('name',"Justin")
|
171
|
-
* o.attr('name',"Brian");
|
172
|
-
*
|
173
|
-
* Listening to add events is useful for 'setup' functionality (in this case
|
174
|
-
* showing the <code>#name</code> element.
|
175
|
-
*
|
176
|
-
* __set__
|
177
|
-
*
|
178
|
-
* Set events are fired when a property takes on a new value. set events are
|
179
|
-
* always fired after an add.
|
180
|
-
*
|
181
|
-
* o.delegate("name","set", function(ev, value){
|
182
|
-
* // called twice
|
183
|
-
* can.$('#name').text(value)
|
184
|
-
* })
|
185
|
-
* o.attr('name',"Justin")
|
186
|
-
* o.attr('name',"Brian");
|
187
|
-
*
|
188
|
-
* __remove__
|
189
|
-
*
|
190
|
-
* Remove events are fired after a property is removed.
|
191
|
-
*
|
192
|
-
* o.delegate("name","remove", function(ev){
|
193
|
-
* // called once
|
194
|
-
* $('#name').text(value)
|
195
|
-
* })
|
196
|
-
* o.attr('name',"Justin");
|
197
|
-
* o.removeAttr('name');
|
198
|
-
*
|
199
|
-
* ## Wildcards - matching multiple properties
|
200
|
-
*
|
201
|
-
* Sometimes, you want to know when any property within some part
|
202
|
-
* of an observe has changed. Delegate lets you use wildcards to
|
203
|
-
* match any property name. The following listens for any change
|
204
|
-
* on an attribute of the params attribute:
|
205
|
-
*
|
206
|
-
* var o = can.Control({
|
207
|
-
* options : {
|
208
|
-
* limit : 100,
|
209
|
-
* offset: 0,
|
210
|
-
* params : {
|
211
|
-
* parentId: 5
|
212
|
-
* }
|
213
|
-
* }
|
214
|
-
* })
|
215
|
-
* o.delegate('options.*','change', function(){
|
216
|
-
* alert('1');
|
217
|
-
* })
|
218
|
-
* o.delegate('options.**','change', function(){
|
219
|
-
* alert('2');
|
220
|
-
* })
|
221
|
-
*
|
222
|
-
* // alerts 1
|
223
|
-
* // alerts 2
|
224
|
-
* o.attr('options.offset',100)
|
225
|
-
*
|
226
|
-
* // alerts 2
|
227
|
-
* o.attr('options.params.parentId',6);
|
228
|
-
*
|
229
|
-
* Using a single wildcard (<code>*</code>) matches single level
|
230
|
-
* properties. Using a double wildcard (<code>**</code>) matches
|
231
|
-
* any deep property.
|
232
|
-
*
|
233
|
-
* ## Listening on multiple properties and values
|
234
|
-
*
|
235
|
-
* Delegate lets you listen on multiple values at once. The following listens
|
236
|
-
* for first and last name changes:
|
237
|
-
*
|
238
|
-
* var o = new can.Observe({
|
239
|
-
* name : {first: "Justin", last: "Meyer"}
|
240
|
-
* })
|
241
|
-
*
|
242
|
-
* o.bind("name.first,name.last",
|
243
|
-
* "set",
|
244
|
-
* function(ev,newVal,oldVal,from){
|
245
|
-
*
|
246
|
-
* })
|
247
|
-
*
|
248
|
-
* ## Listening when properties are a particular value
|
249
|
-
*
|
250
|
-
* Delegate lets you listen when a property is __set__ to a specific value:
|
251
|
-
*
|
252
|
-
* var o = new can.Observe({
|
253
|
-
* name : "Justin"
|
254
|
-
* })
|
255
|
-
*
|
256
|
-
* o.bind("name=Brian",
|
257
|
-
* "set",
|
258
|
-
* function(ev,newVal,oldVal,from){
|
259
|
-
*
|
260
|
-
* })
|
261
|
-
*
|
262
|
-
* @param {String} selector The attributes you want to listen for changes in.
|
263
|
-
*
|
264
|
-
* Selector should be the property or
|
265
|
-
* property names of the element you are searching. Examples:
|
266
|
-
*
|
267
|
-
* "name" - listens to the "name" property changing
|
268
|
-
* "name, address" - listens to "name" or "address" changing
|
269
|
-
* "name address" - listens to "name" or "address" changing
|
270
|
-
* "address.*" - listens to property directly in address
|
271
|
-
* "address.**" - listens to any property change in address
|
272
|
-
* "foo=bar" - listens when foo is "bar"
|
273
|
-
*
|
274
|
-
* @param {String} event The event name. One of ("set","add","remove","change")
|
275
|
-
* @param {Function} handler(ev,newVal,oldVal,prop) The callback handler
|
276
|
-
* called with:
|
277
|
-
*
|
278
|
-
* - newVal - the new value set on the observe
|
279
|
-
* - oldVal - the old value set on the observe
|
280
|
-
* - prop - the prop name that was changed
|
281
|
-
*
|
282
|
-
* @return {jQuery.Delegate} the delegate for chaining
|
283
|
-
*/
|
284
|
-
delegate : function(selector, event, handler){
|
131
|
+
|
132
|
+
can.extend(can.Observe.prototype, {
|
133
|
+
|
134
|
+
delegate: function (selector, event, handler) {
|
285
135
|
selector = can.trim(selector);
|
286
136
|
var delegates = this._observe_delegates || (this._observe_delegates = []),
|
287
|
-
attrs = []
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
137
|
+
attrs = [],
|
138
|
+
selectorRegex = /([^\s=,]+)(?:=("[^",]*"|'[^',]*'|[^\s"',]*))?(,?)\s*/g,
|
139
|
+
matches;
|
140
|
+
|
141
|
+
// parse each property in the selector
|
142
|
+
while (matches = selectorRegex.exec(selector)) {
|
143
|
+
// we need to do a little doctoring to make up for the quotes.
|
144
|
+
if (matches[2] && $.inArray(matches[2].substr(0, 1), ['"', "'"]) >= 0) {
|
145
|
+
matches[2] = matches[2].substr(1, -1);
|
146
|
+
}
|
147
|
+
|
148
|
+
attrs.push({
|
149
|
+
// the attribute name
|
150
|
+
attr: matches[1],
|
151
|
+
// the attribute name, pre-split for speed
|
152
|
+
parts: matches[1].split('.'),
|
153
|
+
// the value associated with this property (if there was one given)
|
154
|
+
value: matches[2],
|
155
|
+
// whether this selector combines with the one after it with AND or OR
|
156
|
+
or: matches[3] === ','
|
157
|
+
});
|
158
|
+
}
|
159
|
+
|
301
160
|
// delegates has pre-processed info about the event
|
302
161
|
delegates.push({
|
303
162
|
// the attrs name for unbinding
|
304
|
-
selector
|
163
|
+
selector: selector,
|
305
164
|
// an object of attribute names and values {type: 'recipe',id: undefined}
|
306
165
|
// undefined means a value was not defined
|
307
|
-
attrs
|
308
|
-
callback
|
166
|
+
attrs: attrs,
|
167
|
+
callback: handler,
|
309
168
|
event: event
|
310
169
|
});
|
311
|
-
if(delegates.length === 1){
|
312
|
-
this.bind("change",delegate)
|
170
|
+
if (delegates.length === 1) {
|
171
|
+
this.bind("change", delegate)
|
313
172
|
}
|
314
173
|
return this;
|
315
174
|
},
|
316
|
-
|
317
|
-
|
318
|
-
* @parent can.Observe.delegate
|
319
|
-
*
|
320
|
-
* `undelegate( selector, event, handler )` removes a delegated event handler from an observe.
|
321
|
-
*
|
322
|
-
* observe.undelegate("name","set", handler )
|
323
|
-
*
|
324
|
-
* @param {String} selector the attribute name of the object you want to undelegate from.
|
325
|
-
* @param {String} event the event name
|
326
|
-
* @param {Function} handler the callback handler
|
327
|
-
* @return {jQuery.Delegate} the delegate for chaining
|
328
|
-
*/
|
329
|
-
undelegate : function(selector, event, handler){
|
175
|
+
|
176
|
+
undelegate: function (selector, event, handler) {
|
330
177
|
selector = can.trim(selector);
|
331
|
-
|
332
|
-
var i =0,
|
178
|
+
|
179
|
+
var i = 0,
|
333
180
|
delegates = this._observe_delegates || [],
|
334
181
|
delegateOb;
|
335
|
-
if(selector){
|
336
|
-
while(i < delegates.length){
|
182
|
+
if (selector) {
|
183
|
+
while (i < delegates.length) {
|
337
184
|
delegateOb = delegates[i];
|
338
|
-
if(
|
339
|
-
(!handler && delegateOb.selector === selector) ){
|
185
|
+
if (delegateOb.callback === handler || (!handler && delegateOb.selector === selector)) {
|
340
186
|
delegateOb.undelegated = true;
|
341
|
-
delegates.splice(i,1)
|
187
|
+
delegates.splice(i, 1)
|
342
188
|
} else {
|
343
189
|
i++;
|
344
190
|
}
|
@@ -347,13 +193,14 @@
|
|
347
193
|
// remove all delegates
|
348
194
|
delegates = [];
|
349
195
|
}
|
350
|
-
if(!delegates.length){
|
196
|
+
if (!delegates.length) {
|
351
197
|
//can.removeData(this, "_observe_delegates");
|
352
|
-
this.unbind("change",delegate)
|
198
|
+
this.unbind("change", delegate)
|
353
199
|
}
|
354
200
|
return this;
|
355
201
|
}
|
356
202
|
});
|
357
203
|
// add helpers for testing ..
|
358
204
|
can.Observe.prototype.delegate.matches = matches;
|
359
|
-
|
205
|
+
|
206
|
+
})(can, this);
|
@@ -1,58 +1,59 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
can.
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
1
|
+
/*
|
2
|
+
* CanJS - 1.1.2 (2012-11-28)
|
3
|
+
* http://canjs.us/
|
4
|
+
* Copyright (c) 2012 Bitovi
|
5
|
+
* Licensed MIT
|
6
|
+
*/
|
7
|
+
(function (can, window, undefined) {
|
8
|
+
// ## can/observe/setter/setter.js
|
9
|
+
can.classize = function (s, join) {
|
10
|
+
// this can be moved out ..
|
11
|
+
// used for getter setter
|
12
|
+
var parts = s.split(can.undHash),
|
13
|
+
i = 0;
|
14
|
+
for (; i < parts.length; i++) {
|
15
|
+
parts[i] = can.capitalize(parts[i]);
|
16
|
+
}
|
17
|
+
|
18
|
+
return parts.join(join || '');
|
15
19
|
}
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
proto =
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
}
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
// if we have a setter
|
43
|
-
if ( this[setName] &&
|
21
|
+
var classize = can.classize,
|
22
|
+
proto = can.Observe.prototype,
|
23
|
+
old = proto.__set;
|
24
|
+
|
25
|
+
proto.__set = function (prop, value, current, success, error) {
|
26
|
+
// check if there's a setter
|
27
|
+
var cap = classize(prop),
|
28
|
+
setName = "set" + cap,
|
29
|
+
errorCallback = function (errors) {
|
30
|
+
var stub = error && error.call(self, errors);
|
31
|
+
|
32
|
+
// if 'setter' is on the page it will trigger
|
33
|
+
// the error itself and we dont want to trigger
|
34
|
+
// the event twice. :)
|
35
|
+
if (stub !== false) {
|
36
|
+
can.trigger(self, "error", [prop, errors], true);
|
37
|
+
}
|
38
|
+
|
39
|
+
return false;
|
40
|
+
},
|
41
|
+
self = this;
|
42
|
+
|
43
|
+
// if we have a setter
|
44
|
+
if (this[setName] &&
|
44
45
|
// call the setter, if returned value is undefined,
|
45
46
|
// this means the setter is async so we
|
46
47
|
// do not call update property and return right away
|
47
|
-
(
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
};
|
48
|
+
(value = this[setName](value, function () {
|
49
|
+
old.call(self, prop, value, current, success, errorCallback)
|
50
|
+
}, errorCallback)) === undefined) {
|
51
|
+
return;
|
52
|
+
}
|
53
|
+
|
54
|
+
old.call(self, prop, value, current, success, errorCallback);
|
55
|
+
|
56
|
+
return this;
|
57
|
+
};
|
57
58
|
|
58
|
-
})(
|
59
|
+
})(can, this);
|