Fix #11543: .has should work on detached elements.
[jquery.git] / src / deferred.js
blob0cb9e06a55eafecc2be2cc2b4b8458e5d66c32a7
1 (function( jQuery ) {
3 var // Static reference to slice
4         sliceDeferred = [].slice;
6 jQuery.extend({
8         Deferred: function( func ) {
9                 var doneList = jQuery.Callbacks( "once memory" ),
10                         failList = jQuery.Callbacks( "once memory" ),
11                         progressList = jQuery.Callbacks( "memory" ),
12                         state = "pending",
13                         lists = {
14                                 resolve: doneList,
15                                 reject: failList,
16                                 notify: progressList
17                         },
18                         promise = {
19                                 done: doneList.add,
20                                 fail: failList.add,
21                                 progress: progressList.add,
23                                 state: function() {
24                                         return state;
25                                 },
27                                 // Deprecated
28                                 isResolved: doneList.fired,
29                                 isRejected: failList.fired,
31                                 always: function() {
32                                         deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
33                                         return this;
34                                 },
35                                 then: function( fnDone, fnFail, fnProgress ) {
36                                         return jQuery.Deferred(function( newDefer ) {
37                                                 jQuery.each( {
38                                                         done: [ fnDone, "resolve" ],
39                                                         fail: [ fnFail, "reject" ],
40                                                         progress: [ fnProgress, "notify" ]
41                                                 }, function( handler, data ) {
42                                                         var fn = data[ 0 ],
43                                                                 action = data[ 1 ],
44                                                                 returned;
45                                                         if ( jQuery.isFunction( fn ) ) {
46                                                                 deferred[ handler ](function() {
47                                                                         returned = fn.apply( this, arguments );
48                                                                         if ( returned && jQuery.isFunction( returned.promise ) ) {
49                                                                                 returned.promise().done( newDefer.resolve ).fail( newDefer.reject ).progress( newDefer.notify );
50                                                                         } else {
51                                                                                 newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
52                                                                         }
53                                                                 });
54                                                         } else {
55                                                                 deferred[ handler ]( newDefer[ action ] );
56                                                         }
57                                                 });
58                                         }).promise();
59                                 },
60                                 // Get a promise for this deferred
61                                 // If obj is provided, the promise aspect is added to the object
62                                 promise: function( obj ) {
63                                         if ( obj == null ) {
64                                                 obj = promise;
65                                         } else {
66                                                 for ( var key in promise ) {
67                                                         obj[ key ] = promise[ key ];
68                                                 }
69                                         }
70                                         return obj;
71                                 }
72                         },
73                         deferred,
74                         key;
76                 // Keep pipe for back-compat
77                 promise.pipe = promise.then;
79                 // Construct deferred
80                 deferred = promise.promise({});
82                 for ( key in lists ) {
83                         deferred[ key ] = lists[ key ].fire;
84                         deferred[ key + "With" ] = lists[ key ].fireWith;
85                 }
87                 // Handle state
88                 deferred.done( function() {
89                         state = "resolved";
90                 }, failList.disable, progressList.lock ).fail( function() {
91                         state = "rejected";
92                 }, doneList.disable, progressList.lock );
94                 // Call given func if any
95                 if ( func ) {
96                         func.call( deferred, deferred );
97                 }
99                 // All done!
100                 return deferred;
101         },
103         // Deferred helper
104         when: function( firstParam ) {
105                 var args = sliceDeferred.call( arguments ),
106                         i = 0,
107                         length = args.length,
108                         pValues = new Array( length ),
109                         count = length,
110                         pCount = length,
111                         deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
112                                 firstParam :
113                                 jQuery.Deferred(),
114                         promise = deferred.promise();
115                 function resolveFunc( i ) {
116                         return function( value ) {
117                                 args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments ) : value;
118                                 if ( !( --count ) ) {
119                                         deferred.resolveWith( deferred, args );
120                                 }
121                         };
122                 }
123                 function progressFunc( i ) {
124                         return function( value ) {
125                                 pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments ) : value;
126                                 deferred.notifyWith( promise, pValues );
127                         };
128                 }
129                 if ( length > 1 ) {
130                         for ( ; i < length; i++ ) {
131                                 if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
132                                         args[ i ].promise().done( resolveFunc(i) ).fail( deferred.reject ).progress( progressFunc(i) );
133                                 } else {
134                                         --count;
135                                 }
136                         }
137                         if ( !count ) {
138                                 deferred.resolveWith( deferred, args );
139                         }
140                 } else if ( deferred !== firstParam ) {
141                         deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
142                 }
143                 return promise;
144         }
147 })( jQuery );