/**
 * Calculate the current value without any easing
 * @param {Number} t Elapsed time (in milliseconds)
 * @param {Number} b Begin value
 * @param {Number} c The current difference (between begin value and end value)
 * @param {Number} d The duration of transition (in milliseconds)
 */
Math.easeNone = function(t, b, c, d) { return c * t/ d + b; }
/**
 * Calculate the current value without any easing
 * @param {Number} t Elapsed time (in milliseconds)
 * @param {Number} b Begin value
 * @param {Number} c The current difference (between begin value and end value)
 * @param {Number} d The duration of transition (in milliseconds)
 */
Math.easeIn = function(t, b, c, d) { return c * t / d + b; }
/**
 * Calculate the current value without any easing
 * @param {Number} t Elapsed time (in milliseconds)
 * @param {Number} b Begin value
 * @param {Number} c The current difference (between begin value and end value)
 * @param {Number} d The duration of transition (in milliseconds)
 */
Math.easeOut = function(t, b, c, d) { return c * t / d + b; }
/**
 * Calculate the current value without any easing
 * @param {Number} t Elapsed time (in milliseconds)
 * @param {Number} b Begin value
 * @param {Number} c The current difference (between begin value and end value)
 * @param {Number} d The duration of transition (in milliseconds)
 */
Math.easeInOut = function(t, b, c, d) { return c * t / d + b; }
/**
 * Calculate the current value with quad easing-in
 * @param {Number} t Elapsed time (in milliseconds)
 * @param {Number} b Begin value
 * @param {Number} c The current difference (between begin value and end value)
 * @param {Number} d The duration of transition (in milliseconds)
 */
Math.easeInQuad = function(t, b, c, d) { return c * (t /= d) * t + b; }
/**
 * Calculate the current value with quad easing-out
 * @param {Number} t Elapsed time (in milliseconds)
 * @param {Number} b Begin value
 * @param {Number} c The current difference (between begin value and end value)
 * @param {Number} d The duration of transition (in milliseconds)
 */
Math.easeOutQuad = function(t, b, c, d) { return -c * ( t/= d) * (t - 2) + b; }
/**
 * Calculate the current value with quad easing-in-out
 * @param {Number} t Elapsed time (in milliseconds)
 * @param {Number} b Begin value
 * @param {Number} c The current difference (between begin value and end value)
 * @param {Number} d The duration of transition (in milliseconds)
 */
Math.easeInOutQuad = function(t, b, c, d)
{
	if ((t /= d / 2) < 1)
		return c / 2 * t * t + b;

	return -c / 2 * ((--t) * (t-2) - 1) + b;
}

// CUBIC
Math.easeInCubic = function(t, b, c, d) { return c * (t /= d) * t * t + b; }
Math.easeOutCubic = function(t, b, c, d) { return c * ((t = t/d - 1) * t * t + 1) + b; }
Math.easeInOutCubic = function(t, b, c, d)
{
	if ((t /= d/2) < 1)
		return c/2 * t * t * t + b;

	return c/2 * ((t -= 2) * t * t + 2) + b;
}

// QUART
Math.easeInQuart = function(t, b, c, d) { return c * (t /= d) * t * t * t + b; }
Math.easeOutQuart = function(t, b, c, d) { return -c * ((t = t/d - 1) * t * t * t - 1) + b; }
Math.easeInOutQuart = function(t, b, c, d)
{
	if ((t /= d/2) < 1)
		return c/2 * t * t * t * t + b;

	return -c/2 * ((t -= 2) * t * t * t - 2) + b;
}

// QUINT
Math.easeInQuint = function(t, b, c, d) { return c * (t /= d) * t * t * t * t + b; }
Math.easeOutQuint = function(t, b, c, d) { return c * ((t = t / d - 1) * t * t * t * t + 1) + b; }
Math.easeInOutQuint = function(t, b, c, d)
{
	if ((t /= d / 2) < 1)
		return c / 2 * t * t * t * t * t + b;

	return c/2 * ((t -= 2) * t * t * t * t + 2) + b;
}

// EXPO
Math.easeInExpo = function(t, b, c, d) { return (t == 0) ? b :  c * Math.pow(2, 10 * (t/d - 1)) + b; }
Math.easeOutExpo = function(t, b, c, d) { return (t == d) ? b+c :  c * (-Math.pow(2, -10 * t/d) + 1) + b; }
Math.easeInOutExpo = function(t, b, c, d)
{
	if (t == 0) return b;
	if (t == d) return b + c;
	if ((t /= d/2) < 1)
		return c/2 * Math.pow(2, 10 * (t - 1)) + b;

	return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
}

// SINE
Math.easeInSine = function(t, b, c, d) { return -c * Math.cos(t/d * (Math.PI/2)) + c + b; }
Math.easeOutSine = function(t, b, c, d) { return c * Math.sin(t/d * (Math.PI/2)) + b; }
Math.easeInOutSine = function(t, b, c, d) { return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; }

// CIRC
Math.easeInCirc = function(t, b, c, d) { return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; }
Math.easeOutCirc = function(t, b, c, d) { return c * Math.sqrt(1 - (t = t/d - 1) * t) + b; }
Math.easeInOutCirc = function(t, b, c, d)
{
	if ((t /= d/2) < 1)
		return -c/2 * (Math.sqrt(1 - t * t) - 1) + b;

	return c/2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
}

// BACK
Math.easeInBack = function(t, b, c, d, s)
{
	if (s == undefined)
		s = 1.70158;

	return c * (t /= d) * t * ((s + 1) * t - s) + b;
}
Math.easeOutBack = function(t, b, c, d, s)
{
	if (s == undefined)
		s = 1.70158;

	return c * ((t = t/d - 1) * t * ((s + 1) * t + s) + 1) + b;
}
Math.easeInOutBack = function(t, b, c, d, s)
{
	if (s == undefined)
		s = 1.70158;
	if ((t /= d/2) < 1)
		return c/2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;

	return c/2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
}

