Moves Deferred-related code into a separate module. Context handling has been simplif...
authorjaubourg <j@ubourg.net>
Thu, 3 Mar 2011 18:38:06 +0000 (19:38 +0100)
committerjaubourg <j@ubourg.net>
Thu, 3 Mar 2011 18:38:06 +0000 (19:38 +0100)
17 files changed:
Makefile
src/core.js
src/deferred.js [new file with mode: 0644]
test/csp.php
test/data/offset/absolute.html
test/data/offset/body.html
test/data/offset/fixed.html
test/data/offset/relative.html
test/data/offset/scroll.html
test/data/offset/static.html
test/data/offset/table.html
test/index.html
test/localfile.html
test/networkerror.html
test/readywait.html
test/unit/core.js
test/unit/deferred.js [new file with mode: 0644]

index bf41bcc..a6aae42 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@ COMPILER = ${JS_ENGINE} ${BUILD_DIR}/uglify.js --unsafe
 POST_COMPILER = ${JS_ENGINE} ${BUILD_DIR}/post-compile.js
 
 BASE_FILES = ${SRC_DIR}/core.js\
+       ${SRC_DIR}/deferred.js\
        ${SRC_DIR}/support.js\
        ${SRC_DIR}/data.js\
        ${SRC_DIR}/queue.js\
