can.jquery-all.js | |
---|---|
/*!
* CanJS - 1.1.4 (2013-02-05)
* http://canjs.us/
* Copyright (c) 2013 Bitovi
* Licensed MIT
*/
module = {
_orig: window.module,
_define: window.define
};
module['jquery'] = jQuery;
module['jquery/jquery.js'] = jQuery;
define = function (id, deps, value) {
module[id] = value();
};
define.amd = {
jQuery: true
}; | |
can/util/can.js | module['can/util/can.js'] = (function () {
var can = window.can || {};
if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) {
window.can = can;
}
can.isDeferred = function (obj) {
var isFunction = this.isFunction; |
Returns | 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)
}
}
return can;
})(); // ## can/util/array/each.js
module['can/util/array/each.js'] = (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;
})(module["can/util/can.js"]); // ## can/util/jquery/jquery.js
module['can/util/jquery/jquery.js'] = (function ($, can) { |
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);
};
return can;
})(module["jquery/jquery.js"], module["can/util/can.js"], module["can/util/array/each.js"]); // ## can/util/string/string.js
module['can/util/string/string.js'] = (function (can) { |
string.jsMiscellaneous string utility functions. | 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 | getNext = function (obj, prop, add) {
return prop in obj ? obj[prop] : (add && (obj[prop] = {}));
}, |
Returns | 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(/>/g, '>').replace(strQuote, '"').replace(strSingleQuote, "'");
},
getObject: function (name, roots, add) { |
The parts of the name we are looking up | var parts = name ? name.split('.') : [],
length = parts.length,
current, r = 0,
ret, i; |
Make sure roots is an | 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 | 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
});
return can;
})(module["can/util/jquery/jquery.js"]); // ## can/construct/construct.js
module['can/construct/construct.js'] = (function (can) { |
construct.js
| 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 ( | var inst = this.instance(),
arg = arguments,
args; |
Call | if (inst.setup) {
args = inst.setup.apply(inst, arguments);
} |
Call | if (inst.init) {
inst.init.apply(inst, args || arguments);
}
return inst;
}, |
Overwrites an object with methods. Used in the | _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 | setup: function (base, fullName) {
this.defaults = can.extend(true, {}, base.defaults, this.defaults);
}, |
Create's a new | instance: function () { |
Prevents running | initializing = 1;
var inst = new this(); |
Allow running | 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 | arguments.callee.extend.apply(arguments.callee, arguments) : |
We are being called with | 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 | 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;
}
});
return can.Construct;
})(module["can/util/string/string.js"]); // ## can/construct/proxy/proxy.js
module['can/construct/proxy/proxy.js'] = (function (can, Construct) {
var isFunction = can.isFunction,
isArray = can.isArray,
makeArray = can.makeArray,
proxy = function (funcs) { |
args that should be curried | var args = makeArray(arguments),
self; |
get the functions to callback | funcs = args.shift(); |
if there is only one function, make funcs into an array | if (!isArray(funcs)) {
funcs = [funcs];
} |
keep a reference to us in self | self = this;
return function class_cb() { |
add the arguments after the curried args | var cur = args.concat(makeArray(arguments)),
isString, length = funcs.length,
f = 0,
func; |
go through each function to call back | for (; f < length; f++) {
func = funcs[f];
if (!func) {
continue;
} |
set called with the name of the function on self (this is how this.view works) | isString = typeof func == "string"; |
call the function | cur = (isString ? self[func] : func).apply(self, cur || []); |
pass the result to the next function (if there is a next function) | if (f < length - 1) {
cur = !isArray(cur) || cur._use_call ? [cur] : cur
}
}
return cur;
}
}
can.Construct.proxy = can.Construct.prototype.proxy = proxy; |
this corrects the case where can/control loads after can/construct/proxy, so static props don't have proxy | var correctedClasses = [can.Observe, can.Control, can.Model],
i = 0;
for (; i < correctedClasses.length; i++) {
if (correctedClasses[i]) {
correctedClasses[i].proxy = proxy;
}
}
return can;
})(module["can/util/jquery/jquery.js"], module["can/construct/construct.js"]); // ## can/construct/super/super.js
module['can/construct/super/super.js'] = (function (can, Construct) { |
tests if we can get super in .toString() | var isFunction = can.isFunction,
fnTest = /xyz/.test(function () {
xyz;
}) ? /\b_super\b/ : /.*/; |
overwrites a single property so it can still call super | can.Construct._overwrite = function (addTo, base, name, val) { |
Check if we're overwriting an existing function | addTo[name] = isFunction(val) && isFunction(base[name]) && fnTest.test(val) ? (function (name, fn) {
return function () {
var tmp = this._super,
ret; |
Add a new ._super() method that is the same method but on the super-class | this._super = base[name]; |
The method only need to be bound temporarily, so we remove it when we're done executing | ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, val) : val;
} |
overwrites an object with methods, sets up _super newProps - new properties oldProps - where the old properties might be addTo - what we are adding to | can.Construct._inherit = function (newProps, oldProps, addTo) {
addTo = addTo || newProps
for (var name in newProps) {
can.Construct._overwrite(addTo, oldProps, name, newProps[name]);
}
}
return can;
})(module["can/util/jquery/jquery.js"], module["can/construct/construct.js"]); // ## can/control/control.js
module['can/control/control.js'] = (function (can) { |
control.js
| 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.setup.apply(this, arguments); |
If you didn't provide a name, or are | 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 | _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 | _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 | paramReplacer.lastIndex = 0;
if (options || !paramReplacer.test(methodName)) { |
If we have options, run sub to replace templates | var convertedName = options ? can.sub(methodName, [options, window]) : methodName;
if (!convertedName) {
return null;
} |
If a | 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 | processors: {}, |
A object of name-value pairs that act as default values for a control instance | defaults: {}
},
{ |
Sets | 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 | 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 | 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 | 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... | 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 | destroy: function () {
var Class = this.constructor,
pluginName = Class.pluginName || Class._fullName,
controls; |
Unbind bindings. | this.off();
if (pluginName && pluginName !== 'can_control') { |
Remove the | this.element.removeClass(pluginName);
} |
Remove from | 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. | 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 | 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 processorsTOOD feature detect? | "touchstart", "touchmove", "touchcancel", "touchend", "touchleave"], function (v) {
processors[v] = basicProcessor;
});
return Control;
})(module["can/util/jquery/jquery.js"], module["can/construct/construct.js"]); // ## can/control/plugin/plugin.js
module['can/control/plugin/plugin.js'] = (function ($, can) { |
used to determine if a control instance is one of controllers controllers can be strings or classes | var i, isAControllerOf = function (instance, controllers) {
for (i = 0; i < controllers.length; i++) {
if (typeof controllers[i] == 'string' ? instance.constructor._shortName == controllers[i] : instance instanceof controllers[i]) {
return true;
}
}
return false;
},
makeArray = can.makeArray,
old = can.Control.setup;
can.Control.setup = function () { |
if you didn't provide a name, or are control, don't do anything | if (this !== can.Control) {
var pluginName = this.pluginName || this._fullName; |
create jQuery plugin | if (pluginName !== 'can_control') {
this.plugin(pluginName);
}
old.apply(this, arguments);
}
};
$.fn.extend({
controls: function () {
var controllerNames = makeArray(arguments),
instances = [],
controls, c, cname; |
check if arguments | this.each(function () {
controls = can.$(this).data("controls");
if (!controls) {
return;
}
for (var i = 0; i < controls.length; i++) {
c = controls[i];
if (!controllerNames.length || isAControllerOf(c, controllerNames)) {
instances.push(c);
}
}
});
return instances;
},
control: function (control) {
return this.controls.apply(this, arguments)[0];
}
});
can.Control.plugin = function (pluginname) {
var control = this;
if (!$.fn[pluginname]) {
$.fn[pluginname] = function (options) {
var args = makeArray(arguments), |
if the arg is a method on this control | isMethod = typeof options == "string" && $.isFunction(control.prototype[options]),
meth = args[0],
returns;
this.each(function () { |
check if created | var plugin = can.$(this).control(control);
if (plugin) {
if (isMethod) { |
call a method on the control with the remaining args | returns = plugin[meth].apply(plugin, args.slice(1));
}
else { |
call the plugin's update method | plugin.update.apply(plugin, args);
}
}
else { |
create a new control instance | control.newInstance.apply(control, [this].concat(args));
}
});
return returns !== undefined ? returns : this;
};
}
}
can.Control.prototype.update = function (options) {
can.extend(this.options, options);
this.on();
};
return can;
})(module["jquery/jquery.js"], module["can/util/jquery/jquery.js"], module["can/control/control.js"]); // ## can/view/view.js
module['can/view/view.js'] = (function (can) { |
view.js
| var isFunction = can.isFunction,
makeArray = can.makeArray, |
Used for hookup | hookupId = 1,
$view = can.view = function (view, data, helpers, callback) { |
If helpers is a | 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 | 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 | 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 | 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 | 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 && callback(result, dataCopy);
}); |
Return the deferred... | return deferred;
}
else { |
No deferreds! Render this bad boy. | var response, |
If there's a | async = isFunction(callback), |
Get the | deferred = get(view, async); |
If we are | 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 _viewid 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 | return def.resolve(func);
}
}); |
Makes sure there's a template, if not, have | var checkText = function (text, url) {
if (!text.length) {
throw "can.view: No template or empty template:" + url;
}
}, |
| 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, |
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 | if (el = document.getElementById(url)) {
suffix = "." + el.type.match(/\/(x\-)?(.+)/)[2];
} |
If there is no suffix, add one. | if (!suffix && !$view.cached[url]) {
url += (suffix = $view.ext);
}
if (can.isArray(suffix)) {
suffix = suffix[0]
} |
Convert to a unique and valid id. | id = $view.toId(url); |
If an absolute path, use | if (url.match(/^\/\//)) {
var sub = url.substr(2);
url = !window.steal ? sub : steal.config().root.mapJoin(sub);
} |
Set the template engine type. | type = $view.types[suffix]; |
If it is cached, | if ($view.cached[id]) { |
Return the cached deferred renderer. | return $view.cached[id]; |
Otherwise if we are getting this from a | } else if (el) { |
Resolve immediately with the element's | return $view.registerView(id, el.innerHTML, type);
} else { |
Make an ajax request for text. | var d = new can.Deferred();
can.ajax({
async: async,
url: url,
dataType: "text",
error: function (jqXHR) {
checkText("", url);
d.reject(jqXHR);
},
success: function (text) { |
Make sure we got some text back. | checkText(text, url);
$view.registerView(id, text, type, d)
}
});
return d;
}
}, |
Gets an | getDeferreds = function (data) {
var deferreds = []; |
pull out deferreds | if (can.isDeferred(data)) {
return [data]
} else {
for (var prop in data) {
if (can.isDeferred(data[prop])) {
deferreds.push(data[prop]);
}
}
}
return deferreds;
}, |
Gets the useful part of a resolved deferred.
This is for | usefulPart = function (resolved) {
return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved
};
can.extend($view, {
register: function (info) {
this.types["." + info.suffix] = info;
$view[info.suffix] = function (id, text) {
if (!text) { |
Return a nameless renderer | var renderer = function () {
return $view.frag(renderer.render.apply(this, arguments));
}
renderer.render = function () {
var renderer = info.renderer(null, id);
return renderer.apply(renderer, arguments);
}
return renderer;
}
$view.preload(id, info.renderer(id, text));
return can.view(id);
}
},
registerScript: function (type, id, src) {
return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
},
preload: function (id, renderer) {
$view.cached[id] = new can.Deferred().resolve(function (data, helpers) {
return renderer.call(data, data, helpers);
});
function frag() {
return $view.frag(renderer.apply(this, arguments));
} |
expose the renderer for mustache | frag.render = renderer;
return frag;
}
});
return can;
})(module["can/util/jquery/jquery.js"]); // ## can/view/scanner.js
module['can/view/scanner.js'] = (function (can) {
var newLine = /(\r|\n)+/g,
tagToContentPropMap = {
option: "textContent",
textarea: "value"
}, |
Escapes characters starting with | clean = function (content) {
return content.split('\\').join("\\\\").split("\n").join("\\n").split('"').join('\\"').split("\t").join("\\t");
},
reverseTagMap = {
tr: "tbody",
option: "select",
td: "tr",
th: "tr",
li: "ul"
}, |
Returns a tagName to use as a temporary placeholder for live content looks forward ... could be slow, but we only do it when necessary | getTag = function (tagName, tokens, i) { |
if a tagName is provided, use that | if (tagName) {
return tagName;
} else { |
otherwise go searching for the next two tokens like "<",TAG | while (i < tokens.length) {
if (tokens[i] == "<" && reverseTagMap[tokens[i + 1]]) {
return reverseTagMap[tokens[i + 1]];
}
i++;
}
}
},
bracketNum = function (content) {
return (--content.split("{").length) - (--content.split("}").length);
},
myEval = function (script) {
eval(script);
},
attrReg = /([^\s]+)[\s]*=[\s]*$/, |
Commands for caching. | startTxt = 'var ___v1ew = [];',
finishTxt = "return ___v1ew.join('')",
put_cmd = "___v1ew.push(",
insert_cmd = put_cmd, |
Global controls (used by other functions to know where we are). Are we inside a tag? | htmlTag = null, |
Are we within a quote within a tag? | quote = null, |
What was the text before the current quote? (used to get the | beforeQuote = null, |
Whether a rescan is in progress | rescan = null, |
Used to mark where the element is. | status = function () { |
| return quote ? "'" + beforeQuote.match(attrReg)[1] + "'" : (htmlTag ? 1 : 0);
};
can.view.Scanner = Scanner = function (options) { |
Set options on self | can.extend(this, {
text: {},
tokens: []
}, options); |
Cache a token lookup | this.tokenReg = [];
this.tokenSimple = {
"<": "<",
">": ">",
'"': '"',
"'": "'"
};
this.tokenComplex = [];
this.tokenMap = {};
for (var i = 0, token; token = this.tokens[i]; i++) { |
Save complex mappings (custom regexp) | if (token[2]) {
this.tokenReg.push(token[2]);
this.tokenComplex.push({
abbr: token[1],
re: new RegExp(token[2]),
rescan: token[3]
});
} |
Save simple mappings (string only, no regexp) | else {
this.tokenReg.push(token[1]);
this.tokenSimple[token[1]] = token[0];
}
this.tokenMap[token[0]] = token[1];
} |
Cache the token registry. | this.tokenReg = new RegExp("(" + this.tokenReg.slice(0).concat(["<", ">", '"', "'"]).join("|") + ")", "g");
};
Scanner.prototype = {
helpers: [
{
name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
fn: function (content) {
var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
parts = content.match(quickFunc);
return "function(__){var " + parts[1] + "=can.$(__);" + parts[2] + "}";
}
}],
scan: function (source, name) {
var tokens = [],
last = 0,
simple = this.tokenSimple,
complex = this.tokenComplex;
source = source.replace(newLine, "\n");
source.replace(this.tokenReg, function (whole, part) { |
offset is the second to last argument | var offset = arguments[arguments.length - 2]; |
if the next token starts after the last token ends push what's in between | if (offset > last) {
tokens.push(source.substring(last, offset));
} |
push the simple token (if there is one) | if (simple[whole]) {
tokens.push(whole);
} |
otherwise lookup complex tokens | else {
for (var i = 0, token; token = complex[i]; i++) {
if (token.re.test(whole)) {
tokens.push(token.abbr); |
Push a rescan function if one exists | if (token.rescan) {
tokens.push(token.rescan(part));
}
break;
}
}
} |
update the position of the last part of the last token | last = offset + part.length;
}); |
if there's something at the end, add it | if (last < source.length) {
tokens.push(source.substr(last));
}
var content = '',
buff = [startTxt + (this.text.start || '')], |
Helper | put = function (content, bonus) {
buff.push(put_cmd, '"', clean(content), '"' + (bonus || '') + ');');
}, |
A stack used to keep track of how we should end a bracket
| endStack = [], |
The last token, used to remember which tag we are in. | lastToken, |
The corresponding magic tag. | startTag = null, |
Was there a magic tag inside an html tag? | magicInTag = false, |
The current tag name. | tagName = '', |
stack of tagNames | tagNames = [], |
Declared here. | bracketCount, i = 0,
token, tmap = this.tokenMap; |
Reinitialize the tag state goodness. | htmlTag = quote = beforeQuote = null;
for (;
(token = tokens[i++]) !== undefined;) {
if (startTag === null) {
switch (token) {
case tmap.left:
case tmap.escapeLeft:
case tmap.returnLeft:
magicInTag = htmlTag && 1;
case tmap.commentLeft: |
A new line -- just add whatever content within a clean. | startTag = token;
if (content.length) {
put(content);
}
content = '';
break;
case tmap.escapeFull: |
This is a full line escape (a line that contains only whitespace and escaped logic) Break it up into escape left and right | magicInTag = htmlTag && 1;
rescan = 1;
startTag = tmap.escapeLeft;
if (content.length) {
put(content);
}
rescan = tokens[i++];
content = rescan.content || rescan;
if (rescan.before) {
put(rescan.before);
}
tokens.splice(i, 0, tmap.right);
break;
case tmap.commentFull: |
Ignore full line comments. | break;
case tmap.templateLeft:
content += tmap.left;
break;
case '<': |
Make sure we are not in a comment. | if (tokens[i].indexOf("!--") !== 0) {
htmlTag = 1;
magicInTag = 0;
}
content += token;
break;
case '>':
htmlTag = 0; |
content.substr(-1) doesn't work in IE7/8 | var emptyElement = content.substr(content.length - 1) == "/"; |
if there was a magic tag
or it's an element that has text content between its tags,
but content is not other tags add a hookup
TODO: we should only add | if (magicInTag || tagToContentPropMap[tagNames[tagNames.length - 1]]) { |
make sure / of /> is on the left of pending | if (emptyElement) {
put(content.substr(0, content.length - 1), ",can.view.pending(),\"/>\"");
} else {
put(content, ",can.view.pending(),\">\"");
}
content = '';
} else {
content += token;
} |
if it's a tag like | if (emptyElement) { |
remove the current tag in the stack | tagNames.pop(); |
set the current tag to the previous parent | tagName = tagNames[tagNames.length - 1];
}
break;
case "'":
case '"': |
If we are in an html tag, finding matching quotes. | if (htmlTag) { |
We have a quote and it matches. | if (quote && quote === token) { |
We are exiting the quote. | quote = null; |
Otherwise we are creating a quote.
TODO: does this handle | } else if (quote === null) {
quote = token;
beforeQuote = lastToken;
}
}
default: |
Track the current tag | if (lastToken === '<') {
tagName = token.split(/\s/)[0];
if (tagName.indexOf("/") === 0 && tagNames.pop() === tagName.substr(1)) { |
set tagName to the last tagName if there are no more tagNames, we'll rely on getTag. | tagName = tagNames[tagNames.length - 1];
} else {
tagNames.push(tagName);
}
}
content += token;
break;
}
} else { |
We have a start tag. | switch (token) {
case tmap.right:
case tmap.returnRight:
switch (startTag) {
case tmap.left: |
Get the number of | bracketCount = bracketNum(content); |
We are ending a block. | if (bracketCount == 1) { |
We are starting on. | buff.push(insert_cmd, "can.view.txt(0,'" + getTag(tagName, tokens, i) + "'," + status() + ",this,function(){", startTxt, content);
endStack.push({
before: "",
after: finishTxt + "}));\n"
});
}
else { |
How are we ending this statement? | last = // If the stack has value and we are ending a block...
endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
endStack.pop() : // Or use the default ending.
{
after: ";"
}; |
If we are ending a returning block, add the finish text which returns the result of the block. | if (last.before) {
buff.push(last.before);
} |
Add the remaining content. | buff.push(content, ";", last.after);
}
break;
case tmap.escapeLeft:
case tmap.returnLeft: |
We have an extra | bracketCount = bracketNum(content); |
If we have more | if (bracketCount) { |
When we return to the same # of | endStack.push({
before: finishTxt,
after: "}));"
});
}
var escaped = startTag === tmap.escapeLeft ? 1 : 0,
commands = {
insert: insert_cmd,
tagName: getTag(tagName, tokens, i),
status: status()
};
for (var ii = 0; ii < this.helpers.length; ii++) { |
Match the helper based on helper regex name value | var helper = this.helpers[ii];
if (helper.name.test(content)) {
content = helper.fn(content, commands); |
dont escape partials | if (helper.name.source == /^>[\s]*\w*/.source) {
escaped = 0;
}
break;
}
} |
Handle special cases | if (typeof content == 'object') {
if (content.raw) {
buff.push(content.raw);
}
} else { |
If we have | buff.push(insert_cmd, "can.view.txt(" + escaped + ",'" + tagName + "'," + status() + ",this,function(){ " + (this.text.escape || '') + "return ", content, |
If we have a block. | bracketCount ? |
Start with startTxt | startTxt : |
If not, add | "}));");
}
if (rescan && rescan.after && rescan.after.length) {
put(rescan.after.length);
rescan = null;
}
break;
}
startTag = null;
content = '';
break;
case tmap.templateLeft:
content += tmap.left;
break;
default:
content += token;
break;
}
}
lastToken = token;
} |
Put it together... | if (content.length) { |
Should be | put(content);
}
buff.push(";");
var template = buff.join(''),
out = {
out: 'with(_VIEW) { with (_CONTEXT) {' + template + " " + finishTxt + "}}"
}; |
Use | myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
return out;
}
};
return Scanner;
})(module["can/view/view.js"]); // ## can/observe/compute/compute.js
module['can/observe/compute/compute.js'] = (function (can) { |
returns the
- observes and attr methods are called by func
- the value returned by func
ex: | 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.push({
obj: obj,
attr: attr
});
};
}
var observed = [], |
Call the "wrapping" function to get the value. | 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 | computeBinder = function (getterSetter, context, callback) { |
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 (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 | 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) {
if (getterSetter && getterSetter.isComputed) {
return getterSetter;
} |
get the value right away TODO: eventually we can defer this until a bind or a read | var computedData, bindings = 0,
computed, canbind = true;
if (typeof getterSetter === "function") {
computed = function (value) {
if (value === undefined) { |
we are reading | if (computedData) { |
If another compute is calling this compute for the value, it needs to bind to this compute's change so it will re-compute and re-bind when this compute changes. | if (bindings && can.Observe.__reading) {
can.Observe.__reading(computed, 'change');
}
return computedData.value;
} else {
return getterSetter.call(context || this)
}
} else {
return getterSetter.apply(context || this, arguments)
}
}
} else { |
we just gave it a value | computed = function (val) {
if (val === undefined) { |
If observing, record that the value is being read. | if (can.Observe.__reading) {
can.Observe.__reading(computed, 'change');
}
return getterSetter;
} else {
var old = getterSetter;
getterSetter = val;
if (old !== val) {
can.Observe.triggerBatch(computed, "change", [val, old]);
}
return val;
}
}
canbind = false;
}
computed.isComputed = true;
can.cid(computed, "compute")
computed.bind = function (ev, handler) {
can.addEvent.apply(computed, arguments);
if (bindings === 0 && canbind) { |
setup live-binding | computedData = computeBinder(getterSetter, context || this, function (newValue, oldValue) {
can.Observe.triggerBatch(computed, "change", [newValue, oldValue])
});
}
bindings++;
}
computed.unbind = function (ev, handler) {
can.removeEvent.apply(computed, arguments);
bindings--;
if (bindings === 0 && canbind) {
computedData.teardown();
}
};
return computed;
};
can.compute.binder = computeBinder;
return can.compute;
})(module["can/util/jquery/jquery.js"]); // ## can/view/render.js
module['can/view/render.js'] = (function (can) { |
text node expando test | var canExpando = true;
try {
document.createTextNode('')._ = 0;
} catch (ex) {
canExpando = false;
}
var attrMap = {
"class": "className",
"value": "value",
"innerText": "innerText",
"textContent": "textContent"
},
tagMap = {
"": "span",
table: "tbody",
tr: "td",
ol: "li",
ul: "li",
tbody: "tr",
thead: "tr",
tfoot: "tr",
select: "option",
optgroup: "option"
},
attributePlaceholder = '__!!__',
attributeReplace = /__!!__/g,
tagToContentPropMap = {
option: "textContent" in document.createElement("option") ? "textContent" : "innerText",
textarea: "value"
},
bool = can.each(["checked", "disabled", "readonly", "required"], function (n) {
attrMap[n] = n;
}), |
a helper to get the parentNode for a given element el if el is in a documentFragment, it will return defaultParentNode | getParentNode = function (el, defaultParentNode) {
return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
},
setAttr = function (el, attrName, val) {
var tagName = el.nodeName.toString().toLowerCase(),
prop = attrMap[attrName]; |
if this is a special property | if (prop) { |
set the value as true / false | el[prop] = can.inArray(attrName, bool) > -1 ? true : val;
if (prop === "value" && (tagName === "input" || tagName === "textarea")) {
el.defaultValue = val;
}
} else {
el.setAttribute(attrName, val);
}
},
getAttr = function (el, attrName) { |
Default to a blank string for IE7/8 | return (attrMap[attrName] ? el[attrMap[attrName]] : el.getAttribute(attrName)) || '';
},
removeAttr = function (el, attrName) {
if (can.inArray(attrName, bool) > -1) {
el[attrName] = false;
} else {
el.removeAttribute(attrName);
}
},
pendingHookups = [], |
Returns text content for anything other than a live-binding | contentText = function (input) { |
If it's a string, return. | if (typeof input == 'string') {
return input;
} |
If has no value, return an empty string. | if (!input && input !== 0) {
return '';
} |
If it's an object, and it has a hookup method. | var hook = (input.hookup && |
Make a function call the hookup method. | function (el, id) {
input.hookup.call(input, el, id);
}) || |
Or if it's a | (typeof input == 'function' && input); |
Finally, if there is a | if (hook) {
pendingHookups.push(hook);
return '';
} |
Finally, if all else is | return "" + input;
}, |
Returns escaped/sanatized content for anything other than a live-binding | contentEscape = function (txt) {
return (typeof txt == 'string' || typeof txt == 'number') ? can.esc(txt) : contentText(txt);
}, |
a mapping of element ids to nodeList ids | nodeMap = {}, |
a mapping of ids to text nodes | textNodeMap = {}, |
a mapping of nodeList ids to nodeList | nodeListMap = {},
expando = "ejs_" + Math.random(),
_id = 0,
id = function (node) {
if (canExpando || node.nodeType !== 3) {
if (node[expando]) {
return node[expando];
}
else {
return node[expando] = (node.nodeName ? "element_" : "obj_") + (++_id);
}
}
else {
for (var textNodeID in textNodeMap) {
if (textNodeMap[textNodeID] === node) {
return textNodeID;
}
}
textNodeMap["text_" + (++_id)] = node;
return "text_" + _id;
}
}, |
removes a nodeListId from a node's nodeListIds | removeNodeListId = function (node, nodeListId) {
var nodeListIds = nodeMap[id(node)];
if (nodeListIds) {
var index = can.inArray(nodeListId, nodeListIds);
if (index >= 0) {
nodeListIds.splice(index, 1);
}
if (!nodeListIds.length) {
delete nodeMap[id(node)];
}
}
},
addNodeListId = function (node, nodeListId) {
var nodeListIds = nodeMap[id(node)];
if (!nodeListIds) {
nodeListIds = nodeMap[id(node)] = [];
}
nodeListIds.push(nodeListId);
},
tagChildren = function (tagName) {
var newTag = tagMap[tagName] || "span";
if (newTag === "span") { |
innerHTML in IE doesn't honor leading whitespace after empty elements | return "@@!!@@";
}
return "<" + newTag + ">" + tagChildren(newTag) + "</" + newTag + ">";
};
can.extend(can.view, {
pending: function () { |
TODO, make this only run for the right tagName | var hooks = pendingHookups.slice(0);
lastHookups = hooks;
pendingHookups = [];
return can.view.hook(function (el) {
can.each(hooks, function (fn) {
fn(el);
});
});
},
registerNode: function (nodeList) {
var nLId = id(nodeList);
nodeListMap[nLId] = nodeList;
can.each(nodeList, function (node) {
addNodeListId(node, nLId);
});
},
unregisterNode: function (nodeList) {
var nLId = id(nodeList);
can.each(nodeList, function (node) {
removeNodeListId(node, nLId);
});
delete nodeListMap[nLId];
},
txt: function (escape, tagName, status, self, func) { |
call the "wrapping" function and get the binding information | var binding = can.compute.binder(func, self, function (newVal, oldVal) { |
call the update method we will define for each type of attribute | update(newVal, oldVal);
}); |
If we had no observes just return the value returned by func. | if (!binding.isListening) {
return (escape || status !== 0 ? contentEscape : contentText)(binding.value);
} |
The following are helper methods or varaibles that will be defined by one of the various live-updating schemes. The parent element we are listening to for teardown | var parentElement, nodeList, teardown = function () {
binding.teardown();
if (nodeList) {
can.view.unregisterNode(nodeList);
}
}, |
if the parent element is removed, teardown the binding | setupTeardownOnDestroy = function (el) {
can.bind.call(el, 'destroyed', teardown);
parentElement = el;
}, |
if there is no parent, undo bindings | teardownCheck = function (parent) {
if (!parent) {
teardown();
can.unbind.call(parentElement, 'destroyed', teardown);
}
}, |
the tag type to insert | tag = (tagMap[tagName] || "span"), |
this will be filled in if binding.isListening | update, |
the property (instead of innerHTML elements) to adjust. For example options should use textContent | contentProp = tagToContentPropMap[tagName]; |
The magic tag is outside or between tags. | if (status === 0 && !contentProp) { |
Return an element tag with a hookup in place of the content | return "<" + tag + can.view.hook(
escape ? |
If we are escaping, replace the parentNode with
a text node who's value is | function (el, parentNode) { |
updates the text of the text node | update = function (newVal) {
node.nodeValue = "" + newVal;
teardownCheck(node.parentNode);
};
var parent = getParentNode(el, parentNode),
node = document.createTextNode(binding.value);
parent.insertBefore(node, el);
parent.removeChild(el);
setupTeardownOnDestroy(parent);
} : |
If we are not escaping, replace the parentNode with a
documentFragment created as with | function (span, parentNode) { |
updates the elements with the new content | update = function (newVal) { |
is this still part of the DOM? | var attached = nodes[0].parentNode; |
update the nodes in the DOM with the new rendered value | if (attached) {
makeAndPut(newVal);
}
teardownCheck(nodes[0].parentNode);
}; |
make sure we have a valid parentNode | parentNode = getParentNode(span, parentNode); |
A helper function to manage inserting the contents and removing the old contents | var nodes, makeAndPut = function (val) { |
create the fragment, but don't hook it up we need to insert it into the document first | var frag = can.view.frag(val, parentNode), |
keep a reference to each node | newNodes = can.makeArray(frag.childNodes),
last = nodes ? nodes[nodes.length - 1] : span; |
Insert it in the | if (last.nextSibling) {
last.parentNode.insertBefore(frag, last.nextSibling);
} else {
last.parentNode.appendChild(frag);
} |
nodes hasn't been set yet | if (!nodes) {
can.remove(can.$(span));
nodes = newNodes; |
set the teardown nodeList | nodeList = nodes;
can.view.registerNode(nodes);
} else { |
Update node Array's to point to new nodes and then remove the old nodes. It has to be in this order for Mootools and IE because somehow, after an element is removed from the DOM, it loses its expando values. | var nodesToRemove = can.makeArray(nodes);
can.view.replace(nodes, newNodes);
can.remove(can.$(nodesToRemove));
}
}; |
nodes are the nodes that any updates will replace at this point, these nodes could be part of a documentFragment | makeAndPut(binding.value, [span]);
setupTeardownOnDestroy(parentNode); |
children have to be properly nested HTML for buildFragment to work properly | }) + ">" + tagChildren(tag) + "</" + tag + ">"; |
In a tag, but not in an attribute | } else if (status === 1) { |
remember the old attr name | var attrName = binding.value.replace(/['"]/g, '').split('=')[0];
pendingHookups.push(function (el) {
update = function (newVal) {
var parts = (newVal || "").replace(/['"]/g, '').split('='),
newAttrName = parts[0]; |
Remove if we have a change and used to have an | if ((newAttrName != attrName) && attrName) {
removeAttr(el, attrName);
} |
Set if we have a new | if (newAttrName) {
setAttr(el, newAttrName, parts[1]);
attrName = newAttrName;
}
};
setupTeardownOnDestroy(el);
});
return binding.value;
} else { // In an attribute...
var attributeName = status === 0 ? contentProp : status; |
if the magic tag is inside the element, like | (status === 0 ? lastHookups : pendingHookups).push(function (el) { |
update will call this attribute's render method and set the attribute accordingly | update = function () {
setAttr(el, attributeName, hook.render(), contentProp);
};
var wrapped = can.$(el),
hooks; |
Get the list of hookups or create one for this element.
Hooks is a map of attribute names to hookup | hooks = can.data(wrapped, 'hooks');
if (!hooks) {
can.data(wrapped, 'hooks', hooks = {});
} |
Get the attribute value. | var attr = getAttr(el, attributeName, contentProp), |
Split the attribute value by the template. Only split out the first !! so if we have multiple hookups in the same attribute, they will be put in the right spot on first render | parts = attr.split(attributePlaceholder),
goodParts = [],
hook;
goodParts.push(parts.shift(), parts.join(attributePlaceholder)); |
If we already had a hookup for this attribute... | if (hooks[attributeName]) { |
Just add to that attribute's list of | hooks[attributeName].bindings.push(binding);
} else { |
Create the hookup data. | hooks[attributeName] = {
render: function () {
var i = 0,
newAttr = attr.replace(attributeReplace, function () {
return contentText(hook.bindings[i++].value);
});
return newAttr;
},
bindings: [binding],
batchNum: undefined
};
} |
Save the hook for slightly faster performance. | hook = hooks[attributeName]; |
Insert the value in parts. | goodParts.splice(1, 0, binding.value); |
Set the attribute. | setAttr(el, attributeName, goodParts.join(""), contentProp); |
Bind on change. liveBind(observed, el, binder,oldObserved); | setupTeardownOnDestroy(el);
});
return attributePlaceholder;
}
},
replace: function (oldNodeList, newNodes) { |
for each node in the node list | oldNodeList = can.makeArray(oldNodeList);
can.each(oldNodeList, function (node) { |
for each nodeList the node is in | can.each(can.makeArray(nodeMap[id(node)]), function (nodeListId) {
var nodeList = nodeListMap[nodeListId],
startIndex = can.inArray(node, nodeList),
endIndex = can.inArray(oldNodeList[oldNodeList.length - 1], nodeList); |
remove this nodeListId from each node | if (startIndex >= 0 && endIndex >= 0) {
for (var i = startIndex; i <= endIndex; i++) {
var n = nodeList[i];
removeNodeListId(n, nodeListId);
} |
swap in new nodes into the nodeLIst | nodeList.splice.apply(nodeList, [startIndex, endIndex - startIndex + 1].concat(newNodes)); |
tell these new nodes they belong to the nodeList | can.each(newNodes, function (node) {
addNodeListId(node, nodeListId);
});
} else {
can.view.unregisterNode(nodeList);
}
});
});
},
canExpando: canExpando, |
Node mappings | textNodeMap: textNodeMap,
nodeMap: nodeMap,
nodeListMap: nodeListMap
});
return can;
})(module["can/view/view.js"], module["can/util/string/string.js"]); // ## can/view/mustache/mustache.js
module['can/view/mustache/mustache.js'] = (function (can) { |
mustache.js
InitializationDefine the view extension. | can.view.ext = ".mustache"; |
Setup internal helper variables and functions.An alias for the context variable used for tracking a stack of contexts. This is also used for passing to helper functions to maintain proper context. | var CONTEXT = '___c0nt3xt', |
An alias for the variable used for the hash object that can be passed
to helpers via | HASH = '___h4sh', |
An alias for the function that adds a new context to the context stack. | STACK = '___st4ck', |
An alias for the most used context stacking call. | CONTEXT_STACK = STACK + '(' + CONTEXT + ',this)',
CONTEXT_OBJ = '{context:' + CONTEXT_STACK + ',options:options}',
isObserve = function (obj) {
return obj !== null && can.isFunction(obj.attr) && obj.constructor && !! obj.constructor.canMakeObserve;
},
isArrayLike = function (obj) {
return obj && obj.splice && typeof obj.length == 'number';
}, |
Mustache | Mustache = function (options, helpers) { |
Support calling Mustache without the constructor. This returns a function that renders the template. | if (this.constructor != Mustache) {
var mustache = new Mustache(options);
return function (data, options) {
return mustache.render(data, options);
};
} |
If we get a | if (typeof options == "function") {
this.template = {
fn: options
};
return;
} |
Set options on self. | can.extend(this, options);
this.template = this.scanner.scan(this.text, this.name);
}; |
Put Mustache on the | can.Mustache = window.Mustache = Mustache;
Mustache.prototype.
render = function (object, options) {
object = object || {};
options = options || {};
if (!options.helpers && !options.partials) {
options.helpers = options;
}
return this.template.fn.call(object, object, {
_data: object,
options: options
});
};
can.extend(Mustache.prototype, { |
Share a singleton scanner for parsing templates. | scanner: new can.view.Scanner({ |
A hash of strings for the scanner to inject at certain points. | text: { |
This is the logic to inject at the beginning of a rendered template.
This includes initializing the | start: 'var ' + CONTEXT + ' = this && this.' + STACK + ' ? this : []; ' + CONTEXT + '.' + STACK + ' = true;' + 'var ' + STACK + ' = function(context, self) {' + 'var s;' + 'if (arguments.length == 1 && context) {' + 's = !context.' + STACK + ' ? [context] : context;' + |
Handle helpers with custom contexts (#228) | '} else if (!context.' + STACK + ') {' + 's = [self, context];' + '} else {' + 's = context && context.' + STACK + ' ? context.concat([self]) : ' + STACK + '(context).concat([self]);' + '}' + 'return (s.' + STACK + ' = true) && s;' + '};'
}, |
An ordered token registry for the scanner.
This needs to be ordered by priority to prevent token parsing errors.
Each token follows the following structure:
[
// Which key in the token map to match.
"tokenMapName",
// A simple token to match, like "{{".
"token",
// Optional. A complex (regexp) token to match that
// overrides the simple token.
"[\s\t]*{{",
// Optional. A function that executes advanced
// manipulation of the matched content. This is
// rarely used.
function(content){ | tokens: [ |
Return unescaped | ["returnLeft", "{{{", "{{[{&]"], |
Full line comments | ["commentFull", "{{!}}", "^[\\s\\t]*{{!.+?}}\\n"], |
Inline comments | ["commentLeft", "{{!", "(\\n[\\s\\t]*{{!|{{!)"], |
Full line escapes This is used for detecting lines with only whitespace and an escaped tag | ["escapeFull", "{{}}", "(^[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}$)", function (content) {
return {
before: /^\n.+?\n$/.test(content) ? '\n' : '',
content: content.match(/\{\{(.+?)\}\}/)[1] || ''
};
}], |
Return escaped | ["escapeLeft", "{{"], |
Close return unescaped | ["returnRight", "}}}"], |
Close tag | ["right", "}}"]], |
Scanning HelpersThis is an array of helpers that transform content that is within escaped tags like | helpers: [ |
PartialsPartials begin with a greater than sign, like {{> box}}. Partials are rendered at runtime (as opposed to compile time), so recursive partials are possible. Just avoid infinite loops. For example, this template and partial: base.mustache: Names{{#names}} {{> user}} {{/names}} user.mustache: {{name}} | {
name: /^>[\s]*\w*/,
fn: function (content, cmd) { |
Get the template name and call back into the render method, passing the name and the current context. | var templateName = can.trim(content.replace(/^>\s?/, '')).replace(/["|']/g, "");
return "options.partials && options.partials['" + templateName + "'] ? can.Mustache.renderPartial(options.partials['" + templateName + "']," + CONTEXT_STACK + ".pop(),options) : can.Mustache.render('" + templateName + "', " + CONTEXT_STACK + ")";
}
}, |
Data HookupThis will attach the data property of | {
name: /^\s*data\s/,
fn: function (content, cmd) {
var attr = content.match(/["|'](.*)["|']/)[1]; |
return a function which calls | return "can.proxy(function(__){can.data(can.$(__),'" + attr + "', this.pop()); }, " + CONTEXT_STACK + ")";
}
}, |
Transformation (default)This transforms all content to its interpolated equivalent, including calls to the corresponding helpers as applicable. This outputs the render code for almost all cases. Definitions
DesignThis covers the design of the render code that the transformation helper generates. PseudocodeA detailed explanation is provided in the following sections, but here is some brief pseudocode
that gives a high level overview of what the generated render code does (with a template similar to InitializationEach rendered template is started with the following initialization code:
var v1ew = [];
var _c0nt3xt = [];
c0nt3xt.st4ck = true;
var st4ck = function(context, self) {
var s;
if (arguments.length == 1 && context) {
s = !context.st4ck ? [context] : context;
} else {
s = context && context.st4ck
? context.concat([self])
: st4ck(context).concat([self]);
}
return (s.__st4ck = true) && s;
};
The SectionsEach section, Implementation | {
name: /^.*$/,
fn: function (content, cmd) {
var mode = false,
result = []; |
Trim the content so we don't have any trailing whitespace. | content = can.trim(content); |
Determine what the active mode is.
* | if (content.length && (mode = content.match(/^([#^/]|else$)/))) {
mode = mode[0];
switch (mode) { |
Open a new section. | case '#':
case '^':
result.push(cmd.insert + 'can.view.txt(0,\'' + cmd.tagName + '\',' + cmd.status + ',this,function(){ return ');
break; |
Close the prior section. | case '/':
return {
raw: 'return ___v1ew.join("");}}])}));'
};
break;
} |
Trim the mode off of the content. | content = content.substring(1);
} |
| if (mode != 'else') {
var args = [],
i = 0,
hashing = false,
arg, split, m; |
Parse the helper arguments. This needs uses this method instead of a split(/\s/) so that strings with spaces can be correctly parsed. | (can.trim(content) + ' ').replace(/((([^\s]+?=)?('.*?'|".*?"))|.*?)\s/g, function (whole, part) {
args.push(part);
}); |
Start the content render block. | result.push('can.Mustache.txt(' + CONTEXT_OBJ + ',' + (mode ? '"' + mode + '"' : 'null') + ','); |
Iterate through the helper arguments, if there are any. | for (; arg = args[i]; i++) {
i && result.push(','); |
Check for special helper arguments (string/number/boolean/hashes). | if (i && (m = arg.match(/^(('.*?'|".*?"|[0-9.]+|true|false)|((.+?)=(('.*?'|".*?"|[0-9.]+|true|false)|(.+))))$/))) { |
Found a native type like string/number/boolean. | if (m[2]) {
result.push(m[0]);
} |
Found a hash object. | else { |
Open the hash object. | if (!hashing) {
hashing = true;
result.push('{' + HASH + ':{');
} |
Add the key/value. | result.push(m[4], ':', m[6] ? m[6] : 'can.Mustache.get("' + m[5].replace(/"/g, '\\"') + '",' + CONTEXT_OBJ + ')'); |
Close the hash if this was the last argument. | if (i == args.length - 1) {
result.push('}}');
}
}
} |
Otherwise output a normal interpolation reference. | else {
result.push('can.Mustache.get("' + |
Include the reference name. | arg.replace(/"/g, '\\"') + '",' + |
Then the stack of context. | CONTEXT_OBJ + |
Flag as a helper method to aid performance, if it is a known helper (anything with > 0 arguments). | (i == 0 && args.length > 1 ? ',true' : ',false') + (i > 0 ? ',true' : ',false') + ')');
}
}
} |
Create an option object for sections of code. | mode && mode != 'else' && result.push(',[{_:function(){');
switch (mode) { |
Truthy section | case '#':
result.push('return ___v1ew.join("");}},{fn:function(' + CONTEXT + '){var ___v1ew = [];');
break; |
If/else section Falsey section | case 'else':
case '^':
result.push('return ___v1ew.join("");}},{inverse:function(' + CONTEXT + '){var ___v1ew = [];');
break; |
Not a section | default:
result.push(');');
break;
} |
Return a raw result if there was a section, otherwise return the default string. | result = result.join('');
return mode ? {
raw: result
} : result;
}
}]
})
}); |
Add in default scanner helpers first. We could probably do this differently if we didn't 'break' on every match. | var helpers = can.view.Scanner.prototype.helpers;
for (var i = 0; i < helpers.length; i++) {
Mustache.prototype.scanner.helpers.unshift(helpers[i]);
};
Mustache.txt = function (context, mode, name) { |
Grab the extra arguments to pass to helpers. | var args = Array.prototype.slice.call(arguments, 3), |
Create a default | options = can.extend.apply(can, [{
fn: function () {},
inverse: function () {}
}].concat(mode ? args.pop() : []));
var extra = {};
if (context.context) {
extra = context.options;
context = context.context;
} |
Check for a registered helper or a helper-like function. | if (helper = (Mustache.getHelper(name, extra) || (can.isFunction(name) && !name.isComputed && {
fn: name
}))) { |
Use the most recent context as | var context = (context[STACK] && context[context.length - 1]) || context, |
Update the options with a function/inverse (the inner templates of a section). | opts = {
fn: can.proxy(options.fn, context),
inverse: can.proxy(options.inverse, context)
},
lastArg = args[args.length - 1]; |
Add the hash to | if (lastArg && lastArg[HASH]) {
opts.hash = args.pop()[HASH];
}
args.push(opts); |
Call the helper. | return helper.fn.apply(context, args) || '';
} |
if a compute, get the value | if (can.isFunction(name) && name.isComputed) {
name = name();
} |
An array of arguments to check for truthyness when evaluating sections. | var validArgs = args.length ? args : [name], |
Whether the arguments meet the condition of the section. | valid = true,
result = [],
i, helper, argIsObserve, arg; |
Validate the arguments based on the section mode. | if (mode) {
for (i = 0; i < validArgs.length; i++) {
arg = validArgs[i];
argIsObserve = typeof arg !== 'undefined' && isObserve(arg); |
Array-like objects are falsey if their length = 0. | if (isArrayLike(arg)) { |
Use .attr to trigger binding on empty lists returned from function | if (mode == '#') {
valid = valid && !! (argIsObserve ? arg.attr('length') : arg.length);
} else if (mode == '^') {
valid = valid && !(argIsObserve ? arg.attr('length') : arg.length);
}
} |
Otherwise just check if it is truthy or not. | else {
valid = mode == '#' ? valid && !! arg : mode == '^' ? valid && !arg : valid;
}
}
} |
Otherwise interpolate like normal. | if (valid) {
switch (mode) { |
Truthy section. | case '#': |
Iterate over arrays | if (isArrayLike(name)) {
var isObserveList = isObserve(name); |
Add the reference to the list in the contexts. | for (i = 0; i < name.length; i++) {
result.push(options.fn.call(name[i] || {}, context) || ''); |
Ensure that live update works on observable lists | isObserveList && name.attr('' + i);
}
return result.join('');
} |
Normal case. | else {
return options.fn.call(name || {}, context) || '';
}
break; |
Falsey section. | case '^':
return options.inverse.call(name || {}, context) || '';
break;
default: |
Add + '' to convert things like numbers to strings. This can cause issues if you are trying to eval on the length but this is the more common case. | return '' + (name !== undefined ? name : '');
break;
}
}
return '';
};
Mustache.get = function (ref, contexts, isHelper, isArgument) {
var options = contexts.options || {};
contexts = contexts.context || contexts; |
Split the reference (like | var names = ref.split('.'),
namesLength = names.length, |
Assume the local object is the last context in the stack. | obj = contexts[contexts.length - 1], |
Assume the parent context is the second to last context in the stack. | context = contexts[contexts.length - 2],
lastValue, value, name, i, j, |
if we walk up and don't find a property, we default to listening on an undefined property of the first context that is an observe | defaultObserve, defaultObserveName; |
Handle | if (/^\.|this$/.test(ref)) { |
If context isn't an object, then it was a value passed by a helper so use it as an override. | if (!/^object|undefined$/.test(typeof context)) {
return context || '';
} |
Otherwise just return the closest object. | else {
while (value = contexts.pop()) {
if (typeof value !== 'undefined') {
return value;
}
}
return '';
}
} |
Handle object resolution (like | else if (!isHelper) { |
Reverse iterate through the contexts (last in, first out). | for (i = contexts.length - 1; i >= 0; i--) { |
Check the context for the reference | value = contexts[i]; |
Make sure the context isn't a failed object before diving into it. | if (value !== undefined) {
for (j = 0; j < namesLength; j++) { |
Keep running up the tree while there are matches. | if (typeof value[names[j]] != 'undefined') {
lastValue = value;
value = value[name = names[j]];
} |
If it's undefined, still match if the parent is an Observe. | else if (isObserve(value)) {
defaultObserve = value;
defaultObserveName = names[j];
lastValue = value = undefined;
break;
}
else {
lastValue = value = undefined;
break;
}
}
} |
Found a matched reference. | if (value !== undefined) {
if (can.isFunction(lastValue[name]) && isArgument) { |
Don't execute functions if they are parameters for a helper and are not a can.compute Need to bind it to the original context so that that information doesn't get lost by the helper | return function () {
return lastValue[name].apply(lastValue, arguments);
};
} else if (can.isFunction(lastValue[name])) { |
Support functions stored in objects. | return lastValue[name]();
} |
Invoke the length to ensure that Observe.List events fire. | else if (isObserve(value) && isArrayLike(value) && value.attr('length')) {
return value;
} |
Add support for observes | else if (isObserve(lastValue)) {
return lastValue.compute(name);
}
else {
return value;
}
}
}
}
if (defaultObserve && |
if there's not a helper by this name and no attribute with this name | !(Mustache.getHelper(ref) && can.inArray(defaultObserveName, can.Observe.keys(defaultObserve)) === -1)) {
return defaultObserve.compute(defaultObserveName);
} |
Support helper-like functions as anonymous helpers | if (obj !== undefined && can.isFunction(obj[ref])) {
return obj[ref];
} |
Support helpers without arguments, but only if there wasn't a matching data reference. | else if (value = Mustache.getHelper(ref, options)) {
return ref;
}
return '';
}; |
HelpersHelpers are functions that can be called from within a template.
These helpers differ from the scanner helpers in that they execute
at runtime instead of during compilation.
Custom helpers can be added via | Mustache._helpers = {};
Mustache.registerHelper = function (name, fn) {
this._helpers[name] = {
name: name,
fn: fn
};
};
Mustache.getHelper = function (name, options) {
return options && options.helpers && options.helpers[name] && {
fn: options.helpers[name]
} || this._helpers[name]
for (var i = 0, helper; helper = [i]; i++) { |
Find the correct helper | if (helper.name == name) {
return helper;
}
}
return null;
};
Mustache.render = function (partial, context) { |
Make sure the partial being passed in isn't a variable like { partial: "foo.mustache" } | if (!can.view.cached[partial] && context[partial]) {
partial = context[partial];
} |
Call into | return can.view.render(partial, context);
};
Mustache.renderPartial = function (partial, context, options) {
return partial.render ? partial.render(context, options) : partial(context, options);
}; |
The built-in Mustache helpers. | can.each({ |
Implements the | 'if': function (expr, options) {
if ( !! expr) {
return options.fn(this);
}
else {
return options.inverse(this);
}
}, |
Implements the | 'unless': function (expr, options) {
if (!expr) {
return options.fn(this);
}
}, |
Implements the | 'each': function (expr, options) {
if ( !! expr && expr.length) {
var result = [];
for (var i = 0; i < expr.length; i++) {
result.push(options.fn(expr[i]));
}
return result.join('');
}
}, |
Implements the | 'with': function (expr, options) {
if ( !! expr) {
return options.fn(expr);
}
}
}, function (fn, name) {
Mustache.registerHelper(name, fn);
}); |
RegistrationRegisters Mustache with can.view. | can.view.register({
suffix: "mustache",
contentType: "x-mustache-template", |
Returns a | script: function (id, src) {
return "can.Mustache(function(_CONTEXT,_VIEW) { " + new Mustache({
text: src,
name: id
}).template.out + " })";
},
renderer: function (id, text) {
return Mustache({
text: text,
name: id
});
}
});
return can;
})(module["can/util/jquery/jquery.js"], module["can/view/view.js"], module["can/view/scanner.js"], module["can/observe/compute/compute.js"], module["can/view/render.js"]); // ## can/view/modifiers/modifiers.js
module['can/view/modifiers/modifiers.js'] = (function ($, can) { |
---- ADD jQUERY HELPERS ----- converts jquery functions to use views | var convert, modify, isTemplate, isHTML, isDOM, getCallback, |
text and val cannot produce an element, so don't run hookups on them | noHookup = {
'val': true,
'text': true
};
convert = function (func_name) { |
save the old jQuery helper | var old = $.fn[func_name]; |
replace it with our new helper | $.fn[func_name] = function () {
var args = can.makeArray(arguments),
callbackNum, callback, self = this,
result; |
if the first arg is a deferred wait until it finishes, and call modify with the result | if (can.isDeferred(args[0])) {
args[0].done(function (res) {
modify.call(self, [res], old);
})
return this;
} |
check if a template | else if (isTemplate(args)) { |
if we should operate async | if ((callbackNum = getCallback(args))) {
callback = args[callbackNum];
args[callbackNum] = function (result) {
modify.call(self, [result], old);
callback.call(self, result);
};
can.view.apply(can.view, args);
return this;
} |
call view with args (there might be deferreds) | result = can.view.apply(can.view, args); |
if we got a string back | if (!can.isDeferred(result)) { |
we are going to call the old method with that string | args = [result];
} else { |
if there is a deferred, wait until it is done before calling modify | result.done(function (res) {
modify.call(self, [res], old);
})
return this;
}
}
return noHookup[func_name] ? old.apply(this, args) : modify.call(this, args, old);
};
}; |
modifies the content of the element but also will run any hookup | modify = function (args, old) {
var res, stub, hooks; |
check if there are new hookups | for (var hasHookups in can.view.hookups) {
break;
} |
if there are hookups, turn into a frag and insert that by using a frag, the element can be recursively hooked up before insterion | if (hasHookups && args[0] && isHTML(args[0])) {
args[0] = can.view.frag(args[0]).childNodes;
} |
then insert into DOM | res = old.apply(this, args);
return res;
}; |
returns true or false if the args indicate a template is being used $('#foo').html('/path/to/template.ejs',{data}) in general, we want to make sure the first arg is a string and the second arg is data | isTemplate = function (args) { |
save the second arg type | var secArgType = typeof args[1]; |
the first arg is a string | return typeof args[0] == "string" && |
the second arg is an object or function | (secArgType == 'object' || secArgType == 'function') && |
but it is not a dom element | !isDOM(args[1]);
}; |
returns true if the arg is a jQuery object or HTMLElement | isDOM = function (arg) {
return arg.nodeType || (arg[0] && arg[0].nodeType)
}; |
returns whether the argument is some sort of HTML data | isHTML = function (arg) {
if (isDOM(arg)) { |
if jQuery object or DOM node we're good | return true;
} else if (typeof arg === "string") { |
if string, do a quick sanity check that we're HTML | arg = can.trim(arg);
return arg.substr(0, 1) === "<" && arg.substr(arg.length - 1, 1) === ">" && arg.length >= 3;
} else { |
don't know what you are | return false;
}
}; |
returns the callback arg number if there is one (for async view use) | getCallback = function (args) {
return typeof args[3] === 'function' ? 3 : typeof args[2] === 'function' && 2;
};
$.fn.hookup = function () {
can.view.frag(this);
return this;
};
can.each([
"prepend",
"append",
"after",
"before",
"text",
"html",
"replaceWith", "val"], function (func) {
convert(func);
});
return can;
})(module["jquery/jquery.js"], module["can/view/view.js"]); // ## can/observe/observe.js
module['can/observe/observe.js'] = (function (can) { |
observe.js
| 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 | hookupBubble = function (val, prop, parent, Ob, List) {
Ob = Ob || Observe;
List = List || Observe.List; |
If it's an | if (val instanceof Observe) { |
We have an | unhookup([val], parent._cid);
} else if (can.isArray(val)) {
val = new List(val);
} else {
val = new Ob(val);
} |
Listen to all changes and | val.bind("change" + parent._cid, function () { |
| 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 | observeId = 0, |
A helper used to serialize an | serialize = function (observe, how, where) { |
Go through each property. | observe.each(function (val, name) { |
If the value is an | where[name] = canMakeObserve(val) && can.isFunction(val[how]) ? |
Call | 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) { |
| this._data = {}; |
The namespace this | can.cid(this, ".observe"); |
Sets all | 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 | 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 | 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 | if (parts.length) {
return current.removeAttr(parts)
} else {
if (prop in this._data) { |
Otherwise, | 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 | _get: function (attr) { |
break up the attr ( | var parts = attrParts(attr), |
get the value of the first attr name ( | 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 | __get: function (attr) {
return attr ? this._data[attr] : this._data;
}, |
Sets | _set: function (attr, value) { |
Convert | var parts = attrParts(attr), |
The immediate prop we are setting. | prop = parts.shift(), |
The current value. | current = this.__get(prop); |
If we have an | if (canMakeObserve(current) && parts.length) { |
That | 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 | if (value !== current) { |
Check if we are adding this for the first time --
if we are, we need to create an | 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);
} |
| 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 | ___set: function (prop, val) {
this._data[prop] = val; |
Add property directly for easy writing.
Check if its on the | 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 | 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) |
| 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 | getArgs = function (args) {
return args[0] && can.isArray(args[0]) ? args[0] : can.makeArray(args);
}; |
Create | can.each({
push: "length",
unshift: 0
}, |
Adds a method
| 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 | 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 | 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
| 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
}, {});
}
return Observe;
})(module["can/util/jquery/jquery.js"], module["can/construct/construct.js"]); // ## can/model/model.js
module['can/model/model.js'] = (function (can) { |
model.js
| 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 | 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; |
| if (type == 'destroy') {
args.shift();
} |
| 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 | 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. | 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 | ajaxMaker = function (ajaxMethod, str) { |
Return a | return function (data) { |
If the ajax method has it's own way of getting | data = ajaxMethod.data ? ajaxMethod.data.apply(this, arguments) : |
Otherwise use the data passed in. | data; |
Return the ajax method with | 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 | arr = can.isArray(instancesRawData), |
Did we get a model list? | ml = (instancesRawData instanceof ML), |
Get the raw | raw = arr ? |
If an | 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 | ___set: function (prop, val) {
can.Observe.prototype.___set.call(this, prop, val) |
If we add an | 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 | 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);
}
}
})
}
})
return can.Model;
})(module["can/util/jquery/jquery.js"], module["can/observe/observe.js"]); // ## can/view/ejs/ejs.js
module['can/view/ejs/ejs.js'] = (function (can) { |
ejs.js
| var extend = can.extend,
EJS = function (options) { |
Supports calling EJS without the constructor This returns a function that renders the template. | if (this.constructor != EJS) {
var ejs = new EJS(options);
return function (data, helpers) {
return ejs.render(data, helpers);
};
} |
If we get a | if (typeof options == "function") {
this.template = {
fn: options
};
return;
} |
Set options on self. | extend(this, options);
this.template = this.scanner.scan(this.text, this.name);
};
can.EJS = EJS;
EJS.prototype.
render = function (object, extraHelpers) {
object = object || {};
return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
};
extend(EJS.prototype, {
scanner: new can.view.Scanner({
tokens: [
["templateLeft", "<%%"], // Template
["templateRight", "%>"], // Right Template
["returnLeft", "<%=="], // Return Unescaped
["escapeLeft", "<%="], // Return Escaped
["commentLeft", "<%#"], // Comment
["left", "<%"], // Run --- this is hack for now
["right", "%>"], // Right -> All have same FOR Mustache ...
["returnRight", "%>"]
]
})
});
EJS.Helpers = function (data, extras) {
this._data = data;
this._extras = extras;
extend(this, extras);
};
EJS.Helpers.prototype = { |
TODO Deprecated!! | list: function (list, cb) {
can.each(list, function (item, i) {
cb(item, i, list)
})
}
}; |
Options for | can.view.register({
suffix: "ejs", |
returns a | script: function (id, src) {
return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
text: src,
name: id
}).template.out + " })";
},
renderer: function (id, text) {
return EJS({
text: text,
name: id
});
}
});
return can;
})(module["can/util/jquery/jquery.js"], module["can/view/view.js"], module["can/util/string/string.js"], module["can/observe/compute/compute.js"], module["can/view/scanner.js"], module["can/view/render.js"]); // ## can/observe/attributes/attributes.js
module['can/observe/attributes/attributes.js'] = (function (can, Observe) {
can.each([can.Observe, can.Model], function (clss) { |
in some cases model might not be defined quite yet. | if (clss === undefined) {
return;
}
can.extend(clss, {
attributes: {},
convert: {
"date": function (str) {
var type = typeof str;
if (type === "string") {
return isNaN(Date.parse(str)) ? null : Date.parse(str)
} else if (type === 'number') {
return new Date(str)
} else {
return str
}
},
"number": function (val) {
return parseFloat(val);
},
"boolean": function (val) {
if (val === 'false' || val === '0' || !val) {
return false;
}
return true;
},
"default": function (val, oldVal, error, type) {
var construct = can.getObject(type),
context = window,
realType; |
if type has a . we need to look it up | if (type.indexOf(".") >= 0) { |
get everything before the last . | realType = type.substring(0, type.lastIndexOf(".")); |
get the object before the last . | context = can.getObject(realType);
}
return typeof construct == "function" ? construct.call(context, val, oldVal) : val;
}
},
serialize: {
"default": function (val, type) {
return isObject(val) && val.serialize ? val.serialize() : val;
},
"date": function (val) {
return val && val.getTime()
}
}
}); |
overwrite setup to do this stuff | var oldSetup = clss.setup;
clss.setup = function (superClass, stat, proto) {
var self = this;
oldSetup.call(self, superClass, stat, proto);
can.each(["attributes"], function (name) {
if (!self[name] || superClass[name] === self[name]) {
self[name] = {};
}
});
can.each(["convert", "serialize"], function (name) {
if (superClass[name] != self[name]) {
self[name] = can.extend({}, superClass[name], self[name]);
}
});
};
});
var oldSetup = can.Observe.prototype.setup;
can.Observe.prototype.setup = function (obj) {
var diff = {};
oldSetup.call(this, obj);
can.each(this.constructor.defaults, function (value, key) {
if (!this.hasOwnProperty(key)) {
diff[key] = value;
}
}, this);
this._init = 1;
this.attr(diff);
delete this._init;
};
can.Observe.prototype.__convert = function (prop, value) { |
check if there is a | var Class = this.constructor,
oldVal = this.attr(prop),
type, converter;
if (Class.attributes) { |
the type of the attribute | type = Class.attributes[prop];
converter = Class.convert[type] || Class.convert['default'];
}
return value === null || !type ? |
just use the value | value : |
otherwise, pass to the converter | converter.call(Class, value, oldVal, function () {}, type);
};
can.Observe.prototype.serialize = function (attrName) {
var where = {},
Class = this.constructor,
attrs = {};
if (attrName != undefined) {
attrs[attrName] = this[attrName];
} else {
attrs = this.__get();
}
can.each(attrs, function (val, name) {
var type, converter;
type = Class.attributes ? Class.attributes[name] : 0;
converter = Class.serialize ? Class.serialize[type] : 0; |
if the value is an object, and has a attrs or serialize function | where[name] = val && typeof val.serialize == 'function' ? |
call attrs or serialize to get the original data back | val.serialize() : |
otherwise if we have a converter | converter ? |
use the converter | converter(val, type) : |
or return the val | val
});
return attrName != undefined ? where[attrName] : where;
};
return can.Observe;
})(module["can/util/jquery/jquery.js"], module["can/observe/observe.js"]); // ## can/observe/delegate/delegate.js
module['can/observe/delegate/delegate.js'] = (function (can) { |
* - 'this' will be the deepest item changed * - 'this' will be any changes within *, but * will be the this returned tells if the parts part of a delegate matches the broken up props of the event gives the prop to use as 'this' - parts - the attribute name of the delegate split in parts ['foo',''] - props - the split props of the event that happened ['foo','bar','0'] - returns - the attribute to delegate too ('foo.bar'), or null if not a match | var matches = function (parts, props) { |
check props parts are the same or | var len = parts.length,
i = 0, |
keeps the matched props we will use | matchedProps = [],
prop; |
if the event matches | for (i; i < len; i++) {
prop = props[i] |
if no more props (but we should be matching them) return null | if (typeof prop !== 'string') {
return null;
} else |
if we have a "**", match everything | if (parts[i] == "**") {
return props.join(".");
} else |
a match, but we want to delegate to "*" | if (parts[i] == "*") { |
only do this if there is nothing after ... | matchedProps.push(prop);
}
else if (prop === parts[i]) {
matchedProps.push(prop);
} else {
return null;
}
}
return matchedProps.join(".");
}, |
gets a change event and tries to figure out which delegates to call | delegate = function (event, prop, how, newVal, oldVal) { |
pre-split properties to save some regexp time | var props = prop.split("."),
delegates = (this._observe_delegates || []).slice(0),
delegate, attr, matchedAttr, hasMatch, valuesEqual;
event.attr = prop;
event.lastAttr = props[props.length - 1]; |
for each delegate | for (var i = 0; delegate = delegates[i++];) { |
if there is a batchNum, this means that this event is part of a series of events caused by a single attrs call. We don't want to issue the same event multiple times setting the batchNum happens later | if ((event.batchNum && delegate.batchNum === event.batchNum) || delegate.undelegated) {
continue;
} |
reset match and values tests | hasMatch = undefined;
valuesEqual = true; |
yeah, all this under here has to be redone v for each attr in a delegate | for (var a = 0; a < delegate.attrs.length; a++) {
attr = delegate.attrs[a]; |
check if it is a match | if (matchedAttr = matches(attr.parts, props)) {
hasMatch = matchedAttr;
} |
if it has a value, make sure it's the right value if it's set, we should probably check that it has a value no matter what | if (attr.value && valuesEqual) {
valuesEqual = attr.value === "" + this.attr(attr.attr)
} else if (valuesEqual && delegate.attrs.length > 1) { |
if there are multiple attributes, each has to at least have some value | valuesEqual = this.attr(attr.attr) !== undefined
}
} |
if there is a match and valuesEqual ... call back | if (hasMatch && valuesEqual) { |
how to get to the changed property from the delegate | var from = prop.replace(hasMatch + ".", ""); |
if this event is part of a batch, set it on the delegate to only send one event | if (event.batchNum) {
delegate.batchNum = event.batchNum
} |
if we listen to change, fire those with the same attrs TODO: the attrs should probably be using from | if (delegate.event === 'change') {
arguments[1] = from;
event.curAttr = hasMatch;
delegate.callback.apply(this.attr(hasMatch), can.makeArray(arguments));
} else if (delegate.event === how) { |
if it's a match, callback with the location of the match | delegate.callback.apply(this.attr(hasMatch), [event, newVal, oldVal, from]);
} else if (delegate.event === 'set' && how == 'add') { |
if we are listening to set, we should also listen to add | delegate.callback.apply(this.attr(hasMatch), [event, newVal, oldVal, from]);
}
}
}
};
can.extend(can.Observe.prototype, {
delegate: function (selector, event, handler) {
selector = can.trim(selector);
var delegates = this._observe_delegates || (this._observe_delegates = []),
attrs = [],
selectorRegex = /([^\s=,]+)(?:=("[^",]*"|'[^',]*'|[^\s"',]*))?(,?)\s*/g,
matches; |
parse each property in the selector | while (matches = selectorRegex.exec(selector)) { |
we need to do a little doctoring to make up for the quotes. | if (matches[2] && $.inArray(matches[2].substr(0, 1), ['"', "'"]) >= 0) {
matches[2] = matches[2].substr(1, -1);
}
attrs.push({ |
the attribute name | attr: matches[1], |
the attribute name, pre-split for speed | parts: matches[1].split('.'), |
the value associated with this property (if there was one given) | value: matches[2], |
whether this selector combines with the one after it with AND or OR | or: matches[3] === ','
});
} |
delegates has pre-processed info about the event | delegates.push({ |
the attrs name for unbinding | selector: selector, |
an object of attribute names and values {type: 'recipe',id: undefined} undefined means a value was not defined | attrs: attrs,
callback: handler,
event: event
});
if (delegates.length === 1) {
this.bind("change", delegate)
}
return this;
},
undelegate: function (selector, event, handler) {
selector = can.trim(selector);
var i = 0,
delegates = this._observe_delegates || [],
delegateOb;
if (selector) {
while (i < delegates.length) {
delegateOb = delegates[i];
if (delegateOb.callback === handler || (!handler && delegateOb.selector === selector)) {
delegateOb.undelegated = true;
delegates.splice(i, 1)
} else {
i++;
}
}
} else { |
remove all delegates | delegates = [];
}
if (!delegates.length) { |
can.removeData(this, "observedelegates"); | this.unbind("change", delegate)
}
return this;
}
}); |
add helpers for testing .. | can.Observe.prototype.delegate.matches = matches;
return can.Observe;
})(module["can/util/jquery/jquery.js"], module["can/observe/observe.js"]); // ## can/observe/setter/setter.js
module['can/observe/setter/setter.js'] = (function (can) {
can.classize = function (s, join) { |
this can be moved out .. used for getter setter | var parts = s.split(can.undHash),
i = 0;
for (; i < parts.length; i++) {
parts[i] = can.capitalize(parts[i]);
}
return parts.join(join || '');
}
var classize = can.classize,
proto = can.Observe.prototype,
old = proto.__set;
proto.__set = function (prop, value, current, success, error) { |
check if there's a setter | var cap = classize(prop),
setName = "set" + cap,
errorCallback = function (errors) {
var stub = error && error.call(self, errors); |
if 'setter' is on the page it will trigger the error itself and we dont want to trigger the event twice. :) | if (stub !== false) {
can.trigger(self, "error", [prop, errors], true);
}
return false;
},
self = this; |
if we have a setter | if (this[setName] && |
call the setter, if returned value is undefined, this means the setter is async so we do not call update property and return right away | (value = this[setName](value, function (value) {
old.call(self, prop, value, current, success, errorCallback)
}, errorCallback)) === undefined) {
return;
}
old.call(self, prop, value, current, success, errorCallback);
return this;
};
return can.Observe;
})(module["can/util/jquery/jquery.js"], module["can/observe/attributes/attributes.js"]); // ## can/observe/validations/validations.js
module['can/observe/validations/validations.js'] = (function (can) { |
validations object is by property. You can have validations that span properties, but this way we know which ones to run. proc should return true if there's an error or the error message | var validate = function (attrNames, options, proc) { |
normalize argumetns | if (!proc) {
proc = options;
options = {};
}
options = options || {};
attrNames = can.makeArray(attrNames) |
run testIf if it exists | if (options.testIf && !options.testIf.call(this)) {
return;
}
var self = this;
can.each(attrNames, function (attrName) { |
Add a test function for each attribute | if (!self.validations[attrName]) {
self.validations[attrName] = [];
}
self.validations[attrName].push(function (newVal) { |
if options has a message return that, otherwise, return the error | var res = proc.call(this, newVal, attrName);
return res === undefined ? undefined : (options.message || res);
})
});
};
var old = can.Observe.prototype.__set;
can.Observe.prototype.__set = function (prop, value, current, success, error) {
var self = this,
validations = self.constructor.validations,
errorCallback = function (errors) {
var stub = error && error.call(self, errors); |
if 'setter' is on the page it will trigger the error itself and we dont want to trigger the event twice. :) | if (stub !== false) {
can.trigger(self, "error", [prop, errors], true);
}
return false;
};
old.call(self, prop, value, current, success, errorCallback);
if (validations && validations[prop]) {
var errors = self.errors(prop);
errors && errorCallback(errors)
}
return this;
}
can.each([can.Observe, can.Model], function (clss) { |
in some cases model might not be defined quite yet. | if (clss === undefined) {
return;
}
var oldSetup = clss.setup;
can.extend(clss, {
setup: function (superClass) {
oldSetup.apply(this, arguments);
if (!this.validations || superClass.validations === this.validations) {
this.validations = {};
}
},
validate: validate,
validationMessages: {
format: "is invalid",
inclusion: "is not a valid option (perhaps out of range)",
lengthShort: "is too short",
lengthLong: "is too long",
presence: "can't be empty",
range: "is out of range"
},
validateFormatOf: function (attrNames, regexp, options) {
validate.call(this, attrNames, options, function (value) {
if ((typeof value !== 'undefined' && value !== null && value !== '') && String(value).match(regexp) == null) {
return this.constructor.validationMessages.format;
}
});
},
validateInclusionOf: function (attrNames, inArray, options) {
validate.call(this, attrNames, options, function (value) {
if (typeof value == 'undefined') {
return;
}
if (can.grep(inArray, function (elm) {
return (elm == value);
}).length == 0) {
return this.constructor.validationMessages.inclusion;
}
});
},
validateLengthOf: function (attrNames, min, max, options) {
validate.call(this, attrNames, options, function (value) {
if (((typeof value === 'undefined' || value === null) && min > 0) || (typeof value !== 'undefined' && value !== null && value.length < min)) {
return this.constructor.validationMessages.lengthShort + " (min=" + min + ")";
} else if (typeof value != 'undefined' && value !== null && value.length > max) {
return this.constructor.validationMessages.lengthLong + " (max=" + max + ")";
}
});
},
validatePresenceOf: function (attrNames, options) {
validate.call(this, attrNames, options, function (value) {
if (typeof value == 'undefined' || value === "" || value === null) {
return this.constructor.validationMessages.presence;
}
});
},
validateRangeOf: function (attrNames, low, hi, options) {
validate.call(this, attrNames, options, function (value) {
if (((typeof value == 'undefined' || value === null) && low > 0) || (typeof value !== 'undefined' && value !== null && (value < low || value > hi))) {
return this.constructor.validationMessages.range + " [" + low + "," + hi + "]";
}
});
}
});
});
can.extend(can.Observe.prototype, {
errors: function (attrs, newVal) { |
convert attrs to an array | if (attrs) {
attrs = can.isArray(attrs) ? attrs : [attrs];
}
var errors = {},
self = this,
attr, |
helper function that adds error messages to errors object attr - the name of the attribute funcs - the validation functions | addErrors = function (attr, funcs) {
can.each(funcs, function (func) {
var res = func.call(self, isTest ? (self.__convert ? self.__convert(attr, newVal) : newVal) : self[attr]);
if (res) {
if (!errors[attr]) {
errors[attr] = [];
}
errors[attr].push(res);
}
});
},
validations = this.constructor.validations,
isTest = attrs && attrs.length === 1 && arguments.length === 2; |
go through each attribute or validation and add any errors | can.each(attrs || validations || {}, function (funcs, attr) { |
if we are iterating through an array, use funcs as the attr name | if (typeof attr == 'number') {
attr = funcs;
funcs = validations[attr];
} |
add errors to the | addErrors(attr, funcs || []);
}); |
return errors as long as we have one | return can.isEmptyObject(errors) ? null : isTest ? errors[attrs[0]] : errors;
}
});
return can.Observe;
})(module["can/util/jquery/jquery.js"], module["can/observe/attributes/attributes.js"]); // ## can/util/string/deparam/deparam.js
module['can/util/string/deparam/deparam.js'] = (function (can) { |
deparam.js
| 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 | 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;
}
});
return can;
})(module["can/util/jquery/jquery.js"], module["can/util/string/string.js"]); // ## can/route/route.js
module['can/route/route.js'] = (function (can) { |
route.js
| var |
| 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 | 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 | 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 | test: new RegExp("^" + test + "($|" + wrapQuote(can.route._querySeparator) + ")"), |
The original URL, same as the index for this entry in routes. | route: url, |
An | 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 | 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
| 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 | 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 | remainder = url.substr(start.length - (parts[parts.length - 1] === can.route._querySeparator ? 1 : 0)), |
If there is a remainder and it contains a | 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 | 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 | 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 "<a " + makeProps(
extend({
href: can.route.url(options, merge)
}, props)) + ">" + name + "</a>";
},
current: function (options) {
return location.hash == "#!" + can.route.param(options)
},
_setup: function () { |
If the hash changes, update the | 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 | 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 | curParams, |
Deparameterizes the portion of the hash of interest and assign the
values to the | 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.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);
}); |
| can.bind.call(document, "ready", can.route.ready); |
Libraries other than jQuery don't execute the document | 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;
return can.route;
})(module["can/util/jquery/jquery.js"], module["can/observe/observe.js"], module["can/util/string/deparam/deparam.js"]); // ## can/util/object/object.js
module['can/util/object/object.js'] = (function (can) {
var isArray = can.isArray, |
essentially returns an object that has all the must have comparisons ... must haves, do not return true when provided undefined | cleanSet = function (obj, compares) {
var copy = can.extend({}, obj);
for (var prop in copy) {
var compare = compares[prop] === undefined ? compares["*"] : compares[prop];
if (same(copy[prop], undefined, compare)) {
delete copy[prop]
}
}
return copy;
},
propCount = function (obj) {
var count = 0;
for (var prop in obj) count++;
return count;
};
can.Object = {};
var same = can.Object.same = function (a, b, compares, aParent, bParent, deep) {
var aType = typeof a,
aArray = isArray(a),
comparesType = typeof compares,
compare;
if (comparesType == 'string' || compares === null) {
compares = compareMethods[compares];
comparesType = 'function'
}
if (comparesType == 'function') {
return compares(a, b, aParent, bParent)
}
compares = compares || {};
if (a instanceof Date) {
return a === b;
}
if (deep === -1) {
return aType === 'object' || a === b;
}
if (aType !== typeof b || aArray !== isArray(b)) {
return false;
}
if (a === b) {
return true;
}
if (aArray) {
if (a.length !== b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
compare = compares[i] === undefined ? compares["*"] : compares[i]
if (!same(a[i], b[i], a, b, compare)) {
return false;
}
};
return true;
} else if (aType === "object" || aType === 'function') {
var bCopy = can.extend({}, b);
for (var prop in a) {
compare = compares[prop] === undefined ? compares["*"] : compares[prop];
if (!same(a[prop], b[prop], compare, a, b, deep === false ? -1 : undefined)) {
return false;
}
delete bCopy[prop];
} |
go through bCopy props ... if there is no compare .. return false | for (prop in bCopy) {
if (compares[prop] === undefined || !same(undefined, b[prop], compares[prop], a, b, deep === false ? -1 : undefined)) {
return false;
}
}
return true;
}
return false;
};
can.Object.subsets = function (checkSet, sets, compares) {
var len = sets.length,
subsets = [],
checkPropCount = propCount(checkSet),
setLength;
for (var i = 0; i < len; i++) { |
check this subset | var set = sets[i];
if (can.Object.subset(checkSet, set, compares)) {
subsets.push(set)
}
}
return subsets;
};
can.Object.subset = function (subset, set, compares) { |
go through set {type: 'folder'} and make sure every property is in subset {type: 'folder', parentId :5} then make sure that set has fewer properties make sure we are only checking 'important' properties in subset (ones that have to have a value) | var setPropCount = 0,
compares = compares || {};
for (var prop in set) {
if (!same(subset[prop], set[prop], compares[prop], subset, set)) {
return false;
}
}
return true;
}
var compareMethods = {
"null": function () {
return true;
},
i: function (a, b) {
return ("" + a).toLowerCase() == ("" + b).toLowerCase()
}
}
return can;
})(module["can/util/jquery/jquery.js"]); // ## can/observe/backup/backup.js
module['can/observe/backup/backup.js'] = (function (can) {
var flatProps = function (a) {
var obj = {};
for (var prop in a) {
if (typeof a[prop] !== 'object' || a[prop] === null || a[prop] instanceof Date) {
obj[prop] = a[prop]
}
}
return obj;
};
can.extend(can.Observe.prototype, {
backup: function () {
this._backupStore = this._attrs();
return this;
},
isDirty: function (checkAssociations) {
return this._backupStore && !can.Object.same(this._attrs(), this._backupStore, undefined, undefined, undefined, !! checkAssociations);
},
restore: function (restoreAssociations) {
var props = restoreAssociations ? this._backupStore : flatProps(this._backupStore)
if (this.isDirty(restoreAssociations)) {
this._attrs(props);
}
return this;
}
})
return can.Observe;
})(module["can/util/jquery/jquery.js"], module["can/observe/observe.js"], module["can/util/object/object.js"]);
window.define = module._define;
window.module = module._orig;
|