// ELASTIC
Math.easeInElastic = function(t, b, c, d, a, p)
{
	if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
	if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
	else var s = p/(2*Math.PI) * Math.asin (c/a);
	return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
}
Math.easeOutElastic = function(t, b, c, d, a, p)
{
	if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
	if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
	else var s = p/(2*Math.PI) * Math.asin (c/a);
	return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
}
Math.easeInOutElastic = function(t, b, c, d, a, p)
{
	if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
	if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
	else var s = p/(2*Math.PI) * Math.asin (c/a);
	if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
}

// BOUNCE
Math.easeInBounce = function(t, b, c, d)
{
	if ((t /= d) < (1/2.75))
		return c * (7.5625 * t * t) + b;
	else if (t < (2/2.75))
		return c * (7.5625 * (t -= (1.5/2.75)) * t + .75) + b;
	else if (t < (2.5/2.75))
		return c * (7.5625 * (t -= (2.25/2.75)) * t + .9375) + b;
	else
		return c * (7.5625 * (t-= (2.625/2.75)) * t + .984375) + b;
}
Math.easeOutBounce = function(t, b, c, d) { return c - Math.easeOut(d-t, 0, c, d) + b; }
Math.easeInOutBounce = function(t, b, c, d)
{
	if (t < d/2)
		return Math.easeIn(t * 2, 0, c, d) * .5 + b;
	else
		return Math.easeOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
}

/*
 * Provides facilities for basic property animations and animation sequencing.
 * Loosely based on Alex Uhlmann's Animation Package for Flash.
 * @document
 * @require window.js
 */


/**
 * @namespace anim
 */
anim = new Object;

/**
 * Combines multiple Animation or Sequence instances.
 * When sequence is run, the children instances are run either
 * successivelly or simultaneously.
 * @param Number type The type of the sequence; either Sequence.TypeSuccessive or Sequence.TypeSimultaneous.
 * @param Object callback If this argument is a Function, it will be used as the callback. If it is an Array,
 * the first element is considered the callback object, and the second the callback method.
 * @constructor
 */
anim.Sequence = function (type, callback)
{
	Dispatcher.call(this, "onstart", "oncomplete");

	this.type = type != null ? type : anim.Sequence.TypeSuccessive;
	this.children = new Array;
	this.currentAnimation = 0;

	if (callback instanceof Function)
		this.addListener("oncomplete", null, callback);
	else if (callback instanceof Array)
		this.addListener("oncomplete", callback[0], callback[1]);

}
anim.Sequence.prototype = new Dispatcher;
anim.Sequence.prototype.constructor = anim.Sequence;

/**
 * Default animation interval resolution (ms pause between callbacks)
 */
anim.defaultResolution = 10;
/**
 * Default animation speed (in ms)
 */
anim.defaultDuration = 100;
/**
 * Default easing equation.
 */
anim.defaultEasing = Math.easeNone;

/**
 * Defines the successing sequence type
 */
anim.Sequence.TypeSuccessive = 0;
/**
 * Defines the simultaneous sequence type
 */
anim.Sequence.TypeSimultaneous = 1;

/**
 * Adds a child anim.
 * The child can be an instance of either Animation or Sequence class
 * @param Object child The instance to add
 * @param Number pause The time (in milliseconds) to wait before starting the child anim.
 */
anim.Sequence.prototype.addChild = function (child, pause)
{
	if (!this.containsChild(child))
	{
		child.addListener("oncomplete", this, "onstepcomplete");
		this.children.push({ child: child, pause: pause || 0 });
	}
}
/**
 * Returns true if the current sequence contains the supplied child.
 * @param String child The child to verify.
 * @return Boolean True us the supplied child exists.
 */
anim.Sequence.prototype.containsChild = function (child)
{
	var contains = false;
	for (var i = 0; i < this.children.length; i++)
	{
		if (this.children[i].child == child)
			contains = true;
	}
	return contains;
}
/**
 * Runs the animation sequence
 */
anim.Sequence.prototype.run = function ()
{
	this.fireEvent("onstart");
	this.currentAnimation = 0;

	if (this.type == anim.Sequence.TypeSimultaneous)
	{
		for (var i = 0; i < this.children.length; i++)
			this.runChild(i);
	}
	else
	{
		this.runChild(this.currentAnimation);
	}
}

anim.Sequence.prototype.stop = function ()
{
	for (var i = 0; i < this.children.length; i++)
		this.stopChild(i);
}

anim.Sequence.prototype.runChild = function (childIndex)
{
	var currentChild = this.children[childIndex];
	var childObj = currentChild.child;
	var childPause = currentChild.pause;

	var timeoutCaller = window.createCallback(childObj, "run");
	currentChild.timeout = window.setTimeout(timeoutCaller, childPause);
}

anim.Sequence.prototype.stopChild = function (childIndex)
{
	var currentChild = this.children[childIndex];
	var childObj = currentChild.child;
	if (currentChild.timeout != null)
		window.clearTimeout(currentChild.timeout);

	childObj.stop();
}

/**
 * Fires sucessively until the sequnce is complete
 */
anim.Sequence.prototype.onstepcomplete = function ()
{
	var sequenceComplete = false;
	if (this.type == anim.Sequence.TypeSuccessive)
	{
		if (this.currentAnimation == this.children.length - 1)
			sequenceComplete = true;
		else
			this.runChild(++this.currentAnimation);
	}
	else
		if (++this.currentAnimation == this.children.length)
			sequenceComplete = true;

	if (sequenceComplete)
	{
		this.fireEvent("oncomplete");
	}
}

