| define( [ |
| "./core", |
| "./var/isFunction", |
| "./var/slice", |
| "./callbacks" |
| ], function( jQuery, isFunction, slice ) { |
| |
| "use strict"; |
| |
| function Identity( v ) { |
| return v; |
| } |
| function Thrower( ex ) { |
| throw ex; |
| } |
| |
| function adoptValue( value, resolve, reject, noValue ) { |
| var method; |
| |
| try { |
| |
| // Check for promise aspect first to privilege synchronous behavior |
| if ( value && isFunction( ( method = value.promise ) ) ) { |
| method.call( value ).done( resolve ).fail( reject ); |
| |
| // Other thenables |
| } else if ( value && isFunction( ( method = value.then ) ) ) { |
| method.call( value, resolve, reject ); |
| |
| // Other non-thenables |
| } else { |
| |
| // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: |
| // * false: [ value ].slice( 0 ) => resolve( value ) |
| // * true: [ value ].slice( 1 ) => resolve() |
| resolve.apply( undefined, [ value ].slice( noValue ) ); |
| } |
| |
| // 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.apply( 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 = 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 && 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 ( 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, |
| isFunction( onProgress ) ? |
| onProgress : |
| Identity, |
| newDefer.notifyWith |
| ) |
| ); |
| |
| // fulfilled_handlers.add( ... ) |
| tuples[ 1 ][ 3 ].add( |
| resolve( |
| 0, |
| newDefer, |
| isFunction( onFulfilled ) ? |
| onFulfilled : |
| Identity |
| ) |
| ); |
| |
| // rejected_handlers.add( ... ) |
| tuples[ 2 ][ 3 ].add( |
| resolve( |
| 0, |
| newDefer, |
| 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, |
| |
| // rejected_handlers.disable |
| // fulfilled_handlers.disable |
| tuples[ 3 - i ][ 3 ].disable, |
| |
| // progress_callbacks.lock |
| tuples[ 0 ][ 2 ].lock, |
| |
| // progress_handlers.lock |
| tuples[ 0 ][ 3 ].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, |
| !remaining ); |
| |
| // Use .then() to unwrap secondary thenables (cf. gh-3000) |
| if ( master.state() === "pending" || |
| 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; |
| } ); |