From 581e4d2c224da2ff910ecac9e589fff8a161cddd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20F=C3=BCrstenberg?= <carl@excito.com>
Date: Mon, 15 Mar 2010 01:09:54 +0100
Subject: [PATCH] Animation slots

When multiple animations are meant to be executed in "parallel",
currently they will be unsynchronus and thus experience an delayed
animation for later elements. To overcome this issue, we add an slot
mechanism which specifies that we can use one single slot for a bounch
of animations, and they all will use the same start time and current
time.
---
 src/effects.js |   87 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 66 insertions(+), 21 deletions(-)

diff --git a/src/effects.js b/src/effects.js
index 97456cc..02934de 100644
--- a/src/effects.js
+++ b/src/effects.js
@@ -105,7 +105,7 @@ jQuery.fn.extend({
 					.animate({opacity: to}, speed, callback);
 	},
 
-	animate: function( prop, speed, easing, callback ) {
+	animate: function( prop, speed, easing, callback, slot ) {
 		var optall = jQuery.speed(speed, easing, callback);
 
 		if ( jQuery.isEmptyObject( prop ) ) {
@@ -152,6 +152,7 @@ jQuery.fn.extend({
 			opt.curAnim = jQuery.extend({}, prop);
 
 			jQuery.each( prop, function( name, val ) {
+					var cur_slot = slot || new jQuery.slot( now(), true );
 				var e = new jQuery.fx( self, opt, name );
 
 				if ( rfxtypes.test(val) ) {
@@ -177,10 +178,10 @@ jQuery.fn.extend({
 							end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
 						}
 
-						e.custom( start, end, unit );
+						e.custom( start, end, unit, cur_slot );
 
 					} else {
-						e.custom( start, val, "" );
+						e.custom( start, val, "", cur_slot );
 					}
 				}
 			});
@@ -200,14 +201,20 @@ jQuery.fn.extend({
 		this.each(function() {
 			// go in reverse order so anything added to the queue during the loop is ignored
 			for ( var i = timers.length - 1; i >= 0; i-- ) {
-				if ( timers[i].elem === this ) {
-					if (gotoEnd) {
-						// force the next step to be the last
-						timers[i](true);
+				var slot = timers[i];
+				for ( var j = slot.members.length - 1; j >= 0; j-- ) {
+					if ( slot.members[j].elem === this ) {
+						if (gotoEnd) {
+							// force the next step to be the last
+							slot.members[j](true);
+						}
+						slot.members.splice(j, 1);
 					}
-
+				}
+				if ( !slot.members.length  ) {
 					timers.splice(i, 1);
 				}
+
 			}
 		});
 
@@ -281,6 +288,13 @@ jQuery.extend({
 
 	timers: [],
 
+	slot: function(time, active) {
+		this.members = [];
+		this.time = this.startTime = time || now();
+		this.added = false;
+		this.active = typeof active == 'undefined' ? false : active;
+	},
+
 	fx: function( elem, options, prop ) {
 		this.options = options;
 		this.elem = elem;
@@ -293,6 +307,19 @@ jQuery.extend({
 
 });
 
+jQuery.slot.prototype = {
+	activate: function() {
+		this.active = true;
+		if( !this.added ) {
+			jQuery.timers.push( this );
+			this.added = true;
+		}
+	},
+	reset: function() {
+		this.startTime = this.time = now();
+	}
+}
+
 jQuery.fx.prototype = {
 	// Simple function for setting a style value
 	update: function() {
@@ -319,8 +346,8 @@ jQuery.fx.prototype = {
 	},
 
 	// Start an animation from one number to another
-	custom: function( from, to, unit ) {
-		this.startTime = now();
+	custom: function( from, to, unit, slot ) {
+		this.startTime = slot.startTime;
 		this.start = from;
 		this.end = to;
 		this.unit = unit || this.unit || "px";
@@ -329,13 +356,20 @@ jQuery.fx.prototype = {
 
 		var self = this;
 		function t( gotoEnd ) {
-			return self.step(gotoEnd);
+			return self.step(gotoEnd, slot);
 		}
 
 		t.elem = this.elem;
 
-		if ( t() && jQuery.timers.push(t) && !timerId ) {
-			timerId = setInterval(jQuery.fx.tick, 13);
+		if( t() ) {
+			slot.members.push( t );
+			if( slot.active && !slot.added ) {
+				jQuery.timers.push( slot );
+				slot.added = true;
+			}
+			if( ! timerId ) {
+				timerId = setInterval(jQuery.fx.tick, 13);
+			}
 		}
 	},
 
@@ -365,10 +399,10 @@ jQuery.fx.prototype = {
 	},
 
 	// Each step of an animation
-	step: function( gotoEnd ) {
-		var t = now(), done = true;
+	step: function( gotoEnd, slot ) {
+		var done = true;
 
-		if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+		if ( gotoEnd || slot.time >= this.options.duration + slot.startTime ) {
 			this.now = this.end;
 			this.pos = this.state = 1;
 			this.update();
@@ -414,7 +448,7 @@ jQuery.fx.prototype = {
 			return false;
 
 		} else {
-			var n = t - this.startTime;
+			var n = slot.time - slot.startTime;
 			this.state = n / this.options.duration;
 
 			// Perform the easing function, defaults to swing
@@ -436,9 +470,18 @@ jQuery.extend( jQuery.fx, {
 		var timers = jQuery.timers;
 
 		for ( var i = 0; i < timers.length; i++ ) {
-			if ( !timers[i]() ) {
+			var slot = timers[i];
+			slot.time = now();
+			for ( var j = 0; j < slot.members.length; j++ ) {
+				if ( !slot.members[j]() ) {
+					slot.members.splice(j--, 1);
+				}
+			}
+			if ( !slot.members.length  ) {
+				slot.added = false;
 				timers.splice(i--, 1);
 			}
+
 		}
 
 		if ( !timers.length ) {
@@ -475,8 +518,10 @@ jQuery.extend( jQuery.fx, {
 
 if ( jQuery.expr && jQuery.expr.filters ) {
 	jQuery.expr.filters.animated = function( elem ) {
-		return jQuery.grep(jQuery.timers, function( fn ) {
-			return elem === fn.elem;
-		}).length;
+		return jQuery.grep(jQuery.timers, function( slot ) {
+				return jQuery.grep(slot.members, function( fn ) {
+						return elem === fn.elem;
+					}).length;
+			}).length;
 	};
 }
-- 
1.7.0