anim.Switch = function (object, property, state, callback)
{
	Dispatcher.call(this, "oncomplete");

	this.object = object;
	this.property = property;
	this.state = state;

	if (callback instanceof Function)
		this.addListener("oncomplete", null, callback);
	else if (callback instanceof Array)
		this.addListener("oncomplete", callback[0], callback[1]);
}
anim.Switch.prototype = new Dispatcher;
anim.Switch.prototype.constructor = anim.Switch;

anim.Switch.prototype.setValue = function ()
{
	this.object[this.property] = this.state;
}

anim.Switch.prototype.run = function ()
{
	if (this.object != null)
	{
		this.setValue();
		this.fireEvent("oncomplete", { object: this.object, state: this.state });
	}
}

anim.VisibilityInherit = function (element, callback)
{
	anim.Switch.call(this, element.style, "visibility", "inherit", callback);
}
anim.VisibilityInherit.prototype = new anim.Switch;
anim.VisibilityInherit.prototype.constructor = anim.VisibilityInherit;

anim.VisibilityShow = function (element, callback)
{
	anim.Switch.call(this, element.style, "visibility", "visible", callback);
}
anim.VisibilityShow.prototype = new anim.Switch;
anim.VisibilityShow.prototype.constructor = anim.VisibilityShow;

anim.VisibilityHide = function (element, callback)
{
	anim.Switch.call(this, element.style, "visibility", window.display(this.object, true), callback);
}
anim.VisibilityHide.prototype = new anim.Switch;
anim.VisibilityHide.prototype.constructor = anim.VisibilityHide;

anim.DisplayVisible = function (element, callback)
{
	anim.Switch.call(this, element.style, "display", "block", callback);
}
anim.DisplayVisible.prototype = new anim.Switch;
anim.DisplayVisible.prototype.constructor = anim.DisplayVisible;

anim.DisplayNone = function (element, callback)
{
	anim.Switch.call(this, element.style, "visibility", "none", callback);
}
anim.DisplayNone.prototype = new anim.Switch;
anim.DisplayNone.prototype.constructor = anim.DisplayNone;

/**
 * Combines multiple tweens of an element into an anim.
 * @param Object element The DOM element to animate.
 * @param Object easingType Defines the easing type. If the value is an instance of Function,
 * this function will be used for easing equations. If the value is an instance of an Array,
 * than the first element is considered the equations, and the remainder
 * second the callback method.
 * @param Object callback If this argument is used, it will be interpreted and added as
 * an oncomplete listener. If it's a Function instance, it will be used as the callback function.
 * If it's an Array instance than the first element is considered the callback object, and the
 * second the callback method.
 * @constructor
 */
Animation = function (element, easingType, callback)
{
	Dispatcher.call(this, "onstart", "onprogress", "oncomplete");

	this.tweens = new Array;
	this.tweensComplete = 0;
	this.element = element;
	this.unit = "px";

	this.callbackInterval = anim.defaultResolution;
	this.easingType = easingType;

	if (callback instanceof Function)
		this.addListener("oncomplete", null, callback);
	else if (callback instanceof Array)
		this.addListener("oncomplete", callback[0], callback[1]);
}
Animation.prototype = new Dispatcher;
Animation.prototype.constructor = Animation;

Animation.prototype.setUnit = function (unit)
{
	this.unit = unit;
	for (var i = 0; i < this.tweens.length; i++)
		this.tweens[i].unit = this.unit;
}

Animation.prototype.setEndValue = function (value)
{
	for (var i = 0; i < this.tweens.length; i++)
		this.tweens[i].setEndValue(value);
}


Animation.prototype.createEventObject = function (eventName)
{
	var eventObj =
	{
		animation: this,
		element: this.element
	};
	return eventObj;
}

/**
 * Adds a tween to the animation
 * @param Object object The object whose property to tween.
 * @param String property The name of the property to tween.
 * @param String unit Optional unit to append to the value, typically a css unit (such as px,pt or %)
 * @param Number endValue The value to tween the property to,.
 * @param Object easingType Defines the easing type. If the value is an instance of Function,
 * this function will be used for easing equations. If the value is an instance of an Array,
 * than the first element is considered the equations, and the remainder
 * second the callback method.
 * @param Boolean roundValues If true, the values returned by the easing function will be rounded.
 */
Animation.prototype.addTween = function (object, property, unit, targetValue, duration, easingType, roundValues)
{
	var tweenObj = new anim.Tween(object, property, unit, targetValue, duration, easingType, roundValues);
	tweenObj.addListener("onprogress", this, "onprogress");
	tweenObj.addListener("oncomplete", this, "oncomplete");
	this.tweens.push(tweenObj);

	return tweenObj;
}

/**
 * Adds a css tween to the animation
 * @param String property The name of the css property to tween.
 * @param String unit Optional unit to append to the value, typically a css unit (such as px,pt or %)
 * @param Number endValue The value to tween the property to,.
 * @param Object easingType Defines the easing type. If the value is an instance of Function,
 * this function will be used for easing equations. If the value is an instance of an Array,
 * than the first element is considered the equations, and the remainder
 * second the callback method.
 * @param Boolean roundValues If true, the values returned by the easing function will be rounded.
 */
Animation.prototype.addCssTween = function (property, unit, targetValue, duration, easingType, roundValues)
{
	return this.addTween(this.element.style, property, unit, targetValue, duration, easingType, roundValues);
}
/**
 * Returns true if the current animation contains the supplied tween.
 * @param String tween The tween to verify.
 * @return Boolean True us the supplied tween exists.
 */