index 1e7250f..9312ee2 100644 (file)
@@ -50,15 +50,9 @@ var jQuery = function( selector, context ) {
        // For matching the engine and version of the browser
        browserMatch,
 
-       // Has the ready events already been bound?
-       readyBound = false,
-
        // The deferred used on DOM ready
        readyList,
 
-       // Promise methods
-       promiseMethods = "then done fail isResolved isRejected promise".split( " " ),
-
        // The ready event handler
        DOMContentLoaded,
 
@@ -408,11 +402,11 @@ jQuery.extend({
        },
 
        bindReady: function() {
-               if ( readyBound ) {
+               if ( readyList ) {
                        return;
                }
 
-               readyBound = true;
+               readyList = jQuery._Deferred();
 
                // Catch cases where $(document).ready() is called after the
                // browser event has already occurred.
@@ -792,167 +786,6 @@ jQuery.extend({
                return (new Date()).getTime();
        },
 
-       // Create a simple deferred (one callbacks list)
-       _Deferred: function() {
-               var // callbacks list
-                       callbacks = [],
-                       // stored [ context , args ]
-                       fired,
-                       // to avoid firing when already doing so
-                       firing,
-                       // flag to know if the deferred has been cancelled
-                       cancelled,
-                       // the deferred itself
-                       deferred  = {
-
-                               // done( f1, f2, ...)
-                               done: function() {
-                                       if ( !cancelled ) {
-                                               var args = arguments,
-                                                       i,
-                                                       length,
-                                                       elem,
-                                                       type,
-                                                       _fired;
-                                               if ( fired ) {
-                                                       _fired = fired;
-                                                       fired = 0;
-                                               }
-                                               for ( i = 0, length = args.length; i < length; i++ ) {
-                                                       elem = args[ i ];
-                                                       type = jQuery.type( elem );
-                                                       if ( type === "array" ) {
-                                                               deferred.done.apply( deferred, elem );
-                                                       } else if ( type === "function" ) {
-                                                               callbacks.push( elem );
-                                                       }
-                                               }
-                                               if ( _fired ) {
-                                                       deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
-                                               }
-                                       }
-                                       return this;
-                               },
-
-                               // resolve with given context and args
-                               resolveWith: function( context, args ) {
-                                       if ( !cancelled && !fired && !firing ) {
-                                               // make sure args are available (#8421)
-                                               args = args || [];
-                                               firing = 1;
-                                               try {
-                                                       while( callbacks[ 0 ] ) {
-                                                               callbacks.shift().apply( context, args );
-                                                       }
-                                               }
-                                               finally {
-                                                       fired = [ context, args ];
-                                                       firing = 0;
-                                               }
-                                       }
-                                       return this;
-                               },
-
-                               // resolve with this as context and given arguments
-                               resolve: function() {
-                                       deferred.resolveWith( jQuery.isFunction( this.promise ) ? this.promise() : this, arguments );
-                                       return this;
-                               },
-
-                               // Has this deferred been resolved?
-                               isResolved: function() {
-                                       return !!( firing || fired );
-                               },
-
-                               // Cancel
-                               cancel: function() {
-                                       cancelled = 1;
-                                       callbacks = [];
-                                       return this;
-                               }
-                       };
-
-               return deferred;
-       },
-
-       // Full fledged deferred (two callbacks list)
-       Deferred: function( func ) {
-               var deferred = jQuery._Deferred(),
-                       failDeferred = jQuery._Deferred(),
-                       promise;
-               // Add errorDeferred methods, then and promise
-               jQuery.extend( deferred, {
-                       then: function( doneCallbacks, failCallbacks ) {
-                               deferred.done( doneCallbacks ).fail( failCallbacks );
-                               return this;
-                       },
-                       fail: failDeferred.done,
-                       rejectWith: failDeferred.resolveWith,
-                       reject: failDeferred.resolve,
-                       isRejected: failDeferred.isResolved,
-                       // Get a promise for this deferred
-                       // If obj is provided, the promise aspect is added to the object
-                       promise: function( obj ) {
-                               if ( obj == null ) {
-                                       if ( promise ) {
-                                               return promise;
-                                       }
-                                       promise = obj = {};
-                               }
-                               var i = promiseMethods.length;
-                               while( i-- ) {
-                                       obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
-                               }
-                               return obj;
-                       }
-               } );
-               // Make sure only one callback list will be used
-               deferred.done( failDeferred.cancel ).fail( deferred.cancel );
-               // Unexpose cancel
-               delete deferred.cancel;
-               // Call given func if any
-               if ( func ) {
-                       func.call( deferred, deferred );
-               }
-               return deferred;
-       },
-
-       // Deferred helper
-       when: function( object ) {
-               var lastIndex = arguments.length,
-                       deferred = lastIndex <= 1 && object && jQuery.isFunction( object.promise ) ?
-                               object :
-                               jQuery.Deferred(),
-                       promise = deferred.promise();
-
-               if ( lastIndex > 1 ) {
-                       var array = slice.call( arguments, 0 ),
-                               count = lastIndex,
-                               iCallback = function( index ) {
-                                       return function( value ) {
-                                               array[ index ] = arguments.length > 1 ? slice.call( arguments, 0 ) : value;
-                                               if ( !( --count ) ) {
-                                                       deferred.resolveWith( promise, array );
-                                               }
-                                       };
-                               };
-                       while( ( lastIndex-- ) ) {
-                               object = array[ lastIndex ];
-                               if ( object && jQuery.isFunction( object.promise ) ) {
-                                       object.promise().then( iCallback(lastIndex), deferred.reject );
-                               } else {
-                                       --count;
-                               }
-                       }
-                       if ( !count ) {
-                               deferred.resolveWith( promise, array );
-                       }
-               } else if ( deferred !== object ) {
-                       deferred.resolve( object );
-               }
-               return promise;
-       },
-
        // Use of jQuery.browser is frowned upon.
        // More details: http://docs.jquery.com/Utilities/jQuery.browser
        uaMatch: function( ua ) {
@@ -991,9 +824,6 @@ jQuery.extend({
        browser: {}
 });
 
-// Create readyList deferred
-readyList = jQuery._Deferred();
-
 // Populate the class2type map
 jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
        class2type[ "[object " + name + "]" ] = name.toLowerCase();
diff --git a/src/deferred.js b/src/deferred.js
new file mode 100644 (file)
index 0000000..f0d7c08
--- /dev/null
@@ -0,0 +1,169 @@
+(function( jQuery ) {
+
+var // Promise methods
+       promiseMethods = "then done fail isResolved isRejected promise".split( " " ),
+       // Static reference to slice
+       sliceDeferred = [].slice;
+
+jQuery.extend({
+       // Create a simple deferred (one callbacks list)
+       _Deferred: function() {
+               var // callbacks list
+                       callbacks = [],
+                       // stored [ context , args ]
+                       fired,
+                       // to avoid firing when already doing so
+                       firing,
+                       // flag to know if the deferred has been cancelled
+                       cancelled,
+                       // the deferred itself
+                       deferred  = {
+
+                               // done( f1, f2, ...)
+                               done: function() {
+                                       if ( !cancelled ) {
+                                               var args = arguments,
+                                                       i,
+                                                       length,
+                                                       elem,
+                                                       type,
+                                                       _fired;
+                                               if ( fired ) {
+                                                       _fired = fired;
+                                                       fired = 0;
+                                               }
+                                               for ( i = 0, length = args.length; i < length; i++ ) {
+                                                       elem = args[ i ];
+                                                       type = jQuery.type( elem );
+                                                       if ( type === "array" ) {
+                                                               deferred.done.apply( deferred, elem );
+                                                       } else if ( type === "function" ) {
+                                                               callbacks.push( elem );
+                                                       }
+                                               }
+                                               if ( _fired ) {
+                                                       deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
+                                               }
+                                       }
+                                       return this;
+                               },
+
+                               // resolve with given context and args
+                               resolveWith: function( context, args ) {
+                                       if ( !cancelled && !fired && !firing ) {
+                                               // make sure args are available (#8421)
+                                               args = args || [];
+                                               firing = 1;
+                                               try {
+                                                       while( callbacks[ 0 ] ) {
+                                                               callbacks.shift().apply( context, args );
+                                                       }
+                                               }
+                                               finally {
+                                                       fired = [ context, args ];
+                                                       firing = 0;
+                                               }
+                                       }
+                                       return this;
+                               },
+
+                               // resolve with this as context and given arguments
+                               resolve: function() {
+                                       deferred.resolveWith( this, arguments );
+                                       return this;
+                               },
+
+                               // Has this deferred been resolved?
+                               isResolved: function() {
+                                       return !!( firing || fired );
+                               },
+
+                               // Cancel
+                               cancel: function() {
+                                       cancelled = 1;
+                                       callbacks = [];
+                                       return this;
+                               }
+                       };
+
+               return deferred;
+       },
+
+       // Full fledged deferred (two callbacks list)
+       Deferred: function( func ) {
+               var deferred = jQuery._Deferred(),
+                       failDeferred = jQuery._Deferred(),
+                       promise;
+               // Add errorDeferred methods, then and promise
+               jQuery.extend( deferred, {
+                       then: function( doneCallbacks, failCallbacks ) {
+                               deferred.done( doneCallbacks ).fail( failCallbacks );
+                               return this;
+                       },
+                       fail: failDeferred.done,
+                       rejectWith: failDeferred.resolveWith,
+                       reject: failDeferred.resolve,
+                       isRejected: failDeferred.isResolved,
+                       // Get a promise for this deferred
+                       // If obj is provided, the promise aspect is added to the object
+                       promise: function( obj ) {
+                               if ( obj == null ) {
+                                       if ( promise ) {
+                                               return promise;
+                                       }
+                                       promise = obj = {};
+                               }
+                               var i = promiseMethods.length;
+                               while( i-- ) {
+                                       obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
+                               }
+                               return obj;
+                       }
+               } );
+               // Make sure only one callback list will be used
+               deferred.done( failDeferred.cancel ).fail( deferred.cancel );
+               // Unexpose cancel
+               delete deferred.cancel;
+               // Call given func if any
+               if ( func ) {
+                       func.call( deferred, deferred );
+               }
+               return deferred;
+       },
+
+       // Deferred helper
+       when: function( firstParam ) {
+               var args = arguments,
+                       i = 0,
+                       length = args.length,
+                       count = length,
+                       deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
+                               firstParam :
+                               jQuery.Deferred();
+               function resolveFunc( i ) {
+                       return function( value ) {
+                               args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+                               if ( !( --count ) ) {
+                                       deferred.resolveWith( deferred, args );
+                               }
+                       };
+               }
+               if ( length > 1 ) {
+                       for( ; i < length; i++ ) {
+                               if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
+                                       args[ i ].promise().then( resolveFunc(i), deferred.reject );
+                               } else {
+                                       --count;
+                               }
+                       }
+                       if ( !count ) {
+                               deferred.resolveWith( deferred, args );
+                       }
+               } else if ( deferred !== firstParam ) {
+                       deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+               }
+               return deferred.promise();
+       }
+});
+
+})( jQuery );
index acf8f32..13c324e 100644 (file)
@@ -6,6 +6,7 @@
        <title>CSP Test Page</title>
 
   <script src="../src/core.js"></script>
+  <script src="../src/deferred.js"></script>
   <script src="../src/support.js"></script>
   <script src="../src/data.js"></script>
   <script src="../src/queue.js"></script>
index b4db30a..9d7990a 100644 (file)
@@ -16,6 +16,7 @@
                        #positionTest { position: absolute; }
                </style>
                <script src="../../../src/core.js"></script>
+               <script src="../../../src/deferred.js"></script>
                <script src="../../../src/support.js"></script>
                <script src="../../../src/sizzle/sizzle.js"></script>
                <script src="../../../src/sizzle-jquery.js"></script>
index e3eb4f8..8dbf282 100644 (file)
@@ -9,6 +9,7 @@
                        #marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
                </style>
                <script src="../../../src/core.js"></script>
+               <script src="../../../src/deferred.js"></script>
                <script src="../../../src/support.js"></script>
                <script src="../../../src/sizzle/sizzle.js"></script>
                <script src="../../../src/sizzle-jquery.js"></script>
index f93c20f..3181718 100644 (file)
@@ -13,6 +13,7 @@
                        #marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
                </style>
                <script src="../../../src/core.js"></script>
+               <script src="../../../src/deferred.js"></script>
                <script src="../../../src/support.js"></script>
                <script src="../../../src/sizzle/sizzle.js"></script>
                <script src="../../../src/sizzle-jquery.js"></script>
index 3586476..280a2fc 100644 (file)
@@ -11,6 +11,7 @@
                        #marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
                </style>
                <script src="../../../src/core.js"></script>
+               <script src="../../../src/deferred.js"></script>
                <script src="../../../src/support.js"></script>
                <script src="../../../src/sizzle/sizzle.js"></script>
                <script src="../../../src/sizzle-jquery.js"></script>
index 50de95e..a0d1f4d 100644 (file)
@@ -14,6 +14,7 @@
                        #marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
                </style>
                <script src="../../../src/core.js"></script>
+               <script src="../../../src/deferred.js"></script>
                <script src="../../../src/support.js"></script>
                <script src="../../../src/sizzle/sizzle.js"></script>
                <script src="../../../src/sizzle-jquery.js"></script>
index b1fb9f1..a61b6d1 100644 (file)
@@ -11,6 +11,7 @@
                        #marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
                </style>
                <script src="../../../src/core.js"></script>
+               <script src="../../../src/deferred.js"></script>
                <script src="../../../src/support.js"></script>
                <script src="../../../src/sizzle/sizzle.js"></script>
                <script src="../../../src/sizzle-jquery.js"></script>
index 528e530..11fb0e7 100644 (file)
@@ -11,6 +11,7 @@
                        #marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
                </style>
                <script src="../../../src/core.js"></script>
+               <script src="../../../src/deferred.js"></script>
                <script src="../../../src/support.js"></script>
                <script src="../../../src/sizzle/sizzle.js"></script>
                <script src="../../../src/sizzle-jquery.js"></script>
index d621315..c7c2ae5 100644 (file)
@@ -9,6 +9,7 @@
        <script src="data/testinit.js"></script>
 
        <script src="../src/core.js"></script>
+       <script src="../src/deferred.js"></script>
        <script src="../src/support.js"></script>
        <script src="../src/data.js"></script>
        <script src="../src/queue.js"></script>
@@ -31,6 +32,7 @@
        <script src="data/testrunner.js"></script>
 
        <script src="unit/core.js"></script>
+       <script src="unit/deferred.js"></script>
        <script src="unit/data.js"></script>
        <script src="unit/queue.js"></script>
        <script src="unit/attributes.js"></script>
index 96e0f98..c552f21 100644 (file)
@@ -5,6 +5,7 @@
        <title>jQuery Local File Test</title>
        <!-- Includes -->
        <script src="../src/core.js"></script>
+       <script src="../src/deferred.js"></script>
        <script src="../src/support.js"></script>
        <script src="../src/data.js"></script>
        <script src="../src/queue.js"></script>
index b06a6ba..f98bf46 100644 (file)
@@ -16,6 +16,7 @@
                div { margin-top: 10px; }
        </style>
        <script src="../src/core.js"></script>
+       <script src="../src/deferred.js"></script>
        <script src="../src/support.js"></script>
        <script src="../src/data.js"></script>
        <script src="../src/queue.js"></script>
index 8e0d3d5..4f12476 100644 (file)
@@ -15,6 +15,7 @@
                #expectedOutput { background-color: green }
        </style>
        <script src="../src/core.js"></script>
+       <script src="../src/deferred.js"></script>
        <script src="../src/support.js"></script>
        <script src="../src/data.js"></script>
        <script src="../src/queue.js"></script>
index 208b21c..6ee8724 100644 (file)
@@ -914,227 +914,6 @@ test("jQuery.parseJSON", function(){
        }
 });
 
-test("jQuery._Deferred()", function() {
-
-       expect( 11 );
-
-       var deferred,
-               object,
-               test;
-
-       deferred = jQuery._Deferred();
-
-       test = false;
-
-       deferred.done( function( value ) {
-               equals( value , "value" , "Test pre-resolve callback" );
-               test = true;
-       } );
-
-       deferred.resolve( "value" );
-
-       ok( test , "Test pre-resolve callbacks called right away" );
-
-       test = false;
-
-       deferred.done( function( value ) {
-               equals( value , "value" , "Test post-resolve callback" );
-               test = true;
-       } );
-
-       ok( test , "Test post-resolve callbacks called right away" );
-
-       deferred.cancel();
-
-       test = true;
-
-       deferred.done( function() {
-               ok( false , "Cancel was ignored" );
-               test = false;
-       } );
-
-       ok( test , "Test cancel" );
-
-       deferred = jQuery._Deferred().resolve();
-
-       try {
-               deferred.done( function() {
-                       throw "Error";
-               } , function() {
-                       ok( true , "Test deferred do not cancel on exception" );
-               } );
-       } catch( e ) {
-               strictEqual( e , "Error" , "Test deferred propagates exceptions");
-               deferred.done();
-       }
-
-       test = "";
-       deferred = jQuery._Deferred().done( function() {
-
-               test += "A";
-
-       }, function() {
-
-               test += "B";
-
-       } ).resolve();
-
-       strictEqual( test , "AB" , "Test multiple done parameters" );
-
-       test = "";
-
-       deferred.done( function() {
-
-               deferred.done( function() {
-
-                       test += "C";
-
-               } );
-
-               test += "A";
-
-       }, function() {
-
-               test += "B";
-       } );
-
-       strictEqual( test , "ABC" , "Test done callbacks order" );
-
-       deferred = jQuery._Deferred();
-
-       deferred.resolveWith( jQuery , [ document ] ).done( function( doc ) {
-               ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" );
-       });
-
-       // #8421
-       deferred = jQuery._Deferred();
-       deferred.resolveWith().done(function() {
-               ok( true, "Test resolveWith can be called with no argument" );
-       });
-});
-
-test("jQuery.Deferred()", function() {
-
-       expect( 10 );
-
-       jQuery.Deferred( function( defer ) {
-               strictEqual( this , defer , "Defer passed as this & first argument" );
-               this.resolve( "done" );
-       }).then( function( value ) {
-               strictEqual( value , "done" , "Passed function executed" );
-       });
-
-       jQuery.Deferred().resolve().then( function() {
-               ok( true , "Success on resolve" );
-       }, function() {
-               ok( false , "Error on resolve" );
-       });
-
-       jQuery.Deferred().reject().then( function() {
-               ok( false , "Success on reject" );
-       }, function() {
-               ok( true , "Error on reject" );
-       });
-
-       ( new jQuery.Deferred( function( defer ) {
-               strictEqual( this , defer , "Defer passed as this & first argument (new)" );
-               this.resolve( "done" );
-       }) ).then( function( value ) {
-               strictEqual( value , "done" , "Passed function executed (new)" );
-       });
-
-       ( new jQuery.Deferred() ).resolve().then( function() {
-               ok( true , "Success on resolve (new)" );
-       }, function() {
-               ok( false , "Error on resolve (new)" );
-       });
-
-       ( new jQuery.Deferred() ).reject().then( function() {
-               ok( false , "Success on reject (new)" );
-       }, function() {
-               ok( true , "Error on reject (new)" );
-       });
-
-       var tmp = jQuery.Deferred();
-
-       strictEqual( tmp.promise() , tmp.promise() , "Test deferred always return same promise" );
-       strictEqual( tmp.promise() , tmp.promise().promise() , "Test deferred's promise always return same promise as deferred" );
-});
-
-test("jQuery.when()", function() {
-
-       expect( 23 );
-
-       // Some other objects
-       jQuery.each( {
-
-               "an empty string": "",
-               "a non-empty string": "some string",
-               "zero": 0,
-               "a number other than zero": 1,
-               "true": true,
-               "false": false,
-               "null": null,
-               "undefined": undefined,
-               "a plain object": {}
-
-       } , function( message , value ) {
-
-               ok( jQuery.isFunction( jQuery.when( value ).then( function( resolveValue ) {
-                       strictEqual( resolveValue , value , "Test the promise was resolved with " + message );
-               } ).promise ) , "Test " + message + " triggers the creation of a new Promise" );
-
-       } );
-
-       ok( jQuery.isFunction( jQuery.when().then( function( resolveValue ) {
-               strictEqual( resolveValue , undefined , "Test the promise was resolved with no parameter" );
-       } ).promise ) , "Test calling when with no parameter triggers the creation of a new Promise" );
-
-       var cache, i;
-
-       for( i = 1 ; i < 4 ; i++ ) {
-               jQuery.when( cache || jQuery.Deferred( function() {
-                       this.resolve( i );
-               }) ).then( function( value ) {
-                       strictEqual( value , 1 , "Function executed" + ( i > 1 ? " only once" : "" ) );
-                       cache = value;
-               }, function() {
-                       ok( false , "Fail called" );
-               });
-       }
-});
-
-test("jQuery.when() - joined", function() {
-
-       expect(8);
-
-       jQuery.when( 1, 2, 3 ).done( function( a, b, c ) {
-               strictEqual( a , 1 , "Test first param is first resolved value - non-observables" );
-               strictEqual( b , 2 , "Test second param is second resolved value - non-observables" );
-               strictEqual( c , 3 , "Test third param is third resolved value - non-observables" );
-       }).fail( function() {
-               ok( false , "Test the created deferred was resolved - non-observables");
-       });
-
-       var successDeferred = jQuery.Deferred().resolve( 1 , 2 , 3 ),
-               errorDeferred = jQuery.Deferred().reject( "error" , "errorParam" );
-
-       jQuery.when( 1 , successDeferred , 3 ).done( function( a, b, c ) {
-               strictEqual( a , 1 , "Test first param is first resolved value - resolved observable" );
-               same( b , [ 1 , 2 , 3 ] , "Test second param is second resolved value - resolved observable" );
-               strictEqual( c , 3 , "Test third param is third resolved value - resolved observable" );
-       }).fail( function() {
-               ok( false , "Test the created deferred was resolved - resolved observable");
-       });
-
-       jQuery.when( 1 , errorDeferred , 3 ).done( function() {
-               ok( false , "Test the created deferred was rejected - rejected observable");
-       }).fail( function( error , errorParam ) {
-               strictEqual( error , "error" , "Test first param is first rejected value - rejected observable" );
-               strictEqual( errorParam , "errorParam" , "Test second param is second rejected value - rejected observable" );
-       });
-});
-
 test("jQuery.sub() - Static Methods", function(){
     expect(18);
     var Subclass = jQuery.sub();
diff --git a/test/unit/deferred.js b/test/unit/deferred.js
new file mode 100644 (file)
index 0000000..6ba4767
--- /dev/null
@@ -0,0 +1,222 @@
+module("deferred", { teardown: moduleTeardown });
+
+test("jQuery._Deferred()", function() {
+
+       expect( 11 );
+
+       var deferred,
+               object,
+               test;
+
+       deferred = jQuery._Deferred();
+
+       test = false;
+
+       deferred.done( function( value ) {
+               equals( value , "value" , "Test pre-resolve callback" );
+               test = true;
+       } );
+
+       deferred.resolve( "value" );
+
+       ok( test , "Test pre-resolve callbacks called right away" );
+
+       test = false;
+
+       deferred.done( function( value ) {
+               equals( value , "value" , "Test post-resolve callback" );
+               test = true;
+       } );
+
+       ok( test , "Test post-resolve callbacks called right away" );
+
+       deferred.cancel();
+
+       test = true;
+
+       deferred.done( function() {
+               ok( false , "Cancel was ignored" );
+               test = false;
+       } );
+
+       ok( test , "Test cancel" );
+
+       deferred = jQuery._Deferred().resolve();
+
+       try {
+               deferred.done( function() {
+                       throw "Error";
+               } , function() {
+                       ok( true , "Test deferred do not cancel on exception" );
+               } );
+       } catch( e ) {
+               strictEqual( e , "Error" , "Test deferred propagates exceptions");
+               deferred.done();
+       }
+
+       test = "";
+       deferred = jQuery._Deferred().done( function() {
+
+               test += "A";
+
+       }, function() {
+
+               test += "B";
+
+       } ).resolve();
+
+       strictEqual( test , "AB" , "Test multiple done parameters" );
+
+       test = "";
+
+       deferred.done( function() {
+
+               deferred.done( function() {
+
+                       test += "C";
+
+               } );
+
+               test += "A";
+
+       }, function() {
+
+               test += "B";
+       } );
+
+       strictEqual( test , "ABC" , "Test done callbacks order" );
+
+       deferred = jQuery._Deferred();
+
+       deferred.resolveWith( jQuery , [ document ] ).done( function( doc ) {
+               ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" );
+       });
+
+       // #8421
+       deferred = jQuery._Deferred();
+       deferred.resolveWith().done(function() {
+               ok( true, "Test resolveWith can be called with no argument" );
+       });
+});
+
+test("jQuery.Deferred()", function() {
+
+       expect( 10 );
+
+       jQuery.Deferred( function( defer ) {
+               strictEqual( this , defer , "Defer passed as this & first argument" );
+               this.resolve( "done" );
+       }).then( function( value ) {
+               strictEqual( value , "done" , "Passed function executed" );
+       });
+
+       jQuery.Deferred().resolve().then( function() {
+               ok( true , "Success on resolve" );
+       }, function() {
+               ok( false , "Error on resolve" );
+       });
+
+       jQuery.Deferred().reject().then( function() {
+               ok( false , "Success on reject" );
+       }, function() {
+               ok( true , "Error on reject" );
+       });
+
+       ( new jQuery.Deferred( function( defer ) {
+               strictEqual( this , defer , "Defer passed as this & first argument (new)" );
+               this.resolve( "done" );
+       }) ).then( function( value ) {
+               strictEqual( value , "done" , "Passed function executed (new)" );
+       });
+
+       ( new jQuery.Deferred() ).resolve().then( function() {
+               ok( true , "Success on resolve (new)" );
+       }, function() {
+               ok( false , "Error on resolve (new)" );
+       });
+
+       ( new jQuery.Deferred() ).reject().then( function() {
+               ok( false , "Success on reject (new)" );
+       }, function() {
+               ok( true , "Error on reject (new)" );
+       });
+
+       var tmp = jQuery.Deferred();
+
+       strictEqual( tmp.promise() , tmp.promise() , "Test deferred always return same promise" );
+       strictEqual( tmp.promise() , tmp.promise().promise() , "Test deferred's promise always return same promise as deferred" );
+});
+
+test("jQuery.when()", function() {
+
+       expect( 23 );
+
+       // Some other objects
+       jQuery.each( {
+
+               "an empty string": "",
+               "a non-empty string": "some string",
+               "zero": 0,
+               "a number other than zero": 1,
+               "true": true,
+               "false": false,
+               "null": null,
+               "undefined": undefined,
+               "a plain object": {}
+
+       } , function( message , value ) {
+
+               ok( jQuery.isFunction( jQuery.when( value ).then( function( resolveValue ) {
+                       strictEqual( resolveValue , value , "Test the promise was resolved with " + message );
+               } ).promise ) , "Test " + message + " triggers the creation of a new Promise" );
+
+       } );
+
+       ok( jQuery.isFunction( jQuery.when().then( function( resolveValue ) {
+               strictEqual( resolveValue , undefined , "Test the promise was resolved with no parameter" );
+       } ).promise ) , "Test calling when with no parameter triggers the creation of a new Promise" );
+
+       var cache, i;
+
+       for( i = 1 ; i < 4 ; i++ ) {
+               jQuery.when( cache || jQuery.Deferred( function() {
+                       this.resolve( i );
+               }) ).then( function( value ) {
+                       strictEqual( value , 1 , "Function executed" + ( i > 1 ? " only once" : "" ) );
+                       cache = value;
+               }, function() {
+                       ok( false , "Fail called" );
+               });
+       }
+});
+
+test("jQuery.when() - joined", function() {
+
+       expect(8);
+
+       jQuery.when( 1, 2, 3 ).done( function( a, b, c ) {
+               strictEqual( a , 1 , "Test first param is first resolved value - non-observables" );
+               strictEqual( b , 2 , "Test second param is second resolved value - non-observables" );
+               strictEqual( c , 3 , "Test third param is third resolved value - non-observables" );
+       }).fail( function() {
+               ok( false , "Test the created deferred was resolved - non-observables");
+       });
+
+       var successDeferred = jQuery.Deferred().resolve( 1 , 2 , 3 ),
+               errorDeferred = jQuery.Deferred().reject( "error" , "errorParam" );
+
+       jQuery.when( 1 , successDeferred , 3 ).done( function( a, b, c ) {
+               strictEqual( a , 1 , "Test first param is first resolved value - resolved observable" );
+               same( b , [ 1 , 2 , 3 ] , "Test second param is second resolved value - resolved observable" );
+               strictEqual( c , 3 , "Test third param is third resolved value - resolved observable" );
+       }).fail( function() {
+               ok( false , "Test the created deferred was resolved - resolved observable");
+       });
+
+       jQuery.when( 1 , errorDeferred , 3 ).done( function() {
+               ok( false , "Test the created deferred was rejected - rejected observable");
+       }).fail( function( error , errorParam ) {
+               strictEqual( error , "error" , "Test first param is first rejected value - rejected observable" );
+               strictEqual( errorParam , "errorParam" , "Test second param is second rejected value - rejected observable" );
+       });
+});