Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
389 lines (323 sloc)
10.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| define( [ | |
| "./core", | |
| "./var/slice", | |
| "./callbacks" | |
| ], function( jQuery, slice ) { | |
| "use strict"; | |
| function Identity( v ) { | |
| return v; | |
| } | |
| function Thrower( ex ) { | |
| throw ex; | |
| } | |
| function adoptValue( value, resolve, reject ) { | |
| var method; | |
| try { | |
| // Check for promise aspect first to privilege synchronous behavior | |
| if ( value && jQuery.isFunction( ( method = value.promise ) ) ) { | |
| method.call( value ).done( resolve ).fail( reject ); | |
| // Other thenables | |
| } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) { | |
| method.call( value, resolve, reject ); | |
| // Other non-thenables | |
| } else { | |
| // Support: Android 4.0 only | |
| // Strict mode functions invoked without .call/.apply get global-object context | |
| resolve.call( undefined, value ); | |
| } | |
| // For Promises/A+, convert exceptions into rejections | |
| // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in | |
| // Deferred#then to conditionally suppress rejection. | |
| } catch ( value ) { | |
| // Support: Android 4.0 only | |
| // Strict mode functions invoked without .call/.apply get global-object context | |
| reject.call( undefined, value ); | |
| } | |
| } | |
| jQuery.extend( { | |
| Deferred: function( func ) { | |
| var tuples = [ | |
| // action, add listener, callbacks, | |
| // ... .then handlers, argument index, [final state] | |
| [ "notify", "progress", jQuery.Callbacks( "memory" ), | |
| jQuery.Callbacks( "memory" ), 2 ], | |
| [ "resolve", "done", jQuery.Callbacks( "once memory" ), | |
| jQuery.Callbacks( "once memory" ), 0, "resolved" ], | |
| [ "reject", "fail", jQuery.Callbacks( "once memory" ), | |
| jQuery.Callbacks( "once memory" ), 1, "rejected" ] | |
| ], | |
| state = "pending", | |
| promise = { | |
| state: function() { | |
| return state; | |
| }, | |
| always: function() { | |
| deferred.done( arguments ).fail( arguments ); | |
| return this; | |
| }, | |
| "catch": function( fn ) { | |
| return promise.then( null, fn ); | |
| }, | |
| // Keep pipe for back-compat | |
| pipe: function( /* fnDone, fnFail, fnProgress */ ) { | |
| var fns = arguments; | |
| return jQuery.Deferred( function( newDefer ) { | |
| jQuery.each( tuples, function( i, tuple ) { | |
| // Map tuples (progress, done, fail) to arguments (done, fail, progress) | |
| var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; | |
| // deferred.progress(function() { bind to newDefer or newDefer.notify }) | |
| // deferred.done(function() { bind to newDefer or newDefer.resolve }) | |
| // deferred.fail(function() { bind to newDefer or newDefer.reject }) | |
| deferred[ tuple[ 1 ] ]( function() { | |
| var returned = fn && fn.apply( this, arguments ); | |
| if ( returned && jQuery.isFunction( returned.promise ) ) { | |
| returned.promise() | |
| .progress( newDefer.notify ) | |
| .done( newDefer.resolve ) | |
| .fail( newDefer.reject ); | |
| } else { | |
| newDefer[ tuple[ 0 ] + "With" ]( | |
| this, | |
| fn ? [ returned ] : arguments | |
| ); | |
| } | |
| } ); | |
| } ); | |
| fns = null; | |
| } ).promise(); | |
| }, | |
| then: function( onFulfilled, onRejected, onProgress ) { | |
| var maxDepth = 0; | |
| function resolve( depth, deferred, handler, special ) { | |
| return function() { | |
| var that = this, | |
| args = arguments, | |
| mightThrow = function() { | |
| var returned, then; | |
| // Support: Promises/A+ section 2.3.3.3.3 | |
| // https://promisesaplus.com/#point-59 | |
| // Ignore double-resolution attempts | |
| if ( depth < maxDepth ) { | |
| return; | |
| } | |
| returned = handler.apply( that, args ); | |
| // Support: Promises/A+ section 2.3.1 | |
| // https://promisesaplus.com/#point-48 | |
| if ( returned === deferred.promise() ) { | |
| throw new TypeError( "Thenable self-resolution" ); | |
| } | |
| // Support: Promises/A+ sections 2.3.3.1, 3.5 | |
| // https://promisesaplus.com/#point-54 | |
| // https://promisesaplus.com/#point-75 | |
| // Retrieve `then` only once | |
| then = returned && | |
| // Support: Promises/A+ section 2.3.4 | |
| // https://promisesaplus.com/#point-64 | |
| // Only check objects and functions for thenability | |
| ( typeof returned === "object" || | |
| typeof returned === "function" ) && | |
| returned.then; | |
| // Handle a returned thenable | |
| if ( jQuery.isFunction( then ) ) { | |
| // Special processors (notify) just wait for resolution | |
| if ( special ) { | |
| then.call( | |
| returned, | |
| resolve( maxDepth, deferred, Identity, special ), | |
| resolve( maxDepth, deferred, Thrower, special ) | |
| ); | |
| // Normal processors (resolve) also hook into progress | |
| } else { | |
| // ...and disregard older resolution values | |
| maxDepth++; | |
| then.call( | |
| returned, | |
| resolve( maxDepth, deferred, Identity, special ), | |
| resolve( maxDepth, deferred, Thrower, special ), | |
| resolve( maxDepth, deferred, Identity, | |
| deferred.notifyWith ) | |
| ); | |
| } | |
| // Handle all other returned values | |
| } else { | |
| // Only substitute handlers pass on context | |
| // and multiple values (non-spec behavior) | |
| if ( handler !== Identity ) { | |
| that = undefined; | |
| args = [ returned ]; | |
| } | |
| // Process the value(s) | |
| // Default process is resolve | |
| ( special || deferred.resolveWith )( that, args ); | |
| } | |
| }, | |
| // Only normal processors (resolve) catch and reject exceptions | |
| process = special ? | |
| mightThrow : | |
| function() { | |
| try { | |
| mightThrow(); | |
| } catch ( e ) { | |
| if ( jQuery.Deferred.exceptionHook ) { | |
| jQuery.Deferred.exceptionHook( e, | |
| process.stackTrace ); | |
| } | |
| // Support: Promises/A+ section 2.3.3.3.4.1 | |
| // https://promisesaplus.com/#point-61 | |
| // Ignore post-resolution exceptions | |
| if ( depth + 1 >= maxDepth ) { | |
| // Only substitute handlers pass on context | |
| // and multiple values (non-spec behavior) | |
| if ( handler !== Thrower ) { | |
| that = undefined; | |
| args = [ e ]; | |
| } | |
| deferred.rejectWith( that, args ); | |
| } | |
| } | |
| }; | |
| // Support: Promises/A+ section 2.3.3.3.1 | |
| // https://promisesaplus.com/#point-57 | |
| // Re-resolve promises immediately to dodge false rejection from | |
| // subsequent errors | |
| if ( depth ) { | |
| process(); | |
| } else { | |
| // Call an optional hook to record the stack, in case of exception | |
| // since it's otherwise lost when execution goes async | |
| if ( jQuery.Deferred.getStackHook ) { | |
| process.stackTrace = jQuery.Deferred.getStackHook(); | |
| } | |
| window.setTimeout( process ); | |
| } | |
| }; | |
| } | |
| return jQuery.Deferred( function( newDefer ) { | |
| // progress_handlers.add( ... ) | |
| tuples[ 0 ][ 3 ].add( | |
| resolve( | |
| 0, | |
| newDefer, | |
| jQuery.isFunction( onProgress ) ? | |
| onProgress : | |
| Identity, | |
| newDefer.notifyWith | |
| ) | |
| ); | |
| // fulfilled_handlers.add( ... ) | |
| tuples[ 1 ][ 3 ].add( | |
| resolve( | |
| 0, | |
| newDefer, | |
| jQuery.isFunction( onFulfilled ) ? | |
| onFulfilled : | |
| Identity | |
| ) | |
| ); | |
| // rejected_handlers.add( ... ) | |
| tuples[ 2 ][ 3 ].add( | |
| resolve( | |
| 0, | |
| newDefer, | |
| jQuery.isFunction( onRejected ) ? | |
| onRejected : | |
| Thrower | |
| ) | |
| ); | |
| } ).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 ? jQuery.extend( obj, promise ) : promise; | |
| } | |
| }, | |
| deferred = {}; | |
| // Add list-specific methods | |
| jQuery.each( tuples, function( i, tuple ) { | |
| var list = tuple[ 2 ], | |
| stateString = tuple[ 5 ]; | |
| // promise.progress = list.add | |
| // promise.done = list.add | |
| // promise.fail = list.add | |
| promise[ tuple[ 1 ] ] = list.add; | |
| // Handle state | |
| if ( stateString ) { | |
| list.add( | |
| function() { | |
| // state = "resolved" (i.e., fulfilled) | |
| // state = "rejected" | |
| state = stateString; | |
| }, | |
| // rejected_callbacks.disable | |
| // fulfilled_callbacks.disable | |
| tuples[ 3 - i ][ 2 ].disable, | |
| // progress_callbacks.lock | |
| tuples[ 0 ][ 2 ].lock | |
| ); | |
| } | |
| // progress_handlers.fire | |
| // fulfilled_handlers.fire | |
| // rejected_handlers.fire | |
| list.add( tuple[ 3 ].fire ); | |
| // deferred.notify = function() { deferred.notifyWith(...) } | |
| // deferred.resolve = function() { deferred.resolveWith(...) } | |
| // deferred.reject = function() { deferred.rejectWith(...) } | |
| deferred[ tuple[ 0 ] ] = function() { | |
| deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); | |
| return this; | |
| }; | |
| // deferred.notifyWith = list.fireWith | |
| // deferred.resolveWith = list.fireWith | |
| // deferred.rejectWith = list.fireWith | |
| 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 | |
| when: function( singleValue ) { | |
| var | |
| // count of uncompleted subordinates | |
| remaining = arguments.length, | |
| // count of unprocessed arguments | |
| i = remaining, | |
| // subordinate fulfillment data | |
| resolveContexts = Array( i ), | |
| resolveValues = slice.call( arguments ), | |
| // the master Deferred | |
| master = jQuery.Deferred(), | |
| // subordinate callback factory | |
| updateFunc = function( i ) { | |
| return function( value ) { | |
| resolveContexts[ i ] = this; | |
| resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; | |
| if ( !( --remaining ) ) { | |
| master.resolveWith( resolveContexts, resolveValues ); | |
| } | |
| }; | |
| }; | |
| // Single- and empty arguments are adopted like Promise.resolve | |
| if ( remaining <= 1 ) { | |
| adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject ); | |
| // Use .then() to unwrap secondary thenables (cf. gh-3000) | |
| if ( master.state() === "pending" || | |
| jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { | |
| return master.then(); | |
| } | |
| } | |
| // Multiple arguments are aggregated like Promise.all array elements | |
| while ( i-- ) { | |
| adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); | |
| } | |
| return master.promise(); | |
| } | |
| } ); | |
| return jQuery; | |
| } ); |