Project

General

Profile

1
define( [
2
	"./core",
3
	"./var/slice",
4
	"./callbacks"
5
], function( jQuery, slice ) {
6

    
7
"use strict";
8

    
9
function Identity( v ) {
10
	return v;
11
}
12
function Thrower( ex ) {
13
	throw ex;
14
}
15

    
16
function adoptValue( value, resolve, reject, noValue ) {
17
	var method;
18

    
19
	try {
20

    
21
		// Check for promise aspect first to privilege synchronous behavior
22
		if ( value && jQuery.isFunction( ( method = value.promise ) ) ) {
23
			method.call( value ).done( resolve ).fail( reject );
24

    
25
		// Other thenables
26
		} else if ( value && jQuery.isFunction( ( method = value.then ) ) ) {
27
			method.call( value, resolve, reject );
28

    
29
		// Other non-thenables
30
		} else {
31

    
32
			// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
33
			// * false: [ value ].slice( 0 ) => resolve( value )
34
			// * true: [ value ].slice( 1 ) => resolve()
35
			resolve.apply( undefined, [ value ].slice( noValue ) );
36
		}
37

    
38
	// For Promises/A+, convert exceptions into rejections
39
	// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
40
	// Deferred#then to conditionally suppress rejection.
41
	} catch ( value ) {
42

    
43
		// Support: Android 4.0 only
44
		// Strict mode functions invoked without .call/.apply get global-object context
45
		reject.apply( undefined, [ value ] );
46
	}
47
}
48

    
49
jQuery.extend( {
50

    
51
	Deferred: function( func ) {
52
		var tuples = [
53

    
54
				// action, add listener, callbacks,
55
				// ... .then handlers, argument index, [final state]
56
				[ "notify", "progress", jQuery.Callbacks( "memory" ),
57
					jQuery.Callbacks( "memory" ), 2 ],
58
				[ "resolve", "done", jQuery.Callbacks( "once memory" ),
59
					jQuery.Callbacks( "once memory" ), 0, "resolved" ],
60
				[ "reject", "fail", jQuery.Callbacks( "once memory" ),
61
					jQuery.Callbacks( "once memory" ), 1, "rejected" ]
62
			],
63
			state = "pending",
64
			promise = {
65
				state: function() {
66
					return state;
67
				},
68
				always: function() {
69
					deferred.done( arguments ).fail( arguments );
70
					return this;
71
				},
72
				"catch": function( fn ) {
73
					return promise.then( null, fn );
74
				},
75

    
76
				// Keep pipe for back-compat
77
				pipe: function( /* fnDone, fnFail, fnProgress */ ) {
78
					var fns = arguments;
79

    
80
					return jQuery.Deferred( function( newDefer ) {
81
						jQuery.each( tuples, function( i, tuple ) {
82

    
83
							// Map tuples (progress, done, fail) to arguments (done, fail, progress)
84
							var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
85

    
86
							// deferred.progress(function() { bind to newDefer or newDefer.notify })
87
							// deferred.done(function() { bind to newDefer or newDefer.resolve })
88
							// deferred.fail(function() { bind to newDefer or newDefer.reject })
89
							deferred[ tuple[ 1 ] ]( function() {
90
								var returned = fn && fn.apply( this, arguments );
91
								if ( returned && jQuery.isFunction( returned.promise ) ) {
92
									returned.promise()
93
										.progress( newDefer.notify )
94
										.done( newDefer.resolve )
95
										.fail( newDefer.reject );
96
								} else {
97
									newDefer[ tuple[ 0 ] + "With" ](
98
										this,
99
										fn ? [ returned ] : arguments
100
									);
101
								}
102
							} );
103
						} );
104
						fns = null;
105
					} ).promise();
106
				},
107
				then: function( onFulfilled, onRejected, onProgress ) {
108
					var maxDepth = 0;
109
					function resolve( depth, deferred, handler, special ) {
110
						return function() {
111
							var that = this,
112
								args = arguments,
113
								mightThrow = function() {
114
									var returned, then;
115

    
116
									// Support: Promises/A+ section 2.3.3.3.3
117
									// https://promisesaplus.com/#point-59
118
									// Ignore double-resolution attempts
119
									if ( depth < maxDepth ) {
120
										return;
121
									}
122

    
123
									returned = handler.apply( that, args );
124

    
125
									// Support: Promises/A+ section 2.3.1
126
									// https://promisesaplus.com/#point-48
127
									if ( returned === deferred.promise() ) {
128
										throw new TypeError( "Thenable self-resolution" );
129
									}
130

    
131
									// Support: Promises/A+ sections 2.3.3.1, 3.5
132
									// https://promisesaplus.com/#point-54
133
									// https://promisesaplus.com/#point-75
134
									// Retrieve `then` only once
135
									then = returned &&
136

    
137
										// Support: Promises/A+ section 2.3.4
138
										// https://promisesaplus.com/#point-64
139
										// Only check objects and functions for thenability
140
										( typeof returned === "object" ||
141
											typeof returned === "function" ) &&
142
										returned.then;
143

    
144
									// Handle a returned thenable
145
									if ( jQuery.isFunction( then ) ) {
146

    
147
										// Special processors (notify) just wait for resolution
148
										if ( special ) {
149
											then.call(
150
												returned,
151
												resolve( maxDepth, deferred, Identity, special ),
152
												resolve( maxDepth, deferred, Thrower, special )
153
											);
154

    
155
										// Normal processors (resolve) also hook into progress
156
										} else {
157

    
158
											// ...and disregard older resolution values
159
											maxDepth++;
160

    
161
											then.call(
162
												returned,
163
												resolve( maxDepth, deferred, Identity, special ),
164
												resolve( maxDepth, deferred, Thrower, special ),
165
												resolve( maxDepth, deferred, Identity,
166
													deferred.notifyWith )
167
											);
168
										}
169

    
170
									// Handle all other returned values
171
									} else {
172

    
173
										// Only substitute handlers pass on context
174
										// and multiple values (non-spec behavior)
175
										if ( handler !== Identity ) {
176
											that = undefined;
177
											args = [ returned ];
178
										}
179

    
180
										// Process the value(s)
181
										// Default process is resolve
182
										( special || deferred.resolveWith )( that, args );
183
									}
184
								},
185

    
186
								// Only normal processors (resolve) catch and reject exceptions
187
								process = special ?
188
									mightThrow :
189
									function() {
190
										try {
191
											mightThrow();
192
										} catch ( e ) {
193

    
194
											if ( jQuery.Deferred.exceptionHook ) {
195
												jQuery.Deferred.exceptionHook( e,
196
													process.stackTrace );
197
											}
198

    
199
											// Support: Promises/A+ section 2.3.3.3.4.1
200
											// https://promisesaplus.com/#point-61
201
											// Ignore post-resolution exceptions
202
											if ( depth + 1 >= maxDepth ) {
203

    
204
												// Only substitute handlers pass on context
205
												// and multiple values (non-spec behavior)
206
												if ( handler !== Thrower ) {
207
													that = undefined;
208
													args = [ e ];
209
												}
210

    
211
												deferred.rejectWith( that, args );
212
											}
213
										}
214
									};
215

    
216
							// Support: Promises/A+ section 2.3.3.3.1
217
							// https://promisesaplus.com/#point-57
218
							// Re-resolve promises immediately to dodge false rejection from
219
							// subsequent errors
220
							if ( depth ) {
221
								process();
222
							} else {
223

    
224
								// Call an optional hook to record the stack, in case of exception
225
								// since it's otherwise lost when execution goes async
226
								if ( jQuery.Deferred.getStackHook ) {
227
									process.stackTrace = jQuery.Deferred.getStackHook();
228
								}
229
								window.setTimeout( process );
230
							}
231
						};
232
					}
233

    
234
					return jQuery.Deferred( function( newDefer ) {
235

    
236
						// progress_handlers.add( ... )
237
						tuples[ 0 ][ 3 ].add(
238
							resolve(
239
								0,
240
								newDefer,
241
								jQuery.isFunction( onProgress ) ?
242
									onProgress :
243
									Identity,
244
								newDefer.notifyWith
245
							)
246
						);
247

    
248
						// fulfilled_handlers.add( ... )
249
						tuples[ 1 ][ 3 ].add(
250
							resolve(
251
								0,
252
								newDefer,
253
								jQuery.isFunction( onFulfilled ) ?
254
									onFulfilled :
255
									Identity
256
							)
257
						);
258

    
259
						// rejected_handlers.add( ... )
260
						tuples[ 2 ][ 3 ].add(
261
							resolve(
262
								0,
263
								newDefer,
264
								jQuery.isFunction( onRejected ) ?
265
									onRejected :
266
									Thrower
267
							)
268
						);
269
					} ).promise();
270
				},
271

    
272
				// Get a promise for this deferred
273
				// If obj is provided, the promise aspect is added to the object
274
				promise: function( obj ) {
275
					return obj != null ? jQuery.extend( obj, promise ) : promise;
276
				}
277
			},
278
			deferred = {};
279

    
280
		// Add list-specific methods
281
		jQuery.each( tuples, function( i, tuple ) {
282
			var list = tuple[ 2 ],
283
				stateString = tuple[ 5 ];
284

    
285
			// promise.progress = list.add
286
			// promise.done = list.add
287
			// promise.fail = list.add
288
			promise[ tuple[ 1 ] ] = list.add;
289

    
290
			// Handle state
291
			if ( stateString ) {
292
				list.add(
293
					function() {
294

    
295
						// state = "resolved" (i.e., fulfilled)
296
						// state = "rejected"
297
						state = stateString;
298
					},
299

    
300
					// rejected_callbacks.disable
301
					// fulfilled_callbacks.disable
302
					tuples[ 3 - i ][ 2 ].disable,
303

    
304
					// progress_callbacks.lock
305
					tuples[ 0 ][ 2 ].lock
306
				);
307
			}
308

    
309
			// progress_handlers.fire
310
			// fulfilled_handlers.fire
311
			// rejected_handlers.fire
312
			list.add( tuple[ 3 ].fire );
313

    
314
			// deferred.notify = function() { deferred.notifyWith(...) }
315
			// deferred.resolve = function() { deferred.resolveWith(...) }
316
			// deferred.reject = function() { deferred.rejectWith(...) }
317
			deferred[ tuple[ 0 ] ] = function() {
318
				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
319
				return this;
320
			};
321

    
322
			// deferred.notifyWith = list.fireWith
323
			// deferred.resolveWith = list.fireWith
324
			// deferred.rejectWith = list.fireWith
325
			deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
326
		} );
327

    
328
		// Make the deferred a promise
329
		promise.promise( deferred );
330

    
331
		// Call given func if any
332
		if ( func ) {
333
			func.call( deferred, deferred );
334
		}
335

    
336
		// All done!
337
		return deferred;
338
	},
339

    
340
	// Deferred helper
341
	when: function( singleValue ) {
342
		var
343

    
344
			// count of uncompleted subordinates
345
			remaining = arguments.length,
346

    
347
			// count of unprocessed arguments
348
			i = remaining,
349

    
350
			// subordinate fulfillment data
351
			resolveContexts = Array( i ),
352
			resolveValues = slice.call( arguments ),
353

    
354
			// the master Deferred
355
			master = jQuery.Deferred(),
356

    
357
			// subordinate callback factory
358
			updateFunc = function( i ) {
359
				return function( value ) {
360
					resolveContexts[ i ] = this;
361
					resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
362
					if ( !( --remaining ) ) {
363
						master.resolveWith( resolveContexts, resolveValues );
364
					}
365
				};
366
			};
367

    
368
		// Single- and empty arguments are adopted like Promise.resolve
369
		if ( remaining <= 1 ) {
370
			adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
371
				!remaining );
372

    
373
			// Use .then() to unwrap secondary thenables (cf. gh-3000)
374
			if ( master.state() === "pending" ||
375
				jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
376

    
377
				return master.then();
378
			}
379
		}
380

    
381
		// Multiple arguments are aggregated like Promise.all array elements
382
		while ( i-- ) {
383
			adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
384
		}
385

    
386
		return master.promise();
387
	}
388
} );
389

    
390
return jQuery;
391
} );
(8-8/22)