]> source.dussan.org Git - jquery.git/commitdiff
Fix #13103. Add .finish() method. Close gh-1118.
authorCorey Frang <gnarf@gnarf.net>
Tue, 8 Jan 2013 01:16:50 +0000 (01:16 +0000)
committerDave Methvin <dave.methvin@gmail.com>
Tue, 8 Jan 2013 01:19:25 +0000 (01:19 +0000)
src/effects.js
src/queue.js
test/unit/effects.js

index f4f435fd22760b378e4949ae1659fe9dc8c741c2..7c73a920b81976f7bd6941b442fe0becf5116ff8 100644 (file)
@@ -72,6 +72,7 @@ function createTweens( animation, props ) {
 
 function Animation( elem, properties, options ) {
        var result,
+               stopped,
                index = 0,
                length = animationPrefilters.length,
                deferred = jQuery.Deferred().always( function() {
@@ -79,6 +80,9 @@ function Animation( elem, properties, options ) {
                        delete tick.elem;
                }),
                tick = function() {
+                       if ( stopped ) {
+                               return false;
+                       }
                        var currentTime = fxNow || createFxNow(),
                                remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
                                // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
@@ -120,7 +124,10 @@ function Animation( elem, properties, options ) {
                                        // if we are going to the end, we want to run all the tweens
                                        // otherwise we skip this part
                                        length = gotoEnd ? animation.tweens.length : 0;
-
+                               if ( stopped ) {
+                                       return this;
+                               }
+                               stopped = true;
                                for ( ; index < length ; index++ ) {
                                        animation.tweens[ index ].run( 1 );
                                }
@@ -468,12 +475,15 @@ jQuery.fn.extend({
                        doAnimation = function() {
                                // Operate on a copy of prop so per-property easing won't be lost
                                var anim = Animation( this, jQuery.extend( {}, prop ), optall );
-
-                               // Empty animations resolve immediately
-                               if ( empty ) {
+                               doAnimation.finish = function() {
+                                       anim.stop( true );
+                               };
+                               // Empty animations, or finishing resolves immediately
+                               if ( empty || jQuery._data( this, "finish" ) ) {
                                        anim.stop( true );
                                }
                        };
+                       doAnimation.finish = doAnimation;
 
                return empty || optall.queue === false ?
                        this.each( doAnimation ) :
@@ -528,6 +538,47 @@ jQuery.fn.extend({
                                jQuery.dequeue( this, type );
                        }
                });
+       },
+       finish: function( type ) {
+               if ( type !== false ) {
+                       type = type || "fx";
+               }
+               return this.each(function() {
+                       var index,
+                               data = jQuery._data( this ),
+                               queue = data[ type + "queue" ],
+                               hooks = data[ type + "queueHooks" ],
+                               timers = jQuery.timers,
+                               length = queue ? queue.length : 0;
+
+                       // enable finishing flag on private data
+                       data.finish = true;
+
+                       // empty the queue first
+                       jQuery.queue( this, type, [] );
+
+                       if ( hooks && hooks.cur && hooks.cur.finish ) {
+                               hooks.cur.finish.call( this );
+                       }
+
+                       // look for any active animations, and finish them
+                       for ( index = timers.length; index--; ) {
+                               if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+                                       timers[ index ].anim.stop( true );
+                                       timers.splice( index, 1 );
+                               }
+                       }
+
+                       // look for any animations in the old queue and finish them
+                       for ( index = 0; index < length; index++ ) {
+                               if ( queue[ index ] && queue[ index ].finish ) {
+                                       queue[ index ].finish.call( this );
+                               }
+                       }
+
+                       // turn off finishing flag
+                       delete data.finish;
+               });
        }
 });
 
index c5a0cbd7dec9577a9a67d838d307d7c8c05ddf31..d4c3f0040a63d572c79922499e81d903191dbaa1 100644 (file)
@@ -35,6 +35,7 @@ jQuery.extend({
                        startLength--;
                }
 
+               hooks.cur = fn;
                if ( fn ) {
 
                        // Add a progress sentinel to prevent the fx queue from being
index 5ab1d2ac8dcef8ed46b5114d598eb9e8b28037f5..1e9093fb878341da3c32cbab523d0225f2562def 100644 (file)
@@ -1875,4 +1875,53 @@ test( "jQuery.fx.start & jQuery.fx.stop hook points", function() {
        jQuery.fx.stop = oldStop;
 });
 
+test( ".finish() completes all queued animations", function() {
+       var animations = {
+                       top: 100,
+                       left: 100,
+                       height: 100,
+                       width: 100
+               },
+               div = jQuery("<div>");
+
+       expect( 11 );
+
+       jQuery.each( animations, function( prop, value ) {
+               var anim = {};
+               anim[ prop ] = value;
+               // the delay shouldn't matter at all!
+               div.css( prop, 1 ).animate( anim, function() {
+                       ok( true, "Called animation callback" );
+               }).delay( 100 );
+       });
+       equal( div.queue().length, 8, "8 animations in the queue" );
+       div.finish();
+       jQuery.each( animations, function( prop, value ) {
+               equal( parseFloat( div.css( prop ) ), value, prop + " finished at correct value" );
+       });
+       equal( div.queue().length, 0, "empty queue when done" );
+       equal( div.is(":animated"), false, ":animated doesn't match" );
+
+       // cleanup
+       div.remove();
+       // leaves a "shadow timer" which does nothing around, need to force a tick
+       jQuery.fx.tick();
+});
+
+test( ".finish() calls finish of custom queue functions", function() {
+       function queueTester( next ) {
+
+       }
+       var div = jQuery( "<div>" );
+
+       expect( 3 );
+       queueTester.finish = function() {
+               ok( true, "Finish called on custom queue function" );
+       };
+
+       div.queue( queueTester ).queue( queueTester ).queue( queueTester ).finish();
+
+       div.remove();
+});
+
 } // if ( jQuery.fx )