Replaces jQuery._Deferred with the much more flexible (and public) jQuery.Callbacks. Hopefully, jQuery.Callbacks can be used as a base for all callback lists needs in jQuery. Also adds progress callbacks to Deferreds (handled in jQuery.when too). Needs more unit tests.
This commit is contained in:
parent
9d4033d629
commit
a31195fd5a
15 changed files with 317 additions and 273 deletions
1
Makefile
1
Makefile
|
@ -10,6 +10,7 @@ COMPILER = ${JS_ENGINE} ${BUILD_DIR}/uglify.js --unsafe
|
||||||
POST_COMPILER = ${JS_ENGINE} ${BUILD_DIR}/post-compile.js
|
POST_COMPILER = ${JS_ENGINE} ${BUILD_DIR}/post-compile.js
|
||||||
|
|
||||||
BASE_FILES = ${SRC_DIR}/core.js\
|
BASE_FILES = ${SRC_DIR}/core.js\
|
||||||
|
${SRC_DIR}/callbacks.js\
|
||||||
${SRC_DIR}/deferred.js\
|
${SRC_DIR}/deferred.js\
|
||||||
${SRC_DIR}/support.js\
|
${SRC_DIR}/support.js\
|
||||||
${SRC_DIR}/data.js\
|
${SRC_DIR}/data.js\
|
||||||
|
|
|
@ -382,7 +382,7 @@ jQuery.extend({
|
||||||
jQuery( callbackContext ) : jQuery.event,
|
jQuery( callbackContext ) : jQuery.event,
|
||||||
// Deferreds
|
// Deferreds
|
||||||
deferred = jQuery.Deferred(),
|
deferred = jQuery.Deferred(),
|
||||||
completeDeferred = jQuery._Deferred(),
|
completeCallbacks = jQuery.Callbacks( "once memory" ),
|
||||||
// Status-dependent callbacks
|
// Status-dependent callbacks
|
||||||
statusCode = s.statusCode || {},
|
statusCode = s.statusCode || {},
|
||||||
// ifModified key
|
// ifModified key
|
||||||
|
@ -560,7 +560,7 @@ jQuery.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete
|
// Complete
|
||||||
completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );
|
completeCallbacks.fireWith( callbackContext, [ jqXHR, statusText ] );
|
||||||
|
|
||||||
if ( fireGlobals ) {
|
if ( fireGlobals ) {
|
||||||
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] );
|
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] );
|
||||||
|
@ -575,7 +575,7 @@ jQuery.extend({
|
||||||
deferred.promise( jqXHR );
|
deferred.promise( jqXHR );
|
||||||
jqXHR.success = jqXHR.done;
|
jqXHR.success = jqXHR.done;
|
||||||
jqXHR.error = jqXHR.fail;
|
jqXHR.error = jqXHR.fail;
|
||||||
jqXHR.complete = completeDeferred.done;
|
jqXHR.complete = completeCallbacks.add;
|
||||||
|
|
||||||
// Status-dependent callbacks
|
// Status-dependent callbacks
|
||||||
jqXHR.statusCode = function( map ) {
|
jqXHR.statusCode = function( map ) {
|
||||||
|
|
206
src/callbacks.js
Normal file
206
src/callbacks.js
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
(function( jQuery ) {
|
||||||
|
|
||||||
|
// String to Object flags format cache
|
||||||
|
var flagsCache = {};
|
||||||
|
|
||||||
|
// Convert String-formatted flags into Object-formatted ones and store in cache
|
||||||
|
function createFlags( flags ) {
|
||||||
|
var object = flagsCache[ flags ] = {},
|
||||||
|
i, length;
|
||||||
|
flags = flags.split( /\s+/ );
|
||||||
|
for ( i = 0, length = flags.length; i < length; i++ ) {
|
||||||
|
object[ flags[i] ] = true;
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a callback list using the following parameters:
|
||||||
|
*
|
||||||
|
* flags: an optional list of space-separated flags that will change how
|
||||||
|
* the callback list behaves
|
||||||
|
*
|
||||||
|
* filter: an optional function that will be applied to each added callbacks,
|
||||||
|
* what filter returns will then be added provided it is not falsy.
|
||||||
|
*
|
||||||
|
* By default a callback list will act like an event callback list and can be
|
||||||
|
* "fired" multiple times.
|
||||||
|
*
|
||||||
|
* Possible flags:
|
||||||
|
*
|
||||||
|
* once: will ensure the callback list can only be fired once (like a Deferred)
|
||||||
|
*
|
||||||
|
* memory: will keep track of previous values and will call any callback added
|
||||||
|
* after the list has been fired right away with the latest "memorized"
|
||||||
|
* values (like a Deferred)
|
||||||
|
*
|
||||||
|
* unique: will ensure a callback can only be added once (no duplicate in the list)
|
||||||
|
*
|
||||||
|
* relocate: like "unique" but will relocate the callback at the end of the list
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
jQuery.Callbacks = function( flags, filter ) {
|
||||||
|
|
||||||
|
// flags are optional
|
||||||
|
if ( typeof flags !== "string" ) {
|
||||||
|
filter = flags;
|
||||||
|
flags = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert flags from String-formatted to Object-formatted
|
||||||
|
// (we check in cache first)
|
||||||
|
flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
|
||||||
|
|
||||||
|
var // Actual callback list
|
||||||
|
list = [],
|
||||||
|
// Stack of fire calls for repeatable lists
|
||||||
|
stack = !flags.once && [],
|
||||||
|
// Last fire value (for non-forgettable lists)
|
||||||
|
memory,
|
||||||
|
// Flag to know if list is currently firing
|
||||||
|
firing,
|
||||||
|
// Add a list of callbacks to the list
|
||||||
|
add = function( args ) {
|
||||||
|
var i,
|
||||||
|
length,
|
||||||
|
elem,
|
||||||
|
type,
|
||||||
|
actual;
|
||||||
|
for ( i = 0, length = args.length; i < length; i++ ) {
|
||||||
|
elem = args[ i ];
|
||||||
|
type = jQuery.type( elem );
|
||||||
|
if ( type === "array" ) {
|
||||||
|
// Inspect recursively
|
||||||
|
add( elem );
|
||||||
|
} else if ( type === "function" ) {
|
||||||
|
// If we have to relocate, we remove the callback
|
||||||
|
// if it already exists
|
||||||
|
if ( flags.relocate ) {
|
||||||
|
object.remove( elem );
|
||||||
|
}
|
||||||
|
// Unless we have a list with unicity and the
|
||||||
|
// function is already there, add it
|
||||||
|
if ( !( flags.unique && object.has( elem ) ) ) {
|
||||||
|
// Get the filtered function if needs be
|
||||||
|
actual = filter ? filter(elem) : elem;
|
||||||
|
if ( actual ) {
|
||||||
|
list.push( [ elem, actual ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
object = {
|
||||||
|
// Add a callback or a collection of callbacks to the list
|
||||||
|
add: function() {
|
||||||
|
if ( list ) {
|
||||||
|
add( arguments );
|
||||||
|
// If we're not firing and we should call right away
|
||||||
|
if ( !firing && flags.memory && memory ) {
|
||||||
|
// Trick the list into thinking it needs to be fired
|
||||||
|
var tmp = memory;
|
||||||
|
memory = undefined;
|
||||||
|
object.fireWith( tmp[ 0 ], tmp[ 1 ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
// Remove a callback from the list
|
||||||
|
remove: function( fn ) {
|
||||||
|
if ( list ) {
|
||||||
|
var i = 0,
|
||||||
|
length = list.length;
|
||||||
|
for ( ; i < length; i++ ) {
|
||||||
|
if ( fn === list[ i ][ 0 ] ) {
|
||||||
|
list.splice( i, 1 );
|
||||||
|
i--;
|
||||||
|
// If we have some unicity property then
|
||||||
|
// we only need to do this once
|
||||||
|
if ( flags.unique || flags.relocate ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
// Control if a given callback is in the list
|
||||||
|
has: function( fn ) {
|
||||||
|
if ( list ) {
|
||||||
|
var i = 0,
|
||||||
|
length = list.length;
|
||||||
|
for ( ; i < length; i++ ) {
|
||||||
|
if ( fn === list[ i ][ 0 ] ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
// 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;
|
||||||
|
},
|
||||||
|
// Lock the list in its current state
|
||||||
|
lock: function() {
|
||||||
|
stack = undefined;
|
||||||
|
if ( !memory ) {
|
||||||
|
object.disable();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
// Call all callbacks with the given context and arguments
|
||||||
|
fireWith: function( context, args ) {
|
||||||
|
var i;
|
||||||
|
if ( list && ( flags.once ? !memory && !firing : stack ) ) {
|
||||||
|
if ( firing ) {
|
||||||
|
stack.push( [ context, args ] );
|
||||||
|
} else {
|
||||||
|
args = args || [];
|
||||||
|
memory = !flags.memory || [ context, args ];
|
||||||
|
firing = true;
|
||||||
|
try {
|
||||||
|
for ( i = 0; list && i < list.length; i++ ) {
|
||||||
|
if ( list[ i ][ 1 ].apply( context, args ) === false && flags.stopOnFalse ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
firing = false;
|
||||||
|
if ( list ) {
|
||||||
|
if ( !flags.once ) {
|
||||||
|
if ( i >= list.length && stack.length ) {
|
||||||
|
object.fire.apply( this, stack.shift() );
|
||||||
|
}
|
||||||
|
} else if ( !flags.memory ) {
|
||||||
|
object.destroy();
|
||||||
|
} else {
|
||||||
|
list = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
// Call all the callbacks with the given arguments
|
||||||
|
fire: function() {
|
||||||
|
object.fireWith( this, arguments );
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
// To know if the callbacks have already been called at least once
|
||||||
|
fired: function() {
|
||||||
|
return !!memory;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return object;
|
||||||
|
};
|
||||||
|
|
||||||
|
})( jQuery );
|
|
@ -58,7 +58,7 @@ var jQuery = function( selector, context ) {
|
||||||
// For matching the engine and version of the browser
|
// For matching the engine and version of the browser
|
||||||
browserMatch,
|
browserMatch,
|
||||||
|
|
||||||
// The deferred used on DOM ready
|
// The callback list used on DOM ready
|
||||||
readyList,
|
readyList,
|
||||||
|
|
||||||
// The ready event handler
|
// The ready event handler
|
||||||
|
@ -257,7 +257,7 @@ jQuery.fn = jQuery.prototype = {
|
||||||
jQuery.bindReady();
|
jQuery.bindReady();
|
||||||
|
|
||||||
// Add the callback
|
// Add the callback
|
||||||
readyList.done( fn );
|
readyList.add( fn );
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
@ -412,7 +412,7 @@ jQuery.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are functions bound, to execute
|
// If there are functions bound, to execute
|
||||||
readyList.resolveWith( document, [ jQuery ] );
|
readyList.fireWith( document, [ jQuery ] );
|
||||||
|
|
||||||
// Trigger any bound ready events
|
// Trigger any bound ready events
|
||||||
if ( jQuery.fn.trigger ) {
|
if ( jQuery.fn.trigger ) {
|
||||||
|
@ -426,7 +426,7 @@ jQuery.extend({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
readyList = jQuery._Deferred();
|
readyList = jQuery.Callbacks( "once memory" );
|
||||||
|
|
||||||
// Catch cases where $(document).ready() is called after the
|
// Catch cases where $(document).ready() is called after the
|
||||||
// browser event has already occurred.
|
// browser event has already occurred.
|
||||||
|
|
229
src/deferred.js
229
src/deferred.js
|
@ -1,169 +1,105 @@
|
||||||
(function( jQuery ) {
|
(function( jQuery ) {
|
||||||
|
|
||||||
var // Promise methods
|
var // Promise methods
|
||||||
promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ),
|
promiseMethods = "done fail progress isResolved isRejected promise then always pipe".split( " " ),
|
||||||
// Static reference to slice
|
// Static reference to slice
|
||||||
sliceDeferred = [].slice;
|
sliceDeferred = [].slice;
|
||||||
|
|
||||||
jQuery.extend({
|
jQuery.extend({
|
||||||
// Create a simple deferred (one callbacks list)
|
|
||||||
_Deferred: function() {
|
|
||||||
var // callbacks list
|
|
||||||
callbacks = [],
|
|
||||||
// stored [ context , args ]
|
|
||||||
fired,
|
|
||||||
// to avoid firing when already doing so
|
|
||||||
firing,
|
|
||||||
// flag to know if the deferred has been cancelled
|
|
||||||
cancelled,
|
|
||||||
// the deferred itself
|
|
||||||
deferred = {
|
|
||||||
|
|
||||||
// done( f1, f2, ...)
|
Deferred: function( func ) {
|
||||||
done: function() {
|
var doneList = jQuery.Callbacks( "once memory" ),
|
||||||
if ( !cancelled ) {
|
failList = jQuery.Callbacks( "once memory" ),
|
||||||
var args = arguments,
|
progressList = jQuery.Callbacks( "memory" ),
|
||||||
i,
|
promise,
|
||||||
length,
|
deferred = {
|
||||||
elem,
|
// Copy existing methods from lists
|
||||||
type,
|
done: doneList.add,
|
||||||
_fired;
|
removeDone: doneList.remove,
|
||||||
if ( fired ) {
|
fail: failList.add,
|
||||||
_fired = fired;
|
removeFail: failList.remove,
|
||||||
fired = 0;
|
progress: progressList.add,
|
||||||
}
|
removeProgress: progressList.remove,
|
||||||
for ( i = 0, length = args.length; i < length; i++ ) {
|
resolve: doneList.fire,
|
||||||
elem = args[ i ];
|
resolveWith: doneList.fireWith,
|
||||||
type = jQuery.type( elem );
|
reject: failList.fire,
|
||||||
if ( type === "array" ) {
|
rejectWith: failList.fireWith,
|
||||||
deferred.done.apply( deferred, elem );
|
ping: progressList.fire,
|
||||||
} else if ( type === "function" ) {
|
pingWith: progressList.pingWith,
|
||||||
callbacks.push( elem );
|
isResolved: doneList.fired,
|
||||||
|
isRejected: failList.fired,
|
||||||
|
|
||||||
|
// Create Deferred-specific methods
|
||||||
|
then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
|
||||||
|
deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
always: function() {
|
||||||
|
return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
|
||||||
|
},
|
||||||
|
pipe: function( fnDone, fnFail, fnProgress ) {
|
||||||
|
return jQuery.Deferred(function( newDefer ) {
|
||||||
|
jQuery.each( {
|
||||||
|
done: [ fnDone, "resolve" ],
|
||||||
|
fail: [ fnFail, "reject" ],
|
||||||
|
progress: [ fnProgress, "ping" ]
|
||||||
|
}, function( handler, data ) {
|
||||||
|
var fn = data[ 0 ],
|
||||||
|
action = data[ 1 ],
|
||||||
|
returned;
|
||||||
|
if ( jQuery.isFunction( fn ) ) {
|
||||||
|
deferred[ handler ](function() {
|
||||||
|
returned = fn.apply( this, arguments );
|
||||||
|
if ( jQuery.isFunction( returned.promise ) ) {
|
||||||
|
returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.ping );
|
||||||
|
} else {
|
||||||
|
newDefer[ action ]( returned );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deferred[ handler ]( newDefer[ action ] );
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}).promise();
|
||||||
|
},
|
||||||
|
// Get a promise for this deferred
|
||||||
|
// If obj is provided, the promise aspect is added to the object
|
||||||
|
promise: function( obj ) {
|
||||||
|
if ( obj == null ) {
|
||||||
|
if ( promise ) {
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
if ( _fired ) {
|
promise = obj = {};
|
||||||
deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return this;
|
var i = promiseMethods.length;
|
||||||
},
|
while( i-- ) {
|
||||||
|
obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
|
||||||
// resolve with given context and args
|
|
||||||
resolveWith: function( context, args ) {
|
|
||||||
if ( !cancelled && !fired && !firing ) {
|
|
||||||
// make sure args are available (#8421)
|
|
||||||
args = args || [];
|
|
||||||
firing = 1;
|
|
||||||
try {
|
|
||||||
while( callbacks[ 0 ] ) {
|
|
||||||
callbacks.shift().apply( context, args );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
fired = [ context, args ];
|
|
||||||
firing = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return this;
|
return obj;
|
||||||
},
|
|
||||||
|
|
||||||
// resolve with this as context and given arguments
|
|
||||||
resolve: function() {
|
|
||||||
deferred.resolveWith( this, arguments );
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Has this deferred been resolved?
|
|
||||||
isResolved: function() {
|
|
||||||
return !!( firing || fired );
|
|
||||||
},
|
|
||||||
|
|
||||||
// Cancel
|
|
||||||
cancel: function() {
|
|
||||||
cancelled = 1;
|
|
||||||
callbacks = [];
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return deferred;
|
// Handle lists exclusiveness
|
||||||
},
|
deferred.done( failList.disable, progressList.lock )
|
||||||
|
.fail( doneList.disable, progressList.lock );
|
||||||
|
|
||||||
// Full fledged deferred (two callbacks list)
|
|
||||||
Deferred: function( func ) {
|
|
||||||
var deferred = jQuery._Deferred(),
|
|
||||||
failDeferred = jQuery._Deferred(),
|
|
||||||
promise;
|
|
||||||
// Add errorDeferred methods, then and promise
|
|
||||||
jQuery.extend( deferred, {
|
|
||||||
then: function( doneCallbacks, failCallbacks ) {
|
|
||||||
deferred.done( doneCallbacks ).fail( failCallbacks );
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
always: function() {
|
|
||||||
return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
|
|
||||||
},
|
|
||||||
fail: failDeferred.done,
|
|
||||||
rejectWith: failDeferred.resolveWith,
|
|
||||||
reject: failDeferred.resolve,
|
|
||||||
isRejected: failDeferred.isResolved,
|
|
||||||
pipe: function( fnDone, fnFail ) {
|
|
||||||
return jQuery.Deferred(function( newDefer ) {
|
|
||||||
jQuery.each( {
|
|
||||||
done: [ fnDone, "resolve" ],
|
|
||||||
fail: [ fnFail, "reject" ]
|
|
||||||
}, function( handler, data ) {
|
|
||||||
var fn = data[ 0 ],
|
|
||||||
action = data[ 1 ],
|
|
||||||
returned;
|
|
||||||
if ( jQuery.isFunction( fn ) ) {
|
|
||||||
deferred[ handler ](function() {
|
|
||||||
returned = fn.apply( this, arguments );
|
|
||||||
if ( returned && jQuery.isFunction( returned.promise ) ) {
|
|
||||||
returned.promise().then( newDefer.resolve, newDefer.reject );
|
|
||||||
} else {
|
|
||||||
newDefer[ action ]( returned );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
deferred[ handler ]( newDefer[ action ] );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).promise();
|
|
||||||
},
|
|
||||||
// Get a promise for this deferred
|
|
||||||
// If obj is provided, the promise aspect is added to the object
|
|
||||||
promise: function( obj ) {
|
|
||||||
if ( obj == null ) {
|
|
||||||
if ( promise ) {
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
promise = obj = {};
|
|
||||||
}
|
|
||||||
var i = promiseMethods.length;
|
|
||||||
while( i-- ) {
|
|
||||||
obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Make sure only one callback list will be used
|
|
||||||
deferred.done( failDeferred.cancel ).fail( deferred.cancel );
|
|
||||||
// Unexpose cancel
|
|
||||||
delete deferred.cancel;
|
|
||||||
// Call given func if any
|
// Call given func if any
|
||||||
if ( func ) {
|
if ( func ) {
|
||||||
func.call( deferred, deferred );
|
func.call( deferred, deferred );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All done!
|
||||||
return deferred;
|
return deferred;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Deferred helper
|
// Deferred helper
|
||||||
when: function( firstParam ) {
|
when: function( firstParam ) {
|
||||||
var args = arguments,
|
var args = sliceDeferred.call( arguments, 0 ),
|
||||||
i = 0,
|
i = 0,
|
||||||
length = args.length,
|
length = args.length,
|
||||||
|
pValues = new Array( length ),
|
||||||
count = length,
|
count = length,
|
||||||
|
pCount = length,
|
||||||
deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
|
deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
|
||||||
firstParam :
|
firstParam :
|
||||||
jQuery.Deferred();
|
jQuery.Deferred();
|
||||||
|
@ -171,17 +107,22 @@ jQuery.extend({
|
||||||
return function( value ) {
|
return function( value ) {
|
||||||
args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
|
args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
|
||||||
if ( !( --count ) ) {
|
if ( !( --count ) ) {
|
||||||
// Strange bug in FF4:
|
deferred.resolveWith( deferred, args );
|
||||||
// Values changed onto the arguments object sometimes end up as undefined values
|
}
|
||||||
// outside the $.when method. Cloning the object into a fresh array solves the issue
|
};
|
||||||
deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
|
}
|
||||||
|
function progressFunc( i ) {
|
||||||
|
return function( value ) {
|
||||||
|
pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
|
||||||
|
if ( !( --count ) ) {
|
||||||
|
deferred.pingWith( deferred, pValue );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if ( length > 1 ) {
|
if ( length > 1 ) {
|
||||||
for( ; i < length; i++ ) {
|
for( ; i < length; i++ ) {
|
||||||
if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
|
if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
|
||||||
args[ i ].promise().then( resolveFunc(i), deferred.reject );
|
args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
|
||||||
} else {
|
} else {
|
||||||
--count;
|
--count;
|
||||||
}
|
}
|
||||||
|
|
16
src/queue.js
16
src/queue.js
|
@ -1,7 +1,7 @@
|
||||||
(function( jQuery ) {
|
(function( jQuery ) {
|
||||||
|
|
||||||
function handleQueueMarkDefer( elem, type, src ) {
|
function handleCBList( elem, type, src ) {
|
||||||
var deferDataKey = type + "defer",
|
var deferDataKey = type + "cblst",
|
||||||
queueDataKey = type + "queue",
|
queueDataKey = type + "queue",
|
||||||
markDataKey = type + "mark",
|
markDataKey = type + "mark",
|
||||||
defer = jQuery.data( elem, deferDataKey, undefined, true );
|
defer = jQuery.data( elem, deferDataKey, undefined, true );
|
||||||
|
@ -14,7 +14,7 @@ function handleQueueMarkDefer( elem, type, src ) {
|
||||||
if ( !jQuery.data( elem, queueDataKey, undefined, true ) &&
|
if ( !jQuery.data( elem, queueDataKey, undefined, true ) &&
|
||||||
!jQuery.data( elem, markDataKey, undefined, true ) ) {
|
!jQuery.data( elem, markDataKey, undefined, true ) ) {
|
||||||
jQuery.removeData( elem, deferDataKey, true );
|
jQuery.removeData( elem, deferDataKey, true );
|
||||||
defer.resolve();
|
defer.fireWith();
|
||||||
}
|
}
|
||||||
}, 0 );
|
}, 0 );
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ jQuery.extend({
|
||||||
jQuery.data( elem, key, count, true );
|
jQuery.data( elem, key, count, true );
|
||||||
} else {
|
} else {
|
||||||
jQuery.removeData( elem, key, true );
|
jQuery.removeData( elem, key, true );
|
||||||
handleQueueMarkDefer( elem, type, "mark" );
|
handleCBList( elem, type, "mark" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -90,7 +90,7 @@ jQuery.extend({
|
||||||
|
|
||||||
if ( !queue.length ) {
|
if ( !queue.length ) {
|
||||||
jQuery.removeData( elem, type + "queue", true );
|
jQuery.removeData( elem, type + "queue", true );
|
||||||
handleQueueMarkDefer( elem, type, "queue" );
|
handleCBList( elem, type, "queue" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -146,7 +146,7 @@ jQuery.fn.extend({
|
||||||
elements = this,
|
elements = this,
|
||||||
i = elements.length,
|
i = elements.length,
|
||||||
count = 1,
|
count = 1,
|
||||||
deferDataKey = type + "defer",
|
deferDataKey = type + "cblst",
|
||||||
queueDataKey = type + "queue",
|
queueDataKey = type + "queue",
|
||||||
markDataKey = type + "mark",
|
markDataKey = type + "mark",
|
||||||
tmp;
|
tmp;
|
||||||
|
@ -159,9 +159,9 @@ jQuery.fn.extend({
|
||||||
if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
|
if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
|
||||||
( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
|
( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
|
||||||
jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
|
jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
|
||||||
jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
|
jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
|
||||||
count++;
|
count++;
|
||||||
tmp.done( resolve );
|
tmp.add( resolve );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#positionTest { position: absolute; }
|
#positionTest { position: absolute; }
|
||||||
</style>
|
</style>
|
||||||
<script src="../../../src/core.js"></script>
|
<script src="../../../src/core.js"></script>
|
||||||
<script src="../../../src/deferred.js"></script>
|
<script src="../../../src/callbacks.js"></script>
|
||||||
<script src="../../../src/support.js"></script>
|
<script src="../../../src/support.js"></script>
|
||||||
<script src="../../../src/sizzle/sizzle.js"></script>
|
<script src="../../../src/sizzle/sizzle.js"></script>
|
||||||
<script src="../../../src/sizzle-jquery.js"></script>
|
<script src="../../../src/sizzle-jquery.js"></script>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
||||||
</style>
|
</style>
|
||||||
<script src="../../../src/core.js"></script>
|
<script src="../../../src/core.js"></script>
|
||||||
<script src="../../../src/deferred.js"></script>
|
<script src="../../../src/callbacks.js"></script>
|
||||||
<script src="../../../src/support.js"></script>
|
<script src="../../../src/support.js"></script>
|
||||||
<script src="../../../src/sizzle/sizzle.js"></script>
|
<script src="../../../src/sizzle/sizzle.js"></script>
|
||||||
<script src="../../../src/sizzle-jquery.js"></script>
|
<script src="../../../src/sizzle-jquery.js"></script>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
||||||
</style>
|
</style>
|
||||||
<script src="../../../src/core.js"></script>
|
<script src="../../../src/core.js"></script>
|
||||||
<script src="../../../src/deferred.js"></script>
|
<script src="../../../src/callbacks.js"></script>
|
||||||
<script src="../../../src/support.js"></script>
|
<script src="../../../src/support.js"></script>
|
||||||
<script src="../../../src/sizzle/sizzle.js"></script>
|
<script src="../../../src/sizzle/sizzle.js"></script>
|
||||||
<script src="../../../src/sizzle-jquery.js"></script>
|
<script src="../../../src/sizzle-jquery.js"></script>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
||||||
</style>
|
</style>
|
||||||
<script src="../../../src/core.js"></script>
|
<script src="../../../src/core.js"></script>
|
||||||
<script src="../../../src/deferred.js"></script>
|
<script src="../../../src/callbacks.js"></script>
|
||||||
<script src="../../../src/support.js"></script>
|
<script src="../../../src/support.js"></script>
|
||||||
<script src="../../../src/sizzle/sizzle.js"></script>
|
<script src="../../../src/sizzle/sizzle.js"></script>
|
||||||
<script src="../../../src/sizzle-jquery.js"></script>
|
<script src="../../../src/sizzle-jquery.js"></script>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
||||||
</style>
|
</style>
|
||||||
<script src="../../../src/core.js"></script>
|
<script src="../../../src/core.js"></script>
|
||||||
<script src="../../../src/deferred.js"></script>
|
<script src="../../../src/callbacks.js"></script>
|
||||||
<script src="../../../src/support.js"></script>
|
<script src="../../../src/support.js"></script>
|
||||||
<script src="../../../src/sizzle/sizzle.js"></script>
|
<script src="../../../src/sizzle/sizzle.js"></script>
|
||||||
<script src="../../../src/sizzle-jquery.js"></script>
|
<script src="../../../src/sizzle-jquery.js"></script>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
||||||
</style>
|
</style>
|
||||||
<script src="../../../src/core.js"></script>
|
<script src="../../../src/core.js"></script>
|
||||||
<script src="../../../src/deferred.js"></script>
|
<script src="../../../src/callbacks.js"></script>
|
||||||
<script src="../../../src/support.js"></script>
|
<script src="../../../src/support.js"></script>
|
||||||
<script src="../../../src/sizzle/sizzle.js"></script>
|
<script src="../../../src/sizzle/sizzle.js"></script>
|
||||||
<script src="../../../src/sizzle-jquery.js"></script>
|
<script src="../../../src/sizzle-jquery.js"></script>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
|
||||||
</style>
|
</style>
|
||||||
<script src="../../../src/core.js"></script>
|
<script src="../../../src/core.js"></script>
|
||||||
<script src="../../../src/deferred.js"></script>
|
<script src="../../../src/callbacks.js"></script>
|
||||||
<script src="../../../src/support.js"></script>
|
<script src="../../../src/support.js"></script>
|
||||||
<script src="../../../src/sizzle/sizzle.js"></script>
|
<script src="../../../src/sizzle/sizzle.js"></script>
|
||||||
<script src="../../../src/sizzle-jquery.js"></script>
|
<script src="../../../src/sizzle-jquery.js"></script>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<script src="data/testinit.js"></script>
|
<script src="data/testinit.js"></script>
|
||||||
|
|
||||||
<script src="../src/core.js"></script>
|
<script src="../src/core.js"></script>
|
||||||
|
<script src="../src/callbacks.js"></script>
|
||||||
<script src="../src/deferred.js"></script>
|
<script src="../src/deferred.js"></script>
|
||||||
<script src="../src/support.js"></script>
|
<script src="../src/support.js"></script>
|
||||||
<script src="../src/data.js"></script>
|
<script src="../src/data.js"></script>
|
||||||
|
@ -34,8 +35,9 @@
|
||||||
<script src="data/testrunner.js"></script>
|
<script src="data/testrunner.js"></script>
|
||||||
|
|
||||||
<script src="unit/core.js"></script>
|
<script src="unit/core.js"></script>
|
||||||
<script src="unit/support.js"></script>
|
<script src="unit/callbacks.js"></script>
|
||||||
<script src="unit/deferred.js"></script>
|
<script src="unit/deferred.js"></script>
|
||||||
|
<script src="unit/support.js"></script>
|
||||||
<script src="unit/data.js"></script>
|
<script src="unit/data.js"></script>
|
||||||
<script src="unit/queue.js"></script>
|
<script src="unit/queue.js"></script>
|
||||||
<script src="unit/attributes.js"></script>
|
<script src="unit/attributes.js"></script>
|
||||||
|
|
|
@ -1,111 +1,5 @@
|
||||||
module("deferred", { teardown: moduleTeardown });
|
module("deferred", { teardown: moduleTeardown });
|
||||||
|
|
||||||
jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
|
|
||||||
|
|
||||||
function createDeferred() {
|
|
||||||
return withNew ? new jQuery._Deferred() : jQuery._Deferred();
|
|
||||||
}
|
|
||||||
|
|
||||||
test("jQuery._Deferred" + withNew, function() {
|
|
||||||
|
|
||||||
expect( 11 );
|
|
||||||
|
|
||||||
var deferred,
|
|
||||||
object,
|
|
||||||
test;
|
|
||||||
|
|
||||||
deferred = createDeferred();
|
|
||||||
|
|
||||||
test = false;
|
|
||||||
|
|
||||||
deferred.done( function( value ) {
|
|
||||||
equals( value , "value" , "Test pre-resolve callback" );
|
|
||||||
test = true;
|
|
||||||
} );
|
|
||||||
|
|
||||||
deferred.resolve( "value" );
|
|
||||||
|
|
||||||
ok( test , "Test pre-resolve callbacks called right away" );
|
|
||||||
|
|
||||||
test = false;
|
|
||||||
|
|
||||||
deferred.done( function( value ) {
|
|
||||||
equals( value , "value" , "Test post-resolve callback" );
|
|
||||||
test = true;
|
|
||||||
} );
|
|
||||||
|
|
||||||
ok( test , "Test post-resolve callbacks called right away" );
|
|
||||||
|
|
||||||
deferred.cancel();
|
|
||||||
|
|
||||||
test = true;
|
|
||||||
|
|
||||||
deferred.done( function() {
|
|
||||||
ok( false , "Cancel was ignored" );
|
|
||||||
test = false;
|
|
||||||
} );
|
|
||||||
|
|
||||||
ok( test , "Test cancel" );
|
|
||||||
|
|
||||||
deferred = createDeferred().resolve();
|
|
||||||
|
|
||||||
try {
|
|
||||||
deferred.done( function() {
|
|
||||||
throw "Error";
|
|
||||||
} , function() {
|
|
||||||
ok( true , "Test deferred do not cancel on exception" );
|
|
||||||
} );
|
|
||||||
} catch( e ) {
|
|
||||||
strictEqual( e , "Error" , "Test deferred propagates exceptions");
|
|
||||||
deferred.done();
|
|
||||||
}
|
|
||||||
|
|
||||||
test = "";
|
|
||||||
deferred = createDeferred().done( function() {
|
|
||||||
|
|
||||||
test += "A";
|
|
||||||
|
|
||||||
}, function() {
|
|
||||||
|
|
||||||
test += "B";
|
|
||||||
|
|
||||||
} ).resolve();
|
|
||||||
|
|
||||||
strictEqual( test , "AB" , "Test multiple done parameters" );
|
|
||||||
|
|
||||||
test = "";
|
|
||||||
|
|
||||||
deferred.done( function() {
|
|
||||||
|
|
||||||
deferred.done( function() {
|
|
||||||
|
|
||||||
test += "C";
|
|
||||||
|
|
||||||
} );
|
|
||||||
|
|
||||||
test += "A";
|
|
||||||
|
|
||||||
}, function() {
|
|
||||||
|
|
||||||
test += "B";
|
|
||||||
} );
|
|
||||||
|
|
||||||
strictEqual( test , "ABC" , "Test done callbacks order" );
|
|
||||||
|
|
||||||
deferred = createDeferred();
|
|
||||||
|
|
||||||
deferred.resolveWith( jQuery , [ document ] ).done( function( doc ) {
|
|
||||||
ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" );
|
|
||||||
});
|
|
||||||
|
|
||||||
// #8421
|
|
||||||
deferred = createDeferred();
|
|
||||||
deferred.resolveWith().done(function() {
|
|
||||||
ok( true, "Test resolveWith can be called with no argument" );
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} );
|
|
||||||
|
|
||||||
jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
|
jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
|
||||||
|
|
||||||
function createDeferred( fn ) {
|
function createDeferred( fn ) {
|
||||||
|
|
Loading…
Reference in a new issue