todomvc
Version:
> Helping you select an MV\* framework
186 lines (160 loc) • 4.32 kB
JavaScript
/*!
* CanJS - 2.0.3
* http://canjs.us/
* Copyright (c) 2013 Bitovi
* Tue, 26 Nov 2013 18:21:22 GMT
* Licensed MIT
* Includes: CanJS default build
* Download from: http://canjs.us/
*/
define(["can/util/can"], function(can){
// deferred.js
// ---------
// _Lightweight, jQuery style deferreds._
// extend is usually provided by the wrapper but to avoid steal.then calls
// we define a simple extend here as well
var extend = function(target, src) {
for(var key in src) {
if(src.hasOwnProperty(key)) {
target[key] = src[key];
}
}
},
Deferred = function( func ) {
if ( ! ( this instanceof Deferred ))
return new Deferred();
this._doneFuncs = [];
this._failFuncs = [];
this._resultArgs = null;
this._status = "";
// Check for option `function` -- call it with this as context and as first
// parameter, as specified in jQuery API.
func && func.call(this, this);
};
can.Deferred = Deferred;
can.when = Deferred.when = function() {
var args = can.makeArray( arguments );
if (args.length < 2) {
var obj = args[0];
if (obj && ( can.isFunction( obj.isResolved ) && can.isFunction( obj.isRejected ))) {
return obj;
} else {
return Deferred().resolve(obj);
}
} else {
var df = Deferred(),
done = 0,
// Resolve params -- params of each resolve, we need to track them down
// to be able to pass them in the correct order if the master
// needs to be resolved.
rp = [];
can.each(args, function(arg, j){
arg.done(function() {
rp[j] = (arguments.length < 2) ? arguments[0] : arguments;
if (++done == args.length) {
df.resolve.apply(df, rp);
}
}).fail(function() {
df.reject((arguments.length === 1) ? arguments[0] : arguments);
});
});
return df;
}
}
var resolveFunc = function(type, _status){
return function(context){
var args = this._resultArgs = (arguments.length > 1) ? arguments[1] : [];
return this.exec(context, this[type], args, _status);
}
},
doneFunc = function(type, _status){
return function(){
var self = this;
// In Safari, the properties of the `arguments` object are not enumerable,
// so we have to convert arguments to an `Array` that allows `can.each` to loop over them.
can.each(Array.prototype.slice.call(arguments), function( v, i, args ) {
if ( ! v )
return;
if ( v.constructor === Array ) {
args.callee.apply(self, v)
} else {
// Immediately call the `function` if the deferred has been resolved.
if (self._status === _status)
v.apply(self, self._resultArgs || []);
self[type].push(v);
}
});
return this;
}
};
extend( Deferred.prototype, {
pipe : function(done, fail){
var d = can.Deferred();
this.done(function(){
d.resolve( done.apply(this, arguments) );
});
this.fail(function(){
if(fail){
d.reject( fail.apply(this, arguments) );
} else {
d.reject.apply(d, arguments);
}
});
return d;
},
resolveWith : resolveFunc("_doneFuncs","rs"),
rejectWith : resolveFunc("_failFuncs","rj"),
done : doneFunc("_doneFuncs","rs"),
fail : doneFunc("_failFuncs","rj"),
always : function() {
var args = can.makeArray(arguments);
if (args.length && args[0])
this.done(args[0]).fail(args[0]);
return this;
},
then : function() {
var args = can.makeArray( arguments );
// Fail `function`(s)
if (args.length > 1 && args[1])
this.fail(args[1]);
// Done `function`(s)
if (args.length && args[0])
this.done(args[0]);
return this;
},
state : function() {
switch(this._status) {
case 'rs':
return 'resolved';
case 'rj':
return 'rejected';
default:
return 'pending';
}
},
isResolved : function() {
return this._status === "rs";
},
isRejected : function() {
return this._status === "rj";
},
reject : function() {
return this.rejectWith(this, arguments);
},
resolve : function() {
return this.resolveWith(this, arguments);
},
exec : function(context, dst, args, st) {
if (this._status !== "")
return this;
this._status = st;
can.each(dst, function(d){
if(typeof d.apply === 'function') {
d.apply(context, args);
}
});
return this;
}
});
return can;
});