/*!
	jsAnim: Powerful javascript animation
	--------------------------------------------
	Copyright 2009 Kevin Dolan
	-http://www.thekevindolan.com
	
	Code licensed under the MIT license
	Copyright (c) 2009 Kevin Dolan

	Permission is hereby granted, free of charge, to any person
	obtaining a copy of this software and associated documentation
	files (the "Software"), to deal in the Software without
	restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the
	Software is furnished to do so, subject to the following
	conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
	OTHER DEALINGS IN THE SOFTWARE.
	
	v0.2
*/

//These vars are used to hold all jsAnimManagers
var jsAnimManagers = new Array();
var jsAnimManagerId = 0;

/*! public, accessible
	jsAnimManager object constructor
	Used by end-user to manage jsAnim objects
	Params:
		-[timestep] : time between frames, defaults to 40
*/
function jsAnimManager(timestep) {

	jsAnimManagers[jsAnimManagerId] = this;
	this.myId = jsAnimManagerId;
	jsAnimManagerId++;

	if(timestep)
		this.timestep = timestep;
	else	
		this.timestep = 40;
	
	this.paused = false;
	
	this.animObjects = new Array();
	this.index = 0;
	
	//Used internally to process a single frame of all active animations
	this.step = function() {
		if(!this.paused) {
			for(x in this.animObjects) {
				this.animObjects[x].step();
			}
			setTimeout("jsAnimManagers["+this.myId+"].step()",this.timestep);
		}
	};
	
	//Used internally to kill a jsAnimObject
	this.kill = function(id) {
		delete this.animObjects[id];
	};
	
	/*! public
		Called to create a new animation object
		Params:
			-objId : id of object being controlled
	*/
	this.createAnimObject = function(objId) {
		var el = document.getElementById(objId);
		var id = this.index;
		this.animObjects[id] = new jsAnimObject(el, id, this);
		this.index++;
		return this.animObjects[id];
	};
	
	/*! public
		Called to pause the animation manager
	*/
	this.pause = function() {
		this.paused = true;
	};
	
	/*! public
		Called to unpause the animation manager
	*/
	this.resume = function() {
		this.paused = false;
		this.step();
	};
	
	/*! public
		Called to set the appropriate style values to allow position to be controlled by jsAnim
		Params:
			-objId : id of object to be registered
			-[fixed] : fixed positioning, false to absolute fixed, defaults to false
	*/
	this.registerPosition = function(objId, fixed) {
		var el = document.getElementById(objId);
		var width = el.offsetWidth;
		var height = el.offsetHeight;
		
		if(fixed)
			el.style.position = "fixed";
		else
			el.style.position = "absolute";
		
		el.style.top = "0px";
		el.style.left = "50%";
		el.halfWidth = Math.floor(width/2);
		el.halfHeight = Math.floor(height/2);
		el.style.marginLeft = (-el.halfWidth)+"px";
		el.style.marginTop = (-el.halfHeight)+"px";
		
		el.positionRegistered = true;
		
		/*! public
			Called to manually set the position of this object
		*/
		el.setPosition = function(x, y) {
			this.style.marginLeft = (x - this.halfWidth)+"px";
			this.style.marginTop = (y - this.halfHeight)+"px";
		};
		
	};
	
	this.step();
	return true;
}

