canjs-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2995 @@
1
+ (function(can, window, undefined){
2
+ // # CanJS v1.0.7
3
+
4
+ // (c) 2012 Bitovi
5
+ // MIT license
6
+ // [http://canjs.us/](http://canjs.us/)
7
+
8
+ // jquery.js
9
+ // ---------
10
+ // _jQuery node list._
11
+ $.extend( can, jQuery, {
12
+ trigger: function( obj, event, args ) {
13
+ obj.trigger ?
14
+ obj.trigger( event, args ) :
15
+ $.event.trigger( event, args, obj, true );
16
+ },
17
+ addEvent: function(ev, cb){
18
+ $([this]).bind(ev, cb)
19
+ return this;
20
+ },
21
+ removeEvent: function(ev, cb){
22
+ $([this]).unbind(ev, cb)
23
+ return this;
24
+ },
25
+ // jquery caches fragments, we always needs a new one
26
+ buildFragment : function(result, element){
27
+ var ret = $.buildFragment([result],[element]);
28
+ return ret.cacheable ? $.clone(ret.fragment) : ret.fragment
29
+ },
30
+ $: jQuery
31
+ });
32
+
33
+ // Wrap binding functions.
34
+ $.each(['bind','unbind','undelegate','delegate'],function(i,func){
35
+ can[func] = function(){
36
+ var t = this[func] ? this : $([this])
37
+ t[func].apply(t, arguments)
38
+ return this;
39
+ }
40
+ })
41
+
42
+ // Wrap modifier functions.
43
+ $.each(["append","filter","addClass","remove","data","get"], function(i,name){
44
+ can[name] = function(wrapped){
45
+ return wrapped[name].apply(wrapped, can.makeArray(arguments).slice(1))
46
+ }
47
+ })
48
+
49
+ // Memory safe destruction.
50
+ var oldClean = $.cleanData;
51
+
52
+ $.cleanData = function( elems ) {
53
+ $.each( elems, function( i, elem ) {
54
+ can.trigger(elem,"destroyed",[],false)
55
+ });
56
+ oldClean(elems);
57
+ };
58
+
59
+ can.each = function (elements, callback, context) {
60
+ var i = 0,
61
+ key;
62
+ if (elements) {
63
+ if (typeof elements.length == 'number' && elements.pop) {
64
+ elements.attr && elements.attr('length');
65
+ for (var len = elements.length; i < len; i++) {
66
+ if (callback.call(context || elements[i], elements[i], i, elements) === false) {
67
+ break;
68
+ }
69
+ }
70
+ } else {
71
+ for (key in elements) {
72
+ if (callback.call(context || elements[i], elements[key], key, elements) === false) {
73
+ break;
74
+ }
75
+ }
76
+ }
77
+ }
78
+ return elements;
79
+ }
80
+ ;
81
+
82
+ // ##string.js
83
+ // _Miscellaneous string utility functions._
84
+
85
+ // Several of the methods in this plugin use code adapated from Prototype
86
+ // Prototype JavaScript framework, version 1.6.0.1.
87
+ // © 2005-2007 Sam Stephenson
88
+ var undHash = /_|-/,
89
+ colons = /==/,
90
+ words = /([A-Z]+)([A-Z][a-z])/g,
91
+ lowUp = /([a-z\d])([A-Z])/g,
92
+ dash = /([a-z\d])([A-Z])/g,
93
+ replacer = /\{([^\}]+)\}/g,
94
+ quote = /"/g,
95
+ singleQuote = /'/g,
96
+
97
+ // Returns the `prop` property from `obj`.
98
+ // If `add` is true and `prop` doesn't exist in `obj`, create it as an
99
+ // empty object.
100
+ getNext = function( obj, prop, add ) {
101
+ return prop in obj ?
102
+ obj[ prop ] :
103
+ ( add && ( obj[ prop ] = {} ));
104
+ },
105
+
106
+ // Returns `true` if the object can have properties (no `null`s).
107
+ isContainer = function( current ) {
108
+ return /^f|^o/.test( typeof current );
109
+ };
110
+
111
+ can.extend(can, {
112
+ // Escapes strings for HTML.
113
+ esc : function( content ) {
114
+ return ( "" + content )
115
+ .replace(/&/g, '&amp;')
116
+ .replace(/</g, '&lt;')
117
+ .replace(/>/g, '&gt;')
118
+ .replace(quote, '&#34;')
119
+ .replace(singleQuote, "&#39;");
120
+ },
121
+
122
+ getObject : function( name, roots, add ) {
123
+
124
+ // The parts of the name we are looking up
125
+ // `['App','Models','Recipe']`
126
+ var parts = name ? name.split('.') : [],
127
+ length = parts.length,
128
+ current,
129
+ r = 0,
130
+ ret, i;
131
+
132
+ // Make sure roots is an `array`.
133
+ roots = can.isArray(roots) ? roots : [roots || window];
134
+
135
+ if ( ! length ) {
136
+ return roots[0];
137
+ }
138
+
139
+ // For each root, mark it as current.
140
+ while( current = roots[r++] ) {
141
+
142
+ // Walk current to the 2nd to last object or until there
143
+ // is not a container.
144
+ for (i =0; i < length - 1 && isContainer( current ); i++ ) {
145
+ current = getNext( current, parts[i], add );
146
+ }
147
+
148
+ // If we can get a property from the 2nd to last object...
149
+ if( isContainer(current) ) {
150
+
151
+ // Get (and possibly set) the property.
152
+ ret = getNext(current, parts[i], add);
153
+
154
+ // If there is a value, we exit.
155
+ if ( ret !== undefined ) {
156
+ // If `add` is `false`, delete the property
157
+ if ( add === false ) {
158
+ delete current[parts[i]];
159
+ }
160
+ return ret;
161
+
162
+ }
163
+ }
164
+ }
165
+ },
166
+ // Capitalizes a string.
167
+ capitalize: function( s, cache ) {
168
+ // Used to make newId.
169
+ return s.charAt(0).toUpperCase() + s.slice(1);
170
+ },
171
+
172
+ // Underscores a string.
173
+ underscore: function( s ) {
174
+ return s
175
+ .replace(colons, '/')
176
+ .replace(words, '$1_$2')
177
+ .replace(lowUp, '$1_$2')
178
+ .replace(dash, '_')
179
+ .toLowerCase();
180
+ },
181
+ // Micro-templating.
182
+ sub: function( str, data, remove ) {
183
+
184
+ var obs = [];
185
+
186
+ obs.push( str.replace( replacer, function( whole, inside ) {
187
+
188
+ // Convert inside to type.
189
+ var ob = can.getObject( inside, data, remove === undefined? remove : !remove );
190
+
191
+ // If a container, push into objs (which will return objects found).
192
+ if ( isContainer( ob ) ) {
193
+ obs.push( ob );
194
+ return "";
195
+ } else {
196
+ return "" + ob;
197
+ }
198
+ }));
199
+
200
+ return obs.length <= 1 ? obs[0] : obs;
201
+ },
202
+
203
+ // These regex's are used throughout the rest of can, so let's make
204
+ // them available.
205
+ replacer : replacer,
206
+ undHash : undHash
207
+ });
208
+
209
+ // ## construct.js
210
+ // `can.Construct`
211
+ // _This is a modified version of
212
+ // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/).
213
+ // It provides class level inheritance and callbacks._
214
+
215
+ // A private flag used to initialize a new class instance without
216
+ // initializing it's bindings.
217
+ var initializing = 0;
218
+
219
+ can.Construct = function() {
220
+ if (arguments.length) {
221
+ return can.Construct.extend.apply(can.Construct, arguments);
222
+ }
223
+ };
224
+
225
+ can.extend(can.Construct, {
226
+ newInstance: function() {
227
+ // Get a raw instance object (`init` is not called).
228
+ var inst = this.instance(),
229
+ arg = arguments,
230
+ args;
231
+
232
+ // Call `setup` if there is a `setup`
233
+ if ( inst.setup ) {
234
+ args = inst.setup.apply(inst, arguments);
235
+ }
236
+
237
+ // Call `init` if there is an `init`
238
+ // If `setup` returned `args`, use those as the arguments
239
+ if ( inst.init ) {
240
+ inst.init.apply(inst, args || arguments);
241
+ }
242
+
243
+ return inst;
244
+ },
245
+ // Overwrites an object with methods. Used in the `super` plugin.
246
+ // `newProps` - New properties to add.
247
+ // `oldProps` - Where the old properties might be (used with `super`).
248
+ // `addTo` - What we are adding to.
249
+ _inherit: function( newProps, oldProps, addTo ) {
250
+ can.extend(addTo || newProps, newProps || {})
251
+ },
252
+ // used for overwriting a single property.
253
+ // this should be used for patching other objects
254
+ // the super plugin overwrites this
255
+ _overwrite : function(what, oldProps, propName, val){
256
+ what[propName] = val;
257
+ },
258
+ // Set `defaults` as the merger of the parent `defaults` and this
259
+ // object's `defaults`. If you overwrite this method, make sure to
260
+ // include option merging logic.
261
+ setup: function( base, fullName ) {
262
+ this.defaults = can.extend(true,{}, base.defaults, this.defaults);
263
+ },
264
+ // Create's a new `class` instance without initializing by setting the
265
+ // `initializing` flag.
266
+ instance: function() {
267
+
268
+ // Prevents running `init`.
269
+ initializing = 1;
270
+
271
+ var inst = new this();
272
+
273
+ // Allow running `init`.
274
+ initializing = 0;
275
+
276
+ return inst;
277
+ },
278
+ // Extends classes.
279
+ extend: function( fullName, klass, proto ) {
280
+ // Figure out what was passed and normalize it.
281
+ if ( typeof fullName != 'string' ) {
282
+ proto = klass;
283
+ klass = fullName;
284
+ fullName = null;
285
+ }
286
+
287
+ if ( ! proto ) {
288
+ proto = klass;
289
+ klass = null;
290
+ }
291
+ proto = proto || {};
292
+
293
+ var _super_class = this,
294
+ _super = this.prototype,
295
+ name, shortName, namespace, prototype;
296
+
297
+ // Instantiate a base class (but only create the instance,
298
+ // don't run the init constructor).
299
+ prototype = this.instance();
300
+
301
+ // Copy the properties over onto the new prototype.
302
+ can.Construct._inherit(proto, _super, prototype);
303
+
304
+ // The dummy class constructor.
305
+ function Constructor() {
306
+ // All construction is actually done in the init method.
307
+ if ( ! initializing ) {
308
+ return this.constructor !== Constructor && arguments.length ?
309
+ // We are being called without `new` or we are extending.
310
+ arguments.callee.extend.apply(arguments.callee, arguments) :
311
+ // We are being called with `new`.
312
+ this.constructor.newInstance.apply(this.constructor, arguments);
313
+ }
314
+ }
315
+
316
+ // Copy old stuff onto class (can probably be merged w/ inherit)
317
+ for ( name in _super_class ) {
318
+ if ( _super_class.hasOwnProperty(name) ) {
319
+ Constructor[name] = _super_class[name];
320
+ }
321
+ }
322
+
323
+ // Copy new static properties on class.
324
+ can.Construct._inherit(klass, _super_class, Constructor);
325
+
326
+ // Setup namespaces.
327
+ if ( fullName ) {
328
+
329
+ var parts = fullName.split('.'),
330
+ shortName = parts.pop(),
331
+ current = can.getObject(parts.join('.'), window, true),
332
+ namespace = current,
333
+ _fullName = can.underscore(fullName.replace(/\./g, "_")),
334
+ _shortName = can.underscore(shortName);
335
+
336
+ //@steal-remove-start
337
+ if(current[shortName]){
338
+
339
+ }
340
+ //@steal-remove-end
341
+
342
+ current[shortName] = Constructor;
343
+ }
344
+
345
+ // Set things that shouldn't be overwritten.
346
+ can.extend(Constructor, {
347
+ constructor: Constructor,
348
+ prototype: prototype,
349
+ namespace: namespace,
350
+ shortName: shortName,
351
+ _shortName : _shortName,
352
+ fullName: fullName,
353
+ _fullName: _fullName
354
+ });
355
+
356
+ // Make sure our prototype looks nice.
357
+ Constructor.prototype.constructor = Constructor;
358
+
359
+
360
+ // Call the class `setup` and `init`
361
+ var t = [_super_class].concat(can.makeArray(arguments)),
362
+ args = Constructor.setup.apply(Constructor, t );
363
+
364
+ if ( Constructor.init ) {
365
+ Constructor.init.apply(Constructor, args || t );
366
+ }
367
+
368
+ return Constructor;
369
+ //
370
+ //
371
+ }
372
+
373
+ });
374
+
375
+ // ## observe.js
376
+ // `can.Observe`
377
+ // _Provides the observable pattern for JavaScript Objects._
378
+ //
379
+ // Returns `true` if something is an object with properties of its own.
380
+ var canMakeObserve = function( obj ) {
381
+ return obj && typeof obj === 'object' && !(obj instanceof Date);
382
+ },
383
+
384
+ // Removes all listeners.
385
+ unhookup = function(items, namespace){
386
+ return can.each(items, function(item){
387
+ if(item && item.unbind){
388
+ item.unbind("change" + namespace);
389
+ }
390
+ });
391
+ },
392
+ // Listens to changes on `val` and "bubbles" the event up.
393
+ // `val` - The object to listen for changes on.
394
+ // `prop` - The property name is at on.
395
+ // `parent` - The parent object of prop.
396
+ hookupBubble = function( val, prop, parent ) {
397
+ // If it's an `array` make a list, otherwise a val.
398
+ if (val instanceof Observe){
399
+ // We have an `observe` already...
400
+ // Make sure it is not listening to this already
401
+ unhookup([val], parent._namespace);
402
+ } else if ( can.isArray(val) ) {
403
+ val = new Observe.List(val);
404
+ } else {
405
+ val = new Observe(val);
406
+ }
407
+
408
+ // Listen to all changes and `batchTrigger` upwards.
409
+ val.bind("change" + parent._namespace, function( ev, attr ) {
410
+ // `batchTrigger` the type on this...
411
+ var args = can.makeArray(arguments),
412
+ ev = args.shift();
413
+ args[0] = prop === "*" ?
414
+ parent.indexOf(val)+"." + args[0] :
415
+ prop + "." + args[0];
416
+ // track objects dispatched on this observe
417
+ ev.triggeredNS = ev.triggeredNS || {};
418
+ // if it has already been dispatched exit
419
+ if (ev.triggeredNS[parent._namespace]) {
420
+ return;
421
+ }
422
+ ev.triggeredNS[parent._namespace] = true;
423
+
424
+ can.trigger(parent, ev, args);
425
+ can.trigger(parent,args[0],args);
426
+ });
427
+
428
+ return val;
429
+ },
430
+
431
+ // An `id` to track events for a given observe.
432
+ observeId = 0,
433
+ // A reference to an `array` of events that will be dispatched.
434
+ collecting = undefined,
435
+ // Call to start collecting events (`Observe` sends all events at
436
+ // once).
437
+ collect = function() {
438
+ if (!collecting ) {
439
+ collecting = [];
440
+ return true;
441
+ }
442
+ },
443
+ // Creates an event on item, but will not send immediately
444
+ // if collecting events.
445
+ // `item` - The item the event should happen on.
446
+ // `event` - The event name, ex: `change`.
447
+ // `args` - Tn array of arguments.
448
+ batchTrigger = function( item, event, args ) {
449
+ // Don't send events if initalizing.
450
+ if ( ! item._init) {
451
+ if (!collecting ) {
452
+ return can.trigger(item, event, args);
453
+ } else {
454
+ collecting.push([
455
+ item,
456
+ {
457
+ type: event,
458
+ batchNum : batchNum
459
+ },
460
+ args ] );
461
+ }
462
+ }
463
+ },
464
+ // Which batch of events this is for -- might not want to send multiple
465
+ // messages on the same batch. This is mostly for event delegation.
466
+ batchNum = 1,
467
+ // Sends all pending events.
468
+ sendCollection = function() {
469
+ var items = collecting.slice(0);
470
+ collecting = undefined;
471
+ batchNum++;
472
+ can.each(items, function( item ) {
473
+ can.trigger.apply(can, item)
474
+ })
475
+
476
+ },
477
+ // A helper used to serialize an `Observe` or `Observe.List`.
478
+ // `observe` - The observable.
479
+ // `how` - To serialize with `attr` or `serialize`.
480
+ // `where` - To put properties, in an `{}` or `[]`.
481
+ serialize = function( observe, how, where ) {
482
+ // Go through each property.
483
+ observe.each(function( val, name ) {
484
+ // If the value is an `object`, and has an `attrs` or `serialize` function.
485
+ where[name] = canMakeObserve(val) && can.isFunction( val[how] ) ?
486
+ // Call `attrs` or `serialize` to get the original data back.
487
+ val[how]() :
488
+ // Otherwise return the value.
489
+ val
490
+ })
491
+ return where;
492
+ },
493
+ $method = function( name ) {
494
+ return function() {
495
+ return can[name].apply(this, arguments );
496
+ }
497
+ },
498
+ bind = $method('addEvent'),
499
+ unbind = $method('removeEvent'),
500
+ attrParts = function(attr){
501
+ return can.isArray(attr) ? attr : (""+attr).split(".")
502
+ };
503
+ var Observe = can.Construct('can.Observe', {
504
+ // keep so it can be overwritten
505
+ setup : function(){
506
+ can.Construct.setup.apply(this, arguments)
507
+ },
508
+ bind : bind,
509
+ unbind: unbind,
510
+ id: "id"
511
+ },
512
+ {
513
+ setup: function( obj ) {
514
+ // `_data` is where we keep the properties.
515
+ this._data = {};
516
+ // The namespace this `object` uses to listen to events.
517
+ this._namespace = ".observe" + (++observeId);
518
+ // Sets all `attrs`.
519
+ this._init = 1;
520
+ this.attr(obj);
521
+ delete this._init;
522
+ },
523
+ attr: function( attr, val ) {
524
+ // This is super obfuscated for space -- basically, we're checking
525
+ // if the type of the attribute is not a `number` or a `string`.
526
+ if ( !~ "ns".indexOf((typeof attr).charAt(0))) {
527
+ return this._attrs(attr, val)
528
+ } else if ( val === undefined ) {// If we are getting a value.
529
+ // Let people know we are reading.
530
+ Observe.__reading && Observe.__reading(this, attr)
531
+ return this._get(attr)
532
+ } else {
533
+ // Otherwise we are setting.
534
+ this._set(attr, val);
535
+ return this;
536
+ }
537
+ },
538
+ each: function() {
539
+ return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments)))
540
+ },
541
+ removeAttr: function( attr ) {
542
+ // Convert the `attr` into parts (if nested).
543
+ var parts = attrParts(attr),
544
+ // The actual property to remove.
545
+ prop = parts.shift(),
546
+ // The current value.
547
+ current = this._data[prop];
548
+
549
+ // If we have more parts, call `removeAttr` on that part.
550
+ if ( parts.length ) {
551
+ return current.removeAttr(parts)
552
+ } else {
553
+ // Otherwise, `delete`.
554
+ delete this._data[prop];
555
+ // Create the event.
556
+ if (!(prop in this.constructor.prototype)) {
557
+ delete this[prop]
558
+ }
559
+ batchTrigger(this, "change", [prop, "remove", undefined, current]);
560
+ batchTrigger(this, prop, [undefined, current]);
561
+ return current;
562
+ }
563
+ },
564
+ // Reads a property from the `object`.
565
+ _get: function( attr ) {
566
+ var parts = attrParts(attr),
567
+ current = this.__get(parts.shift());
568
+ return parts.length ? current ? current._get(parts) : undefined : current;
569
+ },
570
+ // Reads a property directly if an `attr` is provided, otherwise
571
+ // returns the "real" data object itself.
572
+ __get: function( attr ) {
573
+ return attr ? this._data[attr] : this._data;
574
+ },
575
+ // Sets `attr` prop as value on this object where.
576
+ // `attr` - Is a string of properties or an array of property values.
577
+ // `value` - The raw value to set.
578
+ _set: function( attr, value ) {
579
+ // Convert `attr` to attr parts (if it isn't already).
580
+ var parts = attrParts(attr),
581
+ // The immediate prop we are setting.
582
+ prop = parts.shift(),
583
+ // The current value.
584
+ current = this.__get(prop);
585
+
586
+ // If we have an `object` and remaining parts.
587
+ if ( canMakeObserve(current) && parts.length ) {
588
+ // That `object` should set it (this might need to call attr).
589
+ current._set(parts, value)
590
+ } else if (!parts.length ) {
591
+ // We're in "real" set territory.
592
+ if(this.__convert){
593
+ value = this.__convert(prop, value)
594
+ }
595
+ this.__set(prop, value, current)
596
+
597
+ } else {
598
+ throw "can.Observe: Object does not exist"
599
+ }
600
+ },
601
+ __set : function(prop, value, current){
602
+
603
+ // Otherwise, we are setting it on this `object`.
604
+ // TODO: Check if value is object and transform
605
+ // are we changing the value.
606
+ if ( value !== current ) {
607
+
608
+ // Check if we are adding this for the first time --
609
+ // if we are, we need to create an `add` event.
610
+ var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
611
+
612
+ // Set the value on data.
613
+ this.___set(prop,
614
+
615
+ // If we are getting an object.
616
+ canMakeObserve(value) ?
617
+
618
+ // Hook it up to send event.
619
+ hookupBubble(value, prop, this) :
620
+ // Value is normal.
621
+ value);
622
+
623
+ // `batchTrigger` the change event.
624
+ batchTrigger(this, "change", [prop, changeType, value, current]);
625
+ batchTrigger(this, prop, [value, current]);
626
+ // If we can stop listening to our old value, do it.
627
+ current && unhookup([current], this._namespace);
628
+ }
629
+
630
+ },
631
+ // Directly sets a property on this `object`.
632
+ ___set: function( prop, val ) {
633
+ this._data[prop] = val;
634
+ // Add property directly for easy writing.
635
+ // Check if its on the `prototype` so we don't overwrite methods like `attrs`.
636
+ if (!(prop in this.constructor.prototype)) {
637
+ this[prop] = val
638
+ }
639
+ },
640
+ bind: bind,
641
+ unbind: unbind,
642
+ serialize: function() {
643
+ return serialize(this, 'serialize', {});
644
+ },
645
+ _attrs: function( props, remove ) {
646
+ if ( props === undefined ) {
647
+ return serialize(this, 'attr', {})
648
+ }
649
+
650
+ props = can.extend(true, {}, props);
651
+ var prop,
652
+ collectingStarted = collect(),
653
+ self = this,
654
+ newVal;
655
+
656
+ this.each(function(curVal, prop){
657
+ newVal = props[prop];
658
+
659
+ // If we are merging...
660
+ if ( newVal === undefined ) {
661
+ remove && self.removeAttr(prop);
662
+ return;
663
+ }
664
+ if ( canMakeObserve(curVal) && canMakeObserve(newVal) ) {
665
+ curVal.attr(newVal, remove)
666
+ } else if ( curVal != newVal ) {
667
+ self._set(prop, newVal)
668
+ } else {
669
+
670
+ }
671
+ delete props[prop];
672
+ })
673
+ // Add remaining props.
674
+ for ( var prop in props ) {
675
+ newVal = props[prop];
676
+ this._set(prop, newVal)
677
+ }
678
+ if ( collectingStarted ) {
679
+ sendCollection();
680
+ }
681
+ return this;
682
+ }
683
+ });
684
+ // Helpers for `observable` lists.
685
+ var splice = [].splice,
686
+ list = Observe('can.Observe.List',
687
+ {
688
+ setup: function( instances, options ) {
689
+ this.length = 0;
690
+ this._namespace = ".observe" + (++observeId);
691
+ this._init = 1;
692
+ this.bind('change',can.proxy(this._changes,this));
693
+ this.push.apply(this, can.makeArray(instances || []));
694
+ can.extend(this, options);
695
+ delete this._init;
696
+ },
697
+ _changes : function(ev, attr, how, newVal, oldVal){
698
+ // `batchTrigger` direct add and remove events...
699
+ if ( !~ attr.indexOf('.')){
700
+
701
+ if( how === 'add' ) {
702
+ batchTrigger(this, how, [newVal,+attr]);
703
+ batchTrigger(this,'length',[this.length]);
704
+ } else if( how === 'remove' ) {
705
+ batchTrigger(this, how, [oldVal, +attr]);
706
+ batchTrigger(this,'length',[this.length]);
707
+ } else {
708
+ batchTrigger(this,how,[newVal, +attr])
709
+ }
710
+
711
+ }
712
+ },
713
+ __get : function(attr){
714
+ return attr ? this[attr] : this;
715
+ },
716
+ ___set : function(attr, val){
717
+ this[attr] = val;
718
+ if(+attr >= this.length){
719
+ this.length = (+attr+1)
720
+ }
721
+ },
722
+ // Returns the serialized form of this list.
723
+ serialize: function() {
724
+ return serialize(this, 'serialize', []);
725
+ },
726
+ //
727
+ splice: function( index, howMany ) {
728
+ var args = can.makeArray(arguments),
729
+ i;
730
+
731
+ for ( i = 2; i < args.length; i++ ) {
732
+ var val = args[i];
733
+ if ( canMakeObserve(val) ) {
734
+ args[i] = hookupBubble(val, "*", this)
735
+ }
736
+ }
737
+ if ( howMany === undefined ) {
738
+ howMany = args[1] = this.length - index;
739
+ }
740
+ var removed = splice.apply(this, args);
741
+ if ( howMany > 0 ) {
742
+ batchTrigger(this, "change", [""+index, "remove", undefined, removed]);
743
+ unhookup(removed, this._namespace);
744
+ }
745
+ if ( args.length > 2 ) {
746
+ batchTrigger(this, "change", [""+index, "add", args.slice(2), removed]);
747
+ }
748
+ return removed;
749
+ },
750
+ _attrs: function( props, remove ) {
751
+ if ( props === undefined ) {
752
+ return serialize(this, 'attr', []);
753
+ }
754
+
755
+ // Create a copy.
756
+ props = props.slice(0);
757
+
758
+ var len = Math.min(props.length, this.length),
759
+ collectingStarted = collect(),
760
+ prop;
761
+
762
+ for ( var prop = 0; prop < len; prop++ ) {
763
+ var curVal = this[prop],
764
+ newVal = props[prop];
765
+
766
+ if ( canMakeObserve(curVal) && canMakeObserve(newVal) ) {
767
+ curVal.attr(newVal, remove)
768
+ } else if ( curVal != newVal ) {
769
+ this._set(prop, newVal)
770
+ } else {
771
+
772
+ }
773
+ }
774
+ if ( props.length > this.length ) {
775
+ // Add in the remaining props.
776
+ this.push(props.slice(this.length))
777
+ } else if ( props.length < this.length && remove ) {
778
+ this.splice(props.length)
779
+ }
780
+
781
+ if ( collectingStarted ) {
782
+ sendCollection()
783
+ }
784
+ }
785
+ }),
786
+
787
+ // Converts to an `array` of arguments.
788
+ getArgs = function( args ) {
789
+ return args[0] && can.isArray(args[0]) ?
790
+ args[0] :
791
+ can.makeArray(args);
792
+ };
793
+ // Create `push`, `pop`, `shift`, and `unshift`
794
+ can.each({
795
+ push: "length",
796
+ unshift: 0
797
+ },
798
+ // Adds a method
799
+ // `name` - The method name.
800
+ // `where` - Where items in the `array` should be added.
801
+ function( where, name ) {
802
+ list.prototype[name] = function() {
803
+ // Get the items being added.
804
+ var args = getArgs(arguments),
805
+ // Where we are going to add items.
806
+ len = where ? this.length : 0;
807
+
808
+ // Go through and convert anything to an `observe` that needs to be converted.
809
+ for ( var i = 0; i < args.length; i++ ) {
810
+ var val = args[i];
811
+ if ( canMakeObserve(val) ) {
812
+ args[i] = hookupBubble(val, "*", this)
813
+ }
814
+ }
815
+
816
+ // Call the original method.
817
+ var res = [][name].apply(this, args);
818
+
819
+ if ( !this.comparator || !args.length ) {
820
+ batchTrigger(this, "change", [""+len, "add", args, undefined])
821
+ }
822
+
823
+ return res;
824
+ }
825
+ });
826
+
827
+ can.each({
828
+ pop: "length",
829
+ shift: 0
830
+ },
831
+ // Creates a `remove` type method
832
+ function( where, name ) {
833
+ list.prototype[name] = function() {
834
+
835
+ var args = getArgs(arguments),
836
+ len = where && this.length ? this.length - 1 : 0;
837
+
838
+ var res = [][name].apply(this, args)
839
+
840
+ // Create a change where the args are
841
+ // `*` - Change on potentially multiple properties.
842
+ // `remove` - Items removed.
843
+ // `undefined` - The new values (there are none).
844
+ // `res` - The old, removed values (should these be unbound).
845
+ // `len` - Where these items were removed.
846
+ batchTrigger(this, "change", [""+len, "remove", undefined, [res]])
847
+
848
+ if ( res && res.unbind ) {
849
+ res.unbind("change" + this._namespace)
850
+ }
851
+ return res;
852
+ }
853
+ });
854
+
855
+ list.prototype.
856
+ indexOf = [].indexOf || function(item){
857
+ return can.inArray(item, this)
858
+ };
859
+
860
+
861
+ // ## model.js
862
+ // `can.Model`
863
+ // _A `can.Observe` that connects to a RESTful interface._
864
+ //
865
+ // Generic deferred piping function
866
+ var pipe = function( def, model, func ) {
867
+ var d = new can.Deferred();
868
+ def.then(function(){
869
+ arguments[0] = model[func](arguments[0])
870
+ d.resolve.apply(d, arguments)
871
+ },function(){
872
+ d.rejectWith.apply(this,arguments)
873
+ })
874
+ return d;
875
+ },
876
+ modelNum = 0,
877
+ ignoreHookup = /change.observe\d+/,
878
+ getId = function( inst ) {
879
+ return inst[inst.constructor.id]
880
+ },
881
+ // Ajax `options` generator function
882
+ ajax = function( ajaxOb, data, type, dataType, success, error ) {
883
+
884
+
885
+ // If we get a string, handle it.
886
+ if ( typeof ajaxOb == "string" ) {
887
+ // If there's a space, it's probably the type.
888
+ var parts = ajaxOb.split(" ")
889
+ ajaxOb = {
890
+ url : parts.pop()
891
+ };
892
+ if(parts.length){
893
+ ajaxOb.type = parts.pop();
894
+ }
895
+ }
896
+
897
+ // If we are a non-array object, copy to a new attrs.
898
+ ajaxOb.data = typeof data == "object" && !can.isArray(data) ?
899
+ can.extend(ajaxOb.data || {}, data) : data;
900
+
901
+
902
+ // Get the url with any templated values filled out.
903
+ ajaxOb.url = can.sub(ajaxOb.url, ajaxOb.data, true);
904
+
905
+ return can.ajax(can.extend({
906
+ type: type || "post",
907
+ dataType: dataType ||"json",
908
+ success : success,
909
+ error: error
910
+ }, ajaxOb ));
911
+ },
912
+ makeRequest = function( self, type, success, error, method ) {
913
+ var deferred ,
914
+ args = [self.serialize()],
915
+ // The model.
916
+ model = self.constructor,
917
+ jqXHR;
918
+
919
+ // `destroy` does not need data.
920
+ if ( type == 'destroy' ) {
921
+ args.shift();
922
+ }
923
+ // `update` and `destroy` need the `id`.
924
+ if ( type !== 'create' ) {
925
+ args.unshift(getId(self))
926
+ }
927
+
928
+ jqXHR = model[type].apply(model, args);
929
+
930
+ deferred = jqXHR.pipe(function(data){
931
+ self[method || type + "d"](data, jqXHR);
932
+ return self
933
+ })
934
+
935
+ // Hook up `abort`
936
+ if(jqXHR.abort){
937
+ deferred.abort = function(){
938
+ jqXHR.abort();
939
+ }
940
+ }
941
+
942
+ return deferred.then(success,error);
943
+ },
944
+
945
+ // This object describes how to make an ajax request for each ajax method.
946
+ // The available properties are:
947
+ // `url` - The default url to use as indicated as a property on the model.
948
+ // `type` - The default http request type
949
+ // `data` - A method that takes the `arguments` and returns `data` used for ajax.
950
+ //
951
+ //
952
+ //
953
+ ajaxMethods = {
954
+ create : {
955
+ url : "_shortName",
956
+ type :"post"
957
+ },
958
+ update : {
959
+ data : function(id, attrs){
960
+ attrs = attrs || {};
961
+ var identity = this.id;
962
+ if ( attrs[identity] && attrs[identity] !== id ) {
963
+ attrs["new" + can.capitalize(id)] = attrs[identity];
964
+ delete attrs[identity];
965
+ }
966
+ attrs[identity] = id;
967
+ return attrs;
968
+ },
969
+ type : "put"
970
+ },
971
+ destroy : {
972
+ type : "delete",
973
+ data : function(id){
974
+ var args = {};
975
+ args[this.id] = id;
976
+ return args;
977
+ }
978
+ },
979
+ findAll : {
980
+ url : "_shortName"
981
+ },
982
+ findOne: {}
983
+ },
984
+ // Makes an ajax request `function` from a string.
985
+ // `ajaxMethod` - The `ajaxMethod` object defined above.
986
+ // `str` - The string the user provided. Ex: `findAll: "/recipes.json"`.
987
+ ajaxMaker = function(ajaxMethod, str){
988
+ // Return a `function` that serves as the ajax method.
989
+ return function(data){
990
+ // If the ajax method has it's own way of getting `data`, use that.
991
+ data = ajaxMethod.data ?
992
+ ajaxMethod.data.apply(this, arguments) :
993
+ // Otherwise use the data passed in.
994
+ data;
995
+ // Return the ajax method with `data` and the `type` provided.
996
+ return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get")
997
+ }
998
+ }
999
+
1000
+
1001
+
1002
+ can.Observe("can.Model",{
1003
+ setup : function(base){
1004
+ can.Observe.apply(this, arguments);
1005
+ if(this === can.Model){
1006
+ return;
1007
+ }
1008
+ var self = this,
1009
+ clean = can.proxy(this._clean, self);
1010
+
1011
+ can.each(ajaxMethods, function(method, name){
1012
+ if ( ! can.isFunction( self[name] )) {
1013
+ self[name] = ajaxMaker(method, self[name]);
1014
+ }
1015
+ if (self["make"+can.capitalize(name)]){
1016
+ var newMethod = self["make"+can.capitalize(name)](self[name]);
1017
+ can.Construct._overwrite(self, base, name,function(){
1018
+ this._super;
1019
+ this._reqs++;
1020
+ return newMethod.apply(this, arguments).then(clean, clean);
1021
+ })
1022
+ }
1023
+ });
1024
+
1025
+ if(!self.fullName || self.fullName == base.fullName){
1026
+ self.fullName = self._shortName = "Model"+(++modelNum);
1027
+ }
1028
+ // Ddd ajax converters.
1029
+ this.store = {};
1030
+ this._reqs = 0;
1031
+ this._url = this._shortName+"/{"+this.id+"}"
1032
+ },
1033
+ _ajax : ajaxMaker,
1034
+ _clean : function(){
1035
+ this._reqs--;
1036
+ if(!this._reqs){
1037
+ for(var id in this.store) {
1038
+ if(!this.store[id]._bindings){
1039
+ delete this.store[id];
1040
+ }
1041
+ }
1042
+ }
1043
+ },
1044
+ models: function( instancesRawData ) {
1045
+
1046
+ if ( ! instancesRawData ) {
1047
+ return;
1048
+ }
1049
+
1050
+ if ( instancesRawData instanceof this.List ) {
1051
+ return instancesRawData;
1052
+ }
1053
+
1054
+ // Get the list type.
1055
+ var self = this,
1056
+ res = new( self.List || ML),
1057
+ // Did we get an `array`?
1058
+ arr = can.isArray(instancesRawData),
1059
+
1060
+ // Did we get a model list?
1061
+ ml = (instancesRawData instanceof ML),
1062
+
1063
+ // Get the raw `array` of objects.
1064
+ raw = arr ?
1065
+
1066
+ // If an `array`, return the `array`.
1067
+ instancesRawData :
1068
+
1069
+ // Otherwise if a model list.
1070
+ (ml ?
1071
+
1072
+ // Get the raw objects from the list.
1073
+ instancesRawData.serialize() :
1074
+
1075
+ // Get the object's data.
1076
+ instancesRawData.data),
1077
+ i = 0;
1078
+
1079
+
1080
+
1081
+ can.each(raw, function( rawPart ) {
1082
+ res.push( self.model( rawPart ));
1083
+ });
1084
+
1085
+ if ( ! arr ) { // Push other stuff onto `array`.
1086
+ can.each(instancesRawData, function(val, prop){
1087
+ if ( prop !== 'data' ) {
1088
+ res[prop] = val;
1089
+ }
1090
+ })
1091
+ }
1092
+ return res;
1093
+ },
1094
+ model: function( attributes ) {
1095
+ if (!attributes ) {
1096
+ return;
1097
+ }
1098
+ if ( attributes instanceof this ) {
1099
+ attributes = attributes.serialize();
1100
+ }
1101
+ var model = this.store[attributes[this.id]] ? this.store[attributes[this.id]].attr(attributes) : new this( attributes );
1102
+ if(this._reqs){
1103
+ this.store[attributes[this.id]] = model;
1104
+ }
1105
+ return model;
1106
+ }
1107
+ },
1108
+ {
1109
+ isNew: function() {
1110
+ var id = getId(this);
1111
+ return ! ( id || id === 0 ); // If `null` or `undefined`
1112
+ },
1113
+ save: function( success, error ) {
1114
+ return makeRequest(this, this.isNew() ? 'create' : 'update', success, error);
1115
+ },
1116
+ destroy: function( success, error ) {
1117
+ return makeRequest(this, 'destroy', success, error, 'destroyed');
1118
+ },
1119
+ bind : function(eventName){
1120
+ if ( ! ignoreHookup.test( eventName )) {
1121
+ if ( ! this._bindings ) {
1122
+ this.constructor.store[getId(this)] = this;
1123
+ this._bindings = 0;
1124
+ }
1125
+ this._bindings++;
1126
+ }
1127
+
1128
+ return can.Observe.prototype.bind.apply( this, arguments );
1129
+ },
1130
+ unbind : function(eventName){
1131
+ if(!ignoreHookup.test(eventName)) {
1132
+ this._bindings--;
1133
+ if(!this._bindings){
1134
+ delete this.constructor.store[getId(this)];
1135
+ }
1136
+ }
1137
+ return can.Observe.prototype.unbind.apply(this, arguments);
1138
+ },
1139
+ // Change `id`.
1140
+ ___set: function( prop, val ) {
1141
+ can.Observe.prototype.___set.call(this,prop, val)
1142
+ // If we add an `id`, move it to the store.
1143
+ if(prop === this.constructor.id && this._bindings){
1144
+ this.constructor.store[getId(this)] = this;
1145
+ }
1146
+ }
1147
+ });
1148
+
1149
+
1150
+
1151
+
1152
+ can.each({makeFindAll : "models", makeFindOne: "model"}, function(method, name){
1153
+ can.Model[name] = function(oldFind){
1154
+ return function(params, success, error){
1155
+ return pipe( oldFind.call( this, params ),
1156
+ this,
1157
+ method ).then(success,error)
1158
+ }
1159
+ };
1160
+ });
1161
+
1162
+ can.each([
1163
+ "created",
1164
+ "updated",
1165
+ "destroyed"], function( funcName ) {
1166
+ can.Model.prototype[funcName] = function( attrs ) {
1167
+ var stub,
1168
+ constructor = this.constructor;
1169
+
1170
+ // Update attributes if attributes have been passed
1171
+ stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs);
1172
+
1173
+ // Call event on the instance
1174
+ can.trigger(this,funcName);
1175
+ can.trigger(this,"change",funcName)
1176
+
1177
+
1178
+ // Call event on the instance's Class
1179
+ can.trigger(constructor,funcName, this);
1180
+ };
1181
+ });
1182
+
1183
+ // Model lists are just like `Observe.List` except that when their items are
1184
+ // destroyed, it automatically gets removed from the list.
1185
+ var ML = can.Observe.List('can.Model.List',{
1186
+ setup : function(){
1187
+ can.Observe.List.prototype.setup.apply(this, arguments );
1188
+ // Send destroy events.
1189
+ var self = this;
1190
+ this.bind('change', function(ev, how){
1191
+ if(/\w+\.destroyed/.test(how)){
1192
+ self.splice(self.indexOf(ev.target),1);
1193
+ }
1194
+ })
1195
+ }
1196
+ })
1197
+
1198
+ ;
1199
+
1200
+
1201
+ // ## deparam.js
1202
+ // `can.deparam`
1203
+ // _Takes a string of name value pairs and returns a Object literal that represents those params._
1204
+ var digitTest = /^\d+$/,
1205
+ keyBreaker = /([^\[\]]+)|(\[\])/g,
1206
+ paramTest = /([^?#]*)(#.*)?$/,
1207
+ prep = function( str ) {
1208
+ return decodeURIComponent( str.replace(/\+/g, " ") );
1209
+ }
1210
+
1211
+
1212
+ can.extend(can, {
1213
+ deparam: function(params){
1214
+
1215
+ var data = {},
1216
+ pairs, lastPart;
1217
+
1218
+ if ( params && paramTest.test( params )) {
1219
+
1220
+ pairs = params.split('&'),
1221
+
1222
+ can.each( pairs, function( pair ) {
1223
+
1224
+ var parts = pair.split('='),
1225
+ key = prep( parts.shift() ),
1226
+ value = prep( parts.join("=") );
1227
+
1228
+ current = data;
1229
+ parts = key.match(keyBreaker);
1230
+
1231
+ for ( var j = 0, l = parts.length - 1; j < l; j++ ) {
1232
+ if (!current[parts[j]] ) {
1233
+ // If what we are pointing to looks like an `array`
1234
+ current[parts[j]] = digitTest.test(parts[j+1]) || parts[j+1] == "[]" ? [] : {}
1235
+ }
1236
+ current = current[parts[j]];
1237
+ }
1238
+ lastPart = parts.pop()
1239
+ if ( lastPart == "[]" ) {
1240
+ current.push(value)
1241
+ } else {
1242
+ current[lastPart] = value;
1243
+ }
1244
+ });
1245
+ }
1246
+ return data;
1247
+ }
1248
+ });
1249
+
1250
+ // ## route.js
1251
+ // `can.route`
1252
+ // _Helps manage browser history (and client state) by synchronizing the
1253
+ // `window.location.hash` with a `can.Observe`._
1254
+ //
1255
+ // Helper methods used for matching routes.
1256
+ var
1257
+ // `RegExp` used to match route variables of the type ':name'.
1258
+ // Any word character or a period is matched.
1259
+ matcher = /\:([\w\.]+)/g,
1260
+ // Regular expression for identifying &amp;key=value lists.
1261
+ paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
1262
+ // Converts a JS Object into a list of parameters that can be
1263
+ // inserted into an html element tag.
1264
+ makeProps = function( props ) {
1265
+ var tags = [];
1266
+ can.each(props, function(val, name){
1267
+ tags.push( ( name === 'className' ? 'class' : name )+ '="' +
1268
+ (name === "href" ? val : can.esc(val) ) + '"');
1269
+ });
1270
+ return tags.join(" ");
1271
+ },
1272
+ // Checks if a route matches the data provided. If any route variable
1273
+ // is not present in the data, the route does not match. If all route
1274
+ // variables are present in the data, the number of matches is returned
1275
+ // to allow discerning between general and more specific routes.
1276
+ matchesData = function(route, data) {
1277
+ var count = 0, i = 0, defaults = {};
1278
+ // look at default values, if they match ...
1279
+ for( var name in route.defaults ) {
1280
+ if(route.defaults[name] === data[name]){
1281
+ // mark as matched
1282
+ defaults[name] = 1;
1283
+ count++;
1284
+ }
1285
+ }
1286
+ for (; i < route.names.length; i++ ) {
1287
+ if (!data.hasOwnProperty(route.names[i]) ) {
1288
+ return -1;
1289
+ }
1290
+ if(!defaults[route.names[i]]){
1291
+ count++;
1292
+ }
1293
+
1294
+ }
1295
+
1296
+ return count;
1297
+ },
1298
+ onready = !0,
1299
+ location = window.location,
1300
+ each = can.each,
1301
+ extend = can.extend;
1302
+
1303
+ can.route = function( url, defaults ) {
1304
+ defaults = defaults || {}
1305
+ // Extract the variable names and replace with `RegExp` that will match
1306
+ // an atual URL with values.
1307
+ var names = [],
1308
+ test = url.replace(matcher, function( whole, name, i ) {
1309
+ names.push(name);
1310
+ var next = "\\"+( url.substr(i+whole.length,1) || "&" )
1311
+ // a name without a default value HAS to have a value
1312
+ // a name that has a default value can be empty
1313
+ // The `\\` is for string-escaping giving single `\` for `RegExp` escaping.
1314
+ return "([^" +next+"]"+(defaults[name] ? "*" : "+")+")"
1315
+ });
1316
+
1317
+ // Add route in a form that can be easily figured out.
1318
+ can.route.routes[url] = {
1319
+ // A regular expression that will match the route when variable values
1320
+ // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which
1321
+ // will match for any value of `:page` and `:type` (word chars or period).
1322
+ test: new RegExp("^" + test+"($|&)"),
1323
+ // The original URL, same as the index for this entry in routes.
1324
+ route: url,
1325
+ // An `array` of all the variable names in this route.
1326
+ names: names,
1327
+ // Default values provided for the variables.
1328
+ defaults: defaults,
1329
+ // The number of parts in the URL separated by `/`.
1330
+ length: url.split('/').length
1331
+ }
1332
+ return can.route;
1333
+ };
1334
+
1335
+ extend(can.route, {
1336
+ param: function( data , _setRoute ) {
1337
+ // Check if the provided data keys match the names in any routes;
1338
+ // Get the one with the most matches.
1339
+ var route,
1340
+ // Need to have at least 1 match.
1341
+ matches = 0,
1342
+ matchCount,
1343
+ routeName = data.route,
1344
+ propCount = 0;
1345
+
1346
+ delete data.route;
1347
+
1348
+ each(data, function(){propCount++});
1349
+ // Otherwise find route.
1350
+ each(can.route.routes, function(temp, name){
1351
+ // best route is the first with all defaults matching
1352
+
1353
+
1354
+ matchCount = matchesData(temp, data);
1355
+ if ( matchCount > matches ) {
1356
+ route = temp;
1357
+ matches = matchCount
1358
+ }
1359
+ if(matchCount >= propCount){
1360
+ return false;
1361
+ }
1362
+ });
1363
+ // If we have a route name in our `can.route` data, and it's
1364
+ // just as good as what currently matches, use that
1365
+ if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data ) === matches) {
1366
+ route = can.route.routes[routeName];
1367
+ }
1368
+ // If this is match...
1369
+ if ( route ) {
1370
+ var cpy = extend({}, data),
1371
+ // Create the url by replacing the var names with the provided data.
1372
+ // If the default value is found an empty string is inserted.
1373
+ res = route.route.replace(matcher, function( whole, name ) {
1374
+ delete cpy[name];
1375
+ return data[name] === route.defaults[name] ? "" : encodeURIComponent( data[name] );
1376
+ }),
1377
+ after;
1378
+ // Remove matching default values
1379
+ each(route.defaults, function(val,name){
1380
+ if(cpy[name] === val) {
1381
+ delete cpy[name]
1382
+ }
1383
+ })
1384
+
1385
+ // The remaining elements of data are added as
1386
+ // `&amp;` separated parameters to the url.
1387
+ after = can.param(cpy);
1388
+ // if we are paraming for setting the hash
1389
+ // we also want to make sure the route value is updated
1390
+ if(_setRoute){
1391
+ can.route.attr('route',route.route);
1392
+ }
1393
+ return res + (after ? "&" + after : "")
1394
+ }
1395
+ // If no route was found, there is no hash URL, only paramters.
1396
+ return can.isEmptyObject(data) ? "" : "&" + can.param(data);
1397
+ },
1398
+ deparam: function( url ) {
1399
+ // See if the url matches any routes by testing it against the `route.test` `RegExp`.
1400
+ // By comparing the URL length the most specialized route that matches is used.
1401
+ var route = {
1402
+ length: -1
1403
+ };
1404
+ each(can.route.routes, function(temp, name){
1405
+ if ( temp.test.test(url) && temp.length > route.length ) {
1406
+ route = temp;
1407
+ }
1408
+ });
1409
+ // If a route was matched.
1410
+ if ( route.length > -1 ) {
1411
+ var // Since `RegExp` backreferences are used in `route.test` (parens)
1412
+ // the parts will contain the full matched string and each variable (back-referenced) value.
1413
+ parts = url.match(route.test),
1414
+ // Start will contain the full matched string; parts contain the variable values.
1415
+ start = parts.shift(),
1416
+ // The remainder will be the `&amp;key=value` list at the end of the URL.
1417
+ remainder = url.substr(start.length - (parts[parts.length-1] === "&" ? 1 : 0) ),
1418
+ // If there is a remainder and it contains a `&amp;key=value` list deparam it.
1419
+ obj = (remainder && paramsMatcher.test(remainder)) ? can.deparam( remainder.slice(1) ) : {};
1420
+
1421
+ // Add the default values for this route.
1422
+ obj = extend(true, {}, route.defaults, obj);
1423
+ // Overwrite each of the default values in `obj` with those in
1424
+ // parts if that part is not empty.
1425
+ each(parts,function(part, i){
1426
+ if ( part && part !== '&') {
1427
+ obj[route.names[i]] = decodeURIComponent( part );
1428
+ }
1429
+ });
1430
+ obj.route = route.route;
1431
+ return obj;
1432
+ }
1433
+ // If no route was matched, it is parsed as a `&amp;key=value` list.
1434
+ if ( url.charAt(0) !== '&' ) {
1435
+ url = '&' + url;
1436
+ }
1437
+ return paramsMatcher.test(url) ? can.deparam( url.slice(1) ) : {};
1438
+ },
1439
+ data: new can.Observe({}),
1440
+ routes: {},
1441
+ ready: function(val) {
1442
+ if( val === false ) {
1443
+ onready = val;
1444
+ }
1445
+ if( val === true || onready === true ) {
1446
+ setState();
1447
+ }
1448
+ return can.route;
1449
+ },
1450
+ url: function( options, merge ) {
1451
+ if (merge) {
1452
+ options = extend({}, curParams, options)
1453
+ }
1454
+ return "#!" + can.route.param(options)
1455
+ },
1456
+ link: function( name, options, props, merge ) {
1457
+ return "<a " + makeProps(
1458
+ extend({
1459
+ href: can.route.url(options, merge)
1460
+ }, props)) + ">" + name + "</a>";
1461
+ },
1462
+ current: function( options ) {
1463
+ return location.hash == "#!" + can.route.param(options)
1464
+ }
1465
+ });
1466
+
1467
+
1468
+ // The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will
1469
+ // instead act on the `can.route.data` observe.
1470
+ each(['bind','unbind','delegate','undelegate','attr','removeAttr'], function(name){
1471
+ can.route[name] = function(){
1472
+ return can.route.data[name].apply(can.route.data, arguments)
1473
+ }
1474
+ })
1475
+
1476
+ var // A ~~throttled~~ debounced function called multiple times will only fire once the
1477
+ // timer runs down. Each call resets the timer.
1478
+ timer,
1479
+ // Intermediate storage for `can.route.data`.
1480
+ curParams,
1481
+ // Deparameterizes the portion of the hash of interest and assign the
1482
+ // values to the `can.route.data` removing existing values no longer in the hash.
1483
+ // setState is called typically by hashchange which fires asynchronously
1484
+ // So it's possible that someone started changing the data before the
1485
+ // hashchange event fired. For this reason, it will not set the route data
1486
+ // if the data is changing and the hash already matches the hash that was set.
1487
+ setState = function() {
1488
+ var hash = location.href.split(/#!?/)[1] || ""
1489
+ curParams = can.route.deparam( hash );
1490
+
1491
+
1492
+ // if the hash data is currently changing, and
1493
+ // the hash is what we set it to anyway, do NOT change the hash
1494
+ if(!changingData || hash !== lastHash){
1495
+ can.route.attr(curParams, true);
1496
+ }
1497
+ },
1498
+ // The last hash caused by a data change
1499
+ lastHash,
1500
+ // Are data changes pending that haven't yet updated the hash
1501
+ changingData;
1502
+
1503
+ // If the hash changes, update the `can.route.data`.
1504
+ can.bind.call(window,'hashchange', setState);
1505
+
1506
+ // If the `can.route.data` changes, update the hash.
1507
+ // Using `.serialize()` retrieves the raw data contained in the `observable`.
1508
+ // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed.
1509
+ // This might be able to use batchNum and avoid this.
1510
+ can.route.bind("change", function(ev, attr) {
1511
+ // indicate that data is changing
1512
+ changingData = 1;
1513
+ clearTimeout( timer );
1514
+ timer = setTimeout(function() {
1515
+ // indicate that the hash is set to look like the data
1516
+ changingData = 0;
1517
+ var serialized = can.route.data.serialize();
1518
+ location.hash = "#!" + (lastHash = can.route.param(serialized, true))
1519
+ }, 1);
1520
+ });
1521
+ // `onready` event...
1522
+ can.bind.call(document,"ready",can.route.ready);
1523
+
1524
+ (function() {
1525
+
1526
+
1527
+ // ## control.js
1528
+ // `can.Control`
1529
+ // _Controller_
1530
+
1531
+ // Binds an element, returns a function that unbinds.
1532
+ var bind = function( el, ev, callback ) {
1533
+
1534
+ can.bind.call( el, ev, callback )
1535
+
1536
+ return function() {
1537
+ can.unbind.call(el, ev, callback);
1538
+ };
1539
+ },
1540
+ isFunction = can.isFunction,
1541
+ extend = can.extend,
1542
+ each = can.each,
1543
+ slice = [].slice,
1544
+ paramReplacer = /\{([^\}]+)\}/g,
1545
+ special = can.getObject("$.event.special") || {},
1546
+
1547
+ // Binds an element, returns a function that unbinds.
1548
+ delegate = function( el, selector, ev, callback ) {
1549
+ can.delegate.call(el, selector, ev, callback)
1550
+ return function() {
1551
+ can.undelegate.call(el, selector, ev, callback);
1552
+ };
1553
+ },
1554
+
1555
+ // Calls bind or unbind depending if there is a selector.
1556
+ binder = function( el, ev, callback, selector ) {
1557
+ return selector ?
1558
+ delegate( el, can.trim( selector ), ev, callback ) :
1559
+ bind( el, ev, callback );
1560
+ },
1561
+
1562
+ // Moves `this` to the first argument, wraps it with `jQuery` if it's an element
1563
+ shifter = function shifter(context, name) {
1564
+ var method = typeof name == "string" ? context[name] : name;
1565
+ if(!isFunction(method)){
1566
+ method = context[method];
1567
+ }
1568
+ return function() {
1569
+ context.called = name;
1570
+ return method.apply(context, [this.nodeName ? can.$(this) : this].concat( slice.call(arguments, 0)));
1571
+ };
1572
+ },
1573
+ basicProcessor;
1574
+
1575
+ can.Construct("can.Control",
1576
+ {
1577
+ // Setup pre-processes which methods are event listeners.
1578
+ setup: function() {
1579
+
1580
+ // Allow contollers to inherit "defaults" from super-classes as it
1581
+ // done in `can.Construct`
1582
+ can.Construct.setup.apply( this, arguments );
1583
+
1584
+ // If you didn't provide a name, or are `control`, don't do anything.
1585
+ if ( this !== can.Control ) {
1586
+
1587
+ // Cache the underscored names.
1588
+ var control = this,
1589
+ funcName;
1590
+
1591
+ // Calculate and cache actions.
1592
+ control.actions = {};
1593
+ for ( funcName in control.prototype ) {
1594
+ if ( control._isAction(funcName) ) {
1595
+ control.actions[funcName] = control._action(funcName);
1596
+ }
1597
+ }
1598
+ }
1599
+ },
1600
+ // Return `true` if is an action.
1601
+ _isAction: function( methodName ) {
1602
+
1603
+ var val = this.prototype[methodName],
1604
+ type = typeof val;
1605
+ // if not the constructor
1606
+ return (methodName !== 'constructor') &&
1607
+ // and is a function or links to a function
1608
+ ( type == "function" || (type == "string" && isFunction(this.prototype[val] ) ) ) &&
1609
+ // and is in special, a processor, or has a funny character
1610
+ !! ( special[methodName] || processors[methodName] || /[^\w]/.test(methodName) );
1611
+ },
1612
+ // Takes a method name and the options passed to a control
1613
+ // and tries to return the data necessary to pass to a processor
1614
+ // (something that binds things).
1615
+ _action: function( methodName, options ) {
1616
+
1617
+ // If we don't have options (a `control` instance), we'll run this
1618
+ // later.
1619
+ paramReplacer.lastIndex = 0;
1620
+ if ( options || ! paramReplacer.test( methodName )) {
1621
+ // If we have options, run sub to replace templates `{}` with a
1622
+ // value from the options or the window
1623
+ var convertedName = options ? can.sub(methodName, [options, window]) : methodName,
1624
+
1625
+ // If a `{}` resolves to an object, `convertedName` will be
1626
+ // an array
1627
+ arr = can.isArray(convertedName),
1628
+
1629
+ // Get the parts of the function
1630
+ // `[convertedName, delegatePart, eventPart]`
1631
+ // `/^(?:(.*?)\s)?([\w\.\:>]+)$/` - Breaker `RegExp`.
1632
+ parts = (arr ? convertedName[1] : convertedName).match(/^(?:(.*?)\s)?([\w\.\:>]+)$/);
1633
+
1634
+ var event = parts[2],
1635
+ processor = processors[event] || basicProcessor;
1636
+ return {
1637
+ processor: processor,
1638
+ parts: parts,
1639
+ delegate : arr ? convertedName[0] : undefined
1640
+ };
1641
+ }
1642
+ },
1643
+ // An object of `{eventName : function}` pairs that Control uses to
1644
+ // hook up events auto-magically.
1645
+ processors: {},
1646
+ // A object of name-value pairs that act as default values for a
1647
+ // control instance
1648
+ defaults: {}
1649
+ },
1650
+ {
1651
+ // Sets `this.element`, saves the control in `data, binds event
1652
+ // handlers.
1653
+ setup: function( element, options ) {
1654
+
1655
+ var cls = this.constructor,
1656
+ pluginname = cls.pluginName || cls._fullName,
1657
+ arr;
1658
+
1659
+ // Want the raw element here.
1660
+ this.element = can.$(element)
1661
+
1662
+ if ( pluginname && pluginname !== 'can_control') {
1663
+ // Set element and `className` on element.
1664
+ this.element.addClass(pluginname);
1665
+ }
1666
+
1667
+ (arr = can.data(this.element,"controls")) || can.data(this.element,"controls",arr = []);
1668
+ arr.push(this);
1669
+
1670
+ // Option merging.
1671
+ this.options = extend({}, cls.defaults, options);
1672
+
1673
+ // Bind all event handlers.
1674
+ this.on();
1675
+
1676
+ // Get's passed into `init`.
1677
+ return [this.element, this.options];
1678
+ },
1679
+ on: function( el, selector, eventName, func ) {
1680
+
1681
+ if ( ! el ) {
1682
+
1683
+ // Adds bindings.
1684
+ this.off();
1685
+
1686
+ // Go through the cached list of actions and use the processor
1687
+ // to bind
1688
+ var cls = this.constructor,
1689
+ bindings = this._bindings,
1690
+ actions = cls.actions,
1691
+ element = this.element,
1692
+ destroyCB = shifter(this,"destroy"),
1693
+ funcName, ready;
1694
+
1695
+ for ( funcName in actions ) {
1696
+ if ( actions.hasOwnProperty( funcName )) {
1697
+ ready = actions[funcName] || cls._action(funcName, this.options);
1698
+ bindings.push(
1699
+ ready.processor(ready.delegate || element,
1700
+ ready.parts[2],
1701
+ ready.parts[1],
1702
+ funcName,
1703
+ this));
1704
+ }
1705
+ }
1706
+
1707
+
1708
+ // Setup to be destroyed...
1709
+ // don't bind because we don't want to remove it.
1710
+ can.bind.call(element,"destroyed", destroyCB);
1711
+ bindings.push(function( el ) {
1712
+ can.unbind.call(el,"destroyed", destroyCB);
1713
+ });
1714
+ return bindings.length;
1715
+ }
1716
+
1717
+ if ( typeof el == 'string' ) {
1718
+ func = eventName;
1719
+ eventName = selector;
1720
+ selector = el;
1721
+ el = this.element;
1722
+ }
1723
+
1724
+ if ( typeof func == 'string' ) {
1725
+ func = shifter(this,func);
1726
+ }
1727
+
1728
+ this._bindings.push( binder( el, eventName, func, selector ));
1729
+
1730
+ return this._bindings.length;
1731
+ },
1732
+ // Unbinds all event handlers on the controller.
1733
+ off : function(){
1734
+ var el = this.element[0]
1735
+ each(this._bindings || [], function( value ) {
1736
+ value(el);
1737
+ });
1738
+ // Adds bindings.
1739
+ this._bindings = [];
1740
+ },
1741
+ // Prepares a `control` for garbage collection
1742
+ destroy: function() {
1743
+ var Class = this.constructor,
1744
+ pluginName = Class.pluginName || Class._fullName,
1745
+ controls;
1746
+
1747
+ // Unbind bindings.
1748
+ this.off();
1749
+
1750
+ if(pluginName && pluginName !== 'can_control'){
1751
+ // Remove the `className`.
1752
+ this.element.removeClass(pluginName);
1753
+ }
1754
+
1755
+ // Remove from `data`.
1756
+ controls = can.data(this.element,"controls");
1757
+ controls.splice(can.inArray(this, controls),1);
1758
+
1759
+ can.trigger( this, "destroyed"); // In case we want to know if the `control` is removed.
1760
+
1761
+ this.element = null;
1762
+ }
1763
+ });
1764
+
1765
+ var processors = can.Control.processors,
1766
+
1767
+ // Processors do the binding.
1768
+ // They return a function that unbinds when called.
1769
+ //
1770
+ // The basic processor that binds events.
1771
+ basicProcessor = function( el, event, selector, methodName, control ) {
1772
+ return binder( el, event, shifter(control, methodName), selector);
1773
+ };
1774
+
1775
+ // Set common events to be processed as a `basicProcessor`
1776
+ each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup",
1777
+ "keypress", "mousedown", "mousemove", "mouseout", "mouseover",
1778
+ "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin",
1779
+ "focusout", "mouseenter", "mouseleave"], function( v ) {
1780
+ processors[v] = basicProcessor;
1781
+ });
1782
+
1783
+ }());
1784
+
1785
+
1786
+ // ## control/route.js
1787
+ // _Controller route integration._
1788
+
1789
+ can.Control.processors.route = function( el, event, selector, funcName, controller ) {
1790
+ can.route( selector || "" )
1791
+ var batchNum,
1792
+ check = function( ev, attr, how ) {
1793
+ if ( can.route.attr('route') === ( selector || "" ) &&
1794
+ ( ev.batchNum === undefined || ev.batchNum !== batchNum ) ) {
1795
+
1796
+ batchNum = ev.batchNum;
1797
+
1798
+ var d = can.route.attr();
1799
+ delete d.route;
1800
+ if(can.isFunction(controller[funcName])){
1801
+ controller[funcName]( d )
1802
+ }else {
1803
+ controller[controller[funcName]](d)
1804
+ }
1805
+
1806
+ }
1807
+ }
1808
+ can.route.bind( 'change', check );
1809
+ return function() {
1810
+ can.route.unbind( 'change', check )
1811
+ }
1812
+ }
1813
+ ;
1814
+
1815
+ // ## view.js
1816
+ // `can.view`
1817
+ // _Templating abstraction._
1818
+
1819
+ var isFunction = can.isFunction,
1820
+ makeArray = can.makeArray,
1821
+ // Used for hookup `id`s.
1822
+ hookupId = 1,
1823
+ $view = can.view = function(view, data, helpers, callback){
1824
+ // Get the result.
1825
+ var result = $view.render(view, data, helpers, callback);
1826
+ if(can.isDeferred(result)){
1827
+ return result.pipe(function(result){
1828
+ return $view.frag(result);
1829
+ })
1830
+ }
1831
+
1832
+ // Convert it into a dom frag.
1833
+ return $view.frag(result);
1834
+ };
1835
+
1836
+ can.extend( $view, {
1837
+ // creates a frag and hooks it up all at once
1838
+ frag: function(result, parentNode ){
1839
+ return $view.hookup( $view.fragment(result), parentNode );
1840
+ },
1841
+ // simply creates a frag
1842
+ // this is used internally to create a frag
1843
+ // insert it
1844
+ // then hook it up
1845
+ fragment: function(result){
1846
+ var frag = can.buildFragment(result,document.body);
1847
+ // If we have an empty frag...
1848
+ if(!frag.childNodes.length) {
1849
+ frag.appendChild(document.createTextNode(''))
1850
+ }
1851
+ return frag;
1852
+ },
1853
+ // Convert a path like string into something that's ok for an `element` ID.
1854
+ toId : function( src ) {
1855
+ return can.map(src.toString().split(/\/|\./g), function( part ) {
1856
+ // Dont include empty strings in toId functions
1857
+ if ( part ) {
1858
+ return part;
1859
+ }
1860
+ }).join("_");
1861
+ },
1862
+ hookup: function(fragment, parentNode ){
1863
+ var hookupEls = [],
1864
+ id,
1865
+ func,
1866
+ el,
1867
+ i=0;
1868
+
1869
+ // Get all `childNodes`.
1870
+ can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node){
1871
+ if(node.nodeType === 1){
1872
+ hookupEls.push(node)
1873
+ hookupEls.push.apply(hookupEls, can.makeArray( node.getElementsByTagName('*')))
1874
+ }
1875
+ });
1876
+ // Filter by `data-view-id` attribute.
1877
+ for (; el = hookupEls[i++]; ) {
1878
+
1879
+ if ( el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id]) ) {
1880
+ func(el, parentNode, id);
1881
+ delete $view.hookups[id];
1882
+ el.removeAttribute('data-view-id');
1883
+ }
1884
+ }
1885
+ return fragment;
1886
+ },
1887
+ hookups: {},
1888
+ hook: function( cb ) {
1889
+ $view.hookups[++hookupId] = cb;
1890
+ return " data-view-id='"+hookupId+"'";
1891
+ },
1892
+ cached: {},
1893
+ cache: true,
1894
+ register: function( info ) {
1895
+ this.types["." + info.suffix] = info;
1896
+ },
1897
+ types: {},
1898
+ ext: ".ejs",
1899
+ registerScript: function() {},
1900
+ preload: function( ) {},
1901
+ render: function( view, data, helpers, callback ) {
1902
+ // If helpers is a `function`, it is actually a callback.
1903
+ if ( isFunction( helpers )) {
1904
+ callback = helpers;
1905
+ helpers = undefined;
1906
+ }
1907
+
1908
+ // See if we got passed any deferreds.
1909
+ var deferreds = getDeferreds(data);
1910
+
1911
+
1912
+ if ( deferreds.length ) { // Does data contain any deferreds?
1913
+ // The deferred that resolves into the rendered content...
1914
+ var deferred = new can.Deferred();
1915
+
1916
+ // Add the view request to the list of deferreds.
1917
+ deferreds.push(get(view, true))
1918
+
1919
+ // Wait for the view and all deferreds to finish...
1920
+ can.when.apply(can, deferreds).then(function( resolved ) {
1921
+ // Get all the resolved deferreds.
1922
+ var objs = makeArray(arguments),
1923
+ // Renderer is the last index of the data.
1924
+ renderer = objs.pop(),
1925
+ // The result of the template rendering with data.
1926
+ result;
1927
+
1928
+ // Make data look like the resolved deferreds.
1929
+ if ( can.isDeferred(data) ) {
1930
+ data = usefulPart(resolved);
1931
+ }
1932
+ else {
1933
+ // Go through each prop in data again and
1934
+ // replace the defferreds with what they resolved to.
1935
+ for ( var prop in data ) {
1936
+ if ( can.isDeferred(data[prop]) ) {
1937
+ data[prop] = usefulPart(objs.shift());
1938
+ }
1939
+ }
1940
+ }
1941
+ // Get the rendered result.
1942
+ result = renderer(data, helpers);
1943
+
1944
+ // Resolve with the rendered view.
1945
+ deferred.resolve(result);
1946
+ // If there's a `callback`, call it back with the result.
1947
+ callback && callback(result);
1948
+ });
1949
+ // Return the deferred...
1950
+ return deferred;
1951
+ }
1952
+ else {
1953
+ // No deferreds! Render this bad boy.
1954
+ var response,
1955
+ // If there's a `callback` function
1956
+ async = isFunction( callback ),
1957
+ // Get the `view` type
1958
+ deferred = get(view, async);
1959
+
1960
+ // If we are `async`...
1961
+ if ( async ) {
1962
+ // Return the deferred
1963
+ response = deferred;
1964
+ // And fire callback with the rendered result.
1965
+ deferred.then(function( renderer ) {
1966
+ callback(renderer(data, helpers))
1967
+ })
1968
+ } else {
1969
+ // Otherwise, the deferred is complete, so
1970
+ // set response to the result of the rendering.
1971
+ deferred.then(function( renderer ) {
1972
+ response = renderer(data, helpers);
1973
+ });
1974
+ }
1975
+
1976
+ return response;
1977
+ }
1978
+ }
1979
+ });
1980
+ // Returns `true` if something looks like a deferred.
1981
+ can.isDeferred = function( obj ) {
1982
+ return obj && isFunction(obj.then) && isFunction(obj.pipe) // Check if `obj` is a `can.Deferred`.
1983
+ }
1984
+ // Makes sure there's a template, if not, have `steal` provide a warning.
1985
+ var checkText = function( text, url ) {
1986
+ if ( ! text.length ) {
1987
+ //@steal-remove-start
1988
+ //@steal-remove-end
1989
+ throw "can.view: No template or empty template:" + url;
1990
+ }
1991
+ },
1992
+ // `Returns a `view` renderer deferred.
1993
+ // `url` - The url to the template.
1994
+ // `async` - If the ajax request should be asynchronous.
1995
+ // Returns a deferred.
1996
+ get = function( url, async ) {
1997
+
1998
+
1999
+ var suffix = url.match(/\.[\w\d]+$/),
2000
+ type,
2001
+ // If we are reading a script element for the content of the template,
2002
+ // `el` will be set to that script element.
2003
+ el,
2004
+ // A unique identifier for the view (used for caching).
2005
+ // This is typically derived from the element id or
2006
+ // the url for the template.
2007
+ id,
2008
+ // The ajax request used to retrieve the template content.
2009
+ jqXHR,
2010
+ // Used to generate the response.
2011
+ response = function( text ) {
2012
+ // Get the renderer function.
2013
+ var func = type.renderer(id, text),
2014
+ d = new can.Deferred();
2015
+ d.resolve(func)
2016
+ // Cache if we are caching.
2017
+ if ( $view.cache ) {
2018
+ $view.cached[id] = d;
2019
+ }
2020
+ // Return the objects for the response's `dataTypes`
2021
+ // (in this case view).
2022
+ return d;
2023
+ };
2024
+
2025
+ //If the url has a #, we assume we want to use an inline template
2026
+ //from a script element and not current page's HTML
2027
+ if( url.match(/^#/) ) {
2028
+ url = url.substr(1);
2029
+ }
2030
+ // If we have an inline template, derive the suffix from the `text/???` part.
2031
+ // This only supports `<script>` tags.
2032
+ if ( el = document.getElementById(url) ) {
2033
+ suffix = "."+el.type.match(/\/(x\-)?(.+)/)[2];
2034
+ }
2035
+
2036
+ // If there is no suffix, add one.
2037
+ if (!suffix && !$view.cached[url] ) {
2038
+ url += ( suffix = $view.ext );
2039
+ }
2040
+
2041
+ if ( can.isArray( suffix )) {
2042
+ suffix = suffix[0]
2043
+ }
2044
+
2045
+ // Convert to a unique and valid id.
2046
+ id = can.view.toId(url);
2047
+
2048
+ // If an absolute path, use `steal` to get it.
2049
+ // You should only be using `//` if you are using `steal`.
2050
+ if ( url.match(/^\/\//) ) {
2051
+ var sub = url.substr(2);
2052
+ url = ! window.steal ?
2053
+ "/" + sub :
2054
+ steal.root.mapJoin(sub);
2055
+ }
2056
+
2057
+ // Set the template engine type.
2058
+ type = $view.types[suffix];
2059
+
2060
+ // If it is cached,
2061
+ if ( $view.cached[id] ) {
2062
+ // Return the cached deferred renderer.
2063
+ return $view.cached[id];
2064
+
2065
+ // Otherwise if we are getting this from a `<script>` element.
2066
+ } else if ( el ) {
2067
+ // Resolve immediately with the element's `innerHTML`.
2068
+ return response(el.innerHTML);
2069
+ } else {
2070
+ // Make an ajax request for text.
2071
+ var d = new can.Deferred();
2072
+ can.ajax({
2073
+ async: async,
2074
+ url: url,
2075
+ dataType: "text",
2076
+ error: function(jqXHR) {
2077
+ checkText("", url);
2078
+ d.reject(jqXHR);
2079
+ },
2080
+ success: function( text ) {
2081
+ // Make sure we got some text back.
2082
+ checkText(text, url);
2083
+ d.resolve(type.renderer(id, text))
2084
+ // Cache if if we are caching.
2085
+ if ( $view.cache ) {
2086
+ $view.cached[id] = d;
2087
+ }
2088
+
2089
+ }
2090
+ });
2091
+ return d;
2092
+ }
2093
+ },
2094
+ // Gets an `array` of deferreds from an `object`.
2095
+ // This only goes one level deep.
2096
+ getDeferreds = function( data ) {
2097
+ var deferreds = [];
2098
+
2099
+ // pull out deferreds
2100
+ if ( can.isDeferred(data) ) {
2101
+ return [data]
2102
+ } else {
2103
+ for ( var prop in data ) {
2104
+ if ( can.isDeferred(data[prop]) ) {
2105
+ deferreds.push(data[prop]);
2106
+ }
2107
+ }
2108
+ }
2109
+ return deferreds;
2110
+ },
2111
+ // Gets the useful part of a resolved deferred.
2112
+ // This is for `model`s and `can.ajax` that resolve to an `array`.
2113
+ usefulPart = function( resolved ) {
2114
+ return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved
2115
+ };
2116
+
2117
+
2118
+ if ( window.steal ) {
2119
+ steal.type("view js", function( options, success, error ) {
2120
+ var type = can.view.types["." + options.type],
2121
+ id = can.view.toId(options.rootSrc);
2122
+
2123
+ options.text = "steal('" + (type.plugin || "can/view/" + options.type) + "').then(function($){" + "can.view.preload('" + id + "'," + options.text + ");\n})";
2124
+ success();
2125
+ })
2126
+ }
2127
+
2128
+ //!steal-pluginify-remove-start
2129
+ can.extend(can.view, {
2130
+ register: function( info ) {
2131
+ this.types["." + info.suffix] = info;
2132
+
2133
+ if ( window.steal ) {
2134
+ steal.type(info.suffix + " view js", function( options, success, error ) {
2135
+ var type = can.view.types["." + options.type],
2136
+ id = can.view.toId(options.rootSrc+'');
2137
+
2138
+ options.text = type.script(id, options.text)
2139
+ success();
2140
+ })
2141
+ }
2142
+ can.view[info.suffix] = function(id, text){
2143
+ $view.preload(id, info.renderer(id, text) )
2144
+ }
2145
+ },
2146
+ registerScript: function( type, id, src ) {
2147
+ return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
2148
+ },
2149
+ preload: function( id, renderer ) {
2150
+ can.view.cached[id] = new can.Deferred().resolve(function( data, helpers ) {
2151
+ return renderer.call(data, data, helpers);
2152
+ });
2153
+ }
2154
+
2155
+ });
2156
+ //!steal-pluginify-remove-end
2157
+
2158
+
2159
+ // returns the
2160
+ // - observes and attr methods are called by func
2161
+ // - the value returned by func
2162
+ // ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}`
2163
+ var getValueAndObserved = function(func, self){
2164
+
2165
+ var oldReading;
2166
+ if (can.Observe) {
2167
+ // Set a callback on can.Observe to know
2168
+ // when an attr is read.
2169
+ // Keep a reference to the old reader
2170
+ // if there is one. This is used
2171
+ // for nested live binding.
2172
+ oldReading = can.Observe.__reading;
2173
+ can.Observe.__reading = function(obj, attr){
2174
+ // Add the observe and attr that was read
2175
+ // to `observed`
2176
+ observed.push({
2177
+ obj: obj,
2178
+ attr: attr
2179
+ });
2180
+ }
2181
+ }
2182
+
2183
+ var observed = [],
2184
+ // Call the "wrapping" function to get the value. `observed`
2185
+ // will have the observe/attribute pairs that were read.
2186
+ value = func.call(self);
2187
+
2188
+ // Set back so we are no longer reading.
2189
+ if(can.Observe){
2190
+ can.Observe.__reading = oldReading;
2191
+ }
2192
+ return {
2193
+ value : value,
2194
+ observed : observed
2195
+ }
2196
+ },
2197
+ // Calls `callback(newVal, oldVal)` everytime an observed property
2198
+ // called within `getterSetter` is changed and creates a new result of `getterSetter`.
2199
+ // Also returns an object that can teardown all event handlers.
2200
+ computeBinder = function(getterSetter, context, callback){
2201
+ // track what we are observing
2202
+ var observing = {},
2203
+ // a flag indicating if this observe/attr pair is already bound
2204
+ matched = true,
2205
+ // the data to return
2206
+ data = {
2207
+ // we will maintain the value while live-binding is taking place
2208
+ value : undefined,
2209
+ // a teardown method that stops listening
2210
+ teardown: function(){
2211
+ for ( var name in observing ) {
2212
+ var ob = observing[name];
2213
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
2214
+ delete observing[name];
2215
+ }
2216
+ }
2217
+ };
2218
+
2219
+ // when a property value is cahnged
2220
+ var onchanged = function(){
2221
+ // store the old value
2222
+ var oldValue = data.value,
2223
+ // get the new value
2224
+ newvalue = getValueAndBind();
2225
+ // update the value reference (in case someone reads)
2226
+ data.value = newvalue
2227
+ // if a change happened
2228
+ if(newvalue !== oldValue){
2229
+ callback(newvalue, oldValue);
2230
+ };
2231
+ };
2232
+
2233
+ // gets the value returned by `getterSetter` and also binds to any attributes
2234
+ // read by the call
2235
+ var getValueAndBind = function(){
2236
+ var info = getValueAndObserved( getterSetter, context ),
2237
+ newObserveSet = info.observed;
2238
+
2239
+ var value = info.value;
2240
+ matched = !matched;
2241
+
2242
+ // go through every attribute read by this observe
2243
+ can.each(newObserveSet, function(ob){
2244
+ // if the observe/attribute pair is being observed
2245
+ if(observing[ob.obj._namespace+"|"+ob.attr]){
2246
+ // mark at as observed
2247
+ observing[ob.obj._namespace+"|"+ob.attr].matched = matched;
2248
+ } else {
2249
+ // otherwise, set the observe/attribute on oldObserved, marking it as being observed
2250
+ observing[ob.obj._namespace+"|"+ob.attr] = {
2251
+ matched: matched,
2252
+ observe: ob
2253
+ };
2254
+ ob.obj.bind(ob.attr, onchanged)
2255
+ }
2256
+ });
2257
+
2258
+ // Iterate through oldObserved, looking for observe/attributes
2259
+ // that are no longer being bound and unbind them
2260
+ for ( var name in observing ) {
2261
+ var ob = observing[name];
2262
+ if(ob.matched !== matched){
2263
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
2264
+ delete observing[name];
2265
+ }
2266
+ }
2267
+ return value;
2268
+ }
2269
+ // set the initial value
2270
+ data.value = getValueAndBind();
2271
+ data.isListening = ! can.isEmptyObject(observing);
2272
+ return data;
2273
+ }
2274
+
2275
+ // if no one is listening ... we can not calculate every time
2276
+ can.compute = function(getterSetter, context){
2277
+ if(getterSetter.isComputed){
2278
+ return getterSetter;
2279
+ }
2280
+ // get the value right away
2281
+ // TODO: eventually we can defer this until a bind or a read
2282
+ var computedData,
2283
+ bindings = 0,
2284
+ computed,
2285
+ canbind = true;
2286
+ if(typeof getterSetter === "function"){
2287
+ computed = function(value){
2288
+ if(value === undefined){
2289
+ // we are reading
2290
+ if(computedData){
2291
+ return computedData.value;
2292
+ } else {
2293
+ return getterSetter.call(context || this)
2294
+ }
2295
+ } else {
2296
+ return getterSetter.apply(context || this, arguments)
2297
+ }
2298
+ }
2299
+
2300
+ } else {
2301
+ // we just gave it a value
2302
+ computed = function(val){
2303
+ if(val === undefined){
2304
+ return getterSetter;
2305
+ } else {
2306
+ var old = getterSetter;
2307
+ getterSetter = val;
2308
+ if( old !== val){
2309
+ can.trigger(computed, "change",[val, old]);
2310
+ }
2311
+
2312
+ return val;
2313
+ }
2314
+
2315
+ }
2316
+ canbind = false;
2317
+ }
2318
+ computed.isComputed = true;
2319
+
2320
+
2321
+ computed.bind = function(ev, handler){
2322
+ can.addEvent.apply(computed, arguments);
2323
+ if( bindings === 0 && canbind){
2324
+ // setup live-binding
2325
+ computedData = computeBinder(getterSetter, context || this, function(newValue, oldValue){
2326
+ can.trigger(computed, "change",[newValue, oldValue])
2327
+ });
2328
+ }
2329
+ bindings++;
2330
+ }
2331
+ computed.unbind = function(ev, handler){
2332
+ can.removeEvent.apply(computed, arguments);
2333
+ bindings--;
2334
+ if( bindings === 0 && canbind){
2335
+ computedData.teardown();
2336
+ }
2337
+
2338
+ };
2339
+ return computed;
2340
+ };
2341
+ can.compute.binder = computeBinder;
2342
+
2343
+ // ## ejs.js
2344
+ // `can.EJS`
2345
+ // _Embedded JavaScript Templates._
2346
+
2347
+ // Helper methods.
2348
+ var myEval = function( script ) {
2349
+ eval(script);
2350
+ },
2351
+ extend = can.extend,
2352
+ // Regular expressions for caching.
2353
+ quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
2354
+ attrReg = /([^\s]+)=$/,
2355
+ newLine = /(\r|\n)+/g,
2356
+ attributeReplace = /__!!__/g,
2357
+ tagMap = {
2358
+ "": "span",
2359
+ table: "tr",
2360
+ tr: "td",
2361
+ ol: "li",
2362
+ ul: "li",
2363
+ tbody: "tr",
2364
+ thead: "tr",
2365
+ tfoot: "tr"
2366
+ },
2367
+ // Escapes characters starting with `\`.
2368
+ clean = function( content ) {
2369
+ return content
2370
+ .split('\\').join("\\\\")
2371
+ .split("\n").join("\\n")
2372
+ .split('"').join('\\"')
2373
+ .split("\t").join("\\t");
2374
+ },
2375
+ bracketNum = function(content){
2376
+ return (--content.split("{").length) - (--content.split("}").length);
2377
+ },
2378
+ // Cross-browser attribute methods.
2379
+ // These should be mapped to the underlying library.
2380
+ attrMap = {
2381
+ "class" : "className"
2382
+ },
2383
+ bool = can.each(["checked","disabled","readonly","required"], function(n){
2384
+ attrMap[n] = n;
2385
+ }),
2386
+ setAttr = function(el, attrName, val){
2387
+ attrMap[attrName] ?
2388
+ (el[attrMap[attrName]] = can.inArray(attrName,bool) > -1? true : val):
2389
+ el.setAttribute(attrName, val);
2390
+ },
2391
+ getAttr = function(el, attrName){
2392
+ return attrMap[attrName]?
2393
+ el[attrMap[attrName]]:
2394
+ el.getAttribute(attrName);
2395
+ },
2396
+ removeAttr = function(el, attrName){
2397
+ if(can.inArray(attrName,bool) > -1){
2398
+ el[attrName] = false;
2399
+ } else{
2400
+ el.removeAttribute(attrName)
2401
+ }
2402
+ },
2403
+ // a helper to get the parentNode for a given element el
2404
+ // if el is in a documentFragment, it will return defaultParentNode
2405
+ getParentNode = function(el, defaultParentNode){
2406
+ return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
2407
+ },
2408
+ // helper to know if property is not an expando on oldObserved's list of observes
2409
+ // this should probably be removed and oldObserved should just have a
2410
+ // property with observes
2411
+ observeProp = function(name){
2412
+ return name.indexOf("|") >= 0;
2413
+ },
2414
+ // Returns escaped/sanatized content for anything other than a live-binding
2415
+ contentEscape = function( txt ) {
2416
+ return (typeof txt == 'string' || typeof txt == 'number') ?
2417
+ can.esc( txt ) :
2418
+ contentText(txt);
2419
+ },
2420
+ // Returns text content for anything other than a live-binding
2421
+ contentText = function( input ) {
2422
+
2423
+ // If it's a string, return.
2424
+ if ( typeof input == 'string' ) {
2425
+ return input;
2426
+ }
2427
+ // If has no value, return an empty string.
2428
+ if ( !input && input != 0 ) {
2429
+ return '';
2430
+ }
2431
+
2432
+ // If it's an object, and it has a hookup method.
2433
+ var hook = (input.hookup &&
2434
+
2435
+ // Make a function call the hookup method.
2436
+ function( el, id ) {
2437
+ input.hookup.call(input, el, id);
2438
+ }) ||
2439
+
2440
+ // Or if it's a `function`, just use the input.
2441
+ (typeof input == 'function' && input);
2442
+
2443
+ // Finally, if there is a `function` to hookup on some dom,
2444
+ // add it to pending hookups.
2445
+ if ( hook ) {
2446
+ pendingHookups.push(hook);
2447
+ return '';
2448
+ }
2449
+
2450
+ // Finally, if all else is `false`, `toString()` it.
2451
+ return "" + input;
2452
+ },
2453
+ // The EJS constructor function
2454
+ EJS = function( options ) {
2455
+ // Supports calling EJS without the constructor
2456
+ // This returns a function that renders the template.
2457
+ if ( this.constructor != EJS ) {
2458
+ var ejs = new EJS(options);
2459
+ return function( data, helpers ) {
2460
+ return ejs.render(data, helpers);
2461
+ };
2462
+ }
2463
+ // If we get a `function` directly, it probably is coming from
2464
+ // a `steal`-packaged view.
2465
+ if ( typeof options == "function" ) {
2466
+ this.template = {
2467
+ fn: options
2468
+ };
2469
+ return;
2470
+ }
2471
+ // Set options on self.
2472
+ extend(this, options);
2473
+ this.template = scan(this.text, this.name);
2474
+ };
2475
+
2476
+ can.EJS = EJS;
2477
+ EJS.prototype.
2478
+ render = function( object, extraHelpers ) {
2479
+ object = object || {};
2480
+ return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
2481
+ };
2482
+ extend(EJS, {
2483
+ // Called to return the content within a magic tag like `<%= %>`.
2484
+ // - escape - if the content returned should be escaped
2485
+ // - tagName - the tag name the magic tag is within or the one that proceeds the magic tag
2486
+ // - status - where the tag is in. The status can be:
2487
+ // - _STRING_ - The name of the attribute the magic tag is within
2488
+ // - `1` - The magic tag is within a tag like `<div <%= %>>`
2489
+ // - `0` - The magic tag is outside (or between) tags like `<div><%= %></div>`
2490
+ // - self - the `this` the template was called with
2491
+ // - func - the "wrapping" function. For example: `<%= task.attr('name') %>` becomes
2492
+ // `(function(){return task.attr('name')})
2493
+ txt : function(escape, tagName, status, self, func){
2494
+ // call the "wrapping" function and get the binding information
2495
+ var binding = can.compute.binder(func, self, function(newVal, oldVal){
2496
+ // call the update method we will define for each
2497
+ // type of attribute
2498
+ update(newVal, oldVal)
2499
+ });
2500
+
2501
+ // If we had no observes just return the value returned by func.
2502
+ if(!binding.isListening){
2503
+ return (escape || status !== 0? contentEscape : contentText)(binding.value);
2504
+ }
2505
+ // The following are helper methods or varaibles that will
2506
+ // be defined by one of the various live-updating schemes.
2507
+
2508
+ // The parent element we are listening to for teardown
2509
+ var parentElement,
2510
+ // if the parent element is removed, teardown the binding
2511
+ setupTeardownOnDestroy = function(el){
2512
+ can.bind.call(el,'destroyed', binding.teardown)
2513
+ parentElement = el;
2514
+ },
2515
+ // if there is no parent, undo bindings
2516
+ teardownCheck = function(parent){
2517
+ if(!parent){
2518
+ binding.teardown();
2519
+ can.unbind.call(parentElement,'destroyed', binding.teardown)
2520
+ }
2521
+ },
2522
+ // the tag type to insert
2523
+ tag = (tagMap[tagName] || "span"),
2524
+ // this will be filled in if binding.isListening
2525
+ update;
2526
+
2527
+
2528
+ // The magic tag is outside or between tags.
2529
+ if(status == 0){
2530
+ // Return an element tag with a hookup in place of the content
2531
+ return "<" +tag+can.view.hook(
2532
+ escape ?
2533
+ // If we are escaping, replace the parentNode with
2534
+ // a text node who's value is `func`'s return value.
2535
+ function(el, parentNode){
2536
+ // updates the text of the text node
2537
+ update = function(newVal){
2538
+ node.nodeValue = ""+newVal;
2539
+ teardownCheck(node.parentNode);
2540
+ };
2541
+
2542
+ var parent = getParentNode(el, parentNode),
2543
+ node = document.createTextNode(binding.value);
2544
+
2545
+ parent.insertBefore(node, el);
2546
+ parent.removeChild(el);
2547
+ setupTeardownOnDestroy(parent);
2548
+ }
2549
+ :
2550
+ // If we are not escaping, replace the parentNode with a
2551
+ // documentFragment created as with `func`'s return value.
2552
+ function(span, parentNode){
2553
+ // updates the elements with the new content
2554
+ update = function(newVal){
2555
+ // is this still part of the DOM?
2556
+ var attached = nodes[0].parentNode;
2557
+ // update the nodes in the DOM with the new rendered value
2558
+ if( attached ) {
2559
+ nodes = makeAndPut(newVal, nodes);
2560
+ }
2561
+ teardownCheck(nodes[0].parentNode)
2562
+ }
2563
+
2564
+ // make sure we have a valid parentNode
2565
+ parentNode = getParentNode(span, parentNode)
2566
+ // A helper function to manage inserting the contents
2567
+ // and removing the old contents
2568
+ var makeAndPut = function(val, remove){
2569
+ // create the fragment, but don't hook it up
2570
+ // we need to insert it into the document first
2571
+
2572
+ var frag = can.view.frag(val, parentNode),
2573
+ // keep a reference to each node
2574
+ nodes = can.map(frag.childNodes,function(node){
2575
+ return node;
2576
+ }),
2577
+ last = remove[remove.length - 1];
2578
+
2579
+ // Insert it in the `document` or `documentFragment`
2580
+ if( last.nextSibling ){
2581
+ last.parentNode.insertBefore(frag, last.nextSibling)
2582
+ } else {
2583
+ last.parentNode.appendChild(frag)
2584
+ }
2585
+ // Remove the old content.
2586
+ can.remove( can.$(remove) );
2587
+
2588
+ return nodes;
2589
+ },
2590
+ // nodes are the nodes that any updates will replace
2591
+ // at this point, these nodes could be part of a documentFragment
2592
+ nodes = makeAndPut(binding.value, [span]);
2593
+
2594
+
2595
+ setupTeardownOnDestroy(parentNode);
2596
+
2597
+ }) + "></" +tag+">";
2598
+ // In a tag, but not in an attribute
2599
+ } else if(status === 1){
2600
+ // remember the old attr name
2601
+ var attrName = binding.value.replace(/['"]/g, '').split('=')[0];
2602
+ pendingHookups.push(function(el) {
2603
+ update = function(newVal){
2604
+ var parts = (newVal|| "").replace(/['"]/g, '').split('='),
2605
+ newAttrName = parts[0];
2606
+
2607
+ // Remove if we have a change and used to have an `attrName`.
2608
+ if((newAttrName != attrName) && attrName){
2609
+ removeAttr(el,attrName)
2610
+ }
2611
+ // Set if we have a new `attrName`.
2612
+ if(newAttrName){
2613
+ setAttr(el, newAttrName, parts[1]);
2614
+ attrName = newAttrName;
2615
+ }
2616
+ }
2617
+ setupTeardownOnDestroy(el);
2618
+ });
2619
+
2620
+ return binding.value;
2621
+ } else { // In an attribute...
2622
+ pendingHookups.push(function(el){
2623
+ // update will call this attribute's render method
2624
+ // and set the attribute accordingly
2625
+ update = function(){
2626
+ setAttr(el, status, hook.render())
2627
+ }
2628
+
2629
+ var wrapped = can.$(el),
2630
+ hooks;
2631
+
2632
+ // Get the list of hookups or create one for this element.
2633
+ // Hooks is a map of attribute names to hookup `data`s.
2634
+ // Each hookup data has:
2635
+ // `render` - A `function` to render the value of the attribute.
2636
+ // `funcs` - A list of hookup `function`s on that attribute.
2637
+ // `batchNum` - The last event `batchNum`, used for performance.
2638
+ (hooks = can.data(wrapped,'hooks')) || can.data(wrapped, 'hooks', hooks = {});
2639
+
2640
+ // Get the attribute value.
2641
+ var attr = getAttr(el, status),
2642
+ // Split the attribute value by the template.
2643
+ parts = attr.split("__!!__"),
2644
+ hook;
2645
+
2646
+
2647
+ // If we already had a hookup for this attribute...
2648
+ if(hooks[status]) {
2649
+ // Just add to that attribute's list of `function`s.
2650
+ hooks[status].bindings.push(binding);
2651
+ }
2652
+ else {
2653
+ // Create the hookup data.
2654
+ hooks[status] = {
2655
+ render: function() {
2656
+ var i =0,
2657
+ newAttr = attr.replace(attributeReplace, function() {
2658
+ return contentText( hook.bindings[i++].value );
2659
+ });
2660
+ return newAttr;
2661
+ },
2662
+ bindings: [binding],
2663
+ batchNum : undefined
2664
+ };
2665
+ };
2666
+
2667
+ // Save the hook for slightly faster performance.
2668
+ hook = hooks[status];
2669
+
2670
+ // Insert the value in parts.
2671
+ parts.splice(1,0,binding.value);
2672
+
2673
+ // Set the attribute.
2674
+ setAttr(el, status, parts.join(""));
2675
+
2676
+ // Bind on change.
2677
+ //liveBind(observed, el, binder,oldObserved);
2678
+ setupTeardownOnDestroy(el)
2679
+ })
2680
+ return "__!!__";
2681
+ }
2682
+ },
2683
+ pending: function() {
2684
+ if(pendingHookups.length) {
2685
+ var hooks = pendingHookups.slice(0);
2686
+
2687
+ pendingHookups = [];
2688
+ return can.view.hook(function(el){
2689
+ can.each(hooks, function(fn){
2690
+ fn(el);
2691
+ })
2692
+ });
2693
+ }else {
2694
+ return "";
2695
+ }
2696
+ }
2697
+ });
2698
+ // Start scanning code.
2699
+ var tokenReg = new RegExp("(" +[ "<%%", "%%>", "<%==", "<%=",
2700
+ "<%#", "<%", "%>", "<", ">", '"', "'"].join("|")+")","g"),
2701
+ // Commands for caching.
2702
+ startTxt = 'var ___v1ew = [];',
2703
+ finishTxt = "return ___v1ew.join('')",
2704
+ put_cmd = "___v1ew.push(",
2705
+ insert_cmd = put_cmd,
2706
+ // Global controls (used by other functions to know where we are).
2707
+ //
2708
+ // Are we inside a tag?
2709
+ htmlTag = null,
2710
+ // Are we within a quote within a tag?
2711
+ quote = null,
2712
+ // What was the text before the current quote? (used to get the `attr` name)
2713
+ beforeQuote = null,
2714
+ // Used to mark where the element is.
2715
+ status = function(){
2716
+ // `t` - `1`.
2717
+ // `h` - `0`.
2718
+ // `q` - String `beforeQuote`.
2719
+ return quote ? "'"+beforeQuote.match(attrReg)[1]+"'" : (htmlTag ? 1 : 0)
2720
+ },
2721
+ pendingHookups = [],
2722
+ scan = function(source, name){
2723
+ var tokens = [],
2724
+ last = 0;
2725
+
2726
+ source = source.replace(newLine, "\n");
2727
+ source.replace(tokenReg, function(whole, part, offset){
2728
+ // if the next token starts after the last token ends
2729
+ // push what's in between
2730
+ if(offset > last){
2731
+ tokens.push( source.substring(last, offset) );
2732
+ }
2733
+ // push the token
2734
+ tokens.push(part);
2735
+ // update the position of the last part of the last token
2736
+ last = offset+part.length;
2737
+ })
2738
+ // if there's something at the end, add it
2739
+ if(last < source.length){
2740
+ tokens.push(source.substr(last))
2741
+ }
2742
+
2743
+ var content = '',
2744
+ buff = [startTxt],
2745
+ // Helper `function` for putting stuff in the view concat.
2746
+ put = function( content, bonus ) {
2747
+ buff.push(put_cmd, '"', clean(content), '"'+(bonus||'')+');');
2748
+ },
2749
+ // A stack used to keep track of how we should end a bracket
2750
+ // `}`.
2751
+ // Once we have a `<%= %>` with a `leftBracket`,
2752
+ // we store how the file should end here (either `))` or `;`).
2753
+ endStack =[],
2754
+ // The last token, used to remember which tag we are in.
2755
+ lastToken,
2756
+ // The corresponding magic tag.
2757
+ startTag = null,
2758
+ // Was there a magic tag inside an html tag?
2759
+ magicInTag = false,
2760
+ // The current tag name.
2761
+ tagName = '',
2762
+ // stack of tagNames
2763
+ tagNames = [],
2764
+ // Declared here.
2765
+ bracketCount,
2766
+ i = 0,
2767
+ token;
2768
+
2769
+ // Reinitialize the tag state goodness.
2770
+ htmlTag = quote = beforeQuote = null;
2771
+
2772
+ for (; (token = tokens[i++]) !== undefined;) {
2773
+
2774
+ if ( startTag === null ) {
2775
+ switch ( token ) {
2776
+ case '<%':
2777
+ case '<%=':
2778
+ case '<%==':
2779
+ magicInTag = 1;
2780
+ case '<%#':
2781
+ // A new line -- just add whatever content within a clean.
2782
+ // Reset everything.
2783
+ startTag = token;
2784
+ if ( content.length ) {
2785
+ put(content);
2786
+ }
2787
+ content = '';
2788
+ break;
2789
+
2790
+ case '<%%':
2791
+ // Replace `<%%` with `<%`.
2792
+ content += '<%';
2793
+ break;
2794
+ case '<':
2795
+ // Make sure we are not in a comment.
2796
+ if(tokens[i].indexOf("!--") !== 0) {
2797
+ htmlTag = 1;
2798
+ magicInTag = 0;
2799
+ }
2800
+ content += token;
2801
+ break;
2802
+ case '>':
2803
+ htmlTag = 0;
2804
+ // TODO: all `<%=` in tags should be added to pending hookups.
2805
+ if(magicInTag){
2806
+ put(content, ",can.EJS.pending(),\">\"");
2807
+ content = '';
2808
+ } else {
2809
+ content += token;
2810
+ }
2811
+ // if it's a tag like <input/>
2812
+ if(lastToken.substr(-1) == "/"){
2813
+ // remove the current tag in the stack
2814
+ tagNames.pop();
2815
+ // set the current tag to the previous parent
2816
+ tagName = tagNames[tagNames.length-1];
2817
+ }
2818
+ break;
2819
+ case "'":
2820
+ case '"':
2821
+ // If we are in an html tag, finding matching quotes.
2822
+ if(htmlTag){
2823
+ // We have a quote and it matches.
2824
+ if(quote && quote === token){
2825
+ // We are exiting the quote.
2826
+ quote = null;
2827
+ // Otherwise we are creating a quote.
2828
+ // TODO: does this handle `\`?
2829
+ } else if(quote === null){
2830
+ quote = token;
2831
+ beforeQuote = lastToken;
2832
+ }
2833
+ }
2834
+ default:
2835
+ // Track the current tag
2836
+ if(lastToken === '<'){
2837
+ tagName = token.split(' ')[0];
2838
+ // If
2839
+ if( tagName.indexOf("/") === 0 && tagNames.pop() === tagName.substr(1) ) {
2840
+ tagName = tagNames[tagNames.length-1]|| tagName.substr(1)
2841
+ } else {
2842
+ tagNames.push(tagName);
2843
+ }
2844
+ }
2845
+ content += token;
2846
+ break;
2847
+ }
2848
+ }
2849
+ else {
2850
+ // We have a start tag.
2851
+ switch ( token ) {
2852
+ case '%>':
2853
+ // `%>`
2854
+ switch ( startTag ) {
2855
+ case '<%':
2856
+ // `<%`
2857
+
2858
+ // Get the number of `{ minus }`
2859
+ bracketCount = bracketNum(content);
2860
+
2861
+ // We are ending a block.
2862
+ if (bracketCount == 1) {
2863
+
2864
+ // We are starting on.
2865
+ buff.push(insert_cmd, "can.EJS.txt(0,'"+tagName+"'," + status() + ",this,function(){", startTxt, content);
2866
+
2867
+ endStack.push({
2868
+ before: "",
2869
+ after: finishTxt+"}));\n"
2870
+ })
2871
+ }
2872
+ else {
2873
+
2874
+ // How are we ending this statement?
2875
+ var last = // If the stack has value and we are ending a block...
2876
+ endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
2877
+ endStack.pop() : // Or use the default ending.
2878
+ {
2879
+ after: ";"
2880
+ };
2881
+
2882
+ // If we are ending a returning block,
2883
+ // add the finish text which returns the result of the
2884
+ // block.
2885
+ if (last.before) {
2886
+ buff.push(last.before)
2887
+ }
2888
+ // Add the remaining content.
2889
+ buff.push(content, ";",last.after);
2890
+ }
2891
+ break;
2892
+ case '<%=':
2893
+ case '<%==':
2894
+ // We have an extra `{` -> `block`.
2895
+ // Get the number of `{ minus }`.
2896
+ bracketCount = bracketNum(content);
2897
+ // If we have more `{`, it means there is a block.
2898
+ if( bracketCount ){
2899
+ // When we return to the same # of `{` vs `}` end with a `doubleParent`.
2900
+ endStack.push({
2901
+ before : finishTxt,
2902
+ after: "}));"
2903
+ })
2904
+ }
2905
+ // Check if its a func like `()->`
2906
+ if(quickFunc.test(content)){
2907
+ var parts = content.match(quickFunc)
2908
+ content = "function(__){var "+parts[1]+"=can.$(__);"+parts[2]+"}"
2909
+ }
2910
+
2911
+ // If we have `<%== a(function(){ %>` then we want
2912
+ // `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`.
2913
+ buff.push(insert_cmd, "can.EJS.txt("+(startTag === '<%=' ? 1 : 0)+",'"+tagName+"'," + status()+",this,function(){ return ", content,
2914
+ // If we have a block.
2915
+ bracketCount ?
2916
+ // Start with startTxt `"var _v1ew = [];"`.
2917
+ startTxt :
2918
+ // If not, add `doubleParent` to close push and text.
2919
+ "}));"
2920
+ );
2921
+ break;
2922
+ }
2923
+ startTag = null;
2924
+ content = '';
2925
+ break;
2926
+ case '<%%':
2927
+ content += '<%';
2928
+ break;
2929
+ default:
2930
+ content += token;
2931
+ break;
2932
+ }
2933
+
2934
+ }
2935
+ lastToken = token;
2936
+ }
2937
+
2938
+ // Put it together...
2939
+ if ( content.length ) {
2940
+ // Should be `content.dump` in Ruby.
2941
+ put(content)
2942
+ }
2943
+ buff.push(";")
2944
+
2945
+ var template = buff.join(''),
2946
+ out = {
2947
+ out: 'with(_VIEW) { with (_CONTEXT) {' + template + " "+finishTxt+"}}"
2948
+ };
2949
+ // Use `eval` instead of creating a function, because it is easier to debug.
2950
+ myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
2951
+ return out;
2952
+ };
2953
+
2954
+
2955
+
2956
+ EJS.Helpers = function( data, extras ) {
2957
+ this._data = data;
2958
+ this._extras = extras;
2959
+ extend(this, extras);
2960
+ };
2961
+ EJS.Helpers.prototype = {
2962
+ // TODO Deprecated!!
2963
+ list : function(list, cb){
2964
+ can.each(list, function(item, i){
2965
+ cb(item, i, list)
2966
+ })
2967
+ }
2968
+ };
2969
+
2970
+ // Options for `steal`'s build.
2971
+ can.view.register({
2972
+ suffix: "ejs",
2973
+ // returns a `function` that renders the view.
2974
+ script: function( id, src ) {
2975
+ return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
2976
+ text: src,
2977
+ name: id
2978
+ }).template.out + " })";
2979
+ },
2980
+ renderer: function( id, text ) {
2981
+ return EJS({
2982
+ text: text,
2983
+ name: id
2984
+ });
2985
+ }
2986
+ });
2987
+
2988
+ // Register as an AMD module if supported, otherwise attach to the window
2989
+ if ( typeof define === "function" && define.amd ) {
2990
+ define( "can", [], function () { return can; } );
2991
+ } else {
2992
+ window.can = can;
2993
+ }
2994
+
2995
+ })(can = {}, this )