diff --git a/src/core.js b/src/core.js index b81fa764..3331969a 100644 --- a/src/core.js +++ b/src/core.js @@ -565,24 +565,17 @@ jQuery.extend({ noop: function() {}, - // Evalulates a script in a global context + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context globalEval: function( data ) { - if ( data && rnotwhite.test(data) ) { - // Inspired by code by Andrea Giammarchi - // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html - var head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement, - script = document.createElement( "script" ); - - if ( jQuery.support.scriptEval() ) { - script.appendChild( document.createTextNode( data ) ); - } else { - script.text = data; - } - - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709). - head.insertBefore( script, head.firstChild ); - head.removeChild( script ); + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); } }, diff --git a/src/deferred.js b/src/deferred.js index 90f9c808..feaccaef 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -1,7 +1,7 @@ (function( jQuery ) { var // Promise methods - promiseMethods = "then done fail isResolved isRejected promise".split( " " ), + promiseMethods = "done fail isResolved isRejected promise then always chain".split( " " ), // Static reference to slice sliceDeferred = [].slice; @@ -100,10 +100,38 @@ jQuery.extend({ deferred.done( doneCallbacks ).fail( failCallbacks ); return this; }, + always: function() { + return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments ); + }, fail: failDeferred.done, rejectWith: failDeferred.resolveWith, reject: failDeferred.resolve, isRejected: failDeferred.isResolved, + // Chain + chain: function( fnDone, fnFail ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject ); + } else { + newDefer[ action ]( returned ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + } ); + }).promise(); + }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { @@ -169,4 +197,4 @@ jQuery.extend({ } }); -})( jQuery ); +})( jQuery ); \ No newline at end of file diff --git a/src/effects.js b/src/effects.js index d9e9a8b3..5ff85d13 100644 --- a/src/effects.js +++ b/src/effects.js @@ -11,7 +11,17 @@ var elemdisplay = {}, [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ], // opacity animations [ "opacity" ] - ]; + ], + fxNow; + +function clearFxNow() { + fxNow = undefined; +} + +function createFxNow() { + setTimeout( clearFxNow, 0 ); + return ( fxNow = jQuery.now() ); +} jQuery.fn.extend({ show: function( speed, easing, callback ) { @@ -349,7 +359,7 @@ jQuery.fx.prototype = { var self = this, fx = jQuery.fx; - this.startTime = jQuery.now(); + this.startTime = fxNow || createFxNow(); this.start = from; this.end = to; this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" ); @@ -394,7 +404,8 @@ jQuery.fx.prototype = { // Each step of an animation step: function( gotoEnd ) { - var t = jQuery.now(), done = true; + var t = fxNow || createFxNow(), + done = true; if ( gotoEnd || t >= this.options.duration + this.startTime ) { this.now = this.end; diff --git a/src/queue.js b/src/queue.js index 9e3e2fb5..701d06ad 100644 --- a/src/queue.js +++ b/src/queue.js @@ -28,7 +28,8 @@ jQuery.extend({ type = type || "fx"; var queue = jQuery.queue( elem, type ), - fn = queue.shift(); + fn = queue.shift(), + defer; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { @@ -49,6 +50,17 @@ jQuery.extend({ if ( !queue.length ) { jQuery.removeData( elem, type + "queue", true ); + // Look if we have observers and resolve if needed + if (( defer = jQuery.data( elem, type + "defer", undefined, true ) )) { + // Give room for hard-coded callbacks to fire first + // and eventually add another animation on the element + setTimeout( function() { + if ( !jQuery.data( elem, type + "queue", undefined, true ) ) { + jQuery.removeData( elem, type + "defer", true ); + defer.resolve(); + } + }, 0 ); + } } } }); @@ -93,6 +105,37 @@ jQuery.fn.extend({ clearQueue: function( type ) { return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue"; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + jQuery.data( elements[ i ], queueDataKey, undefined, true ) && + jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) { + count++; + tmp.done( resolve ); + } + } + resolve(); + return defer.promise(); } }); diff --git a/src/support.js b/src/support.js index 8b950e25..d5625bed 100644 --- a/src/support.js +++ b/src/support.js @@ -1,44 +1,58 @@ (function( jQuery ) { -(function() { +jQuery.support = (function() { - jQuery.support = {}; + var div = document.createElement( "div" ), + all, + a, + select, + opt, + input, + support, + fragment, + body, + bodyStyle, + tds, + events, + eventName, + i, + isSupported; - var div = document.createElement("div"); - - div.style.display = "none"; + // Preliminary tests div.innerHTML = "
a"; - var all = div.getElementsByTagName("*"), - a = div.getElementsByTagName("a")[0], - select = document.createElement("select"), - opt = select.appendChild( document.createElement("option") ), - input = div.getElementsByTagName("input")[0]; + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; // Can't get basic test support if ( !all || !all.length || !a ) { - return; + return {}; } - jQuery.support = { + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: div.firstChild.nodeType === 3, + leadingWhitespace: ( div.firstChild.nodeType === 3 ), // Make sure that tbody elements aren't automatically inserted // IE will insert them into empty tables - tbody: !div.getElementsByTagName("tbody").length, + tbody: !div.getElementsByTagName( "tbody" ).length, // Make sure that link elements get serialized correctly by innerHTML // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName("link").length, + htmlSerialize: !!div.getElementsByTagName( "link" ).length, // Get the style information from getAttribute - // (IE uses .cssText insted) + // (IE uses .cssText instead) style: /red/.test( a.getAttribute("style") ), // Make sure that URLs aren't manipulated // (IE normalizes it by default) - hrefNormalized: a.getAttribute("href") === "/a", + hrefNormalized: ( a.getAttribute( "href" ) === "/a" ), // Make sure that element opacity exists // (IE uses filter instead) @@ -59,176 +73,152 @@ optSelected: opt.selected, // Will be defined later + submitBubbles: true, + changeBubbles: true, deleteExpando: true, - optDisabled: false, - checkClone: false, noCloneEvent: true, - noCloneChecked: true, - boxModel: null, inlineBlockNeedsLayout: false, shrinkWrapBlocks: false, - reliableHiddenOffsets: true, reliableMarginRight: true }; + // Make sure checked status is properly cloned input.checked = true; - jQuery.support.noCloneChecked = input.cloneNode( true ).checked; + support.noCloneChecked = input.cloneNode( true ).checked; // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as diabled) + // (WebKit marks them as disabled) select.disabled = true; - jQuery.support.optDisabled = !opt.disabled; - - var _scriptEval = null; - jQuery.support.scriptEval = function() { - if ( _scriptEval === null ) { - var root = document.documentElement, - script = document.createElement("script"), - id = "script" + jQuery.now(); - - // Make sure that the execution of code works by injecting a script - // tag with appendChild/createTextNode - // (IE doesn't support this, fails, and uses .text instead) - try { - script.appendChild( document.createTextNode( "window." + id + "=1;" ) ); - } catch(e) {} - - root.insertBefore( script, root.firstChild ); - - if ( window[ id ] ) { - _scriptEval = true; - delete window[ id ]; - } else { - _scriptEval = false; - } - - root.removeChild( script ); - } - - return _scriptEval; - }; + support.optDisabled = !opt.disabled; // Test to see if it's possible to delete an expando from an element // Fails in Internet Explorer try { delete div.test; - - } catch(e) { - jQuery.support.deleteExpando = false; + } catch( e ) { + support.deleteExpando = false; } if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { - div.attachEvent("onclick", function click() { + div.attachEvent( "onclick", function click() { // Cloning a node shouldn't copy over any // bound event handlers (IE does this) - jQuery.support.noCloneEvent = false; - div.detachEvent("onclick", click); - }); - div.cloneNode(true).fireEvent("onclick"); + support.noCloneEvent = false; + div.detachEvent( "onclick", click ); + } ); + div.cloneNode( true ).fireEvent( "onclick" ); } - div = document.createElement("div"); div.innerHTML = ""; - var fragment = document.createDocumentFragment(); + fragment = document.createDocumentFragment(); fragment.appendChild( div.firstChild ); // WebKit doesn't clone checked state correctly in fragments - jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked; + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + div.innerHTML = ""; // Figure out if the W3C box model works as expected - // document.body must exist before we can do this - jQuery(function() { - var div = document.createElement("div"), - body = document.getElementsByTagName("body")[0]; + div.style.width = div.style.paddingLeft = "1px"; - // Frameset documents with no body should not run this code - if ( !body ) { - return; - } + // We use our own, invisible, body + body = document.createElement( "body" ); + bodyStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0 + }; + for ( i in bodyStyle ) { + body.style[ i ] = bodyStyle[ i ]; + } + body.appendChild( div ); + document.documentElement.appendChild( body ); - div.style.width = div.style.paddingLeft = "1px"; - body.appendChild( div ); - jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; + support.boxModel = div.offsetWidth === 2; - if ( "zoom" in div.style ) { - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - // (IE < 8 does this) - div.style.display = "inline"; - div.style.zoom = 1; - jQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2; + if ( "zoom" in div.style ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); - // Check if elements with layout shrink-wrap their children - // (IE 6 does this) - div.style.display = ""; - div.innerHTML = "
"; - jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2; - } + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + } - div.innerHTML = "
t
"; - var tds = div.getElementsByTagName("td"); + div.innerHTML = "
t
"; + tds = div.getElementsByTagName( "td" ); - // Check if table cells still have offsetWidth/Height when they are set - // to display:none and there are still other visible table cells in a - // table row; if so, offsetWidth/Height are not reliable for use when - // determining if an element has been hidden directly using - // display:none (it is still safe to use offsets if a parent element is - // hidden; don safety goggles and see bug #4512 for more information). - // (only IE 8 fails this test) - jQuery.support.reliableHiddenOffsets = tds[0].offsetHeight === 0; + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + isSupported = ( tds[ 0 ].offsetHeight === 0 ); - tds[0].style.display = ""; - tds[1].style.display = "none"; + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; - // Check if empty table cells still have offsetWidth/Height - // (IE < 8 fail this test) - jQuery.support.reliableHiddenOffsets = jQuery.support.reliableHiddenOffsets && tds[0].offsetHeight === 0; - div.innerHTML = ""; + // Check if empty table cells still have offsetWidth/Height + // (IE < 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + div.innerHTML = ""; - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. For more - // info see bug #3333 - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - if ( document.defaultView && document.defaultView.getComputedStyle ) { - div.style.width = "1px"; - div.style.marginRight = "0"; - jQuery.support.reliableMarginRight = ( parseInt(document.defaultView.getComputedStyle(div, null).marginRight, 10) || 0 ) === 0; - } + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( document.defaultView && document.defaultView.getComputedStyle ) { + div.style.width = "1px"; + div.style.marginRight = "0"; + support.reliableMarginRight = + ( parseInt( document.defaultView.getComputedStyle(div).marginRight, 10 ) || 0 ) === 0; + } - body.removeChild( div ).style.display = "none"; - div = tds = null; - }); + // Remove the body element we added + document.documentElement.removeChild( body ); // Technique from Juriy Zaytsev // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ - var eventSupported = function( eventName ) { - var el = document.createElement("div"); - eventName = "on" + eventName; - - // We only care about the case where non-standard event systems - // are used, namely in IE. Short-circuiting here helps us to - // avoid an eval call (in setAttribute) which can cause CSP - // to go haywire. See: https://developer.mozilla.org/en/Security/CSP - if ( !el.attachEvent ) { - return true; + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for( i in { + submit: 1, + change: 1, + focusin: 1 + } ) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; } - - var isSupported = (eventName in el); - if ( !isSupported ) { - el.setAttribute(eventName, "return;"); - isSupported = typeof el[eventName] === "function"; - } - return isSupported; - }; - - jQuery.support.submitBubbles = eventSupported("submit"); - jQuery.support.changeBubbles = eventSupported("change"); - jQuery.support.focusinBubbles = document.attachEvent && eventSupported("focusin"); + } // release memory in IE - div = all = a = null; + body = div = all = a = tds = undefined; + + return support; })(); + +// Keep track of boxModel +jQuery.boxModel = jQuery.support.boxModel; + })( jQuery ); diff --git a/test/unit/core.js b/test/unit/core.js index 03325ab7..79710025 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -172,6 +172,26 @@ test("selector state", function() { ); }); +test( "globalEval", function() { + + expect( 3 ); + + jQuery.globalEval( "var globalEvalTest = true;" ); + ok( window.globalEvalTest, "Test variable declarations are global" ); + + window.globalEvalTest = false; + + jQuery.globalEval( "globalEvalTest = true;" ); + ok( window.globalEvalTest, "Test variable assignments are global" ); + + window.globalEvalTest = false; + + jQuery.globalEval( "this.globalEvalTest = true;" ); + ok( window.globalEvalTest, "Test context (this) is the window object" ); + + window.globalEvalTest = undefined; +}); + if ( !isLocal ) { test("browser", function() { stop(); diff --git a/test/unit/deferred.js b/test/unit/deferred.js index 6ba4767a..c1ed7c57 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -1,153 +1,273 @@ module("deferred", { teardown: moduleTeardown }); -test("jQuery._Deferred()", function() { +jQuery.each( [ "", " - new operator" ], function( _, withNew ) { - 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(); + function createDeferred() { + return withNew ? new jQuery._Deferred() : jQuery._Deferred(); } - test = ""; - deferred = jQuery._Deferred().done( function() { + test("jQuery._Deferred" + withNew, function() { - test += "A"; + expect( 11 ); - }, function() { + var deferred, + object, + test; - test += "B"; + deferred = createDeferred(); - } ).resolve(); + test = false; - strictEqual( test , "AB" , "Test multiple done parameters" ); + deferred.done( function( value ) { + equals( value , "value" , "Test pre-resolve callback" ); + test = true; + } ); - test = ""; + deferred.resolve( "value" ); - deferred.done( function() { + 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 = createDeferred().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 = createDeferred().done( function() { + + test += "A"; + + }, function() { + + test += "B"; + + } ).resolve(); + + strictEqual( test , "AB" , "Test multiple done parameters" ); + + test = ""; deferred.done( function() { - test += "C"; + deferred.done( function() { + test += "C"; + + } ); + + test += "A"; + + }, function() { + + test += "B"; } ); - test += "A"; + strictEqual( test , "ABC" , "Test done callbacks order" ); - }, function() { + deferred = createDeferred(); - test += "B"; + deferred.resolveWith( jQuery , [ document ] ).done( function( doc ) { + ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" ); + }); + + // #8421 + deferred = createDeferred(); + deferred.resolveWith().done(function() { + ok( true, "Test resolveWith can be called with no argument" ); + }); + }); +} ); + +jQuery.each( [ "", " - new operator" ], function( _, withNew ) { + + function createDeferred( fn ) { + return withNew ? new jQuery.Deferred( fn ) : jQuery.Deferred( fn ); + } + + test("jQuery.Deferred" + withNew, function() { + + expect( 8 ); + + createDeferred().resolve().then( function() { + ok( true , "Success on resolve" ); + ok( this.isResolved(), "Deferred is resolved" ); + }, function() { + ok( false , "Error on resolve" ); + }).always( function() { + ok( true , "Always callback on resolve" ); + }); + + createDeferred().reject().then( function() { + ok( false , "Success on reject" ); + }, function() { + ok( true , "Error on reject" ); + ok( this.isRejected(), "Deferred is rejected" ); + }).always( function() { + ok( true , "Always callback on reject" ); + }); + + createDeferred( function( defer ) { + ok( this === defer , "Defer passed as this & first argument" ); + this.resolve( "done" ); + }).then( function( value ) { + strictEqual( value , "done" , "Passed function executed" ); + }); + }); +} ); + +test( "jQuery.Deferred.chain - filtering (done)", function() { + + expect(3); + + var defer = jQuery.Deferred(), + chained = defer.chain(function( a, b ) { + return a * b; + }), + value1, + value2, + value3; + + chained.done(function( result ) { + value3 = result; + }); + + defer.done(function( a, b ) { + value1 = a; + value2 = b; + }); + + defer.resolve( 2, 3 ); + + strictEqual( value1, 2, "first resolve value ok" ); + strictEqual( value2, 3, "second resolve value ok" ); + strictEqual( value3, 6, "result of filter ok" ); + + jQuery.Deferred().reject().chain(function() { + ok( false, "chain should not be called on reject" ); + }); +}); + +test( "jQuery.Deferred.chain - filtering (fail)", function() { + + expect(3); + + var defer = jQuery.Deferred(), + chained = defer.chain( null, function( a, b ) { + return a * b; + } ), + value1, + value2, + value3; + + chained.fail(function( result ) { + value3 = result; + }); + + defer.fail(function( a, b ) { + value1 = a; + value2 = b; + }); + + defer.reject( 2, 3 ); + + strictEqual( value1, 2, "first reject value ok" ); + strictEqual( value2, 3, "second reject value ok" ); + strictEqual( value3, 6, "result of filter ok" ); + + jQuery.Deferred().resolve().chain( null, function() { + ok( false, "chain should not be called on resolve" ); } ); - - 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() { +test( "jQuery.Deferred.chain - deferred (done)", function() { - expect( 10 ); + expect(3); - 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" ); + var defer = jQuery.Deferred(), + chained = defer.chain(function( a, b ) { + return jQuery.Deferred(function( defer ) { + defer.reject( a * b ); + }); + }), + value1, + value2, + value3; + + chained.fail(function( result ) { + value3 = result; }); - jQuery.Deferred().resolve().then( function() { - ok( true , "Success on resolve" ); - }, function() { - ok( false , "Error on resolve" ); + defer.done(function( a, b ) { + value1 = a; + value2 = b; }); - jQuery.Deferred().reject().then( function() { - ok( false , "Success on reject" ); - }, function() { - ok( true , "Error on reject" ); - }); + defer.resolve( 2, 3 ); - ( 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" ); + strictEqual( value1, 2, "first resolve value ok" ); + strictEqual( value2, 3, "second resolve value ok" ); + strictEqual( value3, 6, "result of filter ok" ); }); -test("jQuery.when()", function() { +test( "jQuery.Deferred.chain - deferred (fail)", function() { + + expect(3); + + var defer = jQuery.Deferred(), + chained = defer.chain( null, function( a, b ) { + return jQuery.Deferred(function( defer ) { + defer.resolve( a * b ); + }); + } ), + value1, + value2, + value3; + + chained.done(function( result ) { + value3 = result; + }); + + defer.fail(function( a, b ) { + value1 = a; + value2 = b; + }); + + defer.reject( 2, 3 ); + + strictEqual( value1, 2, "first reject value ok" ); + strictEqual( value2, 3, "second reject value ok" ); + strictEqual( value3, 6, "result of filter ok" ); +}); + +test( "jQuery.when" , function() { expect( 23 ); @@ -166,57 +286,63 @@ test("jQuery.when()", function() { } , function( message , value ) { - ok( jQuery.isFunction( jQuery.when( value ).then( function( resolveValue ) { + ok( jQuery.isFunction( jQuery.when( value ).done(function( resolveValue ) { strictEqual( resolveValue , value , "Test the promise was resolved with " + message ); - } ).promise ) , "Test " + message + " triggers the creation of a new Promise" ); + }).promise ) , "Test " + message + " triggers the creation of a new Promise" ); } ); - ok( jQuery.isFunction( jQuery.when().then( function( resolveValue ) { + ok( jQuery.isFunction( jQuery.when().done(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" ); + }).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 ) { + }) ).done(function( value ) { strictEqual( value , 1 , "Function executed" + ( i > 1 ? " only once" : "" ) ); cache = value; - }, function() { - ok( false , "Fail called" ); }); } }); -test("jQuery.when() - joined", function() { +test("jQuery.when - joined", function() { - expect(8); + expect(25); - 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 deferreds = { + value: 1, + success: jQuery.Deferred().resolve( 1 ), + error: jQuery.Deferred().reject( 0 ), + futureSuccess: jQuery.Deferred(), + futureError: jQuery.Deferred() + }, + willSucceed = { + value: true, + success: true, + error: false, + futureSuccess: true, + futureError: false + }; - 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" ); - }); + jQuery.each( deferreds, function( id1, defer1 ) { + jQuery.each( deferreds, function( id2, defer2 ) { + var shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ], + expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ], + code = id1 + "/" + id2; + jQuery.when( defer1, defer2 ).done(function( a, b ) { + if ( shouldResolve ) { + same( [ a, b ], expected, code + " => resolve" ); + } + }).fail(function( a, b ) { + if ( !shouldResolve ) { + same( [ a, b ], expected, code + " => resolve" ); + } + }); + } ); + } ); + deferreds.futureSuccess.resolve( 1 ); + deferreds.futureError.reject( 0 ); }); diff --git a/test/unit/effects.js b/test/unit/effects.js index c0a812f4..4f678511 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -717,59 +717,64 @@ jQuery.each( { var anim = { width: t_w, height: t_h, opacity: t_o }; - elem.animate(anim, 50, function(){ + elem.animate(anim, 50); + + jQuery.when( elem ).done(function( elem ){ + + elem = elem[ 0 ]; + if ( t_w == "show" ) - equals( this.style.display, "block", "Showing, display should block: " + this.style.display); + equals( elem.style.display, "block", "Showing, display should block: " + elem.style.display); if ( t_w == "hide"||t_w == "show" ) - ok(f_w === "" ? this.style.width === f_w : this.style.width.indexOf(f_w) === 0, "Width must be reset to " + f_w + ": " + this.style.width); + ok(f_w === "" ? elem.style.width === f_w : elem.style.width.indexOf(f_w) === 0, "Width must be reset to " + f_w + ": " + elem.style.width); if ( t_h == "hide"||t_h == "show" ) - ok(f_h === "" ? this.style.height === f_h : this.style.height.indexOf(f_h) === 0, "Height must be reset to " + f_h + ": " + this.style.height); + ok(f_h === "" ? elem.style.height === f_h : elem.style.height.indexOf(f_h) === 0, "Height must be reset to " + f_h + ": " + elem.style.height); - var cur_o = jQuery.style(this, "opacity"); + var cur_o = jQuery.style(elem, "opacity"); if ( t_o == "hide" || t_o == "show" ) equals(cur_o, f_o, "Opacity must be reset to " + f_o + ": " + cur_o); if ( t_w == "hide" ) - equals(this.style.display, "none", "Hiding, display should be none: " + this.style.display); + equals(elem.style.display, "none", "Hiding, display should be none: " + elem.style.display); if ( t_o.constructor == Number ) { equals(cur_o, t_o, "Final opacity should be " + t_o + ": " + cur_o); - ok(jQuery.css(this, "opacity") != "" || cur_o == t_o, "Opacity should be explicitly set to " + t_o + ", is instead: " + cur_o); + ok(jQuery.css(elem, "opacity") != "" || cur_o == t_o, "Opacity should be explicitly set to " + t_o + ", is instead: " + cur_o); } if ( t_w.constructor == Number ) { - equals(this.style.width, t_w + "px", "Final width should be " + t_w + ": " + this.style.width); + equals(elem.style.width, t_w + "px", "Final width should be " + t_w + ": " + elem.style.width); - var cur_w = jQuery.css(this,"width"); + var cur_w = jQuery.css(elem,"width"); - ok(this.style.width != "" || cur_w == t_w, "Width should be explicitly set to " + t_w + ", is instead: " + cur_w); + ok(elem.style.width != "" || cur_w == t_w, "Width should be explicitly set to " + t_w + ", is instead: " + cur_w); } if ( t_h.constructor == Number ) { - equals(this.style.height, t_h + "px", "Final height should be " + t_h + ": " + this.style.height); + equals(elem.style.height, t_h + "px", "Final height should be " + t_h + ": " + elem.style.height); - var cur_h = jQuery.css(this,"height"); + var cur_h = jQuery.css(elem,"height"); - ok(this.style.height != "" || cur_h == t_h, "Height should be explicitly set to " + t_h + ", is instead: " + cur_w); + ok(elem.style.height != "" || cur_h == t_h, "Height should be explicitly set to " + t_h + ", is instead: " + cur_w); } if ( t_h == "show" ) { - var old_h = jQuery.css(this, "height"); - jQuery(this).append("
Some more text
and some more..."); + var old_h = jQuery.css(elem, "height"); + jQuery(elem).append("
Some more text
and some more..."); if ( /Auto/.test( fn ) ) { - notEqual(jQuery.css(this, "height"), old_h, "Make sure height is auto."); + notEqual(jQuery.css(elem, "height"), old_h, "Make sure height is auto."); } else { - equals(jQuery.css(this, "height"), old_h, "Make sure height is not auto."); + equals(jQuery.css(elem, "height"), old_h, "Make sure height is not auto."); } } // manually remove generated element - jQuery(this).remove(); + jQuery(elem).remove(); start(); });