/*! accesible
	jsAnimObject object constructor
	Used internally to hold the state of a single animation manager
	Params:
		-obj : object being animated
		
*/
function jsAnimObject(obj, id, manager) {
	
	this.id = id;
	this.obj = obj;
	this.paused = false;
	this.animEntries = new Array();
	this.animLoops = new Array();
	this.current = 0;
	this.manager = manager;
	
	this.step = function() {
		if(!this.paused) {
			//Ugly code to get only the first element in the array
			for(x in this.animEntries) {
				var finished = this.animEntries[x].step();
				if(finished) {
					this.animLoops[x]--;
					this.animEntries[x].current = 0;
					this.animEntries[x].onLoop();
				}
				if(this.animLoops[x] == 0) {
					this.animEntries[x].onComplete();
					delete this.animEntries[x];
				}
				break;
			}
		}
	};
	
	/*! public
		Called to add an animation to the chain
		Params:
			-params : a collection in the containing the following elements
				- property : the Prop object to animate
				- [from] : optional from value, if unspecified current value is used
				- to : the value to animate to
				- duration : the length of time this animation should take
				- [ease] : the jsAnimEase object to use, if unspecified linear will be used
				- [loop] : the number of times to loop the animation, negative values are infinite, if unspecified 1 will be used
				- [onLoop] : the callback function for loop completion
				- [onComplete] : the callback function for animation completion
	*/
	this.add = function(params) {
		
		var property = params.property;
		var from = params.from
		var to = params.to;
		var duration = params.duration;
		if(params.ease)
			var ease = params.ease;
		else
			var ease = jsAnimEase.linear;
		if(params.loop)
			var loop = params.loop;
		else
			var loop = 1;
		if(params.onLoop)
			var onLoop = params.onLoop;
		else
			var onLoop = function() {};
		if(params.onComplete)
			var onComplete = params.onComplete;
		else
			var onComplete = function() {};
			
		this.animEntries[this.current] = new jsAnimEntry(this.obj, property, from, to, duration, this.manager.timestep, ease, onLoop, onComplete);
		this.animLoops[this.current] = loop;
		
		this.current++;
		
	};
	
	/*! public
		Called to skip the current animation, can be used to exit an infinite loop
	*/
	this.skip = function() {
		//Ugly code to get only the first element in the array
		for(x in this.animEntries) {
			delete this.animEntries[x];
			break;
		}
	};
	
	/*! public
		Called to pause this animator
	*/
	this.pause = function() {
		this.paused = true;
	};
	
	/*! public
		Called to resum this animator
	*/
	this.resume = function() {
		this.paused = false;
	};
	
	/*! public
		Called to kill this animator
	*/
	this.kill = function() {
		this.manager.kill(this.id);
	};
	
	return true;
}

/*! public, accesible
	Pos object constructor
	Called to store an x and y coordinate representing an object's center
		according to the jsAnim coordinate system
	Params:
		-x : x coordinate, 0 is center, negative is left, positive is right
		-y : y coordinate, 0 is top, positive id below
*/
function Pos(x, y) {
	
	//public
	this.x = x;
	
	//public
	this.y = y;
	
	return true;
}

/*! public, accesible
	Dim object constructor
	Called to store a width/height dimension
	Params:
		-w : width
		-h : height
*/
function Dim(w, h) {
	
	//public
	this.w = w;
	
	//public
	this.h = h;
	
	return true;
}

/*! public, accesible
	Col object constructor
	Called to store an RGB color
	Params:
		-r : red value (0,255)
		-g : green value (0,255)
		-b : blue value (0,255)
*/
function Col(r, g, b) {
	
	//public
	this.r = r;
	
	//public
	this.g = g;
	
	//public
	this.b = b;
	
	return true;
}

/*! 
	jsAnimEntry object constructor
	Used internally to hold the state of single animation entry
	Params:
		-property : jsAnimProp object
		-from : initial value
		-to : end value
		-duration : total time of animation (ms)
		-ease : jsAnimEase object
		-timestep : the timestep value of the animation manager
		-onLoop : called after each loop
		-onComplete : called after completion
*/
function jsAnimEntry(obj, property, from, to, duration, timestep, ease, onLoop, onComplete) {
	
	this.obj = obj;
	this.property = property;
	this.from = from;
	this.to = to;
	this.duration = duration;
	this.timestep = timestep;
	this.ease = ease;
	this.current = 0;
	this.onLoop = onLoop;
	this.onComplete = onComplete;
	
	/*!
		Used internally to move the object one step
		Returns : true if this anim entry has completed, false otherwise
	*/
	this.step = function() {
		if(!this.from)
			this.from = this.property.current(this.obj);
	
		if(this.current >= this.duration) {
			var p = this.ease.transform(1);
			this.property.update(this.obj, this.from, this.to, p);
			return true;
		}
		else {
			var t = this.current / this.duration;
			var p = this.ease.transform(t);
			this.property.update(this.obj, this.from, this.to, p);
			this.current += this.timestep;
			return false;
		}
	};
	
	return true;
}

