Adjust tabIndex propHook for modern browsers and return -1 where appropriate. Close...
[jquery.git] / src / deferred.js
blob0efc05dc3a00786990e8eb717441a84791785e9f
1 jQuery.extend({
3         Deferred: function( func ) {
4                 var tuples = [
5                                 // action, add listener, listener list, final state
6                                 [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
7                                 [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
8                                 [ "notify", "progress", jQuery.Callbacks("memory") ]
9                         ],
10                         state = "pending",
11                         promise = {
12                                 state: function() {
13                                         return state;
14                                 },
15                                 always: function() {
16                                         deferred.done( arguments ).fail( arguments );
17                                         return this;
18                                 },
19                                 then: function( /* fnDone, fnFail, fnProgress */ ) {
20                                         var fns = arguments;
21                                         return jQuery.Deferred(function( newDefer ) {
22                                                 jQuery.each( tuples, function( i, tuple ) {
23                                                         var action = tuple[ 0 ],
24                                                                 fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
25                                                         // deferred[ done | fail | progress ] for forwarding actions to newDefer
26                                                         deferred[ tuple[1] ](function() {
27                                                                 var returned = fn && fn.apply( this, arguments );
28                                                                 if ( returned && jQuery.isFunction( returned.promise ) ) {
29                                                                         returned.promise()
30                                                                                 .done( newDefer.resolve )
31                                                                                 .fail( newDefer.reject )
32                                                                                 .progress( newDefer.notify );
33                                                                 } else {
34                                                                         newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
35                                                                 }
36                                                         });
37                                                 });
38                                                 fns = null;
39                                         }).promise();
40                                 },
41                                 // Get a promise for this deferred
42                                 // If obj is provided, the promise aspect is added to the object
43                                 promise: function( obj ) {
44                                         return obj != null ? jQuery.extend( obj, promise ) : promise;
45                                 }
46                         },
47                         deferred = {};
49                 // Keep pipe for back-compat
50                 promise.pipe = promise.then;
52                 // Add list-specific methods
53                 jQuery.each( tuples, function( i, tuple ) {
54                         var list = tuple[ 2 ],
55                                 stateString = tuple[ 3 ];
57                         // promise[ done | fail | progress ] = list.add
58                         promise[ tuple[1] ] = list.add;
60                         // Handle state
61                         if ( stateString ) {
62                                 list.add(function() {
63                                         // state = [ resolved | rejected ]
64                                         state = stateString;
66                                 // [ reject_list | resolve_list ].disable; progress_list.lock
67                                 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
68                         }
70                         // deferred[ resolve | reject | notify ]
71                         deferred[ tuple[0] ] = function() {
72                                 deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
73                                 return this;
74                         };
75                         deferred[ tuple[0] + "With" ] = list.fireWith;
76                 });
78                 // Make the deferred a promise
79                 promise.promise( deferred );
81                 // Call given func if any
82                 if ( func ) {
83                         func.call( deferred, deferred );
84                 }
86                 // All done!
87                 return deferred;
88         },
90         // Deferred helper
91         when: function( subordinate /* , ..., subordinateN */ ) {
92                 var i = 0,
93                         resolveValues = core_slice.call( arguments ),
94                         length = resolveValues.length,
96                         // the count of uncompleted subordinates
97                         remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
99                         // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
100                         deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
102                         // Update function for both resolve and progress values
103                         updateFunc = function( i, contexts, values ) {
104                                 return function( value ) {
105                                         contexts[ i ] = this;
106                                         values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
107                                         if( values === progressValues ) {
108                                                 deferred.notifyWith( contexts, values );
109                                         } else if ( !( --remaining ) ) {
110                                                 deferred.resolveWith( contexts, values );
111                                         }
112                                 };
113                         },
115                         progressValues, progressContexts, resolveContexts;
117                 // add listeners to Deferred subordinates; treat others as resolved
118                 if ( length > 1 ) {
119                         progressValues = new Array( length );
120                         progressContexts = new Array( length );
121                         resolveContexts = new Array( length );
122                         for ( ; i < length; i++ ) {
123                                 if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
124                                         resolveValues[ i ].promise()
125                                                 .done( updateFunc( i, resolveContexts, resolveValues ) )
126                                                 .fail( deferred.reject )
127                                                 .progress( updateFunc( i, progressContexts, progressValues ) );
128                                 } else {
129                                         --remaining;
130                                 }
131                         }
132                 }
134                 // if we're not waiting on anything, resolve the master
135                 if ( !remaining ) {
136                         deferred.resolveWith( resolveContexts, resolveValues );
137                 }
139                 return deferred.promise();
140         }