/*!
* CanJS - 1.1.7
* http://canjs.us/
* Copyright (c) 2013 Bitovi
* Wed, 24 Jul 2013 00:23:52 GMT
* Licensed MIT
* Includes: can/construct,can/observe,can/observe/compute,can/model,can/view,can/view/ejs,can/control,can/route,can/control/route
* Download from: http://canjs.com
*/
(function(undefined) {
// ## util/can.js
var __m4 = (function() {
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.VERSION = '@EDGE';
return can;
})();
// ## util/event.js
var __m6 = (function(can) {
// event.js
// ---------
// _Basic event wrapper._
can.addEvent = function(event, fn) {
if (!this.__bindEvents) {
this.__bindEvents = {};
}
var eventName = event.split(".")[0];
if (!this.__bindEvents[eventName]) {
this.__bindEvents[eventName] = [];
}
this.__bindEvents[eventName].push({
handler: fn,
name: event
});
return this;
};
can.removeEvent = function(event, fn) {
if (!this.__bindEvents) {
return;
}
var i = 0,
events = this.__bindEvents[event.split(".")[0]],
ev;
while (i < events.length) {
ev = events[i]
if ((fn && ev.handler === fn) || (!fn && ev.name === event)) {
events.splice(i, 1);
} else {
i++;
}
}
return this;
};
can.dispatch = function(event) {
if (!this.__bindEvents) {
return;
}
var eventName = event.type.split(".")[0],
handlers = (this.__bindEvents[eventName] || []).slice(0),
self = this,
args = [event].concat(event.data || []);
can.each(handlers, function(ev) {
event.data = args.slice(1);
ev.handler.apply(self, args);
});
}
return can;
})(__m4);
// ## util/fragment.js
var __m7 = (function(can) {
// fragment.js
// ---------
// _DOM Fragment support._
var fragmentRE = /^\s*<(\w+)[^>]*>/,
fragment = function(html, name) {
if (name === undefined) {
name = fragmentRE.test(html) && RegExp.$1;
}
if (html && can.isFunction(html.replace)) {
// Fix "XHTML"-style tags in all browsers
html = html.replace(/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, "<$1>$2>");
}
var container = document.createElement('div'),
temp = document.createElement('div')
// IE's parser will strip any `
` tags when `innerHTML`
// is called on a `tbody`. To get around this, we construct a
// valid table with a `tbody` that has the `innerHTML` we want.
// Then the container is the `firstChild` of the `tbody`.
// [source](http://www.ericvasilik.com/2006/07/code-karma.html).
if (name === "tbody" || name === "tfoot" || name === "thead") {
temp.innerHTML = "";
container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
} else if (name === "tr") {
temp.innerHTML = "";
container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild;
} else if (name === "td" || name === "th") {
temp.innerHTML = "";
container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild.firstChild;
} else if (name === 'option') {
temp.innerHTML = "" + html + " ";
container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
} else {
container.innerHTML = '' + html;
}
// IE8 barfs if you pass slice a `childNodes` object, so make a copy.
var tmp = {},
children = container.childNodes;
tmp.length = children.length;
for (var i = 0; i < children.length; i++) {
tmp[i] = children[i];
}
return [].slice.call(tmp);
}
can.buildFragment = function(html, nodes) {
var parts = fragment(html),
frag = document.createDocumentFragment();
can.each(parts, function(part) {
frag.appendChild(part);
})
return frag;
};
return can;
})(__m4);
// ## util/array/each.js
var __m8 = (function(can) {
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;
};
return can;
})(__m4);
// ## util/object/isplain/isplain.js
var __m9 = (function(can) {
var core_hasOwn = Object.prototype.hasOwnProperty,
isWindow = function(obj) {
return obj != null && obj == obj.window;
},
isPlainObject = function(obj) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if (!obj || (typeof obj !== "object") || obj.nodeType || isWindow(obj)) {
return false;
}
try {
// Not own constructor property must be Object
if (obj.constructor && !core_hasOwn.call(obj, "constructor") && !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
}
} catch (e) {
// IE8,9 Will throw exceptions on certain host objects #9897
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for (key in obj) {}
return key === undefined || core_hasOwn.call(obj, key);
}
can.isPlainObject = isPlainObject;
return can;
})(__m4);
// ## util/deferred.js
var __m10 = (function(can) {
// deferred.js
// ---------
// _Lightweight, jQuery style deferreds._
// extend is usually provided by the wrapper but to avoid steal.then calls
// we define a simple extend here as well
var extend = function(target, src) {
for (var key in src) {
if (src.hasOwnProperty(key)) {
target[key] = src[key];
}
}
},
Deferred = function(func) {
if (!(this instanceof Deferred))
return new Deferred();
this._doneFuncs = [];
this._failFuncs = [];
this._resultArgs = null;
this._status = "";
// Check for option `function` -- call it with this as context and as first
// parameter, as specified in jQuery API.
func && func.call(this, this);
};
can.Deferred = Deferred;
can.when = Deferred.when = function() {
var args = can.makeArray(arguments);
if (args.length < 2) {
var obj = args[0];
if (obj && (can.isFunction(obj.isResolved) && can.isFunction(obj.isRejected))) {
return obj;
} else {
return Deferred().resolve(obj);
}
} else {
var df = Deferred(),
done = 0,
// Resolve params -- params of each resolve, we need to track them down
// to be able to pass them in the correct order if the master
// needs to be resolved.
rp = [];
can.each(args, function(arg, j) {
arg.done(function() {
rp[j] = (arguments.length < 2) ? arguments[0] : arguments;
if (++done == args.length) {
df.resolve.apply(df, rp);
}
}).fail(function() {
df.reject((arguments.length === 1) ? arguments[0] : arguments);
});
});
return df;
}
}
var resolveFunc = function(type, _status) {
return function(context) {
var args = this._resultArgs = (arguments.length > 1) ? arguments[1] : [];
return this.exec(context, this[type], args, _status);
}
},
doneFunc = function(type, _status) {
return function() {
var self = this;
// In Safari, the properties of the `arguments` object are not enumerable,
// so we have to convert arguments to an `Array` that allows `can.each` to loop over them.
can.each(Array.prototype.slice.call(arguments), function(v, i, args) {
if (!v)
return;
if (v.constructor === Array) {
args.callee.apply(self, v)
} else {
// Immediately call the `function` if the deferred has been resolved.
if (self._status === _status)
v.apply(self, self._resultArgs || []);
self[type].push(v);
}
});
return this;
}
};
extend(Deferred.prototype, {
pipe: function(done, fail) {
var d = can.Deferred();
this.done(function() {
d.resolve(done.apply(this, arguments));
});
this.fail(function() {
if (fail) {
d.reject(fail.apply(this, arguments));
} else {
d.reject.apply(d, arguments);
}
});
return d;
},
resolveWith: resolveFunc("_doneFuncs", "rs"),
rejectWith: resolveFunc("_failFuncs", "rj"),
done: doneFunc("_doneFuncs", "rs"),
fail: doneFunc("_failFuncs", "rj"),
always: function() {
var args = can.makeArray(arguments);
if (args.length && args[0])
this.done(args[0]).fail(args[0]);
return this;
},
then: function() {
var args = can.makeArray(arguments);
// Fail `function`(s)
if (args.length > 1 && args[1])
this.fail(args[1]);
// Done `function`(s)
if (args.length && args[0])
this.done(args[0]);
return this;
},
state: function() {
switch (this._status) {
case 'rs':
return 'resolved';
case 'rj':
return 'rejected';
default:
return 'pending';
}
},
isResolved: function() {
return this._status === "rs";
},
isRejected: function() {
return this._status === "rj";
},
reject: function() {
return this.rejectWith(this, arguments);
},
resolve: function() {
return this.resolveWith(this, arguments);
},
exec: function(context, dst, args, st) {
if (this._status !== "")
return this;
this._status = st;
can.each(dst, function(d) {
d.apply(context, args);
});
return this;
}
});
return can;
})(__m4);
// ## util/hashchange.js
var __m11 = (function() {
// This is a workaround for libraries that don't natively listen to the window hashchange event
! function() {
var addEvent = function(el, ev, fn) {
if (el.addEventListener) {
el.addEventListener(ev, fn, false);
} else if (el.attachEvent) {
el.attachEvent('on' + ev, fn);
} else {
el['on' + ev] = fn;
}
},
onHashchange = function() {
can.trigger(window, 'hashchange');
};
addEvent(window, 'hashchange', onHashchange);
}();
})();
// ## util/yui/yui.js
var __m3 = (function(can) {
// ---------
// _YUI node list._
// `can.Y` is set as part of the build process.
// `YUI().use('*')` is called for when `YUI` is statically loaded (like when running tests).
var Y = can.Y = can.Y || YUI().use('*');
// Map string helpers.
can.trim = function(s) {
return Y.Lang.trim(s);
}
// Map array helpers.
can.makeArray = function(arr) {
if (!arr) {
return [];
}
return Y.Array(arr);
};
can.isArray = Y.Lang.isArray;
can.inArray = function(item, arr) {
if (!arr) {
return -1;
}
return Y.Array.indexOf(arr, item);
};
can.map = function(arr, fn) {
return Y.Array.map(can.makeArray(arr || []), fn);
};
// Map object helpers.
can.extend = function(first) {
var deep = first === true ? 1 : 0,
target = arguments[deep],
i = deep + 1,
arg;
for (; arg = arguments[i]; i++) {
Y.mix(target, arg, true, null, null, !! deep);
}
return target;
}
can.param = function(object) {
return Y.QueryString.stringify(object, {
arrayKey: true
})
}
can.isEmptyObject = function(object) {
return Y.Object.isEmpty(object);
}
// Map function helpers.
can.proxy = function(func, context) {
return Y.bind.apply(Y, arguments);
}
can.isFunction = function(f) {
return Y.Lang.isFunction(f);
}
// Element -- get the wrapped helper.
var prepareNodeList = function(nodelist) {
nodelist.each(function(node, i) {
nodelist[i] = node.getDOMNode();
});
nodelist.length = nodelist.size();
return nodelist;
}
can.$ = function(selector) {
if (selector === window) {
return window;
} else if (selector instanceof Y.NodeList) {
return prepareNodeList(selector);
} else if (typeof selector === "object" && !can.isArray(selector) && typeof selector.nodeType === "undefined" && !selector.getDOMNode) {
return selector;
} else {
return prepareNodeList(Y.all(selector));
}
}
can.get = function(wrapped, index) {
return wrapped._nodes[index];
}
can.append = function(wrapped, html) {
wrapped.each(function(node) {
if (typeof html === 'string') {
html = can.buildFragment(html, node)
}
node.append(html)
});
}
can.addClass = function(wrapped, className) {
return wrapped.addClass(className);
}
can.data = function(wrapped, key, value) {
if (value === undefined) {
return wrapped.item(0).getData(key)
} else {
return wrapped.item(0).setData(key, value)
}
}
can.remove = function(wrapped) {
return wrapped.remove() && wrapped.destroy();
}
// Destroyed method.
can._yNodeDestroy = can._yNodeDestroy || Y.Node.prototype.destroy;
Y.Node.prototype.destroy = function() {
can.trigger(this, "destroyed", [], false)
can._yNodeDestroy.apply(this, arguments)
}
// Let `nodelist` know about the new destroy...
Y.NodeList.addMethod("destroy", Y.Node.prototype.destroy);
// Ajax
var optionsMap = {
type: "method",
success: undefined,
error: undefined
}
var updateDeferred = function(request, d) {
// `YUI` only returns a request if it is asynchronous.
if (request && request.io) {
var xhr = request.io;
for (var prop in xhr) {
if (typeof d[prop] == 'function') {
d[prop] = function() {
xhr[prop].apply(xhr, arguments)
}
} else {
d[prop] = prop[xhr]
}
}
}
}
can.ajax = function(options) {
var d = can.Deferred(),
requestOptions = can.extend({}, options);
for (var option in optionsMap) {
if (requestOptions[option] !== undefined) {
requestOptions[optionsMap[option]] = requestOptions[option];
delete requestOptions[option]
}
}
requestOptions.sync = !options.async;
var success = options.success,
error = options.error;
requestOptions.on = {
success: function(transactionid, response) {
var data = response.responseText;
if (options.dataType === 'json') {
data = eval("(" + data + ")")
}
updateDeferred(request, d);
d.resolve(data);
success && success(data, "success", request);
},
failure: function(transactionid, response) {
updateDeferred(request, d);
d.reject(request, "error");
error && error(request, "error");
}
};
var request = Y.io(requestOptions.url, requestOptions);
updateDeferred(request, d);
return d;
}
// Events - The `id` of the `function` to be bound, used as an expando on the `function`
// so we can lookup it's `remove` object.
var yuiEventId = 0,
// Takes a node list, goes through each node
// and adds events data that has a map of events to
// `callbackId` to `remove` object. It looks like
// `{click: {5: {remove: fn}}}`.
addBinding = function(nodelist, selector, ev, cb) {
if (nodelist instanceof Y.NodeList || !nodelist.on || nodelist.getDOMNode) {
nodelist.each(function(node) {
var node = can.$(node);
var events = can.data(node, "events"),
eventName = ev + ":" + selector;
if (!events) {
can.data(node, "events", events = {});
}
if (!events[eventName]) {
events[eventName] = {};
}
if (cb.__bindingsIds === undefined) {
cb.__bindingsIds = yuiEventId++;
}
events[eventName][cb.__bindingsIds] = selector ? node.item(0).delegate(ev, cb, selector) : node.item(0).on(ev, cb);
});
} else {
var obj = nodelist,
events = obj.__canEvents = obj.__canEvents || {};
if (!events[ev]) {
events[ev] = {};
}
if (cb.__bindingsIds === undefined) {
cb.__bindingsIds = yuiEventId++;
}
events[ev][cb.__bindingsIds] = obj.on(ev, cb);
}
},
// Removes a binding on a `nodelist` by finding
// the remove object within the object's data.
removeBinding = function(nodelist, selector, ev, cb) {
if (nodelist instanceof Y.NodeList || !nodelist.on || nodelist.getDOMNode) {
nodelist.each(function(node) {
var node = can.$(node),
events = can.data(node, "events");
if (events) {
var eventName = ev + ":" + selector,
handlers = events[eventName],
handler = handlers[cb.__bindingsIds];
handler.detach();
delete handlers[cb.__bindingsIds];
if (can.isEmptyObject(handlers)) {
delete events[ev];
}
if (can.isEmptyObject(events)) {}
}
});
} else {
var obj = nodelist,
events = obj.__canEvents || {},
handlers = events[ev],
handler = handlers[cb.__bindingsIds];
handler.detach();
delete handlers[cb.__bindingsIds];
if (can.isEmptyObject(handlers)) {
delete events[ev];
}
if (can.isEmptyObject(events)) {}
}
}
can.bind = function(ev, cb) {
// If we can bind to it...
if (this.bind && this.bind !== can.bind) {
this.bind(ev, cb)
} else if (this.on || this.nodeType) {
addBinding(can.$(this), undefined, ev, cb)
} else if (this.addEvent) {
this.addEvent(ev, cb)
} else {
// Make it bind-able...
can.addEvent.call(this, ev, cb)
}
return this;
}
can.unbind = function(ev, cb) {
// If we can bind to it...
if (this.unbind && this.unbind !== can.unbind) {
this.unbind(ev, cb)
} else if (this.on || this.nodeType) {
removeBinding(can.$(this), undefined, ev, cb);
} else {
// Make it bind-able...
can.removeEvent.call(this, ev, cb)
}
return this;
}
can.trigger = function(item, event, args, bubble) {
if (item instanceof Y.NodeList) {
item = item.item(0);
}
if (item.getDOMNode) {
item = item.getDOMNode();
}
if (item.nodeName) {
item = Y.Node(item);
if (bubble === false) {
// Force stop propagation by listening to `on` and then
// immediately disconnecting
item.once(event, function(ev) {
ev.stopPropagation && ev.stopPropagation();
ev.cancelBubble = true;
ev._stopper && ev._stopper();
})
}
realTrigger(item.getDOMNode(), event, {})
} else {
if (typeof event === 'string') {
event = {
type: event
}
}
event.target = event.target || item
event.data = args
can.dispatch.call(item, event)
}
};
// Allow `dom` `destroyed` events.
Y.mix(Y.Node.DOM_EVENTS, {
destroyed: true,
foo: true
});
can.delegate = function(selector, ev, cb) {
if (this.on || this.nodeType) {
addBinding(can.$(this), selector, ev, cb)
} else if (this.delegate) {
this.delegate(selector, ev, cb)
}
return this;
}
can.undelegate = function(selector, ev, cb) {
if (this.on || this.nodeType) {
removeBinding(can.$(this), selector, ev, cb);
} else if (this.undelegate) {
this.undelegate(selector, ev, cb)
}
return this;
}
// `realTrigger` taken from `dojo`.
var leaveRe = /mouse(enter|leave)/,
_fix = function(_, p) {
return "mouse" + (p == "enter" ? "over" : "out");
},
realTrigger = document.createEvent ? function(n, e, a) {
// the same branch
var ev = document.createEvent("HTMLEvents");
e = e.replace(leaveRe, _fix);
ev.initEvent(e, true, true);
a && can.extend(ev, a);
n.dispatchEvent(ev);
} : function(n, e, a) {
// the janktastic branch
var ev = "on" + e,
stop = false,
lc = e.toLowerCase(),
node = n;
try {
// FIXME: is this worth it? for mixed-case native event support:? Opera ends up in the
// createEvent path above, and also fails on _some_ native-named events.
// if(lc !== e && d.indexOf(d.NodeList.events, lc) >= 0){
// // if the event is one of those listed in our NodeList list
// // in lowercase form but is mixed case, throw to avoid
// // fireEvent. /me sighs. http://gist.github.com/315318
// throw("janktastic");
// }
n.fireEvent(ev);
} catch (er) {
// a lame duck to work with. we're probably a 'custom event'
var evdata = can.extend({
type: e,
target: n,
faux: true,
// HACK: [needs] added support for customStopper to _base/event.js
// some tests will fail until del._stopPropagation has support.
_stopper: function() {
stop = this.cancelBubble;
}
}, a);
realTriggerHandler(n, e, evdata);
// handle bubbling of custom events, unless the event was stopped.
while (!stop && n !== document && n.parentNode) {
n = n.parentNode;
realTriggerHandler(n, e, evdata);
//can.isFunction(n[ev]) && n[ev](evdata);
}
}
},
realTriggerHandler = function(n, e, evdata) {
var node = Y.Node(n),
handlers = can.Y.Event.getListeners(node._yuid, e);
if (handlers) {
for (var i = 0; i < handlers.length; i++) {
handlers[i].fire(evdata)
}
}
};
return can;
})(__m4, YUI, __m6, __m7, __m8, __m9, __m10, __m11);
// ## util/string/string.js
var __m2 = (function(can) {
// ##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) {
var result = obj[prop];
if (result === undefined && add === true) {
result = obj[prop] = {}
}
return result
},
// 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,
i, container, rootsLength;
// Make sure roots is an `array`.
roots = can.isArray(roots) ? roots : [roots || window];
rootsLength = roots.length
if (!length) {
return roots[0];
}
// For each root, mark it as current.
for (r; r < rootsLength; r++) {
current = roots[r];
container = undefined;
// Walk current to the 2nd to last object or until there
// is not a container.
for (i = 0; i < length && isContainer(current); i++) {
container = current;
current = getNext(container, parts[i]);
}
// If we found property break cycle
if (container !== undefined && current !== undefined) {
break
}
}
// Remove property from found container
if (add === false && current !== undefined) {
delete container[parts[i - 1]]
}
// When adding property add it to the first root
if (add === true && current === undefined) {
current = roots[0]
for (i = 0; i < length && isContainer(current); i++) {
current = getNext(current, parts[i], true);
}
}
return current;
},
// 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 = [];
str = str || '';
obs.push(str.replace(strReplacer, function(whole, inside) {
// Convert inside to type.
var ob = can.getObject(inside, data, remove === true ? false : undefined);
if (ob === undefined || ob === null) {
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
});
return can;
})(__m3);
// ## construct/construct.js
var __m1 = (function(can) {
// ## 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, {
constructorExtends: true,
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 && Constructor.constructorExtends ?
// We are being called without `new` or we are extending.
arguments.callee.extend.apply(arguments.callee, arguments) :
// We are being called with `new`.
Constructor.newInstance.apply(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,
fullName: fullName,
_fullName: _fullName
});
// Dojo and YUI extend undefined
if (shortName !== undefined) {
Constructor.shortName = shortName;
}
// 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.Construct.prototype.setup = function() {};
can.Construct.prototype.init = function() {};
return can.Construct;
})(__m2);
// ## util/bind/bind.js
var __m13 = (function(can) {
// ## Bind helpers
can.bindAndSetup = function() {
// Add the event to this object
can.addEvent.apply(this, arguments);
// If not initializing, and the first binding
// call bindsetup if the function exists.
if (!this._init) {
if (!this._bindings) {
this._bindings = 1;
// setup live-binding
this._bindsetup && this._bindsetup();
} else {
this._bindings++;
}
}
return this;
};
can.unbindAndTeardown = function(ev, handler) {
// Remove the event handler
can.removeEvent.apply(this, arguments);
this._bindings--;
// If there are no longer any bindings and
// there is a bindteardown method, call it.
if (!this._bindings) {
this._bindteardown && this._bindteardown();
}
return this;
}
return can;
})(__m3);
// ## observe/observe.js
var __m12 = (function(can, bind) {
// ## 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.isDeferred(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 `child` and "bubbles" the event up.
// `child` - 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(child, prop, parent, Ob, List) {
Ob = Ob || Observe;
List = List || Observe.List;
// If it's an `array` make a list, otherwise a child.
if (child instanceof Observe) {
// We have an `observe` already...
// Make sure it is not listening to this already
// It's only listening if it has bindings already.
parent._bindings && unhookup([child], parent._cid);
} else if (can.isArray(child)) {
child = new List(child);
} else {
child = new Ob(child);
}
// only listen if something is listening to you
if (parent._bindings) {
// Listen to all changes and `batchTrigger` upwards.
bindToChildAndBubbleToParent(child, prop, parent)
}
return child;
},
bindToChildAndBubbleToParent = function(child, prop, parent) {
child.bind("change" + parent._cid, function() {
// `batchTrigger` the type on this...
var args = can.makeArray(arguments),
ev = args.shift();
args[0] = (prop === "*" ? [parent.indexOf(child), 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);
});
},
// 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;
},
attrParts = function(attr, keepKey) {
if (keepKey) {
return [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 = [],
makeBindSetup = function(wildcard) {
return function() {
var parent = this;
this._each(function(child, prop) {
if (child && child.bind) {
bindToChildAndBubbleToParent(child, wildcard || prop, parent)
}
})
};
};
var Observe = can.Map = can.Observe = can.Construct({
// keep so it can be overwritten
bind: can.bindAndSetup,
unbind: can.unbindAndTeardown,
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 {
event = typeof event === "string" ? {
type: event
} :
event;
event.batchNum = batchNum;
batchEvents.push([
item,
event,
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;
},
_bindsetup: makeBindSetup(),
_bindteardown: function() {
var cid = this._cid;
this._each(function(child) {
unhookup([child], cid)
})
},
_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))
},
// no live binding iterator
_each: function(callback) {
var data = this.__get();
for (var prop in data) {
if (data.hasOwnProperty(prop)) {
callback(data[prop], prop)
}
}
},
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 (arguments.length === 1) { // 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) {
// Info if this is List or not
var isList = this instanceof can.Observe.List,
// Convert the `attr` into parts (if nested).
parts = attrParts(attr),
// The actual property to remove.
prop = parts.shift(),
// The current value.
current = isList ? this[prop] : this._data[prop];
// If we have more parts, call `removeAttr` on that part.
if (parts.length) {
return current.removeAttr(parts)
} else {
if (isList) {
this.splice(prop, 1)
} 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) {
var value = typeof attr === 'string' && !! ~attr.indexOf('.') && this.__get(attr);
if (value) {
return value;
}
// 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, keepKey) {
// Convert `attr` to attr parts (if it isn't already).
var parts = attrParts(attr, keepKey),
// 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: can.bindAndSetup,
unbind: can.unbindAndTeardown,
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, true)
}
Observe.stopBatch()
return this;
},
compute: function(prop) {
return can.compute(this, prop);
}
});
// Helpers for `observable` lists.
var splice = [].splice,
list = Observe(
{
setup: function(instances, options) {
this.length = 0;
can.cid(this, ".observe")
this._init = 1;
if (can.isDeferred(instances)) {
this.replace(instances)
} else {
this.push.apply(this, can.makeArray(instances || []));
}
// this change needs to be ignored
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)
}
},
_each: function(callback) {
var data = this.__get();
for (var i = 0; i < data.length; i++) {
callback(data[i], i)
}
},
_bindsetup: makeBindSetup("*"),
// 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, this.constructor.Observe, this.constructor)
}
}
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
// `len` - Where these items were removed.
// `remove` - Items removed.
// `undefined` - The new values (there are none).
// `res` - The old, removed values (should these be unbound).
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,
reverse: [].reverse,
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;
}
});
can.List = 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
}, {});
}
return Observe;
})(__m3, __m13, __m1);
// ## observe/compute/compute.js
var __m14 = (function(can, bind) {
// returns the
// - observes and attr methods are called by func
// - the value returned by func
// ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}`
var getValueAndObserved = function(func, self) {
var oldReading;
if (can.Observe) {
// Set a callback on can.Observe to know
// when an attr is read.
// Keep a reference to the old reader
// if there is one. This is used
// for nested live binding.
oldReading = can.Observe.__reading;
can.Observe.__reading = function(obj, attr) {
// Add the observe and attr that was read
// to `observed`
observed.push({
obj: obj,
attr: attr + ""
});
};
}
var observed = [],
// Call the "wrapping" function to get the value. `observed`
// will have the observe/attribute pairs that were read.
value = func.call(self);
// Set back so we are no longer reading.
if (can.Observe) {
can.Observe.__reading = oldReading;
}
return {
value: value,
observed: observed
};
},
// Calls `callback(newVal, oldVal)` everytime an observed property
// called within `getterSetter` is changed and creates a new result of `getterSetter`.
// Also returns an object that can teardown all event handlers.
computeBinder = function(getterSetter, context, callback, computeState) {
// track what we are observing
var observing = {},
// a flag indicating if this observe/attr pair is already bound
matched = true,
// the data to return
data = {
// we will maintain the value while live-binding is taking place
value: undefined,
// a teardown method that stops listening
teardown: function() {
for (var name in observing) {
var ob = observing[name];
ob.observe.obj.unbind(ob.observe.attr, onchanged);
delete observing[name];
}
}
},
batchNum;
// when a property value is changed
var onchanged = function(ev) {
// If the compute is no longer bound (because the same change event led to an unbind)
// then do not call getValueAndBind, or we will leak bindings.
if (computeState && !computeState.bound) {
return;
}
if (ev.batchNum === undefined || ev.batchNum !== batchNum) {
// store the old value
var oldValue = data.value,
// get the new value
newvalue = getValueAndBind();
// update the value reference (in case someone reads)
data.value = newvalue;
// if a change happened
if (newvalue !== oldValue) {
callback(newvalue, oldValue);
}
batchNum = batchNum = ev.batchNum;
}
};
// gets the value returned by `getterSetter` and also binds to any attributes
// read by the call
var getValueAndBind = function() {
var info = getValueAndObserved(getterSetter, context),
newObserveSet = info.observed;
var value = info.value;
matched = !matched;
// go through every attribute read by this observe
can.each(newObserveSet, function(ob) {
// if the observe/attribute pair is being observed
if (observing[ob.obj._cid + "|" + ob.attr]) {
// mark at as observed
observing[ob.obj._cid + "|" + ob.attr].matched = matched;
} else {
// otherwise, set the observe/attribute on oldObserved, marking it as being observed
observing[ob.obj._cid + "|" + ob.attr] = {
matched: matched,
observe: ob
};
ob.obj.bind(ob.attr, onchanged);
}
});
// Iterate through oldObserved, looking for observe/attributes
// that are no longer being bound and unbind them
for (var name in observing) {
var ob = observing[name];
if (ob.matched !== matched) {
ob.observe.obj.unbind(ob.observe.attr, onchanged);
delete observing[name];
}
}
return value;
};
// set the initial value
data.value = getValueAndBind();
data.isListening = !can.isEmptyObject(observing);
return data;
}
// if no one is listening ... we can not calculate every time
can.compute = function(getterSetter, context, eventName) {
if (getterSetter && getterSetter.isComputed) {
return getterSetter;
}
// stores the result of computeBinder
var computedData,
// how many listeners to this this compute
bindings = 0,
// the computed object
computed,
// an object that keeps track if the computed is bound
// onchanged needs to know this. It's possible a change happens and results in
// something that unbinds the compute, it needs to not to try to recalculate who it
// is listening to
computeState = {
bound: false,
// true if this compute is calculated from other computes and observes
hasDependencies: false
},
// The following functions are overwritten depending on how compute() is called
// a method to setup listening
on = function() {},
// a method to teardown listening
off = function() {},
// the current cached value (only valid if bound = true)
value,
// how to read the value
get = function() {
return value
},
// sets the value
set = function(newVal) {
value = newVal;
},
// this compute can be a dependency of other computes
canReadForChangeEvent = true;
computed = function(newVal) {
// setting ...
if (arguments.length) {
// save a reference to the old value
var old = value;
// setter may return a value if
// setter is for a value maintained exclusively by this compute
var setVal = set.call(context, newVal, old);
// if this has dependencies return the current value
if (computed.hasDependencies) {
return get.call(context);
}
if (setVal === undefined) {
// it's possible, like with the DOM, setting does not
// fire a change event, so we must read
value = get.call(context);
} else {
value = setVal;
}
// fire the change
if (old !== value) {
can.Observe.triggerBatch(computed, "change", [value, old]);
}
return value;
} else {
// Let others know to listen to changes in this compute
if (can.Observe && can.Observe.__reading && canReadForChangeEvent) {
can.Observe.__reading(computed, 'change');
}
// if we are bound, use the cached value
if (computeState.bound) {
return value;
} else {
return get.call(context);
}
}
}
if (typeof getterSetter === "function") {
set = getterSetter;
get = getterSetter;
canReadForChangeEvent = eventName === false ? false : true;
computed.hasDependencies = false;
on = function(update) {
computedData = computeBinder(getterSetter, context || this, update, computeState);
computed.hasDependencies = computedData.isListening
value = computedData.value;
}
off = function() {
computedData && computedData.teardown();
}
} else if (context) {
if (typeof context == "string") {
// `can.compute(obj, "propertyName", [eventName])`
var propertyName = context,
isObserve = getterSetter instanceof can.Observe;
if (isObserve) {
computed.hasDependencies = true;
}
get = function() {
if (isObserve) {
return getterSetter.attr(propertyName);
} else {
return getterSetter[propertyName];
}
}
set = function(newValue) {
if (isObserve) {
getterSetter.attr(propertyName, newValue)
} else {
getterSetter[propertyName] = newValue;
}
}
var handler;
on = function(update) {
handler = function() {
update(get(), value)
};
can.bind.call(getterSetter, eventName || propertyName, handler)
// use getValueAndObserved because
// we should not be indicating that some parent
// reads this property if it happens to be binding on it
value = getValueAndObserved(get).value
}
off = function() {
can.unbind.call(getterSetter, eventName || propertyName, handler)
}
} else {
// `can.compute(initialValue, setter)`
if (typeof context === "function") {
value = getterSetter;
set = context;
} else {
// `can.compute(initialValue,{get:, set:, on:, off:})`
value = getterSetter;
var options = context;
get = options.get || get;
set = options.set || set;
on = options.on || on;
off = options.off || off;
}
}
} else {
// `can.compute(5)`
value = getterSetter;
}
computed.isComputed = true;
can.cid(computed, "compute")
var updater = function(newValue, oldValue) {
value = newValue;
// might need a way to look up new and oldVal
can.Observe.triggerBatch(computed, "change", [newValue, oldValue])
}
return can.extend(computed, {
_bindsetup: function() {
computeState.bound = true;
// setup live-binding
on.call(this, updater)
},
_bindteardown: function() {
off.call(this, updater)
computeState.bound = false;
},
bind: can.bindAndSetup,
unbind: can.unbindAndTeardown
});
};
can.compute.binder = computeBinder;
return can.compute;
})(__m3, __m13);
// ## model/model.js
var __m15 = (function(can) {
// ## 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);
try {
args[0] = model[func](args[0]);
d.resolveWith(d, args);
} catch (e) {
d.rejectWith(d, [e].concat(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 args;
// if we pass an array as `self` it it means we are coming from
// the queued request, and we're passing already serialized data
// self's signature will be: [self, serializedData]
if (can.isArray(self)) {
args = self[1];
self = self[0];
} else {
args = self.serialize();
}
args = [args];
var deferred,
// 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",
_reqs: 0,
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
can.Model._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.
can.Model._reqs = 0;
this._url = this._shortName + "/{" + this.id + "}"
},
_ajax: ajaxMaker,
_makeRequest: makeRequest,
_clean: function() {
can.Model._reqs--;
if (!can.Model._reqs) {
for (var id in this.store) {
if (!this.store[id]._bindings) {
delete this.store[id];
}
}
}
return arguments[0];
},
models: function(instancesRawData, oldList) {
// until "end of turn", increment reqs counter so instances will be added to the store
can.Model._reqs++;
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 (typeof raw === 'undefined') {
throw new Error('Could not get any raw data while converting using .models');
}
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);
}
})
}
// at "end of turn", clean up the store
setTimeout(can.proxy(this._clean, this), 1);
return res;
},
model: function(attributes) {
if (!attributes) {
return;
}
if (typeof attributes.serialize === 'function') {
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 (can.Model._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;
var def = can.Deferred();
def.then(success, error);
return def.done(function(data) {
self.destroyed(data)
}).resolve(self);
}
return makeRequest(this, 'destroy', success, error, 'destroyed');
},
_bindsetup: function() {
this.constructor.store[this.__get(this.constructor.id)] = this;
return can.Observe.prototype._bindsetup.apply(this, arguments);
},
_bindteardown: function() {
delete this.constructor.store[getId(this)];
return can.Observe.prototype._bindteardown.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",
makeCreate: "model",
makeUpdate: "model"
}, function(method, name) {
can.Model[name] = function(oldMethod) {
return function() {
var args = can.makeArray(arguments),
oldArgs = can.isFunction(args[1]) ? args.splice(0, 1) : args.splice(0, 2),
def = pipe(oldMethod.apply(this, oldArgs), this, method);
def.then(args[0], args[1]);
// 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);
// triggers change event that bubble's like
// handler( 'change','1.destroyed' ). This is used
// to remove items on destroyed from Model Lists.
// but there should be a better way.
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(params) {
if (can.isPlainObject(params) && !can.isArray(params)) {
can.Observe.List.prototype.setup.apply(this);
this.replace(this.constructor.Observe.findAll(params))
} else {
can.Observe.List.prototype.setup.apply(this, arguments);
}
},
_changes: function(ev, attr) {
can.Observe.List.prototype._changes.apply(this, arguments);
if (/\w+\.destroyed/.test(attr)) {
var index = this.indexOf(ev.target);
if (index != -1) {
this.splice(index, 1);
}
}
}
})
return can.Model;
})(__m3, __m12);
// ## view/view.js
var __m16 = (function(can) {
// ## view.js
// `can.view`
// _Templating abstraction._
var isFunction = can.isFunction,
makeArray = can.makeArray,
// Used for hookup `id`s.
hookupId = 1,
$view = can.view = can.template = 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.then(function(result, data) {
deferred.resolve.call(deferred, pipe(result), data);
}, function() {
deferred.fail.apply(deferred, arguments);
});
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;
},
// auj
// heir
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);
}, function() {
deferred.reject.apply(deferred, arguments)
});
// 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 `