/*! public
	jsAnimEase objects
	Used to control easing
	Methods:
		transform : Transform a number 0-1 representing a time proportion
			to a new number 0-1 representing a progress proportion 
*/
var jsAnimEase = {
	
	/*!public
		Constant Rate
	*/
	linear : {
		transform : function(t) {
			return t;
		}
	},
	
	/*!public
		Starts slow, then speeds up
	*/
	parabolicPos : {
		transform : function(t) {
			return t * t;
		}
	},
	
	/*!public
		Starts fast, then slows down
	*/
	parabolicNeg : {
		transform : function(t) {
			return 1 - (t-1) * (t-1);
		}
	},
	
	/*!public
		Overshoots target then returns to target
		Params:
			-g : overshoot amount [0-1]
	*/
	backout : function(g) {
		return {
			transform : function(t) {
				return (-1 * t * (t + g - 2)) / (1 - g);
			}
		};
	},
	
	/*!public
		Backs up a bit then moves to target
		Params:
			-g : backup amount [0-1]
	*/
	backin : function(g) {
		return {
			transform : function(t) {
				return 1 + ((t+1-g) * ((t+1-g) + g - 2)) / (1 - g);
			}
		};
	},
	
	/*!public
		Goes to target and then back at constant rate
	*/
	bounceLinear : {
		transform : function(t) {
			if(t < 0.5)
				return 2 * t;
			else
				return 1 - 2 * (t - 0.5)
		}
	},
	
	/*!public
		Goes to target and then back at variable rate
	*/
	bounceParabolic : {
		transform : function(t) {
			return -4 * t * (t-1);
		}
	},
	
	/*!public
		Goes to target and then back smoothly
	*/
	bounceSmooth : {
		transform : function(t) {
			return 0.5 - 0.5 * Math.cos(2 * Math.PI * t);
		}
	}
}

/*!
	Utility objects for internal use
*/
var jsAnimUtil = {

	interp : function (v1, v2, percent) {
		if(isNaN(v1))
			v1 = 0;
		if(isNaN(v2))
			v2 = 0;
		var v =  v1 + percent * (v2-v1);
		return Math.floor(v);
	},
	
	getCSS : function (elem, field) {
		var css = document.defaultView && document.defaultView.getComputedStyle ?
			document.defaultView.getComputedStyle(elem, null)
			: elem.currentStyle || elem.style;
		return css[field];
	},
	
	explode : function ( delimiter, string, limit ) {
		// http://kevin.vanzonneveld.net
		// +     original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
		// +     improved by: kenneth
		// +     improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
		// +     improved by: d3x
		// +     bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
		// *     example 1: explode(' ', 'Kevin van Zonneveld');
		// *     returns 1: {0: 'Kevin', 1: 'van', 2: 'Zonneveld'}
		// *     example 2: explode('=', 'a=bc=d', 2);
		// *     returns 2: ['a', 'bc=d']
	 
		var emptyArray = { 0: '' };
		
		// third argument is not required
		if ( arguments.length < 2 ||
			typeof arguments[0] == 'undefined' ||
			typeof arguments[1] == 'undefined' )
		{
			return null;
		}
	 
		if ( delimiter === '' ||
			delimiter === false ||
			delimiter === null )
		{
			return false;
		}
	 
		if ( typeof delimiter == 'function' ||
			typeof delimiter == 'object' ||
			typeof string == 'function' ||
			typeof string == 'object' )
		{
			return emptyArray;
		}
	 
		if ( delimiter === true ) {
			delimiter = '1';
		}
		
		if (!limit) {
			return string.toString().split(delimiter.toString());
		} else {
			// support for limit argument
			var splitted = string.toString().split(delimiter.toString());
			var partA = splitted.splice(0, limit - 1);
			var partB = splitted.join(delimiter.toString());
			partA.push(partB);
			return partA;
		}
	}
}

