From 1afd8183a2d256272a3e9f7696cfa16e1dc2fbe2 Mon Sep 17 00:00:00 2001 From: Pascal Hartig Date: Wed, 13 Mar 2013 17:15:57 +0100 Subject: [PATCH] CanJS: Partly moved to Bower components Refs #475 --- .../canjs/components/canjs/can.jquery.js | 3675 +++++++ .../canjs/components/jquery/jquery.js | 9597 +++++++++++++++++ .../canjs/components/todomvc-common/base.css | 414 + .../canjs/components/todomvc-common/base.js | 23 + .../canjs/components/todomvc-common/bg.png | Bin 0 -> 2126 bytes .../components/todomvc-common/component.json | 12 + architecture-examples/canjs/index.html | 15 +- .../canjs/js/lib/can.jquery-1.1.0.min.js | 64 - 8 files changed, 13727 insertions(+), 73 deletions(-) create mode 100644 architecture-examples/canjs/components/canjs/can.jquery.js create mode 100644 architecture-examples/canjs/components/jquery/jquery.js create mode 100644 architecture-examples/canjs/components/todomvc-common/base.css create mode 100644 architecture-examples/canjs/components/todomvc-common/base.js create mode 100644 architecture-examples/canjs/components/todomvc-common/bg.png create mode 100644 architecture-examples/canjs/components/todomvc-common/component.json delete mode 100644 architecture-examples/canjs/js/lib/can.jquery-1.1.0.min.js diff --git a/architecture-examples/canjs/components/canjs/can.jquery.js b/architecture-examples/canjs/components/canjs/can.jquery.js new file mode 100644 index 0000000000..47239b2dc8 --- /dev/null +++ b/architecture-examples/canjs/components/canjs/can.jquery.js @@ -0,0 +1,3675 @@ +/*! +* CanJS - 1.1.4 (2013-02-05) +* http://canjs.us/ +* Copyright (c) 2013 Bitovi +* Licensed MIT +*/ +(function (window, $, undefined) { + // ## can/util/can.js + var can = window.can || {}; + if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) { + window.can = can; + } + + can.isDeferred = function (obj) { + var isFunction = this.isFunction; + // Returns `true` if something looks like a deferred. + return obj && isFunction(obj.then) && isFunction(obj.pipe); + }; + + var cid = 0; + can.cid = function (object, name) { + if (object._cid) { + return object._cid + } else { + return object._cid = (name || "") + (++cid) + } + } + // ## can/util/array/each.js + can.each = function (elements, callback, context) { + var i = 0, + key; + if (elements) { + if (typeof elements.length === 'number' && elements.pop) { + if (elements.attr) { + elements.attr('length'); + } + for (key = elements.length; i < key; i++) { + if (callback.call(context || elements[i], elements[i], i, elements) === false) { + break; + } + } + } else if (elements.hasOwnProperty) { + for (key in elements) { + if (elements.hasOwnProperty(key)) { + if (callback.call(context || elements[key], elements[key], key, elements) === false) { + break; + } + } + } + } + } + return elements; + }; + + // ## can/util/jquery/jquery.js + // _jQuery node list._ + $.extend(can, $, { + trigger: function (obj, event, args) { + if (obj.trigger) { + obj.trigger(event, args); + } else { + $.event.trigger(event, args, obj, true); + } + }, + addEvent: function (ev, cb) { + $([this]).bind(ev, cb); + return this; + }, + removeEvent: function (ev, cb) { + $([this]).unbind(ev, cb); + return this; + }, + // jquery caches fragments, we always needs a new one + buildFragment: function (elems, context) { + var oldFragment = $.buildFragment, + ret; + + elems = [elems]; + // Set context per 1.8 logic + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + ret = oldFragment.call(jQuery, elems, context); + + return ret.cacheable ? $.clone(ret.fragment) : ret.fragment || ret; + }, + $: $, + each: can.each + }); + + // Wrap binding functions. + $.each(['bind', 'unbind', 'undelegate', 'delegate'], function (i, func) { + can[func] = function () { + var t = this[func] ? this : $([this]); + t[func].apply(t, arguments); + return this; + }; + }); + + // Wrap modifier functions. + $.each(["append", "filter", "addClass", "remove", "data", "get"], function (i, name) { + can[name] = function (wrapped) { + return wrapped[name].apply(wrapped, can.makeArray(arguments).slice(1)); + }; + }); + + // Memory safe destruction. + var oldClean = $.cleanData; + + $.cleanData = function (elems) { + $.each(elems, function (i, elem) { + if (elem) { + can.trigger(elem, "destroyed", [], false); + } + }); + oldClean(elems); + }; + + // ## can/util/string/string.js + // ##string.js + // _Miscellaneous string utility functions._ + // Several of the methods in this plugin use code adapated from Prototype + // Prototype JavaScript framework, version 1.6.0.1. + // © 2005-2007 Sam Stephenson + var strUndHash = /_|-/, + strColons = /\=\=/, + strWords = /([A-Z]+)([A-Z][a-z])/g, + strLowUp = /([a-z\d])([A-Z])/g, + strDash = /([a-z\d])([A-Z])/g, + strReplacer = /\{([^\}]+)\}/g, + strQuote = /"/g, + strSingleQuote = /'/g, + + // Returns the `prop` property from `obj`. + // If `add` is true and `prop` doesn't exist in `obj`, create it as an + // empty object. + getNext = function (obj, prop, add) { + return prop in obj ? obj[prop] : (add && (obj[prop] = {})); + }, + + // Returns `true` if the object can have properties (no `null`s). + isContainer = function (current) { + return (/^f|^o/).test(typeof current); + }; + + can.extend(can, { + // Escapes strings for HTML. + esc: function (content) { + // Convert bad values into empty strings + var isInvalid = content === null || content === undefined || (isNaN(content) && ("" + content === 'NaN')); + return ("" + (isInvalid ? '' : content)).replace(/&/g, '&').replace(//g, '>').replace(strQuote, '"').replace(strSingleQuote, "'"); + }, + + + getObject: function (name, roots, add) { + + // The parts of the name we are looking up + // `['App','Models','Recipe']` + var parts = name ? name.split('.') : [], + length = parts.length, + current, r = 0, + ret, i; + + // Make sure roots is an `array`. + roots = can.isArray(roots) ? roots : [roots || window]; + + if (!length) { + return roots[0]; + } + + // For each root, mark it as current. + while (roots[r]) { + current = roots[r]; + + // Walk current to the 2nd to last object or until there + // is not a container. + for (i = 0; i < length - 1 && isContainer(current); i++) { + current = getNext(current, parts[i], add); + } + + // If we can get a property from the 2nd to last object... + if (isContainer(current)) { + + // Get (and possibly set) the property. + ret = getNext(current, parts[i], add); + + // If there is a value, we exit. + if (ret !== undefined) { + // If `add` is `false`, delete the property + if (add === false) { + delete current[parts[i]]; + } + return ret; + + } + } + r++; + } + }, + // Capitalizes a string. + capitalize: function (s, cache) { + // Used to make newId. + return s.charAt(0).toUpperCase() + s.slice(1); + }, + + // Underscores a string. + underscore: function (s) { + return s.replace(strColons, '/').replace(strWords, '$1_$2').replace(strLowUp, '$1_$2').replace(strDash, '_').toLowerCase(); + }, + // Micro-templating. + sub: function (str, data, remove) { + var obs = []; + + obs.push(str.replace(strReplacer, function (whole, inside) { + + // Convert inside to type. + var ob = can.getObject(inside, data, remove === undefined ? remove : !remove); + + if (ob === undefined) { + obs = null; + return ""; + } + + // If a container, push into objs (which will return objects found). + if (isContainer(ob) && obs) { + obs.push(ob); + return ""; + } + + return "" + ob; + })); + + return obs === null ? obs : (obs.length <= 1 ? obs[0] : obs); + }, + + // These regex's are used throughout the rest of can, so let's make + // them available. + replacer: strReplacer, + undHash: strUndHash + }); + // ## can/construct/construct.js + // ## construct.js + // `can.Construct` + // _This is a modified version of + // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/). + // It provides class level inheritance and callbacks._ + // A private flag used to initialize a new class instance without + // initializing it's bindings. + var initializing = 0; + + + can.Construct = function () { + if (arguments.length) { + return can.Construct.extend.apply(can.Construct, arguments); + } + }; + + + can.extend(can.Construct, { + + newInstance: function () { + // Get a raw instance object (`init` is not called). + var inst = this.instance(), + arg = arguments, + args; + + // Call `setup` if there is a `setup` + if (inst.setup) { + args = inst.setup.apply(inst, arguments); + } + + // Call `init` if there is an `init` + // If `setup` returned `args`, use those as the arguments + if (inst.init) { + inst.init.apply(inst, args || arguments); + } + + return inst; + }, + // Overwrites an object with methods. Used in the `super` plugin. + // `newProps` - New properties to add. + // `oldProps` - Where the old properties might be (used with `super`). + // `addTo` - What we are adding to. + _inherit: function (newProps, oldProps, addTo) { + can.extend(addTo || newProps, newProps || {}) + }, + // used for overwriting a single property. + // this should be used for patching other objects + // the super plugin overwrites this + _overwrite: function (what, oldProps, propName, val) { + what[propName] = val; + }, + // Set `defaults` as the merger of the parent `defaults` and this + // object's `defaults`. If you overwrite this method, make sure to + // include option merging logic. + setup: function (base, fullName) { + this.defaults = can.extend(true, {}, base.defaults, this.defaults); + }, + // Create's a new `class` instance without initializing by setting the + // `initializing` flag. + instance: function () { + + // Prevents running `init`. + initializing = 1; + + var inst = new this(); + + // Allow running `init`. + initializing = 0; + + return inst; + }, + // Extends classes. + extend: function (fullName, klass, proto) { + // Figure out what was passed and normalize it. + if (typeof fullName != 'string') { + proto = klass; + klass = fullName; + fullName = null; + } + + if (!proto) { + proto = klass; + klass = null; + } + proto = proto || {}; + + var _super_class = this, + _super = this.prototype, + name, shortName, namespace, prototype; + + // Instantiate a base class (but only create the instance, + // don't run the init constructor). + prototype = this.instance(); + + // Copy the properties over onto the new prototype. + can.Construct._inherit(proto, _super, prototype); + + // The dummy class constructor. + + + function Constructor() { + // All construction is actually done in the init method. + if (!initializing) { + return this.constructor !== Constructor && arguments.length ? + // We are being called without `new` or we are extending. + arguments.callee.extend.apply(arguments.callee, arguments) : + // We are being called with `new`. + this.constructor.newInstance.apply(this.constructor, arguments); + } + } + + // Copy old stuff onto class (can probably be merged w/ inherit) + for (name in _super_class) { + if (_super_class.hasOwnProperty(name)) { + Constructor[name] = _super_class[name]; + } + } + + // Copy new static properties on class. + can.Construct._inherit(klass, _super_class, Constructor); + + // Setup namespaces. + if (fullName) { + + var parts = fullName.split('.'), + shortName = parts.pop(), + current = can.getObject(parts.join('.'), window, true), + namespace = current, + _fullName = can.underscore(fullName.replace(/\./g, "_")), + _shortName = can.underscore(shortName); + + + + current[shortName] = Constructor; + } + + // Set things that shouldn't be overwritten. + can.extend(Constructor, { + constructor: Constructor, + prototype: prototype, + + namespace: namespace, + + shortName: shortName, + _shortName: _shortName, + + fullName: fullName, + _fullName: _fullName + }); + + // Make sure our prototype looks nice. + Constructor.prototype.constructor = Constructor; + + + // Call the class `setup` and `init` + var t = [_super_class].concat(can.makeArray(arguments)), + args = Constructor.setup.apply(Constructor, t); + + if (Constructor.init) { + Constructor.init.apply(Constructor, args || t); + } + + + return Constructor; + + } + + }); + // ## can/observe/observe.js + // ## observe.js + // `can.Observe` + // _Provides the observable pattern for JavaScript Objects._ + // Returns `true` if something is an object with properties of its own. + var canMakeObserve = function (obj) { + return obj && (can.isArray(obj) || can.isPlainObject(obj) || (obj instanceof can.Observe)); + }, + + // Removes all listeners. + unhookup = function (items, namespace) { + return can.each(items, function (item) { + if (item && item.unbind) { + item.unbind("change" + namespace); + } + }); + }, + // Listens to changes on `val` and "bubbles" the event up. + // `val` - The object to listen for changes on. + // `prop` - The property name is at on. + // `parent` - The parent object of prop. + // `ob` - (optional) The Observe object constructor + // `list` - (optional) The observable list constructor + hookupBubble = function (val, prop, parent, Ob, List) { + Ob = Ob || Observe; + List = List || Observe.List; + + // If it's an `array` make a list, otherwise a val. + if (val instanceof Observe) { + // We have an `observe` already... + // Make sure it is not listening to this already + unhookup([val], parent._cid); + } else if (can.isArray(val)) { + val = new List(val); + } else { + val = new Ob(val); + } + + // Listen to all changes and `batchTrigger` upwards. + val.bind("change" + parent._cid, function () { + // `batchTrigger` the type on this... + var args = can.makeArray(arguments), + ev = args.shift(); + args[0] = (prop === "*" ? [parent.indexOf(val), args[0]] : [prop, args[0]]).join("."); + + // track objects dispatched on this observe + ev.triggeredNS = ev.triggeredNS || {}; + + // if it has already been dispatched exit + if (ev.triggeredNS[parent._cid]) { + return; + } + + ev.triggeredNS[parent._cid] = true; + // send change event with modified attr to parent + can.trigger(parent, ev, args); + // send modified attr event to parent + //can.trigger(parent, args[0], args); + }); + + return val; + }, + + // An `id` to track events for a given observe. + observeId = 0, + // A helper used to serialize an `Observe` or `Observe.List`. + // `observe` - The observable. + // `how` - To serialize with `attr` or `serialize`. + // `where` - To put properties, in an `{}` or `[]`. + serialize = function (observe, how, where) { + // Go through each property. + observe.each(function (val, name) { + // If the value is an `object`, and has an `attrs` or `serialize` function. + where[name] = canMakeObserve(val) && can.isFunction(val[how]) ? + // Call `attrs` or `serialize` to get the original data back. + val[how]() : + // Otherwise return the value. + val; + }); + return where; + }, + $method = function (name) { + return function () { + return can[name].apply(this, arguments); + }; + }, + bind = $method('addEvent'), + unbind = $method('removeEvent'), + attrParts = function (attr) { + return can.isArray(attr) ? attr : ("" + attr).split("."); + }, + // Which batch of events this is for -- might not want to send multiple + // messages on the same batch. This is mostly for event delegation. + batchNum = 1, + // how many times has start been called without a stop + transactions = 0, + // an array of events within a transaction + batchEvents = [], + stopCallbacks = []; + + + + + var Observe = can.Observe = can.Construct({ + + // keep so it can be overwritten + bind: bind, + unbind: unbind, + id: "id", + canMakeObserve: canMakeObserve, + // starts collecting events + // takes a callback for after they are updated + // how could you hook into after ejs + startBatch: function (batchStopHandler) { + transactions++; + batchStopHandler && stopCallbacks.push(batchStopHandler); + }, + + stopBatch: function (force, callStart) { + if (force) { + transactions = 0; + } else { + transactions--; + } + + if (transactions == 0) { + var items = batchEvents.slice(0), + callbacks = stopCallbacks.slice(0); + batchEvents = []; + stopCallbacks = []; + batchNum++; + callStart && this.startBatch(); + can.each(items, function (args) { + can.trigger.apply(can, args); + }); + can.each(callbacks, function (cb) { + cb; + }); + } + }, + + triggerBatch: function (item, event, args) { + // Don't send events if initalizing. + if (!item._init) { + if (transactions == 0) { + return can.trigger(item, event, args); + } else { + batchEvents.push([ + item, + { + type: event, + batchNum: batchNum + }, + args]); + } + } + }, + + keys: function (observe) { + var keys = []; + Observe.__reading && Observe.__reading(observe, '__keys'); + for (var keyName in observe._data) { + keys.push(keyName); + } + return keys; + } + }, + + { + setup: function (obj) { + // `_data` is where we keep the properties. + this._data = {}; + + // The namespace this `object` uses to listen to events. + can.cid(this, ".observe"); + // Sets all `attrs`. + this._init = 1; + this.attr(obj); + this.bind('change' + this._cid, can.proxy(this._changes, this)); + delete this._init; + }, + _changes: function (ev, attr, how, newVal, oldVal) { + Observe.triggerBatch(this, { + type: attr, + batchNum: ev.batchNum + }, [newVal, oldVal]); + }, + _triggerChange: function (attr, how, newVal, oldVal) { + Observe.triggerBatch(this, "change", can.makeArray(arguments)) + }, + + attr: function (attr, val) { + // This is super obfuscated for space -- basically, we're checking + // if the type of the attribute is not a `number` or a `string`. + var type = typeof attr; + if (type !== "string" && type !== "number") { + return this._attrs(attr, val) + } else if (val === undefined) { // If we are getting a value. + // Let people know we are reading. + Observe.__reading && Observe.__reading(this, attr) + return this._get(attr) + } else { + // Otherwise we are setting. + this._set(attr, val); + return this; + } + }, + + each: function () { + Observe.__reading && Observe.__reading(this, '__keys'); + return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments))) + }, + + removeAttr: function (attr) { + // Convert the `attr` into parts (if nested). + var parts = attrParts(attr), + // The actual property to remove. + prop = parts.shift(), + // The current value. + current = this._data[prop]; + + // If we have more parts, call `removeAttr` on that part. + if (parts.length) { + return current.removeAttr(parts) + } else { + if (prop in this._data) { + // Otherwise, `delete`. + delete this._data[prop]; + // Create the event. + if (!(prop in this.constructor.prototype)) { + delete this[prop] + } + // Let others know the number of keys have changed + Observe.triggerBatch(this, "__keys"); + this._triggerChange(prop, "remove", undefined, current); + + } + return current; + } + }, + // Reads a property from the `object`. + _get: function (attr) { + // break up the attr (`"foo.bar"`) into `["foo","bar"]` + var parts = attrParts(attr), + // get the value of the first attr name (`"foo"`) + current = this.__get(parts.shift()); + // if there are other attributes to read + return parts.length ? + // and current has a value + current ? + // lookup the remaining attrs on current + current._get(parts) : + // or if there's no current, return undefined + undefined : + // if there are no more parts, return current + current; + }, + // Reads a property directly if an `attr` is provided, otherwise + // returns the "real" data object itself. + __get: function (attr) { + return attr ? this._data[attr] : this._data; + }, + // Sets `attr` prop as value on this object where. + // `attr` - Is a string of properties or an array of property values. + // `value` - The raw value to set. + _set: function (attr, value) { + // Convert `attr` to attr parts (if it isn't already). + var parts = attrParts(attr), + // The immediate prop we are setting. + prop = parts.shift(), + // The current value. + current = this.__get(prop); + + // If we have an `object` and remaining parts. + if (canMakeObserve(current) && parts.length) { + // That `object` should set it (this might need to call attr). + current._set(parts, value) + } else if (!parts.length) { + // We're in "real" set territory. + if (this.__convert) { + value = this.__convert(prop, value) + } + this.__set(prop, value, current) + } else { + throw "can.Observe: Object does not exist" + } + }, + __set: function (prop, value, current) { + + // Otherwise, we are setting it on this `object`. + // TODO: Check if value is object and transform + // are we changing the value. + if (value !== current) { + // Check if we are adding this for the first time -- + // if we are, we need to create an `add` event. + var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add"; + + // Set the value on data. + this.___set(prop, + + // If we are getting an object. + canMakeObserve(value) ? + + // Hook it up to send event. + hookupBubble(value, prop, this) : + // Value is normal. + value); + + if (changeType == "add") { + // If there is no current value, let others know that + // the the number of keys have changed + Observe.triggerBatch(this, "__keys", undefined); + + } + // `batchTrigger` the change event. + this._triggerChange(prop, changeType, value, current); + + //Observe.triggerBatch(this, prop, [value, current]); + // If we can stop listening to our old value, do it. + current && unhookup([current], this._cid); + } + + }, + // Directly sets a property on this `object`. + ___set: function (prop, val) { + this._data[prop] = val; + // Add property directly for easy writing. + // Check if its on the `prototype` so we don't overwrite methods like `attrs`. + if (!(prop in this.constructor.prototype)) { + this[prop] = val + } + }, + + + bind: bind, + + unbind: unbind, + + serialize: function () { + return serialize(this, 'serialize', {}); + }, + + _attrs: function (props, remove) { + + if (props === undefined) { + return serialize(this, 'attr', {}) + } + + props = can.extend({}, props); + var prop, self = this, + newVal; + Observe.startBatch(); + this.each(function (curVal, prop) { + newVal = props[prop]; + + // If we are merging... + if (newVal === undefined) { + remove && self.removeAttr(prop); + return; + } + + if (self.__convert) { + newVal = self.__convert(prop, newVal) + } + + // if we're dealing with models, want to call _set to let converter run + if (newVal instanceof can.Observe) { + self.__set(prop, newVal, curVal) + // if its an object, let attr merge + } else if (canMakeObserve(curVal) && canMakeObserve(newVal) && curVal.attr) { + curVal.attr(newVal, remove) + // otherwise just set + } else if (curVal != newVal) { + self.__set(prop, newVal, curVal) + } + + delete props[prop]; + }) + // Add remaining props. + for (var prop in props) { + newVal = props[prop]; + this._set(prop, newVal) + } + Observe.stopBatch() + return this; + }, + + + compute: function (prop) { + var self = this, + computer = function (val) { + return self.attr(prop, val); + }; + + return can.compute ? can.compute(computer) : computer; + } + }); + // Helpers for `observable` lists. + var splice = [].splice, + list = Observe( + + { + setup: function (instances, options) { + this.length = 0; + can.cid(this, ".observe") + this._init = 1; + this.push.apply(this, can.makeArray(instances || [])); + this.bind('change' + this._cid, can.proxy(this._changes, this)); + can.extend(this, options); + delete this._init; + }, + _triggerChange: function (attr, how, newVal, oldVal) { + + Observe.prototype._triggerChange.apply(this, arguments) + // `batchTrigger` direct add and remove events... + if (!~attr.indexOf('.')) { + + if (how === 'add') { + Observe.triggerBatch(this, how, [newVal, +attr]); + Observe.triggerBatch(this, 'length', [this.length]); + } else if (how === 'remove') { + Observe.triggerBatch(this, how, [oldVal, +attr]); + Observe.triggerBatch(this, 'length', [this.length]); + } else { + Observe.triggerBatch(this, how, [newVal, +attr]) + } + + } + + }, + __get: function (attr) { + return attr ? this[attr] : this; + }, + ___set: function (attr, val) { + this[attr] = val; + if (+attr >= this.length) { + this.length = (+attr + 1) + } + }, + // Returns the serialized form of this list. + serialize: function () { + return serialize(this, 'serialize', []); + }, + + splice: function (index, howMany) { + var args = can.makeArray(arguments), + i; + + for (i = 2; i < args.length; i++) { + var val = args[i]; + if (canMakeObserve(val)) { + args[i] = hookupBubble(val, "*", this) + } + } + if (howMany === undefined) { + howMany = args[1] = this.length - index; + } + var removed = splice.apply(this, args); + can.Observe.startBatch() + if (howMany > 0) { + this._triggerChange("" + index, "remove", undefined, removed); + unhookup(removed, this._cid); + } + if (args.length > 2) { + this._triggerChange("" + index, "add", args.slice(2), removed); + } + can.Observe.stopBatch(); + return removed; + }, + + _attrs: function (items, remove) { + if (items === undefined) { + return serialize(this, 'attr', []); + } + + // Create a copy. + items = can.makeArray(items); + + Observe.startBatch(); + this._updateAttrs(items, remove); + Observe.stopBatch() + }, + + _updateAttrs: function (items, remove) { + var len = Math.min(items.length, this.length); + + for (var prop = 0; prop < len; prop++) { + var curVal = this[prop], + newVal = items[prop]; + + if (canMakeObserve(curVal) && canMakeObserve(newVal)) { + curVal.attr(newVal, remove) + } else if (curVal != newVal) { + this._set(prop, newVal) + } else { + + } + } + if (items.length > this.length) { + // Add in the remaining props. + this.push.apply(this, items.slice(this.length)); + } else if (items.length < this.length && remove) { + this.splice(items.length) + } + } + }), + + // Converts to an `array` of arguments. + getArgs = function (args) { + return args[0] && can.isArray(args[0]) ? args[0] : can.makeArray(args); + }; + // Create `push`, `pop`, `shift`, and `unshift` + can.each({ + + push: "length", + + unshift: 0 + }, + // Adds a method + // `name` - The method name. + // `where` - Where items in the `array` should be added. + + + function (where, name) { + var orig = [][name] + list.prototype[name] = function () { + // Get the items being added. + var args = [], + // Where we are going to add items. + len = where ? this.length : 0, + i = arguments.length, + res, val, constructor = this.constructor; + + // Go through and convert anything to an `observe` that needs to be converted. + while (i--) { + val = arguments[i]; + args[i] = canMakeObserve(val) ? hookupBubble(val, "*", this, this.constructor.Observe, this.constructor) : val; + } + + // Call the original method. + res = orig.apply(this, args); + + if (!this.comparator || !args.length) { + this._triggerChange("" + len, "add", args, undefined); + } + + return res; + } + }); + + can.each({ + + pop: "length", + + shift: 0 + }, + // Creates a `remove` type method + + + function (where, name) { + list.prototype[name] = function () { + + var args = getArgs(arguments), + len = where && this.length ? this.length - 1 : 0; + + var res = [][name].apply(this, args) + + // Create a change where the args are + // `*` - Change on potentially multiple properties. + // `remove` - Items removed. + // `undefined` - The new values (there are none). + // `res` - The old, removed values (should these be unbound). + // `len` - Where these items were removed. + this._triggerChange("" + len, "remove", undefined, [res]) + + if (res && res.unbind) { + res.unbind("change" + this._cid) + } + return res; + } + }); + + can.extend(list.prototype, { + + indexOf: function (item) { + this.attr('length') + return can.inArray(item, this) + }, + + + join: [].join, + + + slice: function () { + var temp = Array.prototype.slice.apply(this, arguments); + return new this.constructor(temp); + }, + + + concat: function () { + var args = []; + can.each(can.makeArray(arguments), function (arg, i) { + args[i] = arg instanceof can.Observe.List ? arg.serialize() : arg; + }); + return new this.constructor(Array.prototype.concat.apply(this.serialize(), args)); + }, + + + forEach: function (cb, thisarg) { + can.each(this, cb, thisarg || this); + }, + + + replace: function (newList) { + if (can.isDeferred(newList)) { + newList.then(can.proxy(this.replace, this)); + } else { + this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || []))); + } + + return this; + } + }); + + Observe.List = list; + Observe.setup = function () { + can.Construct.setup.apply(this, arguments); + // I would prefer not to do it this way. It should + // be using the attributes plugin to do this type of conversion. + this.List = Observe.List({ + Observe: this + }, {}); + } + // ## can/model/model.js + + // ## model.js + // `can.Model` + // _A `can.Observe` that connects to a RESTful interface._ + // Generic deferred piping function + var pipe = function (def, model, func) { + var d = new can.Deferred(); + def.then(function () { + var args = can.makeArray(arguments); + args[0] = model[func](args[0]); + d.resolveWith(d, args); + }, function () { + d.rejectWith(this, arguments); + }); + + if (typeof def.abort === 'function') { + d.abort = function () { + return def.abort(); + } + } + + return d; + }, + modelNum = 0, + ignoreHookup = /change.observe\d+/, + getId = function (inst) { + // Instead of using attr, use __get for performance. + // Need to set reading + can.Observe.__reading && can.Observe.__reading(inst, inst.constructor.id) + return inst.__get(inst.constructor.id); + }, + // Ajax `options` generator function + ajax = function (ajaxOb, data, type, dataType, success, error) { + + var params = {}; + + // If we get a string, handle it. + if (typeof ajaxOb == "string") { + // If there's a space, it's probably the type. + var parts = ajaxOb.split(/\s/); + params.url = parts.pop(); + if (parts.length) { + params.type = parts.pop(); + } + } else { + can.extend(params, ajaxOb); + } + + // If we are a non-array object, copy to a new attrs. + params.data = typeof data == "object" && !can.isArray(data) ? can.extend(params.data || {}, data) : data; + + // Get the url with any templated values filled out. + params.url = can.sub(params.url, params.data, true); + + return can.ajax(can.extend({ + type: type || "post", + dataType: dataType || "json", + success: success, + error: error + }, params)); + }, + makeRequest = function (self, type, success, error, method) { + var deferred, args = [self.serialize()], + // The model. + model = self.constructor, + jqXHR; + + // `destroy` does not need data. + if (type == 'destroy') { + args.shift(); + } + // `update` and `destroy` need the `id`. + if (type !== 'create') { + args.unshift(getId(self)); + } + + + jqXHR = model[type].apply(model, args); + + deferred = jqXHR.pipe(function (data) { + self[method || type + "d"](data, jqXHR); + return self; + }); + + // Hook up `abort` + if (jqXHR.abort) { + deferred.abort = function () { + jqXHR.abort(); + }; + } + + deferred.then(success, error); + return deferred; + }, + + // This object describes how to make an ajax request for each ajax method. + // The available properties are: + // `url` - The default url to use as indicated as a property on the model. + // `type` - The default http request type + // `data` - A method that takes the `arguments` and returns `data` used for ajax. + ajaxMethods = { + + create: { + url: "_shortName", + type: "post" + }, + + update: { + data: function (id, attrs) { + attrs = attrs || {}; + var identity = this.id; + if (attrs[identity] && attrs[identity] !== id) { + attrs["new" + can.capitalize(id)] = attrs[identity]; + delete attrs[identity]; + } + attrs[identity] = id; + return attrs; + }, + type: "put" + }, + + destroy: { + type: "delete", + data: function (id) { + var args = {}; + args.id = args[this.id] = id; + return args; + } + }, + + findAll: { + url: "_shortName" + }, + + findOne: {} + }, + // Makes an ajax request `function` from a string. + // `ajaxMethod` - The `ajaxMethod` object defined above. + // `str` - The string the user provided. Ex: `findAll: "/recipes.json"`. + ajaxMaker = function (ajaxMethod, str) { + // Return a `function` that serves as the ajax method. + return function (data) { + // If the ajax method has it's own way of getting `data`, use that. + data = ajaxMethod.data ? ajaxMethod.data.apply(this, arguments) : + // Otherwise use the data passed in. + data; + // Return the ajax method with `data` and the `type` provided. + return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get") + } + } + + + + can.Model = can.Observe({ + fullName: "can.Model", + setup: function (base) { + // create store here if someone wants to use model without inheriting from it + this.store = {}; + can.Observe.setup.apply(this, arguments); + // Set default list as model list + if (!can.Model) { + return; + } + this.List = ML({ + Observe: this + }, {}); + var self = this, + clean = can.proxy(this._clean, self); + + + // go through ajax methods and set them up + can.each(ajaxMethods, function (method, name) { + // if an ajax method is not a function, it's either + // a string url like findAll: "/recipes" or an + // ajax options object like {url: "/recipes"} + if (!can.isFunction(self[name])) { + // use ajaxMaker to convert that into a function + // that returns a deferred with the data + self[name] = ajaxMaker(method, self[name]); + } + // check if there's a make function like makeFindAll + // these take deferred function and can do special + // behavior with it (like look up data in a store) + if (self["make" + can.capitalize(name)]) { + // pass the deferred method to the make method to get back + // the "findAll" method. + var newMethod = self["make" + can.capitalize(name)](self[name]); + can.Construct._overwrite(self, base, name, function () { + // increment the numer of requests + this._reqs++; + var def = newMethod.apply(this, arguments); + var then = def.then(clean, clean); + then.abort = def.abort; + + // attach abort to our then and return it + return then; + }) + } + }); + + if (self.fullName == "can.Model" || !self.fullName) { + self.fullName = "Model" + (++modelNum); + } + // Add ajax converters. + this._reqs = 0; + this._url = this._shortName + "/{" + this.id + "}" + }, + _ajax: ajaxMaker, + _clean: function () { + this._reqs--; + if (!this._reqs) { + for (var id in this.store) { + if (!this.store[id]._bindings) { + delete this.store[id]; + } + } + } + return arguments[0]; + }, + + models: function (instancesRawData, oldList) { + + if (!instancesRawData) { + return; + } + + if (instancesRawData instanceof this.List) { + return instancesRawData; + } + + // Get the list type. + var self = this, + tmp = [], + res = oldList instanceof can.Observe.List ? oldList : new(self.List || ML), + // Did we get an `array`? + arr = can.isArray(instancesRawData), + + // Did we get a model list? + ml = (instancesRawData instanceof ML), + + // Get the raw `array` of objects. + raw = arr ? + + // If an `array`, return the `array`. + instancesRawData : + + // Otherwise if a model list. + (ml ? + + // Get the raw objects from the list. + instancesRawData.serialize() : + + // Get the object's data. + instancesRawData.data), + i = 0; + + + + if (res.length) { + res.splice(0); + } + + can.each(raw, function (rawPart) { + tmp.push(self.model(rawPart)); + }); + + // We only want one change event so push everything at once + res.push.apply(res, tmp); + + if (!arr) { // Push other stuff onto `array`. + can.each(instancesRawData, function (val, prop) { + if (prop !== 'data') { + res.attr(prop, val); + } + }) + } + return res; + }, + + model: function (attributes) { + if (!attributes) { + return; + } + if (attributes instanceof this) { + attributes = attributes.serialize(); + } + var id = attributes[this.id], + model = (id || id === 0) && this.store[id] ? this.store[id].attr(attributes, this.removeAttr || false) : new this(attributes); + if (this._reqs) { + this.store[attributes[this.id]] = model; + } + return model; + } + }, + + { + + isNew: function () { + var id = getId(this); + return !(id || id === 0); // If `null` or `undefined` + }, + + save: function (success, error) { + return makeRequest(this, this.isNew() ? 'create' : 'update', success, error); + }, + + destroy: function (success, error) { + if (this.isNew()) { + var self = this; + return can.Deferred().done(function (data) { + self.destroyed(data) + }).resolve(self); + } + return makeRequest(this, 'destroy', success, error, 'destroyed'); + }, + + bind: function (eventName) { + if (!ignoreHookup.test(eventName)) { + if (!this._bindings) { + this.constructor.store[this.__get(this.constructor.id)] = this; + this._bindings = 0; + } + this._bindings++; + } + + return can.Observe.prototype.bind.apply(this, arguments); + }, + + unbind: function (eventName) { + if (!ignoreHookup.test(eventName)) { + this._bindings--; + if (!this._bindings) { + delete this.constructor.store[getId(this)]; + } + } + return can.Observe.prototype.unbind.apply(this, arguments); + }, + // Change `id`. + ___set: function (prop, val) { + can.Observe.prototype.___set.call(this, prop, val) + // If we add an `id`, move it to the store. + if (prop === this.constructor.id && this._bindings) { + this.constructor.store[getId(this)] = this; + } + } + }); + + can.each({ + makeFindAll: "models", + makeFindOne: "model" + }, function (method, name) { + can.Model[name] = function (oldFind) { + return function (params, success, error) { + var def = pipe(oldFind.call(this, params), this, method); + def.then(success, error); + // return the original promise + return def; + }; + }; + }); + + can.each([ + + "created", + + "updated", + + "destroyed"], function (funcName) { + can.Model.prototype[funcName] = function (attrs) { + var stub, constructor = this.constructor; + + // Update attributes if attributes have been passed + stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs); + + // Call event on the instance + can.trigger(this, funcName); + can.trigger(this, "change", funcName) + + + // Call event on the instance's Class + can.trigger(constructor, funcName, this); + }; + }); + + // Model lists are just like `Observe.List` except that when their items are + // destroyed, it automatically gets removed from the list. + var ML = can.Model.List = can.Observe.List({ + setup: function () { + can.Observe.List.prototype.setup.apply(this, arguments); + // Send destroy events. + var self = this; + this.bind('change', function (ev, how) { + if (/\w+\.destroyed/.test(how)) { + var index = self.indexOf(ev.target); + if (index != -1) { + self.splice(index, 1); + } + } + }) + } + }) + + // ## can/util/string/deparam/deparam.js + + // ## deparam.js + // `can.deparam` + // _Takes a string of name value pairs and returns a Object literal that represents those params._ + var digitTest = /^\d+$/, + keyBreaker = /([^\[\]]+)|(\[\])/g, + paramTest = /([^?#]*)(#.*)?$/, + prep = function (str) { + return decodeURIComponent(str.replace(/\+/g, " ")); + }; + + + can.extend(can, { + + deparam: function (params) { + + var data = {}, + pairs, lastPart; + + if (params && paramTest.test(params)) { + + pairs = params.split('&'), + + can.each(pairs, function (pair) { + + var parts = pair.split('='), + key = prep(parts.shift()), + value = prep(parts.join("=")), + current = data; + + parts = key.match(keyBreaker); + + for (var j = 0, l = parts.length - 1; j < l; j++) { + if (!current[parts[j]]) { + // If what we are pointing to looks like an `array` + current[parts[j]] = digitTest.test(parts[j + 1]) || parts[j + 1] == "[]" ? [] : {}; + } + current = current[parts[j]]; + } + lastPart = parts.pop(); + if (lastPart == "[]") { + current.push(value); + } else { + current[lastPart] = value; + } + }); + } + return data; + } + }); + // ## can/route/route.js + // ## route.js + // `can.route` + // _Helps manage browser history (and client state) by synchronizing the + // `window.location.hash` with a `can.Observe`._ + // Helper methods used for matching routes. + var + // `RegExp` used to match route variables of the type ':name'. + // Any word character or a period is matched. + matcher = /\:([\w\.]+)/g, + // Regular expression for identifying &key=value lists. + paramsMatcher = /^(?:&[^=]+=[^&]*)+/, + // Converts a JS Object into a list of parameters that can be + // inserted into an html element tag. + makeProps = function (props) { + var tags = []; + can.each(props, function (val, name) { + tags.push((name === 'className' ? 'class' : name) + '="' + (name === "href" ? val : can.esc(val)) + '"'); + }); + return tags.join(" "); + }, + // Checks if a route matches the data provided. If any route variable + // is not present in the data, the route does not match. If all route + // variables are present in the data, the number of matches is returned + // to allow discerning between general and more specific routes. + matchesData = function (route, data) { + var count = 0, + i = 0, + defaults = {}; + // look at default values, if they match ... + for (var name in route.defaults) { + if (route.defaults[name] === data[name]) { + // mark as matched + defaults[name] = 1; + count++; + } + } + for (; i < route.names.length; i++) { + if (!data.hasOwnProperty(route.names[i])) { + return -1; + } + if (!defaults[route.names[i]]) { + count++; + } + + } + + return count; + }, + onready = !0, + location = window.location, + wrapQuote = function (str) { + return (str + '').replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1"); + }, + each = can.each, + extend = can.extend; + + can.route = function (url, defaults) { + defaults = defaults || {}; + // Extract the variable names and replace with `RegExp` that will match + // an atual URL with values. + var names = [], + test = url.replace(matcher, function (whole, name, i) { + names.push(name); + var next = "\\" + (url.substr(i + whole.length, 1) || can.route._querySeparator); + // a name without a default value HAS to have a value + // a name that has a default value can be empty + // The `\\` is for string-escaping giving single `\` for `RegExp` escaping. + return "([^" + next + "]" + (defaults[name] ? "*" : "+") + ")"; + }); + + // Add route in a form that can be easily figured out. + can.route.routes[url] = { + // A regular expression that will match the route when variable values + // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which + // will match for any value of `:page` and `:type` (word chars or period). + test: new RegExp("^" + test + "($|" + wrapQuote(can.route._querySeparator) + ")"), + // The original URL, same as the index for this entry in routes. + route: url, + // An `array` of all the variable names in this route. + names: names, + // Default values provided for the variables. + defaults: defaults, + // The number of parts in the URL separated by `/`. + length: url.split('/').length + }; + return can.route; + }; + + extend(can.route, { + + _querySeparator: '&', + _paramsMatcher: paramsMatcher, + + + param: function (data, _setRoute) { + // Check if the provided data keys match the names in any routes; + // Get the one with the most matches. + var route, + // Need to have at least 1 match. + matches = 0, + matchCount, routeName = data.route, + propCount = 0; + + delete data.route; + + each(data, function () { + propCount++; + }); + // Otherwise find route. + each(can.route.routes, function (temp, name) { + // best route is the first with all defaults matching + + matchCount = matchesData(temp, data); + if (matchCount > matches) { + route = temp; + matches = matchCount; + } + if (matchCount >= propCount) { + return false; + } + }); + // If we have a route name in our `can.route` data, and it's + // just as good as what currently matches, use that + if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data) === matches) { + route = can.route.routes[routeName]; + } + // If this is match... + if (route) { + var cpy = extend({}, data), + // Create the url by replacing the var names with the provided data. + // If the default value is found an empty string is inserted. + res = route.route.replace(matcher, function (whole, name) { + delete cpy[name]; + return data[name] === route.defaults[name] ? "" : encodeURIComponent(data[name]); + }), + after; + // Remove matching default values + each(route.defaults, function (val, name) { + if (cpy[name] === val) { + delete cpy[name]; + } + }); + + // The remaining elements of data are added as + // `&` separated parameters to the url. + after = can.param(cpy); + // if we are paraming for setting the hash + // we also want to make sure the route value is updated + if (_setRoute) { + can.route.attr('route', route.route); + } + return res + (after ? can.route._querySeparator + after : ""); + } + // If no route was found, there is no hash URL, only paramters. + return can.isEmptyObject(data) ? "" : can.route._querySeparator + can.param(data); + }, + + deparam: function (url) { + // See if the url matches any routes by testing it against the `route.test` `RegExp`. + // By comparing the URL length the most specialized route that matches is used. + var route = { + length: -1 + }; + each(can.route.routes, function (temp, name) { + if (temp.test.test(url) && temp.length > route.length) { + route = temp; + } + }); + // If a route was matched. + if (route.length > -1) { + + var // Since `RegExp` backreferences are used in `route.test` (parens) + // the parts will contain the full matched string and each variable (back-referenced) value. + parts = url.match(route.test), + // Start will contain the full matched string; parts contain the variable values. + start = parts.shift(), + // The remainder will be the `&key=value` list at the end of the URL. + remainder = url.substr(start.length - (parts[parts.length - 1] === can.route._querySeparator ? 1 : 0)), + // If there is a remainder and it contains a `&key=value` list deparam it. + obj = (remainder && can.route._paramsMatcher.test(remainder)) ? can.deparam(remainder.slice(1)) : {}; + + // Add the default values for this route. + obj = extend(true, {}, route.defaults, obj); + // Overwrite each of the default values in `obj` with those in + // parts if that part is not empty. + each(parts, function (part, i) { + if (part && part !== can.route._querySeparator) { + obj[route.names[i]] = decodeURIComponent(part); + } + }); + obj.route = route.route; + return obj; + } + // If no route was matched, it is parsed as a `&key=value` list. + if (url.charAt(0) !== can.route._querySeparator) { + url = can.route._querySeparator + url; + } + return can.route._paramsMatcher.test(url) ? can.deparam(url.slice(1)) : {}; + }, + + data: new can.Observe({}), + + routes: {}, + + ready: function (val) { + if (val === false) { + onready = val; + } + if (val === true || onready === true) { + can.route._setup(); + setState(); + } + return can.route; + }, + + url: function (options, merge) { + if (merge) { + options = extend({}, curParams, options) + } + return "#!" + can.route.param(options); + }, + + link: function (name, options, props, merge) { + return "" + name + ""; + }, + + current: function (options) { + return location.hash == "#!" + can.route.param(options) + }, + _setup: function () { + // If the hash changes, update the `can.route.data`. + can.bind.call(window, 'hashchange', setState); + }, + _getHash: function () { + return location.href.split(/#!?/)[1] || ""; + }, + _setHash: function (serialized) { + var path = (can.route.param(serialized, true)); + location.hash = "#!" + path; + return path; + } + }); + + + // The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will + // instead act on the `can.route.data` observe. + each(['bind', 'unbind', 'delegate', 'undelegate', 'attr', 'removeAttr'], function (name) { + can.route[name] = function () { + return can.route.data[name].apply(can.route.data, arguments) + } + }) + + var // A ~~throttled~~ debounced function called multiple times will only fire once the + // timer runs down. Each call resets the timer. + timer, + // Intermediate storage for `can.route.data`. + curParams, + // Deparameterizes the portion of the hash of interest and assign the + // values to the `can.route.data` removing existing values no longer in the hash. + // setState is called typically by hashchange which fires asynchronously + // So it's possible that someone started changing the data before the + // hashchange event fired. For this reason, it will not set the route data + // if the data is changing or the hash already matches the hash that was set. + setState = can.route.setState = function () { + var hash = can.route._getHash(); + curParams = can.route.deparam(hash); + + // if the hash data is currently changing, or + // the hash is what we set it to anyway, do NOT change the hash + if (!changingData || hash !== lastHash) { + can.route.attr(curParams, true); + } + }, + // The last hash caused by a data change + lastHash, + // Are data changes pending that haven't yet updated the hash + changingData; + + // If the `can.route.data` changes, update the hash. + // Using `.serialize()` retrieves the raw data contained in the `observable`. + // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed. + // This might be able to use batchNum and avoid this. + can.route.bind("change", function (ev, attr) { + // indicate that data is changing + changingData = 1; + clearTimeout(timer); + timer = setTimeout(function () { + // indicate that the hash is set to look like the data + changingData = 0; + var serialized = can.route.data.serialize(); + + lastHash = can.route._setHash(serialized); + }, 1); + }); + // `onready` event... + can.bind.call(document, "ready", can.route.ready); + + // Libraries other than jQuery don't execute the document `ready` listener + // if we are already DOM ready + if ((document.readyState === 'complete' || document.readyState === "interactive") && onready) { + can.route.ready(); + } + + // extend route to have a similar property + // that is often checked in mustache to determine + // an object's observability + can.route.constructor.canMakeObserve = can.Observe.canMakeObserve; + + // ## can/control/control.js + // ## control.js + // `can.Control` + // _Controller_ + // Binds an element, returns a function that unbinds. + var bind = function (el, ev, callback) { + + can.bind.call(el, ev, callback); + + return function () { + can.unbind.call(el, ev, callback); + }; + }, + isFunction = can.isFunction, + extend = can.extend, + each = can.each, + slice = [].slice, + paramReplacer = /\{([^\}]+)\}/g, + special = can.getObject("$.event.special", [can]) || {}, + + // Binds an element, returns a function that unbinds. + delegate = function (el, selector, ev, callback) { + can.delegate.call(el, selector, ev, callback); + return function () { + can.undelegate.call(el, selector, ev, callback); + }; + }, + + // Calls bind or unbind depending if there is a selector. + binder = function (el, ev, callback, selector) { + return selector ? delegate(el, can.trim(selector), ev, callback) : bind(el, ev, callback); + }, + + basicProcessor; + + + var Control = can.Control = can.Construct( + + { + // Setup pre-processes which methods are event listeners. + setup: function () { + + // Allow contollers to inherit "defaults" from super-classes as it + // done in `can.Construct` + can.Construct.setup.apply(this, arguments); + + // If you didn't provide a name, or are `control`, don't do anything. + if (can.Control) { + + // Cache the underscored names. + var control = this, + funcName; + + // Calculate and cache actions. + control.actions = {}; + for (funcName in control.prototype) { + if (control._isAction(funcName)) { + control.actions[funcName] = control._action(funcName); + } + } + } + }, + + // Moves `this` to the first argument, wraps it with `jQuery` if it's an element + _shifter: function (context, name) { + + var method = typeof name == "string" ? context[name] : name; + + if (!isFunction(method)) { + method = context[method]; + } + + return function () { + context.called = name; + return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0))); + }; + }, + + // Return `true` if is an action. + _isAction: function (methodName) { + + var val = this.prototype[methodName], + type = typeof val; + // if not the constructor + return (methodName !== 'constructor') && + // and is a function or links to a function + (type == "function" || (type == "string" && isFunction(this.prototype[val]))) && + // and is in special, a processor, or has a funny character + !! (special[methodName] || processors[methodName] || /[^\w]/.test(methodName)); + }, + // Takes a method name and the options passed to a control + // and tries to return the data necessary to pass to a processor + // (something that binds things). + _action: function (methodName, options) { + + // If we don't have options (a `control` instance), we'll run this + // later. + paramReplacer.lastIndex = 0; + if (options || !paramReplacer.test(methodName)) { + // If we have options, run sub to replace templates `{}` with a + // value from the options or the window + var convertedName = options ? can.sub(methodName, [options, window]) : methodName; + if (!convertedName) { + return null; + } + // If a `{}` template resolves to an object, `convertedName` will be + // an array + var arr = can.isArray(convertedName), + + // Get the name + name = arr ? convertedName[1] : convertedName, + + // Grab the event off the end + parts = name.split(/\s+/g), + event = parts.pop(); + + return { + processor: processors[event] || basicProcessor, + parts: [name, parts.join(" "), event], + delegate: arr ? convertedName[0] : undefined + }; + } + }, + // An object of `{eventName : function}` pairs that Control uses to + // hook up events auto-magically. + processors: {}, + // A object of name-value pairs that act as default values for a + // control instance + defaults: {} + }, + + { + // Sets `this.element`, saves the control in `data, binds event + // handlers. + setup: function (element, options) { + + var cls = this.constructor, + pluginname = cls.pluginName || cls._fullName, + arr; + + // Want the raw element here. + this.element = can.$(element) + + if (pluginname && pluginname !== 'can_control') { + // Set element and `className` on element. + this.element.addClass(pluginname); + } + + (arr = can.data(this.element, "controls")) || can.data(this.element, "controls", arr = []); + arr.push(this); + + // Option merging. + this.options = extend({}, cls.defaults, options); + + // Bind all event handlers. + this.on(); + + // Get's passed into `init`. + return [this.element, this.options]; + }, + + on: function (el, selector, eventName, func) { + if (!el) { + + // Adds bindings. + this.off(); + + // Go through the cached list of actions and use the processor + // to bind + var cls = this.constructor, + bindings = this._bindings, + actions = cls.actions, + element = this.element, + destroyCB = can.Control._shifter(this, "destroy"), + funcName, ready; + + for (funcName in actions) { + // Only push if we have the action and no option is `undefined` + if (actions.hasOwnProperty(funcName) && (ready = actions[funcName] || cls._action(funcName, this.options))) { + bindings.push(ready.processor(ready.delegate || element, ready.parts[2], ready.parts[1], funcName, this)); + } + } + + + // Setup to be destroyed... + // don't bind because we don't want to remove it. + can.bind.call(element, "destroyed", destroyCB); + bindings.push(function (el) { + can.unbind.call(el, "destroyed", destroyCB); + }); + return bindings.length; + } + + if (typeof el == 'string') { + func = eventName; + eventName = selector; + selector = el; + el = this.element; + } + + if (func === undefined) { + func = eventName; + eventName = selector; + selector = null; + } + + if (typeof func == 'string') { + func = can.Control._shifter(this, func); + } + + this._bindings.push(binder(el, eventName, func, selector)); + + return this._bindings.length; + }, + // Unbinds all event handlers on the controller. + off: function () { + var el = this.element[0] + each(this._bindings || [], function (value) { + value(el); + }); + // Adds bindings. + this._bindings = []; + }, + // Prepares a `control` for garbage collection + destroy: function () { + var Class = this.constructor, + pluginName = Class.pluginName || Class._fullName, + controls; + + // Unbind bindings. + this.off(); + + if (pluginName && pluginName !== 'can_control') { + // Remove the `className`. + this.element.removeClass(pluginName); + } + + // Remove from `data`. + controls = can.data(this.element, "controls"); + controls.splice(can.inArray(this, controls), 1); + + can.trigger(this, "destroyed"); // In case we want to know if the `control` is removed. + this.element = null; + } + }); + + var processors = can.Control.processors, + // Processors do the binding. + // They return a function that unbinds when called. + // The basic processor that binds events. + basicProcessor = function (el, event, selector, methodName, control) { + return binder(el, event, can.Control._shifter(control, methodName), selector); + }; + + // Set common events to be processed as a `basicProcessor` + each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup", "keypress", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin", "focusout", "mouseenter", "mouseleave", + // #104 - Add touch events as default processors + // TOOD feature detect? + "touchstart", "touchmove", "touchcancel", "touchend", "touchleave"], function (v) { + processors[v] = basicProcessor; + }); + + // ## can/control/route/route.js + + // ## control/route.js + // _Controller route integration._ + can.Control.processors.route = function (el, event, selector, funcName, controller) { + selector = selector || ""; + can.route(selector); + var batchNum, check = function (ev, attr, how) { + if (can.route.attr('route') === (selector) && (ev.batchNum === undefined || ev.batchNum !== batchNum)) { + + batchNum = ev.batchNum; + + var d = can.route.attr(); + delete d.route; + if (can.isFunction(controller[funcName])) { + controller[funcName](d); + } else { + controller[controller[funcName]](d); + } + + } + }; + can.route.bind('change', check); + return function () { + can.route.unbind('change', check); + }; + }; + + // ## can/view/view.js + // ## view.js + // `can.view` + // _Templating abstraction._ + var isFunction = can.isFunction, + makeArray = can.makeArray, + // Used for hookup `id`s. + hookupId = 1, + + $view = can.view = function (view, data, helpers, callback) { + // If helpers is a `function`, it is actually a callback. + if (isFunction(helpers)) { + callback = helpers; + helpers = undefined; + } + + var pipe = function (result) { + return $view.frag(result); + }, + // In case we got a callback, we need to convert the can.view.render + // result to a document fragment + wrapCallback = isFunction(callback) ? + function (frag) { + callback(pipe(frag)); + } : null, + // Get the result. + result = $view.render(view, data, helpers, wrapCallback), + deferred = can.Deferred(); + + if (isFunction(result)) { + return result; + } + + if (can.isDeferred(result)) { + result.done(function (result, data) { + deferred.resolve.call(deferred, pipe(result), data); + }); + return deferred; + } + + // Convert it into a dom frag. + return pipe(result); + }; + + can.extend($view, { + // creates a frag and hooks it up all at once + frag: function (result, parentNode) { + return $view.hookup($view.fragment(result), parentNode); + }, + + // simply creates a frag + // this is used internally to create a frag + // insert it + // then hook it up + fragment: function (result) { + var frag = can.buildFragment(result, document.body); + // If we have an empty frag... + if (!frag.childNodes.length) { + frag.appendChild(document.createTextNode('')); + } + return frag; + }, + + // Convert a path like string into something that's ok for an `element` ID. + toId: function (src) { + return can.map(src.toString().split(/\/|\./g), function (part) { + // Dont include empty strings in toId functions + if (part) { + return part; + } + }).join("_"); + }, + + hookup: function (fragment, parentNode) { + var hookupEls = [], + id, func; + + // Get all `childNodes`. + can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function (node) { + if (node.nodeType === 1) { + hookupEls.push(node); + hookupEls.push.apply(hookupEls, can.makeArray(node.getElementsByTagName('*'))); + } + }); + + // Filter by `data-view-id` attribute. + can.each(hookupEls, function (el) { + if (el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id])) { + func(el, parentNode, id); + delete $view.hookups[id]; + el.removeAttribute('data-view-id'); + } + }); + + return fragment; + }, + + + hookups: {}, + + + hook: function (cb) { + $view.hookups[++hookupId] = cb; + return " data-view-id='" + hookupId + "'"; + }, + + + cached: {}, + + cachedRenderers: {}, + + + cache: true, + + + register: function (info) { + this.types["." + info.suffix] = info; + }, + + types: {}, + + + ext: ".ejs", + + + registerScript: function () {}, + + + preload: function () {}, + + + render: function (view, data, helpers, callback) { + // If helpers is a `function`, it is actually a callback. + if (isFunction(helpers)) { + callback = helpers; + helpers = undefined; + } + + // See if we got passed any deferreds. + var deferreds = getDeferreds(data); + + if (deferreds.length) { // Does data contain any deferreds? + // The deferred that resolves into the rendered content... + var deferred = new can.Deferred(), + dataCopy = can.extend({}, data); + + // Add the view request to the list of deferreds. + deferreds.push(get(view, true)) + + // Wait for the view and all deferreds to finish... + can.when.apply(can, deferreds).then(function (resolved) { + // Get all the resolved deferreds. + var objs = makeArray(arguments), + // Renderer is the last index of the data. + renderer = objs.pop(), + // The result of the template rendering with data. + result; + + // Make data look like the resolved deferreds. + if (can.isDeferred(data)) { + dataCopy = usefulPart(resolved); + } + else { + // Go through each prop in data again and + // replace the defferreds with what they resolved to. + for (var prop in data) { + if (can.isDeferred(data[prop])) { + dataCopy[prop] = usefulPart(objs.shift()); + } + } + } + + // Get the rendered result. + result = renderer(dataCopy, helpers); + + // Resolve with the rendered view. + deferred.resolve(result, dataCopy); + + // If there's a `callback`, call it back with the result. + callback && callback(result, dataCopy); + }); + // Return the deferred... + return deferred; + } + else { + // No deferreds! Render this bad boy. + var response, + // If there's a `callback` function + async = isFunction(callback), + // Get the `view` type + deferred = get(view, async); + + // If we are `async`... + if (async) { + // Return the deferred + response = deferred; + // And fire callback with the rendered result. + deferred.then(function (renderer) { + callback(data ? renderer(data, helpers) : renderer); + }) + } else { + // if the deferred is resolved, call the cached renderer instead + // this is because it's possible, with recursive deferreds to + // need to render a view while its deferred is _resolving_. A _resolving_ deferred + // is a deferred that was just resolved and is calling back it's success callbacks. + // If a new success handler is called while resoliving, it does not get fired by + // jQuery's deferred system. So instead of adding a new callback + // we use the cached renderer. + // We also add __view_id on the deferred so we can look up it's cached renderer. + // In the future, we might simply store either a deferred or the cached result. + if (deferred.state() === "resolved" && deferred.__view_id) { + var currentRenderer = $view.cachedRenderers[deferred.__view_id]; + return data ? currentRenderer(data, helpers) : currentRenderer; + } else { + // Otherwise, the deferred is complete, so + // set response to the result of the rendering. + deferred.then(function (renderer) { + response = data ? renderer(data, helpers) : renderer; + }); + } + } + + return response; + } + }, + + + registerView: function (id, text, type, def) { + // Get the renderer function. + var func = (type || $view.types[$view.ext]).renderer(id, text); + def = def || new can.Deferred(); + + // Cache if we are caching. + if ($view.cache) { + $view.cached[id] = def; + def.__view_id = id; + $view.cachedRenderers[id] = func; + } + + // Return the objects for the response's `dataTypes` + // (in this case view). + return def.resolve(func); + } + }); + + // Makes sure there's a template, if not, have `steal` provide a warning. + var checkText = function (text, url) { + if (!text.length) { + + throw "can.view: No template or empty template:" + url; + } + }, + // `Returns a `view` renderer deferred. + // `url` - The url to the template. + // `async` - If the ajax request should be asynchronous. + // Returns a deferred. + get = function (url, async) { + var suffix = url.match(/\.[\w\d]+$/), + type, + // If we are reading a script element for the content of the template, + // `el` will be set to that script element. + el, + // A unique identifier for the view (used for caching). + // This is typically derived from the element id or + // the url for the template. + id, + // The ajax request used to retrieve the template content. + jqXHR; + + //If the url has a #, we assume we want to use an inline template + //from a script element and not current page's HTML + if (url.match(/^#/)) { + url = url.substr(1); + } + // If we have an inline template, derive the suffix from the `text/???` part. + // This only supports ` - + + - + diff --git a/architecture-examples/canjs/js/lib/can.jquery-1.1.0.min.js b/architecture-examples/canjs/js/lib/can.jquery-1.1.0.min.js deleted file mode 100644 index 14a1e17150..0000000000 --- a/architecture-examples/canjs/js/lib/can.jquery-1.1.0.min.js +++ /dev/null @@ -1,64 +0,0 @@ -(function(u,n,l){var c=u.can||{};if("undefined"===typeof GLOBALCAN||!1!==GLOBALCAN)u.can=c;c.isDeferred=function(a){var b=this.isFunction;return a&&b(a.then)&&b(a.pipe)};c.each=function(a,b,d){var c=0,f;if(a)if("number"===typeof a.length&&a.pop){a.attr&&a.attr("length");for(f=a.length;c/g,">").replace(x,""").replace(Ma,"'")},getObject:function(a, - b,d){var a=a?a.split("."):[],e=a.length,f,h=0,g,i,b=c.isArray(b)?b:[b||u];if(!e)return b[0];for(;b[h];){f=b[h];for(i=0;i=e.length?e[0]:e},replacer:ka,undHash:/_|-/});var R=0;c.Construct=function(){if(arguments.length)return c.Construct.extend.apply(c.Construct,arguments)};c.extend(c.Construct,{newInstance:function(){var a=this.instance(),b;a.setup&&(b=a.setup.apply(a,arguments));a.init&&a.init.apply(a,b||arguments);return a},_inherit:function(a,b,d){c.extend(d||a,a||{})},_overwrite:function(a, - b,d,c){a[d]=c},setup:function(a){this.defaults=c.extend(!0,{},a.defaults,this.defaults)},instance:function(){R=1;var a=new this;R=0;return a},extend:function(a,b,d){function e(){if(!R)return this.constructor!==e&&arguments.length?arguments.callee.extend.apply(arguments.callee,arguments):this.constructor.newInstance.apply(this.constructor,arguments)}"string"!=typeof a&&(d=b,b=a,a=null);d||(d=b,b=null);var d=d||{},f=this.prototype,h,g,i,y;y=this.instance();c.Construct._inherit(d,f,y);for(h in this)this.hasOwnProperty(h)&& -(e[h]=this[h]);c.Construct._inherit(b,this,e);if(a){i=a.split(".");g=i.pop();i=f=c.getObject(i.join("."),u,!0);var j=c.underscore(a.replace(/\./g,"_")),s=c.underscore(g);f[g]=e}c.extend(e,{constructor:e,prototype:y,namespace:i,shortName:g,_shortName:s,fullName:a,_fullName:j});e.prototype.constructor=e;g=[this].concat(c.makeArray(arguments));y=e.setup.apply(e,g);e.init&&e.init.apply(e,y||g);return e}});var v=function(a){return a&&(c.isArray(a)||c.isPlainObject(a)||a instanceof c.Observe)},M=function(a, - b){return c.each(a,function(a){a&&a.unbind&&a.unbind("change"+b)})},S=function(a,b,d,e,f){e=e||j;f=f||j.List;a instanceof j?M([a],d._cid):a=c.isArray(a)?new f(a):new e(a);a.bind("change"+d._cid,function(){var e=c.makeArray(arguments),f=e.shift();e[0]=("*"===b?[d.indexOf(a),e[0]]:[b,e[0]]).join(".");f.triggeredNS=f.triggeredNS||{};f.triggeredNS[d._cid]||(f.triggeredNS[d._cid]=!0,c.trigger(d,f,e))});return a},N=function(a,b,d){a.each(function(a,f){d[f]=v(a)&&c.isFunction(a[b])?a[b]():a});return d}, - F=function(a){return function(){return c[a].apply(this,arguments)}},G=F("addEvent"),F=F("removeEvent"),T=function(a){return c.isArray(a)?a:(""+a).split(".")},la=1,H=0,U=[],V=[],Na=0;c.cid=function(a,b){return a._cid?a._cid:a._cid=(b||"")+ ++Na};var j=c.Observe=c.Construct({bind:G,unbind:F,id:"id",canMakeObserve:v,startBatch:function(a){H++;a&&V.push(a)},stopBatch:function(a,b){a?H=0:H--;if(0==H){var d=U.slice(0),e=V.slice(0);U=[];V=[];la++;b&&this.startBatch();c.each(d,function(a){c.trigger.apply(c, - a)});c.each(e,function(a){a})}},triggerBatch:function(a,b,d){if(!a._init){if(0==H)return c.trigger(a,b,d);U.push([a,{type:b,batchNum:la},d])}},keys:function(a){var b=[];j.__reading&&j.__reading(a,"__keys");for(var d in a._data)b.push(d);return b}},{setup:function(a){this._data={};c.cid(this,".observe");this._init=1;this.attr(a);this.bind("change"+this._cid,c.proxy(this._changes,this));delete this._init},_changes:function(a,b,d,c,f){j.triggerBatch(this,{type:b,batchNum:a.batchNum},[c,f])},attr:function(a, - b){var d=typeof a;if("string"!==d&&"number"!==d)return this._attrs(a,b);if(b===l)return j.__reading&&j.__reading(this,a),this._get(a);this._set(a,b);return this},each:function(){j.__reading&&j.__reading(this,"__keys");return c.each.apply(l,[this.__get()].concat(c.makeArray(arguments)))},removeAttr:function(a){var a=T(a),b=a.shift(),d=this._data[b];if(a.length)return d.removeAttr(a);b in this._data&&(delete this._data[b],b in this.constructor.prototype||delete this[b],j.triggerBatch(this,"__keys", - l),j.triggerBatch(this,"change",[b,"remove",l,d]),j.triggerBatch(this,b,[l,d]));return d},_get:function(a){var a=T(a),b=this.__get(a.shift());return a.length?b?b._get(a):l:b},__get:function(a){return a?this._data[a]:this._data},_set:function(a,b){var d=T(a),c=d.shift(),f=this.__get(c);if(v(f)&&d.length)f._set(d,b);else{if(d.length)throw"can.Observe: Object does not exist";this.__convert&&(b=this.__convert(c,b));f||j.triggerBatch(this,"__keys",l);this.__set(c,b,f)}},__set:function(a,b,d){if(b!==d){var c= - this.__get().hasOwnProperty(a)?"set":"add";this.___set(a,v(b)?S(b,a,this):b);j.triggerBatch(this,"change",[a,c,b,d]);d&&M([d],this._cid)}},___set:function(a,b){this._data[a]=b;a in this.constructor.prototype||(this[a]=b)},bind:G,unbind:F,serialize:function(){return N(this,"serialize",{})},_attrs:function(a,b){if(a===l)return N(this,"attr",{});var a=c.extend(!0,{},a),d,e=this,f;j.startBatch();this.each(function(d,g){f=a[g];f===l?b&&e.removeAttr(g):(e.__convert&&(f=e.__convert(g,f)),d!==f&&(d instanceof - c.Observe&&f instanceof c.Observe&&M([d],e._cid),f instanceof c.Observe?e._set(g,f):v(d)&&v(f)?d.attr(f,b):d!=f&&e._set(g,f)),delete a[g])});for(d in a)f=a[d],this._set(d,f);j.stopBatch();return this}}),Oa=[].splice,O=j({setup:function(a,b){this.length=0;c.cid(this,".observe");this._init=1;this.push.apply(this,c.makeArray(a||[]));this.bind("change"+this._cid,c.proxy(this._changes,this));c.extend(this,b);delete this._init},_changes:function(a,b,d,c,f){~b.indexOf(".")||("add"===d?(j.triggerBatch(this, - d,[c,+b]),j.triggerBatch(this,"length",[this.length])):"remove"===d?(j.triggerBatch(this,d,[f,+b]),j.triggerBatch(this,"length",[this.length])):j.triggerBatch(this,d,[c,+b]));j.prototype._changes.apply(this,arguments)},__get:function(a){return a?this[a]:this},___set:function(a,b){this[a]=b;+a>=this.length&&(this.length=+a+1)},serialize:function(){return N(this,"serialize",[])},splice:function(a,b){var d=c.makeArray(arguments),e;for(e=2;ethis.length?this.push.apply(this, - a.slice(this.length)):a.lengthe&&(d=b,e=f);if(f>=g)return!1});c.route.routes[h]&&ra(c.route.routes[h],a)===e&&(d=c.route.routes[h]); - if(d){var i=o({},a),h=d.route.replace(qa,function(b,c){delete i[c];return a[c]===d.defaults[c]?"":encodeURIComponent(a[c])}),y;z(d.defaults,function(a,b){i[b]===a&&delete i[b]});y=c.param(i);b&&c.route.attr("route",d.route);return h+(y?c.route._querySeparator+y:"")}return c.isEmptyObject(a)?"":c.route._querySeparator+c.param(a)},deparam:function(a){var b={length:-1};z(c.route.routes,function(c){c.test.test(a)&&c.length>b.length&&(b=c)});if(-1"+a+""},current:function(a){return X.hash=="#!"+c.route.param(a)},_setup:function(){c.bind.call(u,"hashchange",ta)},_getHash:function(){return X.href.split(/#!?/)[1]||""},_setHash:function(a){a=c.route.param(a,!0);X.hash="#!"+a;return a}});z("bind unbind delegate undelegate attr removeAttr".split(" "),function(a){c.route[a]=function(){return c.route.data[a].apply(c.route.data, - arguments)}});var ua,Y,ta=c.route.setState=function(){var a=c.route._getHash();Y=c.route.deparam(a);(!Z||a!==va)&&c.route.attr(Y,!0)},va,Z;c.route.bind("change",function(){Z=1;clearTimeout(ua);ua=setTimeout(function(){Z=0;var a=c.route.data.serialize();va=c.route._setHash(a)},1)});c.bind.call(document,"ready",c.route.ready);c.route.constructor.canMakeObserve=c.Observe.canMakeObserve;var G=function(a,b,d){c.bind.call(a,b,d);return function(){c.unbind.call(a,b,d)}},D=c.isFunction,o=c.extend,z=c.each, - Wa=[].slice,wa=/\{([^\}]+)\}/g,Xa=c.getObject("$.event.special")||{},xa=function(a,b,d,e){c.delegate.call(a,b,d,e);return function(){c.undelegate.call(a,b,d,e)}},$;c.Control=c.Construct({setup:function(){c.Construct.setup.apply(this,arguments);if(c.Control){var a;this.actions={};for(a in this.prototype)this._isAction(a)&&(this.actions[a]=this._action(a))}},_shifter:function(a,b){var d="string"==typeof b?a[b]:b;D(d)||(d=a[d]);return function(){a.called=b;return d.apply(a,[this.nodeName?c.$(this):this].concat(Wa.call(arguments, - 0)))}},_isAction:function(a){var b=this.prototype[a],c=typeof b;return"constructor"!==a&&("function"==c||"string"==c&&D(this.prototype[b]))&&!(!Xa[a]&&!aa[a]&&!/[^\w]/.test(a))},_action:function(a,b){wa.lastIndex=0;if(b||!wa.test(a)){var d=b?c.sub(a,[b,u]):a,e=c.isArray(d),f=e?d[1]:d,h=f.split(/\s+/g),g=h.pop();return{processor:aa[g]||$,parts:[f,h.join(" "),g],delegate:e?d[0]:l}}},processors:{},defaults:{}},{setup:function(a,b){var d=this.constructor,e=d.pluginName||d._fullName;this.element=c.$(a); - e&&"can_control"!==e&&this.element.addClass(e);(e=c.data(this.element,"controls"))||c.data(this.element,"controls",e=[]);e.push(this);this.options=o({},d.defaults,b);this.on();return[this.element,this.options]},on:function(a,b,d,e){if(!a){this.off();var a=this.constructor,b=this._bindings,d=a.actions,e=this.element,f=c.Control._shifter(this,"destroy"),h,g;for(h in d)d.hasOwnProperty(h)&&(g=d[h]||a._action(h,this.options),b.push(g.processor(g.delegate||e,g.parts[2],g.parts[1],h,this)));c.bind.call(e, - "destroyed",f);b.push(function(a){c.unbind.call(a,"destroyed",f)});return b.length}"string"==typeof a&&(e=d,d=b,b=a,a=this.element);e===l&&(e=d,d=b,b=null);"string"==typeof e&&(e=c.Control._shifter(this,e));this._bindings.push(b?xa(a,c.trim(b),d,e):G(a,d,e));return this._bindings.length},off:function(){var a=this.element[0];z(this._bindings||[],function(b){b(a)});this._bindings=[]},destroy:function(){var a=this.constructor,a=a.pluginName||a._fullName;this.off();a&&"can_control"!==a&&this.element.removeClass(a); - a=c.data(this.element,"controls");a.splice(c.inArray(this,a),1);c.trigger(this,"destroyed");this.element=null}});var aa=c.Control.processors;$=function(a,b,d,e,f){e=c.Control._shifter(f,e);return d?xa(a,c.trim(d),b,e):G(a,b,e)};z("change click contextmenu dblclick keydown keyup keypress mousedown mousemove mouseout mouseover mouseup reset resize scroll select submit focusin focusout mouseenter mouseleave touchstart touchmove touchcancel touchend touchleave".split(" "),function(a){aa[a]=$});c.Control.processors.route= - function(a,b,d,e,f){d=d||"";c.route(d);var h,g=function(a){if(c.route.attr("route")===d&&(a.batchNum===l||a.batchNum!==h))if(h=a.batchNum,a=c.route.attr(),delete a.route,c.isFunction(f[e]))f[e](a);else f[f[e]](a)};c.route.bind("change",g);return function(){c.route.unbind("change",g)}};var D=c.isFunction,Ya=c.makeArray,ya=1,k=c.view=function(a,b,d,e){a=k.render(a,b,d,e);return D(a)?a:c.isDeferred(a)?a.pipe(function(a){return k.frag(a)}):k.frag(a)};c.extend(k,{frag:function(a,b){return k.hookup(k.fragment(a), - b)},fragment:function(a){a=c.buildFragment(a,document.body);a.childNodes.length||a.appendChild(document.createTextNode(""));return a},toId:function(a){return c.map(a.toString().split(/\/|\./g),function(a){if(a)return a}).join("_")},hookup:function(a,b){var d=[],e,f;c.each(a.childNodes?c.makeArray(a.childNodes):a,function(a){1===a.nodeType&&(d.push(a),d.push.apply(d,c.makeArray(a.getElementsByTagName("*"))))});c.each(d,function(a){if(a.getAttribute&&(e=a.getAttribute("data-view-id"))&&(f=k.hookups[e]))f(a, - b,e),delete k.hookups[e],a.removeAttribute("data-view-id")});return a},hookups:{},hook:function(a){k.hookups[++ya]=a;return" data-view-id='"+ya+"'"},cached:{},cachedRenderers:{},cache:!0,register:function(a){this.types["."+a.suffix]=a},types:{},ext:".ejs",registerScript:function(){},preload:function(){},render:function(a,b,d,e){D(d)&&(e=d,d=l);var f=Za(b);if(f.length){var h=new c.Deferred;f.push(za(a,!0));c.when.apply(c,f).then(function(a){var f=Ya(arguments),g=f.pop();if(c.isDeferred(b))b=Aa(a); -else for(var j in b)c.isDeferred(b[j])&&(b[j]=Aa(f.shift()));f=g(b,d);h.resolve(f);e&&e(f)});return h}var g,f=D(e),h=za(a,f);if(f)g=h,h.then(function(a){e(b?a(b,d):a)});else{if("resolved"===h.state()&&h.__view_id)return a=k.cachedRenderers[h.__view_id],b?a(b,d):a;h.then(function(a){g=b?a(b,d):a})}return g},registerView:function(a,b,d,e){b=(d||k.types[k.ext]).renderer(a,b);e=e||new c.Deferred;k.cache&&(k.cached[a]=e,e.__view_id=a,k.cachedRenderers[a]=b);return e.resolve(b)}});var Ba=function(a,b){if(!a.length)throw"can.view: No template or empty template:"+ - b;},za=function(a,b){var d=a.match(/\.[\w\d]+$/),e,f,h;a.match(/^#/)&&(a=a.substr(1));if(f=document.getElementById(a))d="."+f.type.match(/\/(x\-)?(.+)/)[2];!d&&!k.cached[a]&&(a+=d=k.ext);c.isArray(d)&&(d=d[0]);h=k.toId(a);if(a.match(/^\/\//))var g=a.substr(2),a=!u.steal?g:steal.config().root.mapJoin(g);e=k.types[d];if(k.cached[h])return k.cached[h];if(f)return k.registerView(h,f.innerHTML,e);var i=new c.Deferred;c.ajax({async:b,url:a,dataType:"text",error:function(b){Ba("",a);i.reject(b)},success:function(b){Ba(b, - a);k.registerView(h,b,e,i)}});return i},Za=function(a){var b=[];if(c.isDeferred(a))return[a];for(var d in a)c.isDeferred(a[d])&&b.push(a[d]);return b},Aa=function(a){return c.isArray(a)&&"success"===a[1]?a[0]:a};u.steal&&steal.type("view js",function(a,b){var c=k.types["."+a.type],e=k.toId(a.id);a.text="steal('"+(c.plugin||"can/view/"+a.type)+"',function(can){return can.view.preload('"+e+"',"+a.text+");\n})";b()});c.extend(k,{register:function(a){this.types["."+a.suffix]=a;u.steal&&steal.type(a.suffix+ - " view js",function(a,c){var e=k.types["."+a.type],f=k.toId(a.id+"");a.text=e.script(f,a.text);c()});k[a.suffix]=function(b,c){k.preload(b,a.renderer(b,c))}},registerScript:function(a,b,c){return"can.view.preload('"+b+"',"+k.types["."+a].script(b,c)+");"},preload:function(a,b){k.cached[a]=(new c.Deferred).resolve(function(a,c){return b.call(a,a,c)});return function(){return k.frag(b.apply(this,arguments))}}});var $a=function(a,b){var d;c.Observe&&(d=c.Observe.__reading,c.Observe.__reading=function(a, - b){e.push({obj:a,attr:b})});var e=[],f=a.call(b);c.Observe&&(c.Observe.__reading=d);return{value:f,observed:e}},Ca=function(a,b,d){var e={},f=!0,h={value:l,teardown:function(){for(var a in e){var b=e[a];b.observe.obj.unbind(b.observe.attr,i);delete e[a]}}},g,i=function(a){if(a.batchNum===l||a.batchNum!==g){var b=h.value,c=j();h.value=c;c!==b&&d(c,b);g=g=a.batchNum}},j=function(){var d=$a(a,b),g=d.observed,d=d.value;f=!f;c.each(g,function(a){e[a.obj._cid+"|"+a.attr]?e[a.obj._cid+"|"+a.attr].matched= - f:(e[a.obj._cid+"|"+a.attr]={matched:f,observe:a},a.obj.bind(a.attr,i))});for(var h in e)g=e[h],g.matched!==f&&(g.observe.obj.unbind(g.observe.attr,i),delete e[h]);return d};h.value=j();h.isListening=!c.isEmptyObject(e);return h};c.compute=function(a,b){if(a&&a.isComputed)return a;var d,e=0,f,h=!0;"function"===typeof a?f=function(g){return g===l?d?(e&&c.Observe.__reading&&c.Observe.__reading(f,"change"),d.value):a.call(b||this):a.apply(b||this,arguments)}:(f=function(b){if(b===l)return c.Observe.__reading&& - c.Observe.__reading(f,"change"),a;var d=a;a=b;d!==b&&c.Observe.triggerBatch(f,"change",[b,d]);return b},h=!1);f.isComputed=!0;f.bind=function(g,i){c.addEvent.apply(f,arguments);e===0&&h&&(d=Ca(a,b||this,function(a,b){c.Observe.triggerBatch(f,"change",[a,b])}));e++};f.unbind=function(a,b){c.removeEvent.apply(f,arguments);e--;e===0&&h&&d.teardown()};return f};c.compute.binder=Ca;var ab=/(\r|\n)+/g,ba={option:"textContent",textarea:"value"},Da={tr:"tbody",option:"select",td:"tr",li:"ul"},bb=function(a, - b,c){if(a)return a;for(;c":">",'"':'"',"'":"'"};this.tokenComplex=[];this.tokenMap={};for(var a=0,b;b=this.tokens[a];a++)b[2]?(this.tokenReg.push(b[2]),this.tokenComplex.push({abbr:b[1],re:RegExp(b[2]), - rescan:b[3]})):(this.tokenReg.push(b[1]),this.tokenSimple[b[1]]=b[0]),this.tokenMap[b[0]]=b[1];this.tokenReg=RegExp("("+this.tokenReg.slice(0).concat(["<",">",'"',"'"]).join("|")+")","g")};Scanner.prototype={helpers:[{name:/\s*\(([\$\w]+)\)\s*->([^\n]*)/,fn:function(a){a=a.match(/\s*\(([\$\w]+)\)\s*->([^\n]*)/);return"function(__){var "+a[1]+"=can.$(__);"+a[2]+"}"}}],scan:function(a,b){var c=[],e=0,f=this.tokenSimple,h=this.tokenComplex,a=a.replace(ab,"\n");a.replace(this.tokenReg,function(b,g){var i= - arguments[arguments.length-2];i>e&&c.push(a.substring(e,i));if(f[b])c.push(b);else for(var j=0,m;m=h[j];j++)if(m.re.test(b)){c.push(m.abbr);m.rescan&&c.push(m.rescan(g));break}e=i+g.length});e":B=0;s="/"==g.substr(g.length-1);n||ba[p[p.length-1]]?(s?j(g.substr(0,g.length-1),',can.view.pending(),"/>"'): - j(g,',can.view.pending(),">"'),g=""):g+=t;s&&(p.pop(),r=p[p.length-1]);break;case "'":case '"':B&&(x&&x===t?x=null:null===x&&(x=t,ca=s));default:"<"===s&&(r=t.split(/\s/)[0],0===r.indexOf("/")&&p.pop()===r.substr(1)?r=p[p.length-1]:p.push(r)),g+=t}else switch(t){case m.right:case m.returnRight:switch(A){case m.left:s=--g.split("{").length- --g.split("}").length;1==s?(i.push("___v1ew.push(","can.view.txt(0,'"+bb(r,c,J)+"',"+da()+",this,function(){","var ___v1ew = [];",g),k.push({before:"",after:"return ___v1ew.join('')}));\n"})): - (e=k.length&&-1==s?k.pop():{after:";"},e.before&&i.push(e.before),i.push(g,";",e.after));break;case m.escapeLeft:case m.returnLeft:(s=--g.split("{").length- --g.split("}").length)&&k.push({before:"return ___v1ew.join('')",after:"}));"});for(var A=A===m.escapeLeft?1:0,eb={insert:"___v1ew.push(",tagName:r,status:da()},ea=0;ea[\s|\w]\w*/.source&&(A=0);break}}"object"==typeof g?g.raw&&i.push(g.raw):i.push("___v1ew.push(", - "can.view.txt("+A+",'"+r+"',"+da()+",this,function(){ "+(this.text.escape||"")+"return ",g,s?"var ___v1ew = [];":"}));");q&&(q.after&&q.after.length)&&(j(q.after.length),q=null)}A=null;g="";break;case m.templateLeft:g+=m.left;break;default:g+=t}s=t}g.length&&j(g);i.push(";");g={out:"with(_VIEW) { with (_CONTEXT) {"+i.join("")+" return ___v1ew.join('')}}"};cb.call(g,"this.fn = (function(_CONTEXT,_VIEW){"+g.out+"});\r\n//@ sourceURL="+b+".js");return g}};var fa=!0;try{document.createTextNode()._=0}catch(ib){fa= - !1}var K={"class":"className",value:"value",textContent:"textContent"},fb={"":"span",table:"tr",tr:"td",ol:"li",ul:"li",tbody:"tr",thead:"tr",tfoot:"tr",select:"option",optgroup:"option"},gb=/__!!__/g,ba={option:"textContent",textarea:"value"},Ea=c.each(["checked","disabled","readonly","required"],function(a){K[a]=a}),ga=function(a,b,d){K[b]?a[K[b]]=-1@@!!@@";if(1===d){var r=h.value.replace(/['"]/g,"").split("=")[0];L.push(function(a){o=function(b){var b=(b||"").replace(/['"]/g,"").split("="),d=b[0];if(d!=r&&r){var e=r;-1"],["returnLeft","<%=="],["escapeLeft","<%="],["commentLeft","<%#"],["left","<%"],["right","%>"],["returnRight","%>"]]})});w.Helpers=function(a,b){this._data=a;this._extras=b;o(this,b)};w.Helpers.prototype={list:function(a,b){c.each(a,function(c,e){b(c,e,a)})}}; - c.view.register({suffix:"ejs",script:function(a,b){return"can.EJS(function(_CONTEXT,_VIEW) { "+(new w({text:b,name:a})).template.out+" })"},renderer:function(a,b){return w({text:b,name:a})}})})(this,jQuery); \ No newline at end of file