Remove .hyphen property from tests (left behind in refactoring)
[jquery.git] / src / deferred.js
blobe525fc06871ce053990853c7f69070e53cf22206
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 = fns[ i ];
25                                                         // deferred[ done | fail | progress ] for forwarding actions to newDefer
26                                                         deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
27                                                                 function() {
28                                                                         var returned = fn.apply( this, arguments );
29                                                                         if ( returned && jQuery.isFunction( returned.promise ) ) {
30                                                                                 returned.promise()
31                                                                                         .done( newDefer.resolve )
32                                                                                         .fail( newDefer.reject )
33                                                                                         .progress( newDefer.notify );
34                                                                         } else {
35                                                                                 newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
36                                                                         }
37                                                                 } :
38                                                                 newDefer[ action ]
39                                                         );
40                                                 });
41                                                 fns = null;
42                                         }).promise();
43                                 },
44                                 // Get a promise for this deferred
45                                 // If obj is provided, the promise aspect is added to the object
46                                 promise: function( obj ) {
47                                         return obj != null ? jQuery.extend( obj, promise ) : promise;
48                                 }
49                         },
50                         deferred = {};
52                 // Keep pipe for back-compat
53                 promise.pipe = promise.then;
55                 // Add list-specific methods
56                 jQuery.each( tuples, function( i, tuple ) {
57                         var list = tuple[ 2 ],
58                                 stateString = tuple[ 3 ];
60                         // promise[ done | fail | progress ] = list.add
61                         promise[ tuple[1] ] = list.add;
63                         // Handle state
64                         if ( stateString ) {
65                                 list.add(function() {
66                                         // state = [ resolved | rejected ]
67                                         state = stateString;
69                                 // [ reject_list | resolve_list ].disable; progress_list.lock
70                                 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
71                         }
73                         // deferred[ resolve | reject | notify ] = list.fire
74                         deferred[ tuple[0] ] = list.fire;
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         }