/*! public
	Prop objects
	Used to keep track of which property is being controlled
	Methods:
		update : update the property to where it should be at the given time
		current : return a natural representation of the current property
*/
var Prop = {
	/*! public
		Wait, while doing no animating
	*/
	wait : {
		update : function(obj, from, to, percent) {},
		current : function(obj) {return 0;}
	},

	/*! public
		Follows a linear path
	*/
	position : {
		update : function(obj, from, to, percent) {
			var x = jsAnimUtil.interp(from.x, to.x, percent);
			var y = jsAnimUtil.interp(from.y, to.y, percent);
			
			obj.setPosition(x, y);
		},
		
		current : function(obj) {
			var left = parseInt(obj.style.marginLeft);
			var top = parseInt(obj.style.marginTop);
			var x = left + obj.halfWidth;
			var y = top + obj.halfHeight;
			return new Pos(x,y);
		}
	},
	
	/*! public
		Follows a semicircular path
		Params:
			-clockwise : True for clockwise, false otherwise
	*/
	positionSemicircle : function(clockwise) { 
		return {
			update : function(obj, from, to, percent) {
				var centerX = (from.x + to.x) / 2;
				var centerY = (from.y + to.y) / 2;
				
				var h = centerY - from.y;
				var w = from.x - centerX;
				
				var dist = Math.sqrt(h * h + w * w);
				
				if(w == 0) {
					if(h > 0)
						var initAngle = - Math.PI / 2;
					else
						var initAngle = Math.PI / 2;
				}
				else {
					var atan = Math.atan(h / Math.abs(w));
					if(w > 0)
						var initAngle = atan;
					else {
						var initAngle = Math.PI - atan;
					}
				}
				
				if(clockwise)
					var addAngle = - percent * Math.PI;
				else
					var addAngle = percent * Math.PI;	
					
				var angle = initAngle + addAngle;
				
				var x = Math.floor(centerX + dist * Math.cos(angle));
				var y = Math.floor(centerY - dist * Math.sin(angle));
				
				obj.setPosition(x, y);
			},
			
			current : function(obj) {
				var left = parseInt(obj.style.marginLeft);
				var top = parseInt(obj.style.marginTop);
				var x = left + obj.halfWidth;
				var y = top + obj.halfHeight;
				return new Pos(x,y);
			}
		}
	},
	
	/*! public
		Follows a circular path through target then back to start
		Params:
			-clockwise : True for clockwise, false otherwise
	*/
	positionCircle : function(clockwise) { 
		return {
			update : function(obj, from, to, percent) {
				var centerX = (from.x + to.x) / 2;
				var centerY = (from.y + to.y) / 2;
				
				var h = centerY - from.y;
				var w = from.x - centerX;
				
				var dist = Math.sqrt(h * h + w * w);
				
				if(w == 0) {
					if(h > 0)
						var initAngle = - Math.PI / 2;
					else
						var initAngle = Math.PI / 2;
				}
				else {
					var atan = Math.atan(h / Math.abs(w));
					if(w > 0)
						var initAngle = atan;
					else {
						var initAngle = Math.PI - atan;
					}
				}
				
				if(clockwise)
					var addAngle = 2 * percent * Math.PI;
				else
					var addAngle = -2 * percent * Math.PI;	
					
				var angle = initAngle + addAngle;
				
				var x = Math.floor(centerX + dist * Math.cos(angle));
				var y = Math.floor(centerY + dist * Math.sin(angle));
				
				obj.setPosition(x, y);
			},
			
			current : function(obj) {
				var left = parseInt(obj.style.marginLeft);
				var top = parseInt(obj.style.marginTop);
				var x = left + obj.halfWidth;
				var y = top + obj.halfHeight;
				return new Pos(x,y);
			}
		}
	},
	
	//public
	top : {
		update : function(obj, from, to, percent)  {
			obj.style.top = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'top'));
		}
		
	},
	
	//public
	right : {
		update : function(obj, from, to, percent)  {
			obj.style.right = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'right'));
		}
	},
	
	//public
	bottom : {
		update : function(obj, from, to, percent)  {
			obj.style.bottom = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'bottom'));
		}
	},
	
	//public
	left : {
		update : function(obj, from, to, percent)  {
			obj.style.left = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'left'));
		}
	},
	
	//public
	margin : {
		update : function(obj, from, to, percent)  {
			obj.style.margin = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'margin'));
		}
	},
	
	//public
	marginTop : {
		update : function(obj, from, to, percent)  {
			obj.style.marginTop = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'marginTop'));
		}
	},
	
	//public
	marginRight : {
		update : function(obj, from, to, percent)  {
			obj.style.marginRight = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'marginRight'));
		}
	},
	
	//public
	marginBottom : {
		update : function(obj, from, to, percent)  {
			obj.style.marginBottom = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'marginBottom'));
		}
	},
	
	//public
	marginLeft : {
		update : function(obj, from, to, percent)  {
			obj.style.marginLeft = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'marginLeft'));
		}
	},
	
	//public
	padding : {
		update : function(obj, from, to, percent)  {
			obj.style.padding = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'padding'));
		}
	},
	
	//public
	paddingTop : {
		update : function(obj, from, to, percent)  {
			obj.style.paddingTop = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'paddingTop'));
		}
	},
	
	//public
	paddingRight : {
		update : function(obj, from, to, percent)  {
			obj.style.paddingRight = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'paddingRight'));
		}
	},
	
	//public
	paddingBottom : {
		update : function(obj, from, to, percent)  {
			obj.style.paddingBottom = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'paddingBottom'));
		}
	},
	
	//public
	paddingLeft : {
		update : function(obj, from, to, percent)  {
			obj.style.paddingLeft = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'paddingLeft'));
		}
	},
	
	//public
	borderWidth : {
		update : function(obj, from, to, percent)  {
			obj.style.borderWidth = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'borderWidth'));
		}
	},
	
	//public
	borderTopWidth : {
		update : function(obj, from, to, percent)  {
			obj.style.borderTopWidth = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'borderTopWidth'));
		}
	},
	
	//public
	borderRightWidth : {
		update : function(obj, from, to, percent)  {
			obj.style.borderRightWidth = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'borderRightWidth'));
		}
	},
	
	//public
	borderBottomWidth : {
		update : function(obj, from, to, percent)  {
			obj.style.borderBottomWidth = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'borderBottomWidth'));
		}
	},
	
	//public
	borderLeftWidth : {
		update : function(obj, from, to, percent)  {
			obj.style.borderLeftWidth = jsAnimUtil.interp(from, to, percent) + "px";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'borderLeftWidth'));
		}
	},
	
	//public
	fontSize : {
		update : function(obj, from, to, percent)  {
			obj.style.fontSize = jsAnimUtil.interp(from, to, percent) + "pt";
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'fontSize'));
		}
	},
	
	//public
	height : {
		update : function(obj, from, to, percent)  {
			var v = jsAnimUtil.interp(from, to, percent);
			
			obj.style.height = v + "px";
			
			//Update the position if registered
			if(obj.positionRegistered) {
				var y = parseInt(obj.style.marginTop) + obj.halfHeight;
				obj.halfHeight = Math.floor(obj.offsetHeight/2);
				obj.style.marginTop = y - obj.halfHeight + "px";
			}
		},
		
		current : function(obj) {
			var ht = jsAnimUtil.getCSS(obj, 'height');
			if(ht == "auto")
				return obj.offsetHeight;
			else
				return parseInt(ht);
		}
	},
	
	//public
	width : {
		update : function(obj, from, to, percent)  {
			var v = jsAnimUtil.interp(from, to, percent);
			
			obj.style.width = v + "px";
			
			//Update the position if registered
			if(obj.positionRegistered) {
				var x = parseInt(obj.style.marginLeft) + obj.halfWidth;
				obj.halfWidth = Math.floor(obj.offsetWidth/2);
				obj.style.marginLeft = x - obj.halfWidth + "px";
			}
		},
		
		current : function(obj) {
			return parseInt(jsAnimUtil.getCSS(obj, 'width'));
		}
	},
	
	//public
	dimension : {
		update : function(obj, from, to, percent)  {
			var h = jsAnimUtil.interp(from.h, to.h, percent);
			var w = jsAnimUtil.interp(from.w, to.w, percent);
			
			obj.style.height = h + "px";
			obj.style.width = w + "px";
			
			//Update the position if registered
			if(obj.positionRegistered) {
				var y = parseInt(obj.style.marginTop) + obj.halfHeight;
				obj.halfHeight = Math.floor(obj.offsetHeight/2);
				obj.style.marginTop = (y - obj.halfHeight) + "px";
			
				var x = parseInt(obj.style.marginLeft) + obj.halfWidth;
				obj.halfWidth = Math.floor(obj.offsetWidth/2);
				obj.style.marginLeft = (x - obj.halfWidth) + "px";
			}
		},
		
		current : function(obj) {
			var ht = jsAnimUtil.getCSS(obj, 'height');
			if(ht == "auto")
				var h = obj.offsetHeight;
			else
				var h = parseInt(ht);
			var w = parseInt(jsAnimUtil.getCSS(obj, 'width'));
			return new Dim(w, h);
		}
	},
	
	//public
	color : {
		update : function(obj, from, to, percent)  {
			r = jsAnimUtil.interp(from.r, to.r, percent);
			g = jsAnimUtil.interp(from.g, to.g, percent);
			b = jsAnimUtil.interp(from.b, to.b, percent);
			
			obj.style.color = "rgb("+r+","+g+","+b+")";
		},
		
		current : function(obj) {
			var color = jsAnimUtil.getCSS(obj, 'color');
			color = color.substring(4,color.length-1);
			var rgb = jsAnimUtil.explode(",",color);
			return new Col(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2]));
		}
	},
	
	//public
	backgroundColor : {
		update : function(obj, from, to, percent)  {
			r = jsAnimUtil.interp(from.r, to.r, percent);
			g = jsAnimUtil.interp(from.g, to.g, percent);
			b = jsAnimUtil.interp(from.b, to.b, percent);

			obj.style.backgroundColor = "rgb("+r+","+g+","+b+")";
		},
		
		current : function(obj) {
			var color = jsAnimUtil.getCSS(obj, 'backgroundColor');
			color = color.substring(4,color.length-1);
			var rgb = jsAnimUtil.explode(",",color);
			
			return new Col(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2]));
		}
	},
	
	//public
	borderColor : {
		update : function(obj, from, to, percent)  {
			r = jsAnimUtil.interp(from.r, to.r, percent);
			g = jsAnimUtil.interp(from.g, to.g, percent);
			b = jsAnimUtil.interp(from.b, to.b, percent);
			
			obj.style.borderColor = "rgb("+r+","+g+","+b+")";
		},
		
		current : function(obj) {
			var color = jsAnimUtil.getCSS(obj, 'borderColor');
			color = color.substring(4,color.length-1);
			var rgb = jsAnimUtil.explode(",",color);
			return new Col(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2]));
		}
	},
	
	//public
	opacity : {
		update : function(obj, from, to, percent)  {
			v = jsAnimUtil.interp(100*from, 100*to, percent);			
			obj.style.opacity = v / 100;
		},
		
		current : function(obj) {
			return jsAnimUtil.getCSS(obj, 'opacity');
		}
	}
}
