diff options
author | Corey Frang <gnarf@gnarf.net> | 2012-05-22 23:04:45 -0400 |
---|---|---|
committer | Dave Methvin <dave.methvin@gmail.com> | 2012-05-22 23:04:45 -0400 |
commit | 4621a0131b98461e41082aa0aaf73f9c6f4ca9ce (patch) | |
tree | 49288a493e5e4bcd6205409f106caac09fc0e5d9 /src | |
parent | ae20e732f02c7e3bdd76324979b1a816c567ec22 (diff) | |
download | jquery-4621a0131b98461e41082aa0aaf73f9c6f4ca9ce.tar.gz jquery-4621a0131b98461e41082aa0aaf73f9c6f4ca9ce.zip |
Optimizations to animation queue/promise logic, closes gh-776.
Diffstat (limited to 'src')
-rw-r--r-- | src/effects.js | 69 | ||||
-rw-r--r-- | src/queue.js | 125 |
2 files changed, 88 insertions, 106 deletions
diff --git a/src/effects.js b/src/effects.js index 41e87e3af..c213e69d2 100644 --- a/src/effects.js +++ b/src/effects.js @@ -4,7 +4,7 @@ var fxNow, timerId, iframe, iframeDoc, elemdisplay = {}, rfxtypes = /^(?:toggle|show|hide)$/, rfxnum = /^([\-+]=)?((?:\d*\.)?\d+)([a-z%]*)$/i, - rrun = /\.run$/, + rrun = /queueHooks$/, animationPrefilters = [ defaultPrefilter ], tweeners = { "*": [function( prop, value ) { @@ -212,12 +212,34 @@ jQuery.Animation = jQuery.extend( Animation, { }); function defaultPrefilter( elem, props, opts ) { - var index, prop, value, length, dataShow, tween, + var index, prop, value, length, dataShow, tween, hooks, oldfire, + anim = this, style = elem.style, orig = {}, handled = [], hidden = elem.nodeType && isHidden( elem ); + // handle queue: false promises + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + anim.always(function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + }); + } + // height/width overflow pass if ( elem.nodeType === 1 && ( props.height || props.width ) ) { // Make sure that nothing sneaks out @@ -244,7 +266,7 @@ function defaultPrefilter( elem, props, opts ) { if ( opts.overflow ) { style.overflow = "hidden"; - this.finish(function() { + anim.finish(function() { style.overflow = opts.overflow[ 0 ]; style.overflowX = opts.overflow[ 1 ]; style.overflowY = opts.overflow[ 2 ]; @@ -270,11 +292,11 @@ function defaultPrefilter( elem, props, opts ) { if ( hidden ) { showHide([ elem ], true ); } else { - this.finish(function() { + anim.finish(function() { showHide([ elem ]); }); } - this.finish(function() { + anim.finish(function() { var prop; jQuery.removeData( elem, "fxshow", true ); for ( prop in orig ) { @@ -283,7 +305,7 @@ function defaultPrefilter( elem, props, opts ) { }); for ( index = 0 ; index < length ; index++ ) { prop = handled[ index ]; - tween = this.createTween( prop, hidden ? dataShow[ prop ] : 0 ); + tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 ); orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop ); if ( !( prop in dataShow ) ) { @@ -482,10 +504,10 @@ jQuery.fn.extend({ this.queue( optall.queue, doAnimation ); }, stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( elem, data, index ) { - var hooks = data[ index ]; - jQuery.removeData( elem, index, true ); - hooks.stop( gotoEnd ); + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); }; if ( typeof type !== "string" ) { @@ -498,30 +520,27 @@ jQuery.fn.extend({ } return this.each(function() { - var index, - hadTimers = false, + var dequeue = true, + index = type != null && type + "queueHooks", timers = jQuery.timers, data = jQuery._data( this ); - // clear marker counters if we know they won't be - if ( !gotoEnd ) { - jQuery._unmark( true, this ); - } - - if ( type == null ) { + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { for ( index in data ) { if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( this, data, index ); + stopQueue( data[ index ] ); } } - } else if ( data[ index = type + ".run" ] && data[ index ].stop ){ - stopQueue( this, data, index ); } for ( index = timers.length; index--; ) { if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { timers[ index ].anim.stop( gotoEnd ); - hadTimers = true; + dequeue = false; timers.splice( index, 1 ); } } @@ -529,7 +548,7 @@ jQuery.fn.extend({ // start the next in the queue if the last step wasn't forced // timers currently will call their complete callbacks, which will dequeue // but only if they were gotoEnd - if ( !( gotoEnd && hadTimers ) ) { + if ( dequeue || !gotoEnd ) { jQuery.dequeue( this, type ); } }); @@ -589,15 +608,13 @@ jQuery.speed = function( speed, easing, fn ) { // Queueing opt.old = opt.complete; - opt.complete = function( noUnmark ) { + opt.complete = function() { if ( jQuery.isFunction( opt.old ) ) { opt.old.call( this ); } if ( opt.queue ) { jQuery.dequeue( this, opt.queue ); - } else if ( noUnmark !== false ) { - jQuery._unmark( this ); } }; diff --git a/src/queue.js b/src/queue.js index 907baf4d6..175603781 100644 --- a/src/queue.js +++ b/src/queue.js @@ -1,68 +1,22 @@ (function( jQuery ) { -function handleQueueMarkDefer( elem, type, src ) { - var deferDataKey = type + "defer", - queueDataKey = type + "queue", - markDataKey = type + "mark", - defer = jQuery._data( elem, deferDataKey ); - if ( defer && - ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && - ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { - // Give room for hard-coded callbacks to fire first - // and eventually mark/queue something else on the element - setTimeout( function() { - if ( !jQuery._data( elem, queueDataKey ) && - !jQuery._data( elem, markDataKey ) ) { - jQuery.removeData( elem, deferDataKey, true ); - defer.fire(); - } - }, 0 ); - } -} - jQuery.extend({ - - _mark: function( elem, type ) { - if ( elem ) { - type = ( type || "fx" ) + "mark"; - jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); - } - }, - - _unmark: function( force, elem, type ) { - if ( force !== true ) { - type = elem; - elem = force; - force = false; - } - if ( elem ) { - type = type || "fx"; - var key = type + "mark", - count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); - if ( count ) { - jQuery._data( elem, key, count ); - } else { - jQuery.removeData( elem, key, true ); - handleQueueMarkDefer( elem, type, "mark" ); - } - } - }, - queue: function( elem, type, data ) { - var q; + var queue; + if ( elem ) { type = ( type || "fx" ) + "queue"; - q = jQuery._data( elem, type ); + queue = jQuery._data( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { - if ( !q || jQuery.isArray(data) ) { - q = jQuery._data( elem, type, jQuery.makeArray(data) ); + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); } else { - q.push( data ); + queue.push( data ); } } - return q || []; + return queue || []; } }, @@ -71,7 +25,10 @@ jQuery.extend({ var queue = jQuery.queue( elem, type ), fn = queue.shift(), - hooks = {}; + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { @@ -79,22 +36,31 @@ jQuery.extend({ } if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { queue.unshift( "inprogress" ); } - jQuery._data( elem, type + ".run", hooks ); - fn.call( elem, function() { - jQuery.dequeue( elem, type ); - }, hooks ); + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); } - - if ( !queue.length ) { - jQuery.removeData( elem, type + "queue " + type + ".run", true ); - handleQueueMarkDefer( elem, type, "queue" ); + if ( !queue.length && hooks ) { + hooks.empty.fire(); } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery.removeData( elem, type + "queue", true ); + jQuery.removeData( elem, key, true ); + }) + }); } }); @@ -117,6 +83,9 @@ jQuery.fn.extend({ this.each(function() { var queue = jQuery.queue( this, type, data ); + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this, type ); } @@ -146,31 +115,27 @@ jQuery.fn.extend({ // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, object ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + 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", - markDataKey = type + "mark", - tmp; - 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 ], markDataKey, undefined, true ) ) && - jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { + if ( (tmp = jQuery._data( elements[ i ], type + "queueHooks" )) && tmp.empty ) { count++; - tmp.add( resolve ); + tmp.empty.add( resolve ); } } resolve(); |