(function(can, window, undefined){
// # CanJS v1.0.7
// (c) 2012 Bitovi
// MIT license
// [http://canjs.us/](http://canjs.us/)
// jquery.js
// ---------
// _jQuery node list._
$.extend( can, jQuery, {
trigger: function( obj, event, args ) {
obj.trigger ?
obj.trigger( event, args ) :
$.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(result, element){
var ret = $.buildFragment([result],[element]);
return ret.cacheable ? $.clone(ret.fragment) : ret.fragment
},
$: jQuery
});
// 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 ) {
can.trigger(elem,"destroyed",[],false)
});
oldClean(elems);
};
can.each = function (elements, callback, context) {
var i = 0,
key;
if (elements) {
if (typeof elements.length == 'number' && elements.pop) {
elements.attr && elements.attr('length');
for (var len = elements.length; i < len; i++) {
if (callback.call(context || elements[i], elements[i], i, elements) === false) {
break;
}
}
} else {
for (key in elements) {
if (callback.call(context || elements[i], elements[key], key, elements) === false) {
break;
}
}
}
}
return elements;
}
;
// ##string.js
// _Miscellaneous string utility functions._
// Several of the methods in this plugin use code adapated from Prototype
// Prototype JavaScript framework, version 1.6.0.1.
// © 2005-2007 Sam Stephenson
var undHash = /_|-/,
colons = /==/,
words = /([A-Z]+)([A-Z][a-z])/g,
lowUp = /([a-z\d])([A-Z])/g,
dash = /([a-z\d])([A-Z])/g,
replacer = /\{([^\}]+)\}/g,
quote = /"/g,
singleQuote = /'/g,
// Returns the `prop` property from `obj`.
// If `add` is true and `prop` doesn't exist in `obj`, create it as an
// empty object.
getNext = function( obj, prop, add ) {
return prop in obj ?
obj[ prop ] :
( add && ( obj[ prop ] = {} ));
},
// Returns `true` if the object can have properties (no `null`s).
isContainer = function( current ) {
return /^f|^o/.test( typeof current );
};
can.extend(can, {
// Escapes strings for HTML.
esc : function( content ) {
return ( "" + content )
.replace(/&/g, '&')
.replace(//g, '>')
.replace(quote, '"')
.replace(singleQuote, "'");
},
getObject : function( name, roots, add ) {
// The parts of the name we are looking up
// `['App','Models','Recipe']`
var parts = name ? name.split('.') : [],
length = parts.length,
current,
r = 0,
ret, i;
// Make sure roots is an `array`.
roots = can.isArray(roots) ? roots : [roots || window];
if ( ! length ) {
return roots[0];
}
// For each root, mark it as current.
while( current = roots[r++] ) {
// Walk current to the 2nd to last object or until there
// is not a container.
for (i =0; i < length - 1 && isContainer( current ); i++ ) {
current = getNext( current, parts[i], add );
}
// If we can get a property from the 2nd to last object...
if( isContainer(current) ) {
// Get (and possibly set) the property.
ret = getNext(current, parts[i], add);
// If there is a value, we exit.
if ( ret !== undefined ) {
// If `add` is `false`, delete the property
if ( add === false ) {
delete current[parts[i]];
}
return ret;
}
}
}
},
// 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(colons, '/')
.replace(words, '$1_$2')
.replace(lowUp, '$1_$2')
.replace(dash, '_')
.toLowerCase();
},
// Micro-templating.
sub: function( str, data, remove ) {
var obs = [];
obs.push( str.replace( replacer, function( whole, inside ) {
// Convert inside to type.
var ob = can.getObject( inside, data, remove === undefined? remove : !remove );
// If a container, push into objs (which will return objects found).
if ( isContainer( ob ) ) {
obs.push( ob );
return "";
} else {
return "" + ob;
}
}));
return obs.length <= 1 ? obs[0] : obs;
},
// These regex's are used throughout the rest of can, so let's make
// them available.
replacer : replacer,
undHash : undHash
});
// ## construct.js
// `can.Construct`
// _This is a modified version of
// [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/).
// It provides class level inheritance and callbacks._
// A private flag used to initialize a new class instance without
// initializing it's bindings.
var initializing = 0;
can.Construct = function() {
if (arguments.length) {
return can.Construct.extend.apply(can.Construct, arguments);
}
};
can.extend(can.Construct, {
newInstance: function() {
// Get a raw instance object (`init` is not called).
var inst = this.instance(),
arg = arguments,
args;
// Call `setup` if there is a `setup`
if ( inst.setup ) {
args = inst.setup.apply(inst, arguments);
}
// Call `init` if there is an `init`
// If `setup` returned `args`, use those as the arguments
if ( inst.init ) {
inst.init.apply(inst, args || arguments);
}
return inst;
},
// Overwrites an object with methods. Used in the `super` plugin.
// `newProps` - New properties to add.
// `oldProps` - Where the old properties might be (used with `super`).
// `addTo` - What we are adding to.
_inherit: function( newProps, oldProps, addTo ) {
can.extend(addTo || newProps, newProps || {})
},
// used for overwriting a single property.
// this should be used for patching other objects
// the super plugin overwrites this
_overwrite : function(what, oldProps, propName, val){
what[propName] = val;
},
// Set `defaults` as the merger of the parent `defaults` and this
// object's `defaults`. If you overwrite this method, make sure to
// include option merging logic.
setup: function( base, fullName ) {
this.defaults = can.extend(true,{}, base.defaults, this.defaults);
},
// Create's a new `class` instance without initializing by setting the
// `initializing` flag.
instance: function() {
// Prevents running `init`.
initializing = 1;
var inst = new this();
// Allow running `init`.
initializing = 0;
return inst;
},
// Extends classes.
extend: function( fullName, klass, proto ) {
// Figure out what was passed and normalize it.
if ( typeof fullName != 'string' ) {
proto = klass;
klass = fullName;
fullName = null;
}
if ( ! proto ) {
proto = klass;
klass = null;
}
proto = proto || {};
var _super_class = this,
_super = this.prototype,
name, shortName, namespace, prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor).
prototype = this.instance();
// Copy the properties over onto the new prototype.
can.Construct._inherit(proto, _super, prototype);
// The dummy class constructor.
function Constructor() {
// All construction is actually done in the init method.
if ( ! initializing ) {
return this.constructor !== Constructor && arguments.length ?
// We are being called without `new` or we are extending.
arguments.callee.extend.apply(arguments.callee, arguments) :
// We are being called with `new`.
this.constructor.newInstance.apply(this.constructor, arguments);
}
}
// Copy old stuff onto class (can probably be merged w/ inherit)
for ( name in _super_class ) {
if ( _super_class.hasOwnProperty(name) ) {
Constructor[name] = _super_class[name];
}
}
// Copy new static properties on class.
can.Construct._inherit(klass, _super_class, Constructor);
// Setup namespaces.
if ( fullName ) {
var parts = fullName.split('.'),
shortName = parts.pop(),
current = can.getObject(parts.join('.'), window, true),
namespace = current,
_fullName = can.underscore(fullName.replace(/\./g, "_")),
_shortName = can.underscore(shortName);
//@steal-remove-start
if(current[shortName]){
}
//@steal-remove-end
current[shortName] = Constructor;
}
// Set things that shouldn't be overwritten.
can.extend(Constructor, {
constructor: Constructor,
prototype: prototype,
namespace: namespace,
shortName: shortName,
_shortName : _shortName,
fullName: fullName,
_fullName: _fullName
});
// Make sure our prototype looks nice.
Constructor.prototype.constructor = Constructor;
// Call the class `setup` and `init`
var t = [_super_class].concat(can.makeArray(arguments)),
args = Constructor.setup.apply(Constructor, t );
if ( Constructor.init ) {
Constructor.init.apply(Constructor, args || t );
}
return Constructor;
//
//
}
});
// ## observe.js
// `can.Observe`
// _Provides the observable pattern for JavaScript Objects._
//
// Returns `true` if something is an object with properties of its own.
var canMakeObserve = function( obj ) {
return obj && typeof obj === 'object' && !(obj instanceof Date);
},
// Removes all listeners.
unhookup = function(items, namespace){
return can.each(items, function(item){
if(item && item.unbind){
item.unbind("change" + namespace);
}
});
},
// Listens to changes on `val` and "bubbles" the event up.
// `val` - The object to listen for changes on.
// `prop` - The property name is at on.
// `parent` - The parent object of prop.
hookupBubble = function( val, prop, parent ) {
// If it's an `array` make a list, otherwise a val.
if (val instanceof Observe){
// We have an `observe` already...
// Make sure it is not listening to this already
unhookup([val], parent._namespace);
} else if ( can.isArray(val) ) {
val = new Observe.List(val);
} else {
val = new Observe(val);
}
// Listen to all changes and `batchTrigger` upwards.
val.bind("change" + parent._namespace, function( ev, attr ) {
// `batchTrigger` the type on this...
var args = can.makeArray(arguments),
ev = args.shift();
args[0] = prop === "*" ?
parent.indexOf(val)+"." + args[0] :
prop + "." + args[0];
// track objects dispatched on this observe
ev.triggeredNS = ev.triggeredNS || {};
// if it has already been dispatched exit
if (ev.triggeredNS[parent._namespace]) {
return;
}
ev.triggeredNS[parent._namespace] = true;
can.trigger(parent, ev, args);
can.trigger(parent,args[0],args);
});
return val;
},
// An `id` to track events for a given observe.
observeId = 0,
// A reference to an `array` of events that will be dispatched.
collecting = undefined,
// Call to start collecting events (`Observe` sends all events at
// once).
collect = function() {
if (!collecting ) {
collecting = [];
return true;
}
},
// Creates an event on item, but will not send immediately
// if collecting events.
// `item` - The item the event should happen on.
// `event` - The event name, ex: `change`.
// `args` - Tn array of arguments.
batchTrigger = function( item, event, args ) {
// Don't send events if initalizing.
if ( ! item._init) {
if (!collecting ) {
return can.trigger(item, event, args);
} else {
collecting.push([
item,
{
type: event,
batchNum : batchNum
},
args ] );
}
}
},
// 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,
// Sends all pending events.
sendCollection = function() {
var items = collecting.slice(0);
collecting = undefined;
batchNum++;
can.each(items, function( item ) {
can.trigger.apply(can, item)
})
},
// A helper used to serialize an `Observe` or `Observe.List`.
// `observe` - The observable.
// `how` - To serialize with `attr` or `serialize`.
// `where` - To put properties, in an `{}` or `[]`.
serialize = function( observe, how, where ) {
// Go through each property.
observe.each(function( val, name ) {
// If the value is an `object`, and has an `attrs` or `serialize` function.
where[name] = canMakeObserve(val) && can.isFunction( val[how] ) ?
// Call `attrs` or `serialize` to get the original data back.
val[how]() :
// Otherwise return the value.
val
})
return where;
},
$method = function( name ) {
return function() {
return can[name].apply(this, arguments );
}
},
bind = $method('addEvent'),
unbind = $method('removeEvent'),
attrParts = function(attr){
return can.isArray(attr) ? attr : (""+attr).split(".")
};
var Observe = can.Construct('can.Observe', {
// keep so it can be overwritten
setup : function(){
can.Construct.setup.apply(this, arguments)
},
bind : bind,
unbind: unbind,
id: "id"
},
{
setup: function( obj ) {
// `_data` is where we keep the properties.
this._data = {};
// The namespace this `object` uses to listen to events.
this._namespace = ".observe" + (++observeId);
// Sets all `attrs`.
this._init = 1;
this.attr(obj);
delete this._init;
},
attr: function( attr, val ) {
// This is super obfuscated for space -- basically, we're checking
// if the type of the attribute is not a `number` or a `string`.
if ( !~ "ns".indexOf((typeof attr).charAt(0))) {
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() {
return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments)))
},
removeAttr: function( attr ) {
// Convert the `attr` into parts (if nested).
var parts = attrParts(attr),
// The actual property to remove.
prop = parts.shift(),
// The current value.
current = this._data[prop];
// If we have more parts, call `removeAttr` on that part.
if ( parts.length ) {
return current.removeAttr(parts)
} else {
// Otherwise, `delete`.
delete this._data[prop];
// Create the event.
if (!(prop in this.constructor.prototype)) {
delete this[prop]
}
batchTrigger(this, "change", [prop, "remove", undefined, current]);
batchTrigger(this, prop, [undefined, current]);
return current;
}
},
// Reads a property from the `object`.
_get: function( attr ) {
var parts = attrParts(attr),
current = this.__get(parts.shift());
return parts.length ? current ? current._get(parts) : undefined : current;
},
// Reads a property directly if an `attr` is provided, otherwise
// returns the "real" data object itself.
__get: function( attr ) {
return attr ? this._data[attr] : this._data;
},
// Sets `attr` prop as value on this object where.
// `attr` - Is a string of properties or an array of property values.
// `value` - The raw value to set.
_set: function( attr, value ) {
// Convert `attr` to attr parts (if it isn't already).
var parts = attrParts(attr),
// The immediate prop we are setting.
prop = parts.shift(),
// The current value.
current = this.__get(prop);
// If we have an `object` and remaining parts.
if ( canMakeObserve(current) && parts.length ) {
// That `object` should set it (this might need to call attr).
current._set(parts, value)
} else if (!parts.length ) {
// We're in "real" set territory.
if(this.__convert){
value = this.__convert(prop, value)
}
this.__set(prop, value, current)
} else {
throw "can.Observe: Object does not exist"
}
},
__set : function(prop, value, current){
// Otherwise, we are setting it on this `object`.
// TODO: Check if value is object and transform
// are we changing the value.
if ( value !== current ) {
// Check if we are adding this for the first time --
// if we are, we need to create an `add` event.
var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
// Set the value on data.
this.___set(prop,
// If we are getting an object.
canMakeObserve(value) ?
// Hook it up to send event.
hookupBubble(value, prop, this) :
// Value is normal.
value);
// `batchTrigger` the change event.
batchTrigger(this, "change", [prop, changeType, value, current]);
batchTrigger(this, prop, [value, current]);
// If we can stop listening to our old value, do it.
current && unhookup([current], this._namespace);
}
},
// Directly sets a property on this `object`.
___set: function( prop, val ) {
this._data[prop] = val;
// Add property directly for easy writing.
// Check if its on the `prototype` so we don't overwrite methods like `attrs`.
if (!(prop in this.constructor.prototype)) {
this[prop] = val
}
},
bind: bind,
unbind: unbind,
serialize: function() {
return serialize(this, 'serialize', {});
},
_attrs: function( props, remove ) {
if ( props === undefined ) {
return serialize(this, 'attr', {})
}
props = can.extend(true, {}, props);
var prop,
collectingStarted = collect(),
self = this,
newVal;
this.each(function(curVal, prop){
newVal = props[prop];
// If we are merging...
if ( newVal === undefined ) {
remove && self.removeAttr(prop);
return;
}
if ( canMakeObserve(curVal) && canMakeObserve(newVal) ) {
curVal.attr(newVal, remove)
} else if ( curVal != newVal ) {
self._set(prop, newVal)
} else {
}
delete props[prop];
})
// Add remaining props.
for ( var prop in props ) {
newVal = props[prop];
this._set(prop, newVal)
}
if ( collectingStarted ) {
sendCollection();
}
return this;
}
});
// Helpers for `observable` lists.
var splice = [].splice,
list = Observe('can.Observe.List',
{
setup: function( instances, options ) {
this.length = 0;
this._namespace = ".observe" + (++observeId);
this._init = 1;
this.bind('change',can.proxy(this._changes,this));
this.push.apply(this, can.makeArray(instances || []));
can.extend(this, options);
delete this._init;
},
_changes : function(ev, attr, how, newVal, oldVal){
// `batchTrigger` direct add and remove events...
if ( !~ attr.indexOf('.')){
if( how === 'add' ) {
batchTrigger(this, how, [newVal,+attr]);
batchTrigger(this,'length',[this.length]);
} else if( how === 'remove' ) {
batchTrigger(this, how, [oldVal, +attr]);
batchTrigger(this,'length',[this.length]);
} else {
batchTrigger(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);
if ( howMany > 0 ) {
batchTrigger(this, "change", [""+index, "remove", undefined, removed]);
unhookup(removed, this._namespace);
}
if ( args.length > 2 ) {
batchTrigger(this, "change", [""+index, "add", args.slice(2), removed]);
}
return removed;
},
_attrs: function( props, remove ) {
if ( props === undefined ) {
return serialize(this, 'attr', []);
}
// Create a copy.
props = props.slice(0);
var len = Math.min(props.length, this.length),
collectingStarted = collect(),
prop;
for ( var prop = 0; prop < len; prop++ ) {
var curVal = this[prop],
newVal = props[prop];
if ( canMakeObserve(curVal) && canMakeObserve(newVal) ) {
curVal.attr(newVal, remove)
} else if ( curVal != newVal ) {
this._set(prop, newVal)
} else {
}
}
if ( props.length > this.length ) {
// Add in the remaining props.
this.push(props.slice(this.length))
} else if ( props.length < this.length && remove ) {
this.splice(props.length)
}
if ( collectingStarted ) {
sendCollection()
}
}
}),
// Converts to an `array` of arguments.
getArgs = function( args ) {
return args[0] && can.isArray(args[0]) ?
args[0] :
can.makeArray(args);
};
// Create `push`, `pop`, `shift`, and `unshift`
can.each({
push: "length",
unshift: 0
},
// Adds a method
// `name` - The method name.
// `where` - Where items in the `array` should be added.
function( where, name ) {
list.prototype[name] = function() {
// Get the items being added.
var args = getArgs(arguments),
// Where we are going to add items.
len = where ? this.length : 0;
// Go through and convert anything to an `observe` that needs to be converted.
for ( var i = 0; i < args.length; i++ ) {
var val = args[i];
if ( canMakeObserve(val) ) {
args[i] = hookupBubble(val, "*", this)
}
}
// Call the original method.
var res = [][name].apply(this, args);
if ( !this.comparator || !args.length ) {
batchTrigger(this, "change", [""+len, "add", args, undefined])
}
return res;
}
});
can.each({
pop: "length",
shift: 0
},
// Creates a `remove` type method
function( where, name ) {
list.prototype[name] = function() {
var args = getArgs(arguments),
len = where && this.length ? this.length - 1 : 0;
var res = [][name].apply(this, args)
// Create a change where the args are
// `*` - Change on potentially multiple properties.
// `remove` - Items removed.
// `undefined` - The new values (there are none).
// `res` - The old, removed values (should these be unbound).
// `len` - Where these items were removed.
batchTrigger(this, "change", [""+len, "remove", undefined, [res]])
if ( res && res.unbind ) {
res.unbind("change" + this._namespace)
}
return res;
}
});
list.prototype.
indexOf = [].indexOf || function(item){
return can.inArray(item, this)
};
// ## model.js
// `can.Model`
// _A `can.Observe` that connects to a RESTful interface._
//
// Generic deferred piping function
var pipe = function( def, model, func ) {
var d = new can.Deferred();
def.then(function(){
arguments[0] = model[func](arguments[0])
d.resolve.apply(d, arguments)
},function(){
d.rejectWith.apply(this,arguments)
})
return d;
},
modelNum = 0,
ignoreHookup = /change.observe\d+/,
getId = function( inst ) {
return inst[inst.constructor.id]
},
// Ajax `options` generator function
ajax = function( ajaxOb, data, type, dataType, success, error ) {
// 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(" ")
ajaxOb = {
url : parts.pop()
};
if(parts.length){
ajaxOb.type = parts.pop();
}
}
// If we are a non-array object, copy to a new attrs.
ajaxOb.data = typeof data == "object" && !can.isArray(data) ?
can.extend(ajaxOb.data || {}, data) : data;
// Get the url with any templated values filled out.
ajaxOb.url = can.sub(ajaxOb.url, ajaxOb.data, true);
return can.ajax(can.extend({
type: type || "post",
dataType: dataType ||"json",
success : success,
error: error
}, ajaxOb ));
},
makeRequest = function( self, type, success, error, method ) {
var deferred ,
args = [self.serialize()],
// The model.
model = self.constructor,
jqXHR;
// `destroy` does not need data.
if ( type == 'destroy' ) {
args.shift();
}
// `update` and `destroy` need the `id`.
if ( type !== 'create' ) {
args.unshift(getId(self))
}
jqXHR = model[type].apply(model, args);
deferred = jqXHR.pipe(function(data){
self[method || type + "d"](data, jqXHR);
return self
})
// Hook up `abort`
if(jqXHR.abort){
deferred.abort = function(){
jqXHR.abort();
}
}
return deferred.then(success,error);
},
// This object describes how to make an ajax request for each ajax method.
// The available properties are:
// `url` - The default url to use as indicated as a property on the model.
// `type` - The default http request type
// `data` - A method that takes the `arguments` and returns `data` used for ajax.
//
//
//
ajaxMethods = {
create : {
url : "_shortName",
type :"post"
},
update : {
data : function(id, attrs){
attrs = attrs || {};
var identity = this.id;
if ( attrs[identity] && attrs[identity] !== id ) {
attrs["new" + can.capitalize(id)] = attrs[identity];
delete attrs[identity];
}
attrs[identity] = id;
return attrs;
},
type : "put"
},
destroy : {
type : "delete",
data : function(id){
var args = {};
args[this.id] = id;
return args;
}
},
findAll : {
url : "_shortName"
},
findOne: {}
},
// Makes an ajax request `function` from a string.
// `ajaxMethod` - The `ajaxMethod` object defined above.
// `str` - The string the user provided. Ex: `findAll: "/recipes.json"`.
ajaxMaker = function(ajaxMethod, str){
// Return a `function` that serves as the ajax method.
return function(data){
// If the ajax method has it's own way of getting `data`, use that.
data = ajaxMethod.data ?
ajaxMethod.data.apply(this, arguments) :
// Otherwise use the data passed in.
data;
// Return the ajax method with `data` and the `type` provided.
return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get")
}
}
can.Observe("can.Model",{
setup : function(base){
can.Observe.apply(this, arguments);
if(this === can.Model){
return;
}
var self = this,
clean = can.proxy(this._clean, self);
can.each(ajaxMethods, function(method, name){
if ( ! can.isFunction( self[name] )) {
self[name] = ajaxMaker(method, self[name]);
}
if (self["make"+can.capitalize(name)]){
var newMethod = self["make"+can.capitalize(name)](self[name]);
can.Construct._overwrite(self, base, name,function(){
this._super;
this._reqs++;
return newMethod.apply(this, arguments).then(clean, clean);
})
}
});
if(!self.fullName || self.fullName == base.fullName){
self.fullName = self._shortName = "Model"+(++modelNum);
}
// Ddd ajax converters.
this.store = {};
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];
}
}
}
},
models: function( instancesRawData ) {
if ( ! instancesRawData ) {
return;
}
if ( instancesRawData instanceof this.List ) {
return instancesRawData;
}
// Get the list type.
var self = this,
res = new( self.List || ML),
// Did we get an `array`?
arr = can.isArray(instancesRawData),
// Did we get a model list?
ml = (instancesRawData instanceof ML),
// Get the raw `array` of objects.
raw = arr ?
// If an `array`, return the `array`.
instancesRawData :
// Otherwise if a model list.
(ml ?
// Get the raw objects from the list.
instancesRawData.serialize() :
// Get the object's data.
instancesRawData.data),
i = 0;
can.each(raw, function( rawPart ) {
res.push( self.model( rawPart ));
});
if ( ! arr ) { // Push other stuff onto `array`.
can.each(instancesRawData, function(val, prop){
if ( prop !== 'data' ) {
res[prop] = val;
}
})
}
return res;
},
model: function( attributes ) {
if (!attributes ) {
return;
}
if ( attributes instanceof this ) {
attributes = attributes.serialize();
}
var model = this.store[attributes[this.id]] ? this.store[attributes[this.id]].attr(attributes) : 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 ) {
return makeRequest(this, 'destroy', success, error, 'destroyed');
},
bind : function(eventName){
if ( ! ignoreHookup.test( eventName )) {
if ( ! this._bindings ) {
this.constructor.store[getId(this)] = this;
this._bindings = 0;
}
this._bindings++;
}
return can.Observe.prototype.bind.apply( this, arguments );
},
unbind : function(eventName){
if(!ignoreHookup.test(eventName)) {
this._bindings--;
if(!this._bindings){
delete this.constructor.store[getId(this)];
}
}
return can.Observe.prototype.unbind.apply(this, arguments);
},
// Change `id`.
___set: function( prop, val ) {
can.Observe.prototype.___set.call(this,prop, val)
// If we add an `id`, move it to the store.
if(prop === this.constructor.id && this._bindings){
this.constructor.store[getId(this)] = this;
}
}
});
can.each({makeFindAll : "models", makeFindOne: "model"}, function(method, name){
can.Model[name] = function(oldFind){
return function(params, success, error){
return pipe( oldFind.call( this, params ),
this,
method ).then(success,error)
}
};
});
can.each([
"created",
"updated",
"destroyed"], function( funcName ) {
can.Model.prototype[funcName] = function( attrs ) {
var stub,
constructor = this.constructor;
// Update attributes if attributes have been passed
stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs);
// Call event on the instance
can.trigger(this,funcName);
can.trigger(this,"change",funcName)
// Call event on the instance's Class
can.trigger(constructor,funcName, this);
};
});
// Model lists are just like `Observe.List` except that when their items are
// destroyed, it automatically gets removed from the list.
var ML = can.Observe.List('can.Model.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)){
self.splice(self.indexOf(ev.target),1);
}
})
}
})
;
// ## deparam.js
// `can.deparam`
// _Takes a string of name value pairs and returns a Object literal that represents those params._
var digitTest = /^\d+$/,
keyBreaker = /([^\[\]]+)|(\[\])/g,
paramTest = /([^?#]*)(#.*)?$/,
prep = function( str ) {
return decodeURIComponent( str.replace(/\+/g, " ") );
}
can.extend(can, {
deparam: function(params){
var data = {},
pairs, lastPart;
if ( params && paramTest.test( params )) {
pairs = params.split('&'),
can.each( pairs, function( pair ) {
var parts = pair.split('='),
key = prep( parts.shift() ),
value = prep( parts.join("=") );
current = data;
parts = key.match(keyBreaker);
for ( var j = 0, l = parts.length - 1; j < l; j++ ) {
if (!current[parts[j]] ) {
// If what we are pointing to looks like an `array`
current[parts[j]] = digitTest.test(parts[j+1]) || parts[j+1] == "[]" ? [] : {}
}
current = current[parts[j]];
}
lastPart = parts.pop()
if ( lastPart == "[]" ) {
current.push(value)
} else {
current[lastPart] = value;
}
});
}
return data;
}
});
// ## route.js
// `can.route`
// _Helps manage browser history (and client state) by synchronizing the
// `window.location.hash` with a `can.Observe`._
//
// Helper methods used for matching routes.
var
// `RegExp` used to match route variables of the type ':name'.
// Any word character or a period is matched.
matcher = /\:([\w\.]+)/g,
// Regular expression for identifying &key=value lists.
paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
// Converts a JS Object into a list of parameters that can be
// inserted into an html element tag.
makeProps = function( props ) {
var tags = [];
can.each(props, function(val, name){
tags.push( ( name === 'className' ? 'class' : name )+ '="' +
(name === "href" ? val : can.esc(val) ) + '"');
});
return tags.join(" ");
},
// Checks if a route matches the data provided. If any route variable
// is not present in the data, the route does not match. If all route
// variables are present in the data, the number of matches is returned
// to allow discerning between general and more specific routes.
matchesData = function(route, data) {
var count = 0, i = 0, defaults = {};
// look at default values, if they match ...
for( var name in route.defaults ) {
if(route.defaults[name] === data[name]){
// mark as matched
defaults[name] = 1;
count++;
}
}
for (; i < route.names.length; i++ ) {
if (!data.hasOwnProperty(route.names[i]) ) {
return -1;
}
if(!defaults[route.names[i]]){
count++;
}
}
return count;
},
onready = !0,
location = window.location,
each = can.each,
extend = can.extend;
can.route = function( url, defaults ) {
defaults = defaults || {}
// Extract the variable names and replace with `RegExp` that will match
// an atual URL with values.
var names = [],
test = url.replace(matcher, function( whole, name, i ) {
names.push(name);
var next = "\\"+( url.substr(i+whole.length,1) || "&" )
// a name without a default value HAS to have a value
// a name that has a default value can be empty
// The `\\` is for string-escaping giving single `\` for `RegExp` escaping.
return "([^" +next+"]"+(defaults[name] ? "*" : "+")+")"
});
// Add route in a form that can be easily figured out.
can.route.routes[url] = {
// A regular expression that will match the route when variable values
// are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which
// will match for any value of `:page` and `:type` (word chars or period).
test: new RegExp("^" + test+"($|&)"),
// The original URL, same as the index for this entry in routes.
route: url,
// An `array` of all the variable names in this route.
names: names,
// Default values provided for the variables.
defaults: defaults,
// The number of parts in the URL separated by `/`.
length: url.split('/').length
}
return can.route;
};
extend(can.route, {
param: function( data , _setRoute ) {
// Check if the provided data keys match the names in any routes;
// Get the one with the most matches.
var route,
// Need to have at least 1 match.
matches = 0,
matchCount,
routeName = data.route,
propCount = 0;
delete data.route;
each(data, function(){propCount++});
// Otherwise find route.
each(can.route.routes, function(temp, name){
// best route is the first with all defaults matching
matchCount = matchesData(temp, data);
if ( matchCount > matches ) {
route = temp;
matches = matchCount
}
if(matchCount >= propCount){
return false;
}
});
// If we have a route name in our `can.route` data, and it's
// just as good as what currently matches, use that
if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data ) === matches) {
route = can.route.routes[routeName];
}
// If this is match...
if ( route ) {
var cpy = extend({}, data),
// Create the url by replacing the var names with the provided data.
// If the default value is found an empty string is inserted.
res = route.route.replace(matcher, function( whole, name ) {
delete cpy[name];
return data[name] === route.defaults[name] ? "" : encodeURIComponent( data[name] );
}),
after;
// Remove matching default values
each(route.defaults, function(val,name){
if(cpy[name] === val) {
delete cpy[name]
}
})
// The remaining elements of data are added as
// `&` separated parameters to the url.
after = can.param(cpy);
// if we are paraming for setting the hash
// we also want to make sure the route value is updated
if(_setRoute){
can.route.attr('route',route.route);
}
return res + (after ? "&" + after : "")
}
// If no route was found, there is no hash URL, only paramters.
return can.isEmptyObject(data) ? "" : "&" + can.param(data);
},
deparam: function( url ) {
// See if the url matches any routes by testing it against the `route.test` `RegExp`.
// By comparing the URL length the most specialized route that matches is used.
var route = {
length: -1
};
each(can.route.routes, function(temp, name){
if ( temp.test.test(url) && temp.length > route.length ) {
route = temp;
}
});
// If a route was matched.
if ( route.length > -1 ) {
var // Since `RegExp` backreferences are used in `route.test` (parens)
// the parts will contain the full matched string and each variable (back-referenced) value.
parts = url.match(route.test),
// Start will contain the full matched string; parts contain the variable values.
start = parts.shift(),
// The remainder will be the `&key=value` list at the end of the URL.
remainder = url.substr(start.length - (parts[parts.length-1] === "&" ? 1 : 0) ),
// If there is a remainder and it contains a `&key=value` list deparam it.
obj = (remainder && paramsMatcher.test(remainder)) ? can.deparam( remainder.slice(1) ) : {};
// Add the default values for this route.
obj = extend(true, {}, route.defaults, obj);
// Overwrite each of the default values in `obj` with those in
// parts if that part is not empty.
each(parts,function(part, i){
if ( part && part !== '&') {
obj[route.names[i]] = decodeURIComponent( part );
}
});
obj.route = route.route;
return obj;
}
// If no route was matched, it is parsed as a `&key=value` list.
if ( url.charAt(0) !== '&' ) {
url = '&' + url;
}
return 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 ) {
setState();
}
return can.route;
},
url: function( options, merge ) {
if (merge) {
options = extend({}, curParams, options)
}
return "#!" + can.route.param(options)
},
link: function( name, options, props, merge ) {
return "" + name + "";
},
current: function( options ) {
return location.hash == "#!" + can.route.param(options)
}
});
// The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will
// instead act on the `can.route.data` observe.
each(['bind','unbind','delegate','undelegate','attr','removeAttr'], function(name){
can.route[name] = function(){
return can.route.data[name].apply(can.route.data, arguments)
}
})
var // A ~~throttled~~ debounced function called multiple times will only fire once the
// timer runs down. Each call resets the timer.
timer,
// Intermediate storage for `can.route.data`.
curParams,
// Deparameterizes the portion of the hash of interest and assign the
// values to the `can.route.data` removing existing values no longer in the hash.
// setState is called typically by hashchange which fires asynchronously
// So it's possible that someone started changing the data before the
// hashchange event fired. For this reason, it will not set the route data
// if the data is changing and the hash already matches the hash that was set.
setState = function() {
var hash = location.href.split(/#!?/)[1] || ""
curParams = can.route.deparam( hash );
// if the hash data is currently changing, and
// 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 hash changes, update the `can.route.data`.
can.bind.call(window,'hashchange', setState);
// If the `can.route.data` changes, update the hash.
// Using `.serialize()` retrieves the raw data contained in the `observable`.
// This function is ~~throttled~~ debounced so it only updates once even if multiple values changed.
// This might be able to use batchNum and avoid this.
can.route.bind("change", function(ev, attr) {
// indicate that data is changing
changingData = 1;
clearTimeout( timer );
timer = setTimeout(function() {
// indicate that the hash is set to look like the data
changingData = 0;
var serialized = can.route.data.serialize();
location.hash = "#!" + (lastHash = can.route.param(serialized, true))
}, 1);
});
// `onready` event...
can.bind.call(document,"ready",can.route.ready);
(function() {
// ## control.js
// `can.Control`
// _Controller_
// Binds an element, returns a function that unbinds.
var bind = function( el, ev, callback ) {
can.bind.call( el, ev, callback )
return function() {
can.unbind.call(el, ev, callback);
};
},
isFunction = can.isFunction,
extend = can.extend,
each = can.each,
slice = [].slice,
paramReplacer = /\{([^\}]+)\}/g,
special = can.getObject("$.event.special") || {},
// 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 );
},
// Moves `this` to the first argument, wraps it with `jQuery` if it's an element
shifter = function shifter(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)));
};
},
basicProcessor;
can.Construct("can.Control",
{
// Setup pre-processes which methods are event listeners.
setup: function() {
// Allow contollers to inherit "defaults" from super-classes as it
// done in `can.Construct`
can.Construct.setup.apply( this, arguments );
// If you didn't provide a name, or are `control`, don't do anything.
if ( this !== 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);
}
}
}
},
// Return `true` if is an action.
_isAction: function( methodName ) {
var val = this.prototype[methodName],
type = typeof val;
// if not the constructor
return (methodName !== 'constructor') &&
// and is a function or links to a function
( type == "function" || (type == "string" && isFunction(this.prototype[val] ) ) ) &&
// and is in special, a processor, or has a funny character
!! ( special[methodName] || processors[methodName] || /[^\w]/.test(methodName) );
},
// Takes a method name and the options passed to a control
// and tries to return the data necessary to pass to a processor
// (something that binds things).
_action: function( methodName, options ) {
// If we don't have options (a `control` instance), we'll run this
// later.
paramReplacer.lastIndex = 0;
if ( options || ! paramReplacer.test( methodName )) {
// If we have options, run sub to replace templates `{}` with a
// value from the options or the window
var convertedName = options ? can.sub(methodName, [options, window]) : methodName,
// If a `{}` resolves to an object, `convertedName` will be
// an array
arr = can.isArray(convertedName),
// Get the parts of the function
// `[convertedName, delegatePart, eventPart]`
// `/^(?:(.*?)\s)?([\w\.\:>]+)$/` - Breaker `RegExp`.
parts = (arr ? convertedName[1] : convertedName).match(/^(?:(.*?)\s)?([\w\.\:>]+)$/);
var event = parts[2],
processor = processors[event] || basicProcessor;
return {
processor: processor,
parts: parts,
delegate : arr ? convertedName[0] : undefined
};
}
},
// An object of `{eventName : function}` pairs that Control uses to
// hook up events auto-magically.
processors: {},
// A object of name-value pairs that act as default values for a
// control instance
defaults: {}
},
{
// Sets `this.element`, saves the control in `data, binds event
// handlers.
setup: function( element, options ) {
var cls = this.constructor,
pluginname = cls.pluginName || cls._fullName,
arr;
// Want the raw element here.
this.element = can.$(element)
if ( pluginname && pluginname !== 'can_control') {
// Set element and `className` on element.
this.element.addClass(pluginname);
}
(arr = can.data(this.element,"controls")) || can.data(this.element,"controls",arr = []);
arr.push(this);
// Option merging.
this.options = extend({}, cls.defaults, options);
// Bind all event handlers.
this.on();
// Get's passed into `init`.
return [this.element, this.options];
},
on: function( el, selector, eventName, func ) {
if ( ! el ) {
// Adds bindings.
this.off();
// Go through the cached list of actions and use the processor
// to bind
var cls = this.constructor,
bindings = this._bindings,
actions = cls.actions,
element = this.element,
destroyCB = shifter(this,"destroy"),
funcName, ready;
for ( funcName in actions ) {
if ( actions.hasOwnProperty( funcName )) {
ready = actions[funcName] || cls._action(funcName, this.options);
bindings.push(
ready.processor(ready.delegate || element,
ready.parts[2],
ready.parts[1],
funcName,
this));
}
}
// Setup to be destroyed...
// don't bind because we don't want to remove it.
can.bind.call(element,"destroyed", destroyCB);
bindings.push(function( el ) {
can.unbind.call(el,"destroyed", destroyCB);
});
return bindings.length;
}
if ( typeof el == 'string' ) {
func = eventName;
eventName = selector;
selector = el;
el = this.element;
}
if ( typeof func == 'string' ) {
func = shifter(this,func);
}
this._bindings.push( binder( el, eventName, func, selector ));
return this._bindings.length;
},
// Unbinds all event handlers on the controller.
off : function(){
var el = this.element[0]
each(this._bindings || [], function( value ) {
value(el);
});
// Adds bindings.
this._bindings = [];
},
// Prepares a `control` for garbage collection
destroy: function() {
var Class = this.constructor,
pluginName = Class.pluginName || Class._fullName,
controls;
// Unbind bindings.
this.off();
if(pluginName && pluginName !== 'can_control'){
// Remove the `className`.
this.element.removeClass(pluginName);
}
// Remove from `data`.
controls = can.data(this.element,"controls");
controls.splice(can.inArray(this, controls),1);
can.trigger( this, "destroyed"); // In case we want to know if the `control` is removed.
this.element = null;
}
});
var processors = can.Control.processors,
// Processors do the binding.
// They return a function that unbinds when called.
//
// The basic processor that binds events.
basicProcessor = function( el, event, selector, methodName, control ) {
return binder( el, event, shifter(control, methodName), selector);
};
// Set common events to be processed as a `basicProcessor`
each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup",
"keypress", "mousedown", "mousemove", "mouseout", "mouseover",
"mouseup", "reset", "resize", "scroll", "select", "submit", "focusin",
"focusout", "mouseenter", "mouseleave"], function( v ) {
processors[v] = basicProcessor;
});
}());
// ## control/route.js
// _Controller route integration._
can.Control.processors.route = function( el, event, selector, funcName, controller ) {
can.route( selector || "" )
var batchNum,
check = function( ev, attr, how ) {
if ( can.route.attr('route') === ( selector || "" ) &&
( ev.batchNum === undefined || ev.batchNum !== batchNum ) ) {
batchNum = ev.batchNum;
var d = can.route.attr();
delete d.route;
if(can.isFunction(controller[funcName])){
controller[funcName]( d )
}else {
controller[controller[funcName]](d)
}
}
}
can.route.bind( 'change', check );
return function() {
can.route.unbind( 'change', check )
}
}
;
// ## view.js
// `can.view`
// _Templating abstraction._
var isFunction = can.isFunction,
makeArray = can.makeArray,
// Used for hookup `id`s.
hookupId = 1,
$view = can.view = function(view, data, helpers, callback){
// Get the result.
var result = $view.render(view, data, helpers, callback);
if(can.isDeferred(result)){
return result.pipe(function(result){
return $view.frag(result);
})
}
// Convert it into a dom frag.
return $view.frag(result);
};
can.extend( $view, {
// creates a frag and hooks it up all at once
frag: function(result, parentNode ){
return $view.hookup( $view.fragment(result), parentNode );
},
// simply creates a frag
// this is used internally to create a frag
// insert it
// then hook it up
fragment: function(result){
var frag = can.buildFragment(result,document.body);
// If we have an empty frag...
if(!frag.childNodes.length) {
frag.appendChild(document.createTextNode(''))
}
return frag;
},
// Convert a path like string into something that's ok for an `element` ID.
toId : function( src ) {
return can.map(src.toString().split(/\/|\./g), function( part ) {
// Dont include empty strings in toId functions
if ( part ) {
return part;
}
}).join("_");
},
hookup: function(fragment, parentNode ){
var hookupEls = [],
id,
func,
el,
i=0;
// Get all `childNodes`.
can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node){
if(node.nodeType === 1){
hookupEls.push(node)
hookupEls.push.apply(hookupEls, can.makeArray( node.getElementsByTagName('*')))
}
});
// Filter by `data-view-id` attribute.
for (; el = hookupEls[i++]; ) {
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: {},
cache: true,
register: function( info ) {
this.types["." + info.suffix] = info;
},
types: {},
ext: ".ejs",
registerScript: function() {},
preload: function( ) {},
render: function( view, data, helpers, callback ) {
// If helpers is a `function`, it is actually a callback.
if ( isFunction( helpers )) {
callback = helpers;
helpers = undefined;
}
// See if we got passed any deferreds.
var deferreds = getDeferreds(data);
if ( deferreds.length ) { // Does data contain any deferreds?
// The deferred that resolves into the rendered content...
var deferred = new can.Deferred();
// 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) ) {
data = 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]) ) {
data[prop] = usefulPart(objs.shift());
}
}
}
// Get the rendered result.
result = renderer(data, helpers);
// Resolve with the rendered view.
deferred.resolve(result);
// If there's a `callback`, call it back with the result.
callback && callback(result);
});
// Return the deferred...
return deferred;
}
else {
// No deferreds! Render this bad boy.
var response,
// If there's a `callback` function
async = isFunction( callback ),
// Get the `view` type
deferred = get(view, async);
// If we are `async`...
if ( async ) {
// Return the deferred
response = deferred;
// And fire callback with the rendered result.
deferred.then(function( renderer ) {
callback(renderer(data, helpers))
})
} else {
// Otherwise, the deferred is complete, so
// set response to the result of the rendering.
deferred.then(function( renderer ) {
response = renderer(data, helpers);
});
}
return response;
}
}
});
// Returns `true` if something looks like a deferred.
can.isDeferred = function( obj ) {
return obj && isFunction(obj.then) && isFunction(obj.pipe) // Check if `obj` is a `can.Deferred`.
}
// Makes sure there's a template, if not, have `steal` provide a warning.
var checkText = function( text, url ) {
if ( ! text.length ) {
//@steal-remove-start
//@steal-remove-end
throw "can.view: No template or empty template:" + url;
}
},
// `Returns a `view` renderer deferred.
// `url` - The url to the template.
// `async` - If the ajax request should be asynchronous.
// Returns a deferred.
get = function( url, async ) {
var suffix = url.match(/\.[\w\d]+$/),
type,
// If we are reading a script element for the content of the template,
// `el` will be set to that script element.
el,
// A unique identifier for the view (used for caching).
// This is typically derived from the element id or
// the url for the template.
id,
// The ajax request used to retrieve the template content.
jqXHR,
// Used to generate the response.
response = function( text ) {
// Get the renderer function.
var func = type.renderer(id, text),
d = new can.Deferred();
d.resolve(func)
// Cache if we are caching.
if ( $view.cache ) {
$view.cached[id] = d;
}
// Return the objects for the response's `dataTypes`
// (in this case view).
return d;
};
//If the url has a #, we assume we want to use an inline template
//from a script element and not current page's HTML
if( url.match(/^#/) ) {
url = url.substr(1);
}
// If we have an inline template, derive the suffix from the `text/???` part.
// This only supports `