/*

	Javascript animation object model
	
	Requires classes.js & cpuObjectModel.js
	
	Author: Mantzoukas Alexander
	Company: Enorasis S.A.
	Version: 4.3.6.0
	Email: alexander.mantzoukas@enorasis.com

*/

var AnimationType = new Enum(
	{
		Linear: 1,
		Decelerated: 2,
		Accelerated: 3
	}
);

var Animation = new Class();

Animation.Inherit( Process );

Animation.Declare(
	{
		Animations: Static(new Array()),
		
		GetAllOfType: function( type )
		{
			var animArray = new Array();
			for(var i = 0; i < this.Animations.length; ++i)
				if (this.Animations[i].Type == type)
					animArray[ animArray.length ] = this.Animations[i];
			return animArray;
		},
		
		GetAllOfTypeOnElement: function( type, el )
		{
			var animArray = new Array();
			for(var i = 0; i < this.Animations.length; ++i)
				if (this.Animations[i].Type == type && this.Animations[i].element[0] == el)
					animArray[ animArray.length ] = this.Animations[i];
			return animArray;
		},
		
		StopAllOfTypeOnElement: function( type, el )
		{
			if (typeof el == "string")
				el = document.getElementById(el);
			
			for(var i = 0; i < this.Animations.length; ++i)
			{
				for(var j = 0; j < this.Animations[i].element.length; ++j)
				{
					if (this.Animations[i].Type == type && this.Animations[i].element[j] == el
						&& this.Animations[i] != this)
					{
						this.Animations[i].Stop();
						break;
					}
				}
			}
		},
		
		RemoveAllOfTypeOnElement: function( type, el )
		{
			if (typeof el == "string")
				el = document.getElementById(el);
			
			for(var i = 0; i < this.Animations.length; ++i)
			{
				for(var j = 0; j < this.Animations[i].element.length; ++j)
				{
					if (this.Animations[i].Type == type && this.Animations[i].element[j] == el
						&& this.Animations[i] != this)
					{
						this.Animations[i].End();
						this.Animations.splice(i, 1);
						--i;
						break;
					}
				}
			}
		},
		
		element: new Array(),
		property: new Array(),
		prefix: new Array(),
		currentValue: new Array(),
		suffix: new Array(),
		startingValue: new Array(),
		time: new Array(),
		targetValue: new Array(),
		
		isTemporary: true,
		
		AnimatedAsType: AnimationType.Decelerated,
		
		Constructor: function( name, main )
		{
			this.CreateThread( Reference(this.threadMain, this) );
			
			this.Priority = 1;
			this.Animations[ this.Animations.length ] = this;
		},
		
		threadMain: function()
		{
			if (this.onAnimate && this.onAnimate())
			{
				for(var n = 0; n < this.element.length; ++n)
				{
					if (this.element[n].style[ this.property[n] ] != null)
					{
						this.element[n].style[ this.property[n] ]
							= this.prefix[n] + this.currentValue[n] + this.suffix[n];
					}
					else if (this.element[n][ this.property[n] ] != null)
					{
						this.element[n][ this.property[n] ]
							= this.prefix[n] + this.currentValue[n] + this.suffix[n];
					}
				}
			}
			else
			{
				this.Reinit();
				if (this.onEnd) this.onEnd();
				if (this.isTemporary)
					this.End();
			}
		},
		
		RegisterAnimation: function(el, prop, pre, cValue, suf, time, targetValue)
		{
			if (typeof el == "string")
				this.element[ this.element.length ] = document.getElementById(el);
			else
				this.element[ this.element.length ] = el;
			
			this.property[ this.property.length ] = prop;
			this.prefix[ this.prefix.length ] = pre;
			this.currentValue[ this.currentValue.length ] = cValue;
			this.suffix[ this.suffix.length ] = suf;
			this.startingValue[ this.startingValue.length ] = cValue;
			this.time[ this.time.length ] = time;
			this.targetValue[ this.targetValue.length ] = targetValue;
		},
		
		PreRegisterAnimation: function(prop, pre, cValue, suf, targetValue)
		{
			this.element[ this.element.length ] = null;
			this.property[ this.property.length ] = prop;
			this.prefix[ this.prefix.length ] = pre;
			this.currentValue[ this.currentValue.length ] = cValue;
			this.suffix[ this.suffix.length ] = suf;
			this.startingValue[ this.startingValue.length ] = cValue;
			this.time[ this.time.length ] = null;
			this.targetValue[ this.targetValue.length ] = targetValue;
		},
		
		Clean: function()
		{
			this.Stop();
			while(this.element.length)
			{
				this.element.splice(0,1);
				this.property.splice(0,1);
				this.prefix.splice(0,1);
				this.currentValue.splice(0,1);
				this.suffix.splice(0,1);
				this.startingValue.splice(0,1);
				this.time.splice(0,1);
				this.targetValue.splice(0,1);
			}
		},
		
		Reinit: function()
		{
			this.Stop();
			for(var n = 0; n < this.currentValue.length; ++n)
				this.currentValue[ n ] = this.startingValue[ n ];
		},
		
		CopyStateInAnimation: function()
		{
			for(var n = 0; n < this.currentValue.length; ++n)
			{
				var str = null;
				if (this.element[n].style[ this.property[n] ])
					str = String( this.element[n].style[ this.property[n] ] );
				var num = this.startingValue[n];
				
				try
				{
					str = str.match(/-?[0123456789\.]+/);
					num = Number( str );
					if ( num == null || num == 'undefined' || isNaN(num) )
						num = this.startingValue[n];
				}
				catch(e)
				{};
				
				this.startingValue[n] = num;
				this.currentValue[n] = num;
			}
		},
		
		Restart: function()
		{
			this.Stop();
			for(var n = 0; n < this.currentValue.length; ++n)
				this.currentValue[ n ] = this.startingValue[ n ];
			this.Start();
		},
		
		RunOn: function(el, time)
		{
			this.Stop();
			if (typeof el == "string")
				el = document.getElementById(el);
			for(var n = 0; n < this.element.length; ++n)
			{
				this.element[ n ] = el;
				if (time) this.time[ n ] = time;
			}
			this.CopyStateInAnimation();
			this.Start();
		},
		
		Begin: function(time)
		{
			if (time)
				for(var n = 0; n < this.element.length; ++n)
					this.time[ n ] = time;
			this.Start();
		},
		
		onAnimate: function()
		{
			var animates = false;
			
			for(var i = 0; i < this.currentValue.length; ++i)
				if (this.currentValue[ i ] != null)
			{
				if (this.targetValue[ i ] == null) return false;
				var dist = Math.abs(this.currentValue[ i ] - this.targetValue[ i ]);
				if ( dist > 0.009 )
				{
					animates = true;
					break;
				}
			}
			
			if ( animates )
			{
				for(var i = 0; i < this.currentValue.length; ++i)
					if (this.currentValue[ i ] != null)
				{
					var dx = 0;
					var dist = Math.abs(this.currentValue[ i ] - this.targetValue[ i ]);
					
					switch(this.AnimatedAsType)
					{
						case AnimationType.Linear:
							var maxDist = Math.abs(this.startingValue[ i ] - this.targetValue[ i ]);
							dx = maxDist / this.time[ i ];
							break;
						case AnimationType.Decelerated:
							var maxDist = Math.abs(this.startingValue[ i ] - this.targetValue[ i ]);
							var dist2 = maxDist*maxDist -
								(maxDist - dist)*
								(maxDist - dist);
							dx = Math.sqrt(dist2) / this.time[ i ];
							break;
						case AnimationType.Accelerated:
							var distFromStart = Math.abs(this.currentValue[ i ] - this.startingValue[ i ]);
							var maxDist = Math.abs(this.startingValue[ i ] - this.targetValue[ i ]);
							var dist2 = maxDist*maxDist -
								(maxDist - distFromStart)*
								(maxDist - distFromStart);
							dx = Math.sqrt(dist2) / this.time[ i ];
							if (dx < 0.000001)
								dx = maxDist / this.time[ i ];
							break;
					}
					
					if (this.currentValue[ i ] < this.targetValue[ i ])
					{
						this.currentValue[ i ] += dx;
						if (this.currentValue[ i ] > this.targetValue[ i ])
							this.currentValue[ i ] = this.targetValue[ i ];
					}
					else
					{
						this.currentValue[ i ] -= dx;
						if (this.currentValue[ i ] < this.targetValue[ i ])
							this.currentValue[ i ] = this.targetValue[ i ];
					}
					
					dist = Math.abs(this.currentValue[ i ] - this.targetValue[ i ]);
					if (dist < 0.01)
					{
						this.currentValue[ i ] = this.targetValue[ i ];
					}
				}
				return true;
			}
			
			for(var i = 0; i < this.currentValue.length; ++i)
				if (this.currentValue[ i ] != null)
					this.currentValue[ i ] = this.targetValue[ i ];
			return false;
		},
		
		onEnd: null
	}
);

