(function(root){ // Let's borrow a couple of things from Underscore that we'll need // _.each var breaker = {}, AP = Array.prototype, OP = Object.prototype, hasOwn = OP.hasOwnProperty, toString = OP.toString, forEach = AP.forEach, indexOf = AP.indexOf, slice = AP.slice; var _each = function( obj, iterator, context ) { var key, i, l; if ( !obj ) { return; } if ( forEach && obj.forEach === forEach ) { obj.forEach( iterator, context ); } else if ( obj.length === +obj.length ) { for ( i = 0, l = obj.length; i < l; i++ ) { if ( i in obj && iterator.call( context, obj[i], i, obj ) === breaker ) { return; } } } else { for ( key in obj ) { if ( hasOwn.call( obj, key ) ) { if ( iterator.call( context, obj[key], key, obj) === breaker ) { return; } } } } }; // _.isFunction var _isFunction = function( obj ) { return !!(obj && obj.constructor && obj.call && obj.apply); }; // _.extend var _extend = function( obj ) { _each( slice.call( arguments, 1), function( source ) { var prop; for ( prop in source ) { if ( source[prop] !== void 0 ) { obj[ prop ] = source[ prop ]; } } }); return obj; }; // $.inArray var _inArray = function( elem, arr, i ) { var len; if ( arr ) { if ( indexOf ) { return indexOf.call( arr, elem, i ); } len = arr.length; i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; for ( ; i < len; i++ ) { // Skip accessing in sparse arrays if ( i in arr && arr[ i ] === elem ) { return i; } } } return -1; }; // And some jQuery specific helpers var class2type = {}; // Populate the class2type map _each("Boolean Number String Function Array Date RegExp Object".split(" "), function(name, i) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); var _type = function( obj ) { return obj == null ? String( obj ) : class2type[ toString.call(obj) ] || "object"; }; // Now start the jQuery-cum-Underscore implementation. Some very // minor changes to the jQuery source to get this working. // Internal Deferred namespace var _d = {}; // String to Object options format cache var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { var object = optionsCache[ options ] = {}; _each( options.split( /\s+/ ), function( flag ) { object[ flag ] = true; }); return object; } _d.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : _extend( {}, options ); var // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // First, we save the current length var start = list.length; (function add( args ) { _each( args, function( arg ) { var type = _type( arg ); if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list remove: function() { if ( list ) { _each( arguments, function( arg ) { var index; while( ( index = _inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; }, // Control if a given callback is in the list has: function( fn ) { return _inArray( fn, list ) > -1; }, // Remove all callbacks from the list empty: function() { list = []; return this; }, // Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( list && ( !fired || stack ) ) { if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; }; _d.Deferred = function( func ) { var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", _d.Callbacks("once memory"), "resolved" ], [ "reject", "fail", _d.Callbacks("once memory"), "rejected" ], [ "notify", "progress", _d.Callbacks("memory") ] ], state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return _d.Deferred(function( newDefer ) { _each( tuples, function( tuple, i ) { var action = tuple[ 0 ], fn = fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ]( _isFunction( fn ) ? function() { var returned; try { returned = fn.apply( this, arguments ); } catch(e){ newDefer.reject(e); return; } if ( returned && _isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else { newDefer[ action !== "notify" ? 'resolveWith' : action + 'With']( this === deferred ? newDefer : this, [ returned ] ); } } : newDefer[ action ] ); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? _extend( obj, promise ) : promise; } }, deferred = {}; // Keep pipe for back-compat promise.pipe = promise.then; // Add list-specific methods _each( tuples, function( tuple, i ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; // Handle state if ( stateString ) { list.add(function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ] = list.fire deferred[ tuple[0] ] = list.fire; deferred[ tuple[0] + "With" ] = list.fireWith; }); // Make the deferred a promise promise.promise( deferred ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }; // Deferred helper _d.when = function( subordinate /* , ..., subordinateN */ ) { var i = 0, resolveValues = ( _type(subordinate) === 'array' && arguments.length === 1 ) ? subordinate : slice.call( arguments ), length = resolveValues.length; if ( _type(subordinate) === 'array' && subordinate.length === 1 ) { subordinate = subordinate[ 0 ]; } // the count of uncompleted subordinates var remaining = length !== 1 || ( subordinate && _isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that. deferred = remaining === 1 ? subordinate : _d.Deferred(), // Update function for both resolve and progress values updateFunc = function( i, contexts, values ) { return function( value ) { contexts[ i ] = this; values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if( values === progressValues ) { deferred.notifyWith( contexts, values ); } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); } }; }, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); for ( ; i < length; i++ ) { if ( resolveValues[ i ] && _isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress( updateFunc( i, progressContexts, progressValues ) ); } else { --remaining; } } } // if we're not waiting on anything, resolve the master if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); }; // Try exporting as a Common.js Module if ( typeof module !== "undefined" && module.exports ) { module.exports = _d; // Or mixin to Underscore.js } else if ( typeof root._ !== "undefined" ) { root._.mixin(_d); // Or assign it to window._ } else { root._ = _d; } })(this);