scratch – Diff between revs 58 and 125
?pathlinks?
Rev 58 | Rev 125 | |||
---|---|---|---|---|
Line 1... | Line 1... | |||
1 | define( [ |
1 | define([ |
|
2 | "./core", |
2 | "./core", |
|
3 | "./var/slice", |
3 | "./var/slice", |
|
4 | "./callbacks" |
4 | "./callbacks" |
|
5 | ], function( jQuery, slice ) { |
5 | ], function( jQuery, slice ) { |
|
Line 6... | Line -... | |||
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 ) { |
- | ||
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 | // Support: Android 4.0 only |
- | ||
33 | // Strict mode functions invoked without .call/.apply get global-object context |
- | ||
34 | resolve.call( undefined, value ); |
- | ||
35 | } |
- | ||
36 | |
- | ||
37 | // For Promises/A+, convert exceptions into rejections |
- | ||
38 | // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in |
- | ||
39 | // Deferred#then to conditionally suppress rejection. |
- | ||
40 | } catch ( value ) { |
- | ||
41 | |
- | ||
42 | // Support: Android 4.0 only |
- | ||
43 | // Strict mode functions invoked without .call/.apply get global-object context |
- | ||
44 | reject.call( undefined, value ); |
- | ||
45 | } |
- | ||
46 | } |
- | ||
47 | |
6 | |
|
Line 48... | Line 7... | |||
48 | jQuery.extend( { |
7 | jQuery.extend({ |
|
49 | |
8 | |
|
50 | Deferred: function( func ) { |
- | ||
51 | var tuples = [ |
9 | Deferred: function( func ) { |
|
52 | |
- | ||
53 | // action, add listener, callbacks, |
- | ||
54 | // ... .then handlers, argument index, [final state] |
- | ||
55 | [ "notify", "progress", jQuery.Callbacks( "memory" ), |
10 | var tuples = [ |
|
56 | jQuery.Callbacks( "memory" ), 2 ], |
11 | // action, add listener, listener list, final state |
|
57 | [ "resolve", "done", jQuery.Callbacks( "once memory" ), |
12 | [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], |
|
58 | jQuery.Callbacks( "once memory" ), 0, "resolved" ], |
- | ||
59 | [ "reject", "fail", jQuery.Callbacks( "once memory" ), |
13 | [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], |
|
60 | jQuery.Callbacks( "once memory" ), 1, "rejected" ] |
14 | [ "notify", "progress", jQuery.Callbacks("memory") ] |
|
61 | ], |
15 | ], |
|
62 | state = "pending", |
16 | state = "pending", |
|
63 | promise = { |
17 | promise = { |
|
64 | state: function() { |
18 | state: function() { |
|
65 | return state; |
19 | return state; |
|
66 | }, |
20 | }, |
|
67 | always: function() { |
21 | always: function() { |
|
68 | deferred.done( arguments ).fail( arguments ); |
22 | deferred.done( arguments ).fail( arguments ); |
|
69 | return this; |
- | ||
70 | }, |
- | ||
71 | "catch": function( fn ) { |
- | ||
72 | return promise.then( null, fn ); |
- | ||
73 | }, |
- | ||
74 | |
23 | return this; |
|
75 | // Keep pipe for back-compat |
24 | }, |
|
76 | pipe: function( /* fnDone, fnFail, fnProgress */ ) { |
- | ||
77 | var fns = arguments; |
25 | then: function( /* fnDone, fnFail, fnProgress */ ) { |
|
78 | |
26 | var fns = arguments; |
|
79 | return jQuery.Deferred( function( newDefer ) { |
- | ||
80 | jQuery.each( tuples, function( i, tuple ) { |
- | ||
81 | |
27 | return jQuery.Deferred(function( newDefer ) { |
|
82 | // Map tuples (progress, done, fail) to arguments (done, fail, progress) |
- | ||
83 | var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; |
- | ||
84 | |
28 | jQuery.each( tuples, function( i, tuple ) { |
|
85 | // deferred.progress(function() { bind to newDefer or newDefer.notify }) |
- | ||
86 | // deferred.done(function() { bind to newDefer or newDefer.resolve }) |
29 | var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; |
|
87 | // deferred.fail(function() { bind to newDefer or newDefer.reject }) |
30 | // deferred[ done | fail | progress ] for forwarding actions to newDefer |
|
88 | deferred[ tuple[ 1 ] ]( function() { |
31 | deferred[ tuple[1] ](function() { |
|
89 | var returned = fn && fn.apply( this, arguments ); |
32 | var returned = fn && fn.apply( this, arguments ); |
|
90 | if ( returned && jQuery.isFunction( returned.promise ) ) { |
- | ||
91 | returned.promise() |
33 | if ( returned && jQuery.isFunction( returned.promise ) ) { |
|
92 | .progress( newDefer.notify ) |
34 | returned.promise() |
|
- | 35 | .done( newDefer.resolve ) |
||
93 | .done( newDefer.resolve ) |
36 | .fail( newDefer.reject ) |
|
94 | .fail( newDefer.reject ); |
37 | .progress( newDefer.notify ); |
|
95 | } else { |
- | ||
96 | newDefer[ tuple[ 0 ] + "With" ]( |
- | ||
97 | this, |
- | ||
98 | fn ? [ returned ] : arguments |
38 | } else { |
|
99 | ); |
39 | newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); |
|
100 | } |
40 | } |
|
101 | } ); |
41 | }); |
|
102 | } ); |
- | ||
103 | fns = null; |
- | ||
104 | } ).promise(); |
- | ||
105 | }, |
- | ||
106 | then: function( onFulfilled, onRejected, onProgress ) { |
- | ||
107 | var maxDepth = 0; |
- | ||
108 | function resolve( depth, deferred, handler, special ) { |
- | ||
109 | return function() { |
- | ||
110 | var that = this, |
- | ||
111 | args = arguments, |
- | ||
112 | mightThrow = function() { |
- | ||
113 | var returned, then; |
- | ||
114 | |
- | ||
115 | // Support: Promises/A+ section 2.3.3.3.3 |
- | ||
116 | // https://promisesaplus.com/#point-59 |
- | ||
117 | // Ignore double-resolution attempts |
- | ||
118 | if ( depth < maxDepth ) { |
- | ||
119 | return; |
- | ||
120 | } |
- | ||
121 | |
- | ||
122 | returned = handler.apply( that, args ); |
- | ||
123 | |
- | ||
124 | // Support: Promises/A+ section 2.3.1 |
- | ||
125 | // https://promisesaplus.com/#point-48 |
- | ||
126 | if ( returned === deferred.promise() ) { |
- | ||
127 | throw new TypeError( "Thenable self-resolution" ); |
- | ||
128 | } |
- | ||
129 | |
- | ||
130 | // Support: Promises/A+ sections 2.3.3.1, 3.5 |
- | ||
131 | // https://promisesaplus.com/#point-54 |
- | ||
132 | // https://promisesaplus.com/#point-75 |
- | ||
133 | // Retrieve `then` only once |
- | ||
134 | then = returned && |
- | ||
135 | |
- | ||
136 | // Support: Promises/A+ section 2.3.4 |
- | ||
137 | // https://promisesaplus.com/#point-64 |
- | ||
138 | // Only check objects and functions for thenability |
- | ||
139 | ( typeof returned === "object" || |
- | ||
140 | typeof returned === "function" ) && |
- | ||
141 | returned.then; |
- | ||
142 | |
- | ||
143 | // Handle a returned thenable |
- | ||
144 | if ( jQuery.isFunction( then ) ) { |
- | ||
145 | |
- | ||
146 | // Special processors (notify) just wait for resolution |
- | ||
147 | if ( special ) { |
- | ||
148 | then.call( |
- | ||
149 | returned, |
- | ||
150 | resolve( maxDepth, deferred, Identity, special ), |
- | ||
151 | resolve( maxDepth, deferred, Thrower, special ) |
- | ||
152 | ); |
- | ||
153 | |
- | ||
154 | // Normal processors (resolve) also hook into progress |
- | ||
155 | } else { |
- | ||
156 | |
- | ||
157 | // ...and disregard older resolution values |
- | ||
158 | maxDepth++; |
- | ||
159 | |
- | ||
160 | then.call( |
- | ||
161 | returned, |
- | ||
162 | resolve( maxDepth, deferred, Identity, special ), |
- | ||
163 | resolve( maxDepth, deferred, Thrower, special ), |
- | ||
164 | resolve( maxDepth, deferred, Identity, |
- | ||
165 | deferred.notifyWith ) |
- | ||
166 | ); |
- | ||
167 | } |
- | ||
168 | |
- | ||
169 | // Handle all other returned values |
- | ||
170 | } else { |
- | ||
171 | |
- | ||
172 | // Only substitute handlers pass on context |
- | ||
173 | // and multiple values (non-spec behavior) |
- | ||
174 | if ( handler !== Identity ) { |
- | ||
175 | that = undefined; |
- | ||
176 | args = [ returned ]; |
- | ||
177 | } |
- | ||
178 | |
- | ||
179 | // Process the value(s) |
- | ||
180 | // Default process is resolve |
- | ||
181 | ( special || deferred.resolveWith )( that, args ); |
- | ||
182 | } |
- | ||
183 | }, |
- | ||
184 | |
- | ||
185 | // Only normal processors (resolve) catch and reject exceptions |
- | ||
186 | process = special ? |
- | ||
187 | mightThrow : |
- | ||
188 | function() { |
- | ||
189 | try { |
- | ||
190 | mightThrow(); |
- | ||
191 | } catch ( e ) { |
- | ||
192 | |
- | ||
193 | if ( jQuery.Deferred.exceptionHook ) { |
- | ||
194 | jQuery.Deferred.exceptionHook( e, |
- | ||
195 | process.stackTrace ); |
- | ||
196 | } |
- | ||
197 | |
- | ||
198 | // Support: Promises/A+ section 2.3.3.3.4.1 |
- | ||
199 | // https://promisesaplus.com/#point-61 |
- | ||
200 | // Ignore post-resolution exceptions |
- | ||
201 | if ( depth + 1 >= maxDepth ) { |
- | ||
202 | |
- | ||
203 | // Only substitute handlers pass on context |
- | ||
204 | // and multiple values (non-spec behavior) |
- | ||
205 | if ( handler !== Thrower ) { |
- | ||
206 | that = undefined; |
- | ||
207 | args = [ e ]; |
- | ||
208 | } |
- | ||
209 | |
- | ||
210 | deferred.rejectWith( that, args ); |
- | ||
211 | } |
- | ||
212 | } |
- | ||
213 | }; |
- | ||
214 | |
- | ||
215 | // Support: Promises/A+ section 2.3.3.3.1 |
- | ||
216 | // https://promisesaplus.com/#point-57 |
- | ||
217 | // Re-resolve promises immediately to dodge false rejection from |
- | ||
218 | // subsequent errors |
- | ||
219 | if ( depth ) { |
- | ||
220 | process(); |
- | ||
221 | } else { |
- | ||
222 | |
- | ||
223 | // Call an optional hook to record the stack, in case of exception |
- | ||
224 | // since it's otherwise lost when execution goes async |
- | ||
225 | if ( jQuery.Deferred.getStackHook ) { |
- | ||
226 | process.stackTrace = jQuery.Deferred.getStackHook(); |
- | ||
227 | } |
- | ||
228 | window.setTimeout( process ); |
- | ||
229 | } |
- | ||
230 | }; |
- | ||
231 | } |
- | ||
232 | |
- | ||
233 | return jQuery.Deferred( function( newDefer ) { |
- | ||
234 | |
- | ||
235 | // progress_handlers.add( ... ) |
- | ||
236 | tuples[ 0 ][ 3 ].add( |
- | ||
237 | resolve( |
- | ||
238 | 0, |
- | ||
239 | newDefer, |
- | ||
240 | jQuery.isFunction( onProgress ) ? |
- | ||
241 | onProgress : |
- | ||
242 | Identity, |
- | ||
243 | newDefer.notifyWith |
- | ||
244 | ) |
- | ||
245 | ); |
- | ||
246 | |
- | ||
247 | // fulfilled_handlers.add( ... ) |
- | ||
248 | tuples[ 1 ][ 3 ].add( |
- | ||
249 | resolve( |
- | ||
250 | 0, |
- | ||
251 | newDefer, |
- | ||
252 | jQuery.isFunction( onFulfilled ) ? |
- | ||
253 | onFulfilled : |
- | ||
254 | Identity |
- | ||
255 | ) |
- | ||
256 | ); |
- | ||
257 | |
- | ||
258 | // rejected_handlers.add( ... ) |
- | ||
259 | tuples[ 2 ][ 3 ].add( |
- | ||
260 | resolve( |
- | ||
261 | 0, |
- | ||
262 | newDefer, |
- | ||
263 | jQuery.isFunction( onRejected ) ? |
- | ||
264 | onRejected : |
- | ||
265 | Thrower |
- | ||
266 | ) |
42 | }); |
|
267 | ); |
43 | fns = null; |
|
268 | } ).promise(); |
- | ||
269 | }, |
44 | }).promise(); |
|
270 | |
45 | }, |
|
271 | // Get a promise for this deferred |
46 | // Get a promise for this deferred |
|
272 | // If obj is provided, the promise aspect is added to the object |
47 | // If obj is provided, the promise aspect is added to the object |
|
273 | promise: function( obj ) { |
48 | promise: function( obj ) { |
|
274 | return obj != null ? jQuery.extend( obj, promise ) : promise; |
49 | return obj != null ? jQuery.extend( obj, promise ) : promise; |
|
275 | } |
50 | } |
|
Line -... | Line 51... | |||
- | 51 | }, |
||
- | 52 | deferred = {}; |
||
- | 53 | |
||
276 | }, |
54 | // Keep pipe for back-compat |
|
277 | deferred = {}; |
55 | promise.pipe = promise.then; |
|
278 | |
56 | |
|
279 | // Add list-specific methods |
57 | // Add list-specific methods |
|
Line 280... | Line 58... | |||
280 | jQuery.each( tuples, function( i, tuple ) { |
58 | jQuery.each( tuples, function( i, tuple ) { |
|
281 | var list = tuple[ 2 ], |
- | ||
282 | stateString = tuple[ 5 ]; |
- | ||
283 | |
59 | var list = tuple[ 2 ], |
|
Line 284... | Line 60... | |||
284 | // promise.progress = list.add |
60 | stateString = tuple[ 3 ]; |
|
285 | // promise.done = list.add |
61 | |
|
286 | // promise.fail = list.add |
62 | // promise[ done | fail | progress ] = list.add |
|
- | 63 | promise[ tuple[1] ] = list.add; |
||
287 | promise[ tuple[ 1 ] ] = list.add; |
64 | |
|
Line 288... | Line 65... | |||
288 | |
65 | // Handle state |
|
289 | // Handle state |
- | ||
290 | if ( stateString ) { |
- | ||
291 | list.add( |
- | ||
292 | function() { |
- | ||
293 | |
- | ||
294 | // state = "resolved" (i.e., fulfilled) |
- | ||
295 | // state = "rejected" |
66 | if ( stateString ) { |
|
296 | state = stateString; |
- | ||
297 | }, |
- | ||
298 | |
- | ||
299 | // rejected_callbacks.disable |
- | ||
300 | // fulfilled_callbacks.disable |
67 | list.add(function() { |
|
Line 301... | Line -... | |||
301 | tuples[ 3 - i ][ 2 ].disable, |
- | ||
302 | |
- | ||
303 | // progress_callbacks.lock |
- | ||
304 | tuples[ 0 ][ 2 ].lock |
- | ||
305 | ); |
- | ||
306 | } |
68 | // state = [ resolved | rejected ] |
|
307 | |
- | ||
308 | // progress_handlers.fire |
- | ||
309 | // fulfilled_handlers.fire |
69 | state = stateString; |
|
310 | // rejected_handlers.fire |
70 | |
|
311 | list.add( tuple[ 3 ].fire ); |
71 | // [ reject_list | resolve_list ].disable; progress_list.lock |
|
312 | |
72 | }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); |
|
313 | // deferred.notify = function() { deferred.notifyWith(...) } |
- | ||
314 | // deferred.resolve = function() { deferred.resolveWith(...) } |
- | ||
315 | // deferred.reject = function() { deferred.rejectWith(...) } |
- | ||
316 | deferred[ tuple[ 0 ] ] = function() { |
- | ||
317 | deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); |
73 | } |
|
318 | return this; |
74 | |
|
Line 319... | Line 75... | |||
319 | }; |
75 | // deferred[ resolve | reject | notify ] |
|
320 | |
76 | deferred[ tuple[0] ] = function() { |
|
Line 321... | Line 77... | |||
321 | // deferred.notifyWith = list.fireWith |
77 | deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); |
|
Line 335... | Line 91... | |||
335 | // All done! |
91 | // All done! |
|
336 | return deferred; |
92 | return deferred; |
|
337 | }, |
93 | }, |
|
Line 338... | Line 94... | |||
338 | |
94 | |
|
339 | // Deferred helper |
95 | // Deferred helper |
|
340 | when: function( singleValue ) { |
96 | when: function( subordinate /* , ..., subordinateN */ ) { |
|
341 | var |
- | ||
342 | |
- | ||
343 | // count of uncompleted subordinates |
- | ||
344 | remaining = arguments.length, |
- | ||
345 | |
- | ||
346 | // count of unprocessed arguments |
- | ||
347 | i = remaining, |
- | ||
348 | |
- | ||
349 | // subordinate fulfillment data |
- | ||
350 | resolveContexts = Array( i ), |
97 | var i = 0, |
|
- | 98 | resolveValues = slice.call( arguments ), |
||
- | 99 | length = resolveValues.length, |
||
- | 100 | |
||
- | 101 | // the count of uncompleted subordinates |
||
Line 351... | Line 102... | |||
351 | resolveValues = slice.call( arguments ), |
102 | remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, |
|
352 | |
103 | |
|
Line 353... | Line 104... | |||
353 | // the master Deferred |
104 | // the master Deferred. If resolveValues consist of only a single Deferred, just use that. |
|
354 | master = jQuery.Deferred(), |
105 | deferred = remaining === 1 ? subordinate : jQuery.Deferred(), |
|
355 | |
106 | |
|
356 | // subordinate callback factory |
107 | // Update function for both resolve and progress values |
|
357 | updateFunc = function( i ) { |
108 | updateFunc = function( i, contexts, values ) { |
|
- | 109 | return function( value ) { |
||
- | 110 | contexts[ i ] = this; |
||
358 | return function( value ) { |
111 | values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; |
|
359 | resolveContexts[ i ] = this; |
112 | if ( values === progressValues ) { |
|
360 | resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; |
113 | deferred.notifyWith( contexts, values ); |
|
361 | if ( !( --remaining ) ) { |
114 | } else if ( !( --remaining ) ) { |
|
362 | master.resolveWith( resolveContexts, resolveValues ); |
115 | deferred.resolveWith( contexts, values ); |
|
Line 363... | Line -... | |||
363 | } |
- | ||
364 | }; |
- | ||
365 | }; |
116 | } |
|
366 | |
- | ||
367 | // Single- and empty arguments are adopted like Promise.resolve |
- | ||
368 | if ( remaining <= 1 ) { |
- | ||
369 | adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject ); |
- | ||
Line -... | Line 117... | |||
- | 117 | }; |
||
- | 118 | }, |
||
- | 119 | |
||
- | 120 | progressValues, progressContexts, resolveContexts; |
||
- | 121 | |
||
- | 122 | // Add listeners to Deferred subordinates; treat others as resolved |
||
- | 123 | if ( length > 1 ) { |
||
- | 124 | progressValues = new Array( length ); |
||
- | 125 | progressContexts = new Array( length ); |
||
- | 126 | resolveContexts = new Array( length ); |
||
- | 127 | for ( ; i < length; i++ ) { |
||
- | 128 | if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { |
||
370 | |
129 | resolveValues[ i ].promise() |
|
- | 130 | .done( updateFunc( i, resolveContexts, resolveValues ) ) |
||
371 | // Use .then() to unwrap secondary thenables (cf. gh-3000) |
131 | .fail( deferred.reject ) |
|
372 | if ( master.state() === "pending" || |
132 | .progress( updateFunc( i, progressContexts, progressValues ) ); |
|
Line 373... | Line 133... | |||
373 | jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { |
133 | } else { |
|
374 | |
134 | --remaining; |
|
375 | return master.then(); |
135 | } |
|
376 | } |
136 | } |
|
Line 377... | Line 137... | |||
377 | } |
137 | } |
|
378 | |
138 | |
|
379 | // Multiple arguments are aggregated like Promise.all array elements |
139 | // If we're not waiting on anything, resolve the master |
|
Line 380... | Line 140... | |||
380 | while ( i-- ) { |
140 | if ( !remaining ) { |
|
381 | adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); |
141 | deferred.resolveWith( resolveContexts, resolveValues ); |