var ColorAnimation = new Class();

ColorAnimation.Inherit( Animation );

ColorAnimation.Declare(
	{
		Constructor: function( name, main )
		{
		},
		
		threadMain: function()
		{
			if (this.onAnimate && this.onAnimate())
			{
				for(var n = 3; n < this.element.length; n+=4)
				{
					if (this.element[n].style[ this.property[n] ] != null)
					{
						this.element[n].style[ this.property[n] ]
							= this.prefix[n] + "rgb(" + 
								Math.round( this.currentValue[n-3] ) + ", " +
								Math.round( this.currentValue[n-2] ) + ", " +
								Math.round( this.currentValue[n-1] ) +
								")" + this.suffix[n];
					}
				}
			}
			else
			{
				this.Reinit();
				if (this.onEnd) this.onEnd();
				if (this.isTemporary)
					this.End();
			}
		},
		
		RegisterAnimation: function(el, prop, pre, cValue, suf, time, targetValue)
		{
			for(var i = 0; i < 3; ++i)
			{
				this.element[ this.element.length ] = null;
				this.property[ this.property.length ] = prop;
				this.prefix[ this.prefix.length ] = null;
				this.currentValue[ this.currentValue.length ] = cValue[i];
				this.suffix[ this.suffix.length ] = null;
				this.startingValue[ this.startingValue.length ] = cValue[i];
				this.time[ this.time.length ] = null;
				this.targetValue[ this.targetValue.length ] = targetValue[i];
			}
			
			if (typeof el == "string")
				this.element[ this.element.length ] = document.getElementById(el);
			else
				this.element[ this.element.length ] = el;
			
			this.property[ this.property.length ] = prop;
			this.prefix[ this.prefix.length ] = pre;
			this.currentValue[ this.currentValue.length ] = cValue[i];
			this.suffix[ this.suffix.length ] = suf;
			this.startingValue[ this.startingValue.length ] = cValue[i];
			this.time[ this.time.length ] = time;
			this.targetValue[ this.targetValue.length ] = targetValue[i];;
		},
		
		PreRegisterAnimation: function(prop, pre, cValue, suf, targetValue)
		{
			for(var i = 0; i < 3; ++i)
			{
				this.element[ this.element.length ] = null;
				this.property[ this.property.length ] = prop;
				this.prefix[ this.prefix.length ] = null;
				this.currentValue[ this.currentValue.length ] = cValue[i];
				this.suffix[ this.suffix.length ] = null;
				this.startingValue[ this.startingValue.length ] = cValue[i];
				this.time[ this.time.length ] = null;
				this.targetValue[ this.targetValue.length ] = targetValue[i];
			}
			
			this.element[ this.element.length ] = null;
			this.property[ this.property.length ] = prop;
			this.prefix[ this.prefix.length ] = pre;
			this.currentValue[ this.currentValue.length ] = null;
			this.suffix[ this.suffix.length ] = suf;
			this.startingValue[ this.startingValue.length ] = null;
			this.time[ this.time.length ] = null;
			this.targetValue[ this.targetValue.length ] = null;
		},
		
		CopyStateInAnimation: function()
		{
			for(var n = 3; n < this.currentValue.length; n += 4)
			{
				var str = null;
				if (this.element[n].style[ this.property[n] ] != null)
					str = String( this.element[n].style[ this.property[n] ] );
				
				var num1 = this.startingValue[ n - 3 ];
				var num2 = this.startingValue[ n - 2 ];
				var num3 = this.startingValue[ n - 1 ];
				
				try
				{
					var nums = str.match(/[Rr][Gg][Bb]\s*\(\s*([0123456789]+)\s*,\s*([0123456789]+)\s*,\s*([0123456789]+)\s*\)/);
					
					if (!nums)
					{
						var nums = str.match(/#([0123456789aAbBcCdDeEfF]{1,2})([0123456789aAbBcCdDeEfF]{1,2})([0123456789aAbBcCdDeEfF]{1,2})/);
						num1 = Number( eval( "0x" + nums[1] ) );
						num2 = Number( eval( "0x" + nums[2] ) );
						num3 = Number( eval( "0x" + nums[3] ) );
					}
					else
					{
						num1 = Number( nums[1] );
						num2 = Number( nums[2] );
						num3 = Number( nums[3] );
					}
					
					if ( num1 == null || num1 == 'undefined' || isNaN(num1) )
						num1 = this.startingValue[ n - 3 ];
					if ( num2 == null || num2 == 'undefined' || isNaN(num2) )
						num2 = this.startingValue[ n - 2 ];
					if ( num3 == null || num3 == 'undefined' || isNaN(num3) )
						num3 = this.startingValue[ n - 1 ];
				}
				catch(e)
				{};
				
				this.startingValue[ n - 3 ] = num1;
				this.currentValue[ n - 3 ] = num1;
				
				this.startingValue[ n - 2 ] = num2;
				this.currentValue[ n - 2 ] = num2;
				
				this.startingValue[ n - 1 ] = num3;
				this.currentValue[ n - 1 ] = num3;
			}
		}
	}
);

var FadeOut = new Class();

FadeOut.Inherit(Animation);

FadeOut.Declare(
	{
		Constructor: function( name, main )
		{
			this.PreRegisterAnimation( "opacity", "", 0.99, "", 0.01 );
			this.PreRegisterAnimation( "filter", "alpha(opacity=", 99, ")", 1 );
			this.Priority = 1;
		}
	}
);

var FadeIn = new Class();

FadeIn.Inherit(Animation);

FadeIn.Declare(
	{
		Constructor: function( name, main )
		{
			this.PreRegisterAnimation( "opacity", "", 0.01, "", 0.99 );
			this.PreRegisterAnimation( "filter", "alpha(opacity=", 1, ")", 99 );
			this.Priority = 1;
		}
	}
);

var MoveTo = new Class();

MoveTo.Inherit(Animation);

MoveTo.Declare(
	{
		Constructor: function( name, main, fx, fy, tx, ty )
		{
			this.PreRegisterAnimation( "left", "", fx, "px", tx );
			this.PreRegisterAnimation( "top", "", fy, "px", ty );
			this.Priority = 1;
		}
	}
);