Animation.prototype.containsTween = function (property)
{
	for (var i = 0; i < this.tweens.length; i++)
		if (this.tweens[i].property == property)
			return true;

	return false;
}
Animation.prototype.fireEvent = function (eventType)
{
	var eventObj = new Object;
	for (var i = 0; i < this.tweens.length; i++)
		eventObj[this.tweens[i].property] = this.tweens[i].getValue();

	Dispatcher.prototype.fireEvent.call(this, eventType, eventObj);
}

/**
 * Called continously as the tween onprogress events fire
 */
Animation.prototype.onprogress = function ()
{
	this.fireEvent("onprogress");
}
/**
 * Called when the animation is complete.
 */
Animation.prototype.oncomplete = function (eventObj)
{
	if (++this.tweensComplete == this.tweens.length)
		this.fireEvent("oncomplete");
}
/**
 * Runs the animation
 */
Animation.prototype.run = function ()
{
	this.fireEvent("onstart");
	this.tweensComplete = 0;
	for (var i = 0; i < this.tweens.length; i++)
		this.tweens[i].run();
}

Animation.prototype.stop = function ()
{
	for (var i = 0; i < this.tweens.length; i++)
		this.tweens[i].stop();
}

anim.Color = function (element, color, value, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	this.unit = "";
	this.color = color;

	this.tweenRed = this.addCssTween(color, this.unit, value.r, duration, easingType, true);
	this.tweenGreen = this.addCssTween(color, this.unit, value.g, duration, easingType, true);
	this.tweenBlue = this.addCssTween(color, this.unit, value.b, duration, easingType, true);

	this.tweenRed.setPropertyGetter([this, "getRed"]);
	this.tweenRed.setPropertySetter([this, "setRed"]);
	this.tweenGreen.setPropertyGetter([this, "getGreen"]);
	this.tweenGreen.setPropertySetter([this, "setGreen"]);
	this.tweenBlue.setPropertyGetter([this, "getBlue"]);
	this.tweenBlue.setPropertySetter([this, "setBlue"]);

	this.setColor(this.getColor());
}
anim.Color.prototype = new Animation;
anim.Color.prototype.constructor = anim.Color;

anim.Color.prototype.getColor = function ()
{
	var cp = dom.currentStyle(this.element);
	var color = cp ? cp[this.color] : null;

	if (color != null)
	{
		if (color.match(/auto|transparent/))
		{
			var parent = this.element.parentNode;
			while (parent && parent.style[this.color].match(/auto|transparent/))
				parent = parent.parentNode;
			if (parent != null)
				color = parent.style[this.color];
		}
		if (color != null)
		{
			if (color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/))
			{
				color = new anim.Rgb(parseInt(RegExp.$1), parseInt(RegExp.$2), parseInt(RegExp.$3));
			}
			else if (color.match(/^#([\da-fA-F])([\da-fA-F])([\da-fA-F])$/))
			{
				var r = parseInt(RegExp.$1 + RegExp.$1, 16);
				var g = parseInt(RegExp.$2 + RegExp.$2, 16);
				var b = parseInt(RegExp.$3 + RegExp.$3, 16);
				color = new anim.Rgb(r, g, b);
			}
			else if (color.match(/^#([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/))
			{
				var r = parseInt(RegExp.$1, 16);
				var g = parseInt(RegExp.$2, 16);
				var b = parseInt(RegExp.$3, 16);
				color = new anim.Rgb(r, g, b);
			}
			else if (anim.X11Colors[color] != null)
			{
				if (anim.X11Colors[color].match(/^#([\da-fA-F]){2}([\da-fA-F]){2}([\da-fA-F]){2}$/))
				{
					var r = parseInt(RegExp.$1, 16);
					var g = parseInt(RegExp.$2, 16);
					var b = parseInt(RegExp.$3, 16);
					color = new anim.Rgb(r, g, b);
				}
			}
		}
	}
	if (color == null)
		color = new anim.Rgb(255, 255, 255);

	return color;
}

anim.Color.prototype.setColor = function (color)
{
	this.element.style[this.color] = color.toString();
}
anim.Color.prototype.getRed = function ()
{
	return this.getColor().r;
}
anim.Color.prototype.getGreen = function ()
{
	return this.getColor().g;
}
anim.Color.prototype.getBlue = function ()
{
	return this.getColor().b;
}
anim.Color.prototype.setRed = function (value)
{
	var color = this.getColor();
	color.r = value;
	this.setColor(color);
}
anim.Color.prototype.setGreen = function (value)
{
	var color = this.getColor();
	color.g = value;
	this.setColor(color);
}
anim.Color.prototype.setBlue = function (value)
{
	var color = this.getColor();
	color.b = value;
	this.setColor(color);
}

anim.Clip = function (element, clipRect, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	this.unit = "px";

	this.tweenTop  = this.addCssTween("top", this.unit, clipRect.top, duration, easingType, true);
	this.tweenRight = this.addCssTween("right", this.unit, clipRect.right, duration, easingType, true);
	this.tweenBottom = this.addCssTween("bottom", this.unit, clipRect.bottom, duration, easingType, true);
	this.tweenLeft = this.addCssTween("left", this.unit, clipRect.left, duration, easingType, true);

	this.tweenTop.setPropertyGetter([this, "getTop"]);
	this.tweenRight.setPropertyGetter([this, "getRight"]);
	this.tweenBottom.setPropertyGetter([this, "getBottom"]);
	this.tweenLeft.setPropertyGetter([this, "getLeft"]);

	this.tweenTop.setPropertySetter([this, "setTop"]);
	this.tweenRight.setPropertySetter([this, "setRight"]);
	this.tweenBottom.setPropertySetter([this, "setBottom"]);
	this.tweenLeft.setPropertySetter([this, "setLeft"]);

	if (!this.element.style.clip)
		this.setRect(new anim.Rect(0, this.element.offsetWidth, this.element.offsetHeight, 0));
}
anim.Clip.prototype = new Animation;
anim.Clip.prototype.constructor = anim.Clip;

anim.Clip.prototype.setRect = function (rect)
{
	this.element.style.clip = "rect({0}{4} {1}{4} {2}{4} {3}{4})".format(rect.top, rect.right, rect.bottom, rect.left, this.unit);
}

anim.Clip.prototype.getRect = function ()
{
	var rect = new anim.Rect(0, this.element.offsetWidth, this.element.offsetHeight, 0);
	var cs = dom.currentStyle(this.element);
	var clipRect = cs ? cs.clip : null;

	if (clipRect && clipRect.match(/rect\((\S+) (\S+) (\S+) (\S+)\)/))
	{
		rect.top = parseInt(RegExp.$1);
		rect.right = parseInt(RegExp.$2);
		rect.bottom = parseInt(RegExp.$3);
		rect.left = parseInt(RegExp.$4);
	}
	return rect;
}

anim.Clip.prototype.getTop = function ()
{
	return this.getRect().top;
}

anim.Clip.prototype.getRight = function ()
{
	return this.getRect().right;
}

anim.Clip.prototype.getBottom = function ()
{
	return this.getRect().bottom;
}

anim.Clip.prototype.getLeft = function ()
{
	return this.getRect().left;
}

anim.Clip.prototype.setTop = function (value)
{
	var rect = this.getRect();
	rect.top = value;
	this.setRect(rect);
}

anim.Clip.prototype.setRight = function (value)
{
	var rect = this.getRect();
	rect.right = value;
	this.setRect(rect);
}

anim.Clip.prototype.setBottom = function (value)
{
	var rect = this.getRect();
	rect.bottom = value;
	this.setRect(rect);
}

anim.Clip.prototype.setLeft = function (value)
{
	var rect = this.getRect();
	rect.left = value;
	this.setRect(rect);
}


/**
 * Animates an element's css coordinates.
 * @param Object element The element to move
 * @param Number targetX The elements target x coordinate
 * @param Number targetY The elements target y coordinate
 * @param Number duration The duration (in milliseconds) of this anim.
 * @param Object easingType Defines the easing type. If the value is an instance of Function,
 * this function will be used for easing equations. If the value is an instance of an Array,
 * than the first element is considered the equations, and the remainder
 * second the callback method.
 * @param Object callback If this argument is used, it will be interpreted and added as
 * an oncomplete listener. If it's a Function instance, it will be used as the callback function.
 * If it's an Array instance than the first element is considered the callback object, and the
 * second the callback method.
 */
anim.Move = function (element, targetX, targetY, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	this.tweenLeft = this.addCssTween("left", "px", targetX, duration, easingType, true);
	this.tweenTop  = this.addCssTween("top", "px", targetY, duration, easingType, true);

	this.tweenLeft.setPropertyGetter([this, "getLeft"]);
	this.tweenTop.setPropertyGetter([this, "getTop"]);
}
anim.Move.prototype = new Animation;
anim.Move.prototype.constructor = anim.Move;

/**
 * Returns the 'left' property from the current element's style object.
 * @return Number Current object's 'left' property.
 */
anim.Move.prototype.getLeft = function ()
{
	var cs = dom.currentStyle(this.element);
	var left = cs ? cs.left : "auto";
	if (left == "auto")
		left = 0;

	return parseInt(left);
}

/**
 * Returns the 'top' property from the current element's style object.
 * @return Number Current object's 'top' property.
 */
anim.Move.prototype.getTop = function ()
{
	var cs = dom.currentStyle(this.element);
	var top = cs ? cs.top : "auto";
	if (top == "auto")
		top = 0;

	return parseInt(top);
}

anim.Move.prototype.setEndValue = function (x, y)
{
	this.tweenLeft.setEndValue(x);
	this.tweenTop.setEndValue(y);
}

anim.MoveX = function (element, targetX, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	this.tweenLeft = this.addCssTween("left", "px", targetX, duration, easingType, true);
	this.tweenLeft.setPropertyGetter([this, "getLeft"]);
}
anim.MoveX.prototype = new Animation;
anim.MoveX.prototype.constructor = anim.MoveX;
anim.MoveX.prototype.getLeft = anim.Move.prototype.getLeft;

anim.MoveY = function (element, targetY, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	this.tweenTop  = this.addCssTween("top", "px", targetY, duration, easingType, true);
	this.tweenTop.setPropertyGetter([this, "getTop"]);
}
anim.MoveY.prototype = new Animation;
anim.MoveY.prototype.constructor = anim.MoveY;
anim.MoveY.prototype.getTop = anim.Move.prototype.getTop;

/**
 * Represents an animation that stretches/shrinks the width and height of an element.
 * @param Object element The element to stretch or shrink
 * @param Number targetWidth The element's target width
 * @param Number targetHeight The element's target height
 * @param Object easingType Defines the easing type. If the value is an instance of Function,
 * this function will be used for easing equations. If the value is an instance of an Array,
 * than the first element is considered the equations, and the remainder
 * second the callback method.
 * @param Object callback If this argument is used, it will be interpreted and added as
 * an oncomplete listener. If it's a Function instance, it will be used as the callback function.
 * If it's an Array instance than the first element is considered the callback object, and the
 * second the callback method.
 */
anim.Stretch = function (element, targetWidth, targetHeight, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	var tweenWidth = this.addCssTween("width", this.unit, targetWidth, duration, easingType, true);
	var tweenHeight = this.addCssTween("height", this.unit, targetHeight, duration, easingType, true);

	tweenWidth.setPropertyGetter([this, "getWidth"]);
	tweenHeight.setPropertyGetter([this, "getHeight"]);
}
anim.Stretch.prototype = new Animation;
anim.Stretch.prototype.constructor = anim.Stretch;

/**
 * Returns the 'width' property from the current element's style object.
 * @return Number Current object's 'width' property.
 */
anim.Stretch.prototype.getWidth = function ()
{
	var cs = dom.currentStyle(this.element);
	var width = cs ? cs.width : "auto";
	if (width == "auto")
	{
		if (this.unit == "px")
			width = this.element.offsetWidth;
		else
			width = 100;

		this.element.style.width = width + this.unit;
	}

	return parseInt(width);
}

/**
 * Returns the 'height' property from the current element's style object.
 * @return Number Current object's 'height' property.
 */
anim.Stretch.prototype.getHeight = function ()
{
	var cs = dom.currentStyle(this.element);
	var height = cs ? cs.height : "auto";
	if (height == "auto")
	{
		if (this.unit == "px")
			height = this.element.offsetHeight;
		else
			height = 100;

		this.element.style.height = height + this.unit;
	}

	return parseInt(height);
}

anim.StretchWidth = function (element, targetWidth, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	var tweenWidth = this.addCssTween("width", "px", targetWidth, duration, easingType, true);

	tweenWidth.setPropertyGetter([this, "getWidth"]);
}
anim.StretchWidth.prototype = new Animation;
anim.StretchWidth.prototype.constructor = anim.StretchWidth;
anim.StretchWidth.prototype.getWidth = anim.Stretch.prototype.getWidth;

/**
 * Animates an element's scrollLeft and scrollTop properties.
 * @param Object element The element to scroll
 * @param Number targetLeft The elements target scrollLeft value.
 * @param Number targetTop The move's target scrollTop value
 * @param Number duration The duration (in milliseconds) of this anim.
 * @param Object easingType Defines the easing type. If the value is an instance of Function,
 * this function will be used for easing equations. If the value is an instance of an Array,
 * than the first element is considered the equations, and the remainder
 * second the callback method.
 * @param Object callback If this argument is used, it will be interpreted and added as
 * an oncomplete listener. If it's a Function instance, it will be used as the callback function.
 * If it's an Array instance than the first element is considered the callback object, and the
 * second the callback method.
 */
anim.Scroll = function (element, targetTop, targetLeft, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	this.tweenLeft = this.addTween(element, "scrollLeft", "", targetLeft, duration, easingType, true);
	this.tweenTop  = this.addTween(element, "scrollTop", "", targetTop, duration, easingType, true);
}
anim.Scroll.prototype = new Animation;
anim.Scroll.prototype.constructor = anim.Scroll;


/**
 * Animates an elements's css height.
 * @param Object element The element to move value.
 * @param Number targetHeight The element's target height
 * @param Number duration The duration (in milliseconds) of this anim.
 * @param Object easingType Defines the easing type. If the value is an instance of Function,
 * this function will be used for easing equations. If the value is an instance of an Array,
 * than the first element is considered the equations, and the remainder
 * second the callback method.
 * @param Object callback If this argument is used, it will be interpreted and added as
 * an oncomplete listener. If it's a Function instance, it will be used as the callback function.
 * If it's an Array instance than the first element is considered the callback object, and the
 * second the callback method.
 */
anim.StretchHeight = function (element, targetHeight, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	var tweenHeight = this.addCssTween("height", "px", targetHeight, duration, easingType, true);

	tweenHeight.setPropertyGetter([this, "getHeight"]);
}
anim.StretchHeight.prototype = new Animation;
anim.StretchHeight.prototype.constructor = anim.StretchHeight;
anim.StretchHeight.prototype.getHeight = anim.Stretch.prototype.getHeight;

/**
 * Animates an elements's css alpha.
 * @param Object element The element to fade
 * @param Number targetAlpha The element's target alpha value.
 * @param Object easingType Defines the easing type. If the value is an instance of Function,
 * this function will be used for easing equations. If the value is an instance of an Array,
 * than the first element is considered the equations, and the remainder
 * second the callback method.
 * @param Object callback If this argument is used, it will be interpreted and added as
 * an oncomplete listener. If it's a Function instance, it will be used as the callback function.
 * If it's an Array instance than the first element is considered the callback object, and the
 * second the callback method.
 */
anim.Alpha = function (element, targetAlpha, duration, easingType, callback)
{
	Animation.call(this, element, easingType, callback);

	var tweenAlpha = this.addCssTween("opacity", "", targetAlpha, duration, easingType, true);

	tweenAlpha.setPropertyGetter([this, "getAlpha"]);
	tweenAlpha.setPropertySetter([this, "setAlpha"]);

	this.ie_filter = null;

	if (window.is_ie)
	{
		for (var i = 0; i < this.element.filters.length; i++)
		{
			if (this.element.filters[i].opacity != null)
				this.ie_filter = this.element.filters[i];
		}
		if (this.ie_filter == null)
		{
			this.element.style.filter += "alpha(opacity=100)";
			this.ie_filter = this.element.filters[this.element.filters.length-1];
		}
	}
	else
	{
		if (this.element.style.opacity == "")
			this.element.style.opacity = 1;
	}
}
anim.Alpha.prototype = new Animation;
anim.Alpha.prototype.constructor = anim.Alpha;

anim.Alpha.prototype.getAlpha = function ()
{
	var opacity = 100;
	if (window.is_ie)
		opacity = this.ie_filter.opacity;
	else
	{
		var cs = dom.currentStyle(this.element);
		if (cs)
		{
			var op = parseFloat(cs.opacity);
			if (isNaN(op))
				op = 1;
		}
		else
			op = 1;

		opacity = op * 100;
	}
	return opacity;
}
anim.Alpha.prototype.setAlpha = function (value)
{
	if (window.is_ie)
		this.ie_filter.opacity = value;
	else
		this.element.style.opacity = value / 100;
}

/**
 * Tweens an object's property
 * @param Object object The object whose property should be tweened
 * @param String property The name of the property to tween.
 * @param String unit Optional unit to append to the value, typically a css unit (such as px,pt or %)
 * @param Number endValue The value to tween the property to,.
 * @param Object easingType Defines the easing type. If the value is an instance of Function,
 * this function will be used for easing equations. If the value is an instance of an Array,
 * than the first element is considered the equations, and the remainder
 * second the callback method.
 * @param Boolean roundValues If true, the values returned by the easing function will be rounded.
 */
anim.Tween = function (object, property, unit, endValue, duration, easingType, roundValues)
{
	Dispatcher.call(this, "onstart", "onprogress", "oncomplete");

	this.isPaused = false;

	this.endValue = endValue;
	this.duration = duration;
	this.object = object;
	this.unit = unit || "";

	this.property = property;
	this.timeStart = null;
	this.beginValue = null;
	this.roundValues = roundValues;

	this.propertyGet = [this, "_propertyGet"];
	this.propertySet = [this, "_propertySet"];

	this.easingFx = anim.defaultEasing;
	this.easingParams = null;

	if (easingType instanceof Array)
	{
		this.easingFx = easingType[0];
		this.easingParams = easingType.slice(1);
	}
	else if (easingType != null)
	{
		this.easingFx = easingType;
		this.easingParams = null;
	}
}
anim.Tween.prototype = new Dispatcher;
anim.Tween.prototype.constructor = anim.Tween;

anim.Tween.prototype.setEndValue = function (value)
{
	this.endValue = value;
}

/**
 * Assigns the object and method to handle the retrieving of property value.
 * @param Array getterFx Two element array where the first argument is the object
 * whose method should be called, and the second is the name of the method to call.
 */
anim.Tween.prototype.setPropertyGetter = function (getterFx)
{
	this.propertyGet = getterFx;
}

/**
 * Assigns the object and method to handle the setting of property value.
 * @param Array getterFx Two element array where the first argument is the object
 * whose method should be called, and the second is the name of the method to call.
 */
anim.Tween.prototype.setPropertySetter = function (setterFx)
{
	this.propertySet = setterFx;
}

/**
 * Returns the actual element's value of the current tween's property.
 * @return Number The current property value.
 */
anim.Tween.prototype._propertyGet = function ()
{
	return parseInt(this.object[this.property]);
}

/**
 * Sets the actual element's value of the current tween's property.
 * @param Number value The value to set.
 */
anim.Tween.prototype._propertySet = function (value)
{
	try
	{
		this.object[this.property] = value + this.unit;
	}
	catch(e) {
		log.error("Setting property '{0}' to value '{1}' failed: {2}".format(this.property, value + this.unit, e.message));
	}
}

/**
 * Returns the value of the call to propertyGet.
 * @return Number The current property value.
 */
anim.Tween.prototype.getValue = function ()
{
	return this.propertyGet[0][this.propertyGet[1]]();
}

/**
 * Calls propertySet.
 * @param Number value The value to set.
 */
anim.Tween.prototype.setValue = function (value)
{
	this.propertySet[0][this.propertySet[1]](value);
}

/**
 * Runs the tween
 */
anim.Tween.prototype.run = function ()
{
	this.timeStart = new Date().getTime();
	this.beginValue = this.getValue();

	if (!isNaN(this.beginValue))
	{
		this.intervalCallback = window.createCallback(this, "step");
		this.interval = window.setInterval(this.intervalCallback, anim.defaultResolution);
	}
}

/**
 * Called continuously until the tween is complete.
 */
anim.Tween.prototype.step = function ()
{
	var timeElapsed = new Date().getTime() - this.timeStart;
	var easingParams = [
		timeElapsed,
		this.beginValue,
		(this.endValue - this.beginValue),
		this.duration
	];

	if (this.easingParams)
		for (var i = 0; i < this.easingParams.length; i++)
			easingParams.push(this.easingParams[i]);

	var targetValue = this.easingFx.apply(this, easingParams);

	if (this.roundValues)
		targetValue = Math.round(targetValue);

	this.setValue(targetValue);
	this.fireEvent("onprogress", { tween: this, value: currValue });

	var currValue = this.getValue();

	if (currValue == this.endValue || timeElapsed >= this.duration)
	{
		this.stop();
		this.setValue(this.endValue);

		this.fireEvent("onprogress", { tween: this, value: this.endValue });
		this.fireEvent("oncomplete", { tween: this, value: this.endValue });
		this.intervalCallback = null;
	}
}

anim.Tween.prototype.stop = function ()
{
	window.clearInterval(this.interval);
}

anim.Rect = function (top, right, bottom, left)
{
	this.top = top;
	this.right = right;
	this.bottom = bottom;
	this.left = left;
}

anim.Rect.clipRect = function (elem, type)
{
	var w = elem.offsetWidth;
	var h = elem.offsetHeight;
	switch (type)
	{
		case "T":
			return new anim.Rect(0, w, 0, 0);
		case "L":
			return new anim.Rect(0, 0, h, 0);
		case "B":
			return new anim.Rect(h, w, h, 0);
		case "R":
			return new anim.Rect(0, w, h, w);
		case "TL":
			return new anim.Rect(0, 0, 0, 0);
		case "TR":
			return new anim.Rect(0, w, 0, w);
		case "BL":
			return new anim.Rect(h, 0, h, 0);
		case "BR":
			return new anim.Rect(h, w, h, w);
		default:
			return new anim.Rect(0, w, h, 0);
	}
}


anim.Rgb = function (r, g, b)
{
	this.r = r;
	this.g = g;
	this.b = b;
}
anim.Rgb.prototype.toString = function ()
{
	if (arguments[0] == "hex")
	{
		var r = this.decimalToHex(this.r);
		var g = this.decimalToHex(this.g);
		var b = this.decimalToHex(this.b);

		r = r.length == 1 ? "0" + r : r;
		g = g.length == 1 ? "0" + g : g;
		b = b.length == 1 ? "0" + b : b;

		return ("#" + r + g + b);
	}
	else
		return "rgb({0}, {1}, {2})".format(this.r, this.g, this.b);
}

var HD ="0123456789ABCDEF";
anim.Rgb.decimalToHex = function (value)
{
	var string = String(value);
	var hex = HD.substr(value & 15, 1);
	while(value > 15)
	{
		value >>= 4;
		hex = HD.substr(value & 15, 1) + hex;
	}
	return hex;
}
anim.X11Colors =
{
	aliceblue: "#f0f8ff", antiquewhite: "#faebd7", aqua: "#00ffff", aquamarine: "#7fffd4", azure: "#f0ffff", beige: "#f5f5dc", bisque: "#ffe4c4", black: "#000000", blanchedalmond: "#ffebcd",
	blue: "#0000ff", blueviolet: "#8a2be2", brown: "#a52a2a", burlywood: "#deb887", cadetblue: "#5f9ea0", chartreuse: "#7fff00", chocolate: "#d2691e", coral: "#ff7f50", cornflowerblue: "#6495ed",
	cornsilk: "#fff8dc", crimson: "#dc143c", cyan: "#00ffff", darkblue: "#00008b", darkcyan: "#008b8b", darkgoldenrod: "#b8860b", darkgray: "#a9a9a9", darkgreen: "#006400", darkkhaki: "#bdb76b",
	darkmagenta: "#8b008b", darkolivegreen: "#556b2f", darkorange: "#ff8c00", darkorchid: "#9932cc", darkred: "#8b0000", darksalmon: "#e9967a", darkseagreen: "#8fbc8f", darkslateblue: "#483d8b",
	darkslategray: "#2f4f4f", darkturquoise: "#00ced1", darkviolet: "#9400d3", deeppink: "#ff1493", deepskyblue: "#00bfff", dimgray: "#696969", dodgerblue: "#1e90ff", firebrick: "#b22222",
	floralwhite: "#fffaf0", forestgreen: "#228b22", fuchsia: "#ff00ff", gainsboro: "#dcdcdc", ghostwhite: "#f8f8ff", gold: "#ffd700", goldenrod: "#daa520", gray: "#808080", green: "#008000",
	greenyellow: "#adff2f", honeydew: "#f0fff0", hotpink: "#ff69b4", indianred: "#cd5c5c", indigo: "#4b0082", ivory: "#fffff0", khaki: "#f0e68c", lavender: "#e6e6fa", lavenderblush: "#fff0f5",
	lawngreen: "#7cfc00", lemonchiffon: "#fffacd", lightblue: "#add8e6", lightcoral: "#f08080", lightcyan: "#e0ffff", lightgoldenrodyellow: "#fafad2", lightgreen: "#90ee90", lightgrey: "#d3d3d3",
	lightpink: "#ffb6c1", lightsalmon: "#ffa07a", lightseagreen: "#20b2aa", lightskyblue: "#87cefa", lightslategray: "#778899", lightsteelblue: "#b0c4de", lightyellow: "#ffffe0", lime: "#00ff00",
	limegreen: "#32cd32", linen: "#faf0e6", magenta: "#ff00ff", maroon: "#800000", mediumaquamarine: "#66cdaa", mediumblue: "#0000cd", mediumorchid: "#ba55d3", mediumpurple: "#9370db",
	mediumseagreen: "#3cb371", mediumslateblue: "#7b68ee", mediumspringgreen: "#00fa9a", mediumturquoise: "#48d1cc", mediumvioletred: "#c71585", midnightblue: "#191970", mintcream: "#f5fffa",
	mistyrose: "#ffe4e1", moccasin: "#ffe4b5", navajowhite: "#ffdead", navy: "#000080", oldlace: "#fdf5e6", olive: "#808000", olivedrab: "#6b8e23", orange: "#ffa500", orangered: "#ff4500",
	orchid: "#da70d6", palegoldenrod: "#eee8aa", palegreen: "#98fb98", paleturquoise: "#afeeee", palevioletred: "#db7093", papayawhip: "#ffefd5", peachpuff: "#ffdab9", peru: "#cd853f",
	pink: "#ffc0cb", plum: "#dda0dd", powderblue: "#b0e0e6", purple: "#800080", red: "#ff0000", rosybrown: "#bc8f8f", royalblue: "#4169e1", saddlebrown: "#8b4513", salmon: "#fa8072",
	sandybrown: "#f4a460", seagreen: "#2e8b57", seashell: "#fff5ee", sienna: "#a0522d", silver: "#c0c0c0", skyblue: "#87ceeb", slateblue: "#6a5acd", slategray: "#708090", snow: "#fffafa",
	springgreen: "#00ff7f", steelblue: "#4682b4", tan: "#d2b48c", teal: "#008080", thistle: "#d8bfd8", tomato: "#ff6347", turquoise: "#40e0d0", violet: "#ee82ee", wheat: "#f5deb3",
	white: "#ffffff", whitesmoke: "#f5f5f5", yellow: "#ffff00", yellowgreen: "#9acd32"
};

