/**
 * Animation collection, based on Michael Fosters X Library
 */
var Animator = {};
Animator.Base = __class__();
Animator.Base.prototype = {
	__construct : function(iRes)
	{
		this.res = iRes || 10;	
	},
	
	init : function (sEleId, iTotalTime, fnOnRun, fnOnTarget, fnOnEnd, iAcceleration, iQuarterCycles) //iQuorterCycles - bounces 1, 2, 3, etc.; default is 0, no bounce
	{
		this.ele = $get(sEleId) || {};
		this.totalTime = iTotalTime || 0;
		this.onRun = fnOnRun || TOOL.dummy; 
		this.onTarget = fnOnTarget || TOOL.dummy; 
		this.onEnd = fnOnEnd || TOOL.dummy;
		this.acceleration = iAcceleration || 0;
		this.velocity = this.velocityFunctions[this.acceleration];
		this.quarterCycles = 1 + (iQuarterCycles || 0);
		this.freq = 1 / this.totalTime;
		if(this.acceleration)
		{
			this.freq *= this.quarterCycles * Math.PI;
			if(this.acceleration == 1 || this.acceleration == 2)
			{
				this.freq /= 2;
			}
			else
			{
				this.quarterCycles = 1;
			}
		}
		this.xDisplacement = this.x2 - this.x1;
		this.yDisplacement = this.y2 - this.y1;
		this.zDisplacement = this.z2 - this.z1;
	},
	
	animationEngine : function()
	{
		this.elapsedTime = new Date().getTime() - this.startTime;
		if (this.elapsedTime < this.totalTime)
		{
			this.factor = this.velocity(this.elapsedTime * this.freq);
			this.x = this.xDisplacement * this.factor + this.x1;
			this.y = this.yDisplacement * this.factor + this.y1;
			this.z = this.zDisplacement * this.factor + this.z1;
			this.onRun();
		}
		else
		{
			clearInterval(this.timer); 
			this.timer = null;
			if(this.quarterCycles%2)
			{
				this.x = this.x2; this.y = this.y2; this.z = this.z2;
			}
			else
			{
				this.x = this.x1; this.y = this.y1; this.z = this.z1;
			}
			this.onTarget(this);
			var vRepeat = false;
			if(typeof this.onEnd == 'function') vRepeat = this.onEnd(this);
			else if (typeof this.onEnd == 'string') vRepeat = eval(this.onEnd);
			if(vRepeat) this.resume(1);
		}
	},
	
	run : function(bResume)
	{
		if(!bResume) this.startTime = new Date().getTime();
		var fnEngine = this.animationEngine.bind(this);
		if(!this.timer) this.timer = setInterval (fnEngine, this.res);	
	},
	
	pause : function()
	{
		clearInterval(this.timer);
		this.timer = null;
	},
	
	resume : function(bFreshStart)
	{
		if (typeof this.timer != 'undefined' && !this.timer)
		{
			this.startTime = new Date().getTime();
			if (!bFreshStart) this.startTime -= this.elapsedTime;
			this.run(!bFreshStart);
		}
	},
	
	velocityFunctions : [
		function(r){return r;}, //constant velocity
		function(r){return Math.abs(Math.sin(r));}, // sine acceleration - start fast, end slow
		function(r){return 1-Math.abs(Math.cos(r));}, // cosine acceleration - start slow, end fast
		function(r){return (1-Math.cos(r))/2;} // combined  acceleration - start fast, move constant, end fast
	]
}
Animator.Arc = __class__();
Animator.Arc.inherits(Animator.Base, {
	__construct : function()
	{
		this.parent.constructor.apply(this, arguments);
	},
	
	animate : function(sEleId, iRadii1, iRadii2, iStartAngle, iEndAngle, iTotalTime, iAcceleration, iBounces, fnOnEnd)
	{
		//start and end angles
		this.x1 = iStartAngle * (Math.PI / 180);
		this.x2 = iEndAngle * (Math.PI / 180);
		
		//start point
		var x0 = TOOL.getComputedStyle(sEleId, 'left', true) + (TOOL.getComputedStyle(sEleId, 'width', true)/2);
		var y0 = TOOL.getComputedStyle(sEleId, 'top', true) + (TOOL.getComputedStyle(sEleId, 'height', true)/2);
		
		//arc center start
		this.xc = x0 - (iRadii1 * Math.cos(this.x1));
		this.yc = y0 - (iRadii2 * Math.sin(this.x1));
		
		//elipse radii
		this.elipseRadiiX = iRadii1;
		this.elipseRadiiY = iRadii2;
		
		var fnHandle = this.arc.bind(this);
		this.init(sEleId, iTotalTime, fnHandle, fnHandle, fnOnEnd, iAcceleration, iBounces);
		this.run()
	},
	
	arc : function()
	{
		this.ele.style.left = (Math.round(this.elipseRadiiX * Math.cos(this.x) + this.xc - (TOOL.getComputedStyle(this.ele, 'width', true)/2))) + 'px';
		this.ele.style.top = (Math.round(this.elipseRadiiY * Math.sin(this.x) + this.yc - (TOOL.getComputedStyle(this.ele, 'height', true)/2))) + 'px';
	}
});
Animator.Css = __class__();
Animator.Css.inherits(Animator.Base,{
	__construct : function()
	{
		this.parent.constructor.apply(this, arguments);
	},
	
	animate : function(sEleId, sCssProp, iTargetValue, iTotalTime, iAType, iBounces, fnOnEnd)
	{
		this.x1 = TOOL.getComputedStyle(sEleId, sCssProp, true);
		this.x2 = iTargetValue;
		this.prop = TOOL.camelize(sCssProp);
		var fnSetStyle = this.setStyle.bind(this);
		this.init(sEleId, iTotalTime, fnSetStyle, fnSetStyle, fnOnEnd, iAType, iBounces);
		this.run();
	},
	
	setStyle : function()
	{
		this.ele.style[this.prop] = Math.round(this.x) + 'px';
	}
});
Animator.Opacity = __class__();
Animator.Opacity.inherits(Animator.Base, {
	__construct : function()
	{
		this.parent.constructor.apply(this, arguments);
	},
	
	animate : function(sEleId, iOpacity, iTotalTime, iAcceleration, iQuarterCycles, fnOnEnd)
	{
		this.x1 = $get(sEleId).getOpacity();
		this.x2 = iOpacity;
		var fnHandle = this.shade.bind(this);
		this.init(sEleId, iTotalTime, fnHandle, fnHandle, fnOnEnd, iAcceleration, iQuarterCycles);
		this.run();
	},
	
	shade : function()
	{
		this.ele.setOpacity(this.x);
	}
});
