// FX!

Effects_class = function () {
	this.namedQueues = { };
	this.globalQueue = [ ];
}

Effects_class.prototype.enqueue = function (effectList, params) {
	if (!params) {
		params = { };
	}

	var global = params.global || false;
	var qName = params.global ? null : (params.shared || 'r'+Math.random());

	if (qName && !this.namedQueues[qName]) {
		this.namedQueues[qName] = [ ];
		this.namedQueues[qName].name = qName;
	}
	var queue = qName ? this.namedQueues[qName] : this.globalQueue;

	if (params.onComplete) {
		if (params.context) {
			queue.onComplete = function () { params.onComplete.apply(params.context, [ ]); }
		} else {
			queue.onComplete = params.onComplete;
		}
	}

	for (var i = 0; i < effectList.length; i++) {
		queue.push(effectList[i]);
	}

	this._runQueue(queue);
}

Effects_class.prototype.createParallel = function (effectList, params) {
	var onComplete = null;
	if (params) {
		if (params.onComplete) {
			if (params.context) {
				onComplete = function () { params.onComplete.apply(params.context, [ ]); }
			} else {
				onComplete = params.onComplete;
			}
		}
	}

	var InitClosure = function(context) {
		return function() {
			context._initParallel(effectList, onComplete);
		};
	};
		
	effectList.animate = InitClosure(this);
	return effectList;
}

Effects_class.prototype.runParallel = function (effectList, params) {
	var ef = this.createParallel(effectList, params);
	ef.animate();
	return ef;
}

Effects_class.prototype.create = function (element, type, params) {
	var ef = new Effect(element, type, params);
	return ef;
}

Effects_class.prototype.run = function (element, type, params) {
	var ef = this.create(element, type, params);
	ef.animate();
	return ef;
}

Effects_class.prototype._runQueue = function (queue) {
	if (queue.length) {
		var ef = queue.shift();
		ef.runningQueue = queue;
		ef.animate();
	} else {
		if (queue.onComplete) {
			queue.onComplete();
			queue.onComplete = null;
		}
		if (queue.name) {
			// this doesn't get called when queueing a parallel effect??
			delete this.namedQueues[queue.name];
		}
	}
}

Effects_class.prototype._initParallel = function (effectList, onComplete) {
	for (var i = 0; i < effectList.length; i++) {
		effectList[i].animateParallel();
	}

	this._animateParallel(effectList, onComplete);
}

Effects_class.prototype._animateParallel = function (effectList, onComplete) {
	var isAnimating = false;
	for (var i = 0; i < effectList.length; i++) {
		effectList[i].nextFrame();
		isAnimating = isAnimating || effectList[i].isAnimating;
	}

	if (isAnimating) {
		var ParaClosure = function(context) {
			return function() {
				context._animateParallel(effectList, onComplete);
			};
		};
			
		setTimeout(ParaClosure(this), 0);
	} else {
		if (effectList.runningQueue) {
			this._runQueue(effectList.runningQueue);
			effectList.runningQueue = null;
		}

		if (onComplete) {
			onComplete();
		}
	}
}

Effects = new Effects_class();

/*-----------------------------------------------------------------------------------
Effect:

var effect = new Effect([oDomElement|sID],{type:sType,to:int},iDuration(in seconds));
effect.animate();

Borrowed from:

TERMS OF USE - EASING EQUATIONS
Open source under the BSD License.
Copyright (c) 2001 Robert Penner
JavaScript version copyright (C) 2006 by Philippe Maegerman
All rights reserved.

-------------------------------------------------------------------------------------*/

var Effect = function(oEl,sType,oParams) {
	
	//Default argument values
	oParams = oParams || false;
	
	if(!oParams) {
	
		return;
		
	}
	
	//Constants
	
	this.oEl = Element.get(oEl);
	
	this._animType = sType || false;
	
	this.valueGetter = this.getValueGetterSetters().getter;
	
	this.valueSetter = this.getValueGetterSetters().setter;
	
	this._rawTo = oParams.to;
	
	this._rawFrom = oParams.from !== undefined ? oParams.from : this.valueGetter();
	
	this._easing = Easing; 
	
	this._easeMethod = oParams.easing && this._easing[oParams.easing] && this._easing[oParams.easing] !== undefined ? this._easing[oParams.easing] : this._easing.easeNone;
	
	this._duration = oParams.duration;
	
	this.isAnimating = false;
	
	this.setOnCompleteHandler(oParams.onComplete, oParams.context);
	
	//Time
	
	this._startTime = 0;
	
	this._prevTime = 0;
	
	this._time = 0;
	
}



Effect.prototype.animate = function(){
	
	this.animateParallel();

	this.onEnterFrame();
	
}

Effect.prototype.animateParallel = function(){
	this._to = (typeof this._rawTo == 'function') ? this._rawTo() : this._rawTo;
	this._from = (typeof this._rawFrom == 'function') ? this._rawFrom() : this._rawFrom;
	
	// So far backgorund is the only array
	// may have to figure out a more generic way to do this in the future.
	if (this._animType == 'background') {
		if (!(this._to instanceof Array)) {
			this._to = this.toRGB(this._to);
		}
		if (!(this._from instanceof Array)) {
			this._from = this.toRGB(this._from);
		}
	}

	this._delta = this.getDelta();
	
	this._startTime = new Date().getTime();
	
	this.isAnimating = true;

	// parallel animation calls nextFrame() explicitly
	//this.onEnterFrame();
	
}

Effect.prototype.onEnterFrame = function() {

	if(this.isAnimating) {
		
		this.nextFrame();
		
		var AnimClosure = function(context) {
			return function() {
				context.onEnterFrame()
			};
		};
		
		setTimeout(AnimClosure(this), 0);
		
	}

}

Effect.prototype.nextFrame = function() {

	this.setTime((this.getTimer() - this._startTime) / 1000);
	
}

Effect.prototype.getTimer = function() {

	return new Date().getTime() - this._time; 

}

Effect.prototype.setTime = function(t){
	
	this._prevTime = this._time;
	
	if (t > this._duration) {
		
		this._time = this._duration;
		
		this.update();
		
		this.stop();
				
	}  else {
		
		this._time = t;
		
		this.update();
		
	}
}

Effect.prototype.update = function(value) {

	if (this._from instanceof Array) {
		this.valueSetter([  this._easeMethod(this._time,this._from[0],this._delta[0],this._duration),
							this._easeMethod(this._time,this._from[1],this._delta[1],this._duration),
							this._easeMethod(this._time,this._from[2],this._delta[2],this._duration)
						]);
	} else {
		this.valueSetter(this._easeMethod(this._time,this._from,this._delta,this._duration));
	}
	
}
	
Effect.prototype.stop = function() {
	
	this.isAnimating = false;
	
	if (this.runningQueue) {
		Effects._runQueue(this.runningQueue);
		this.runningQueue = null;
	}
	
	this.update();
	
	this.onComplete();

}

Effect.prototype.getDelta = function(){
	
	return (this._to instanceof Array) ? 
		[ this._to[0] - this._from[0],this._to[1] - this._from[1],  this._to[2] - this._from[2] ] :
		this._to - this._from;
	
}

Effect.prototype.setOnCompleteHandler = function(f,c){
	if (c && f) {
		this.onComplete = function () { f.apply(c, arguments) };
	} else {
		this.onComplete = f || function(){return false;}; 
	}
}

/*
return a setter/getter function for oEl:
if you pass it a value, it will set that value for this.oEl and return it, if you dont, it will just return the
current value for oEl
*/
Effect.prototype.getValueGetterSetters = function() {

	var oGS; //{getter:,setter:,object} 

	switch(this._animType) {
	
		case "width":

			return {
				getter:function() {	
					return Element.getSize(this.oEl).width
				},
				setter:function(iWidth) {
					iWidth = iWidth >= 0 ? iWidth : 0;
					Element.setWidth(this.oEl,iWidth);
				}
			}
		
		break;
		case "height":
		
			return {
				getter:function() {	
					return Element.getSize(this.oEl).height
				},
				setter:function(iHeight) {
					iHeight = iHeight >= 0 ? iHeight : 0;
					Element.setHeight(this.oEl,Math.round(iHeight));
				}
			}
		
		break;
		case "x":
			
			return {
				getter:function() {	
					return this.oEl.offsetLeft;
				},
				setter:function(iX) {
					this.oEl.style.left = iX + "px";
				}
			}
		
		break;
		case "y":
			
			return {
				getter:function() {	
					return this.oEl.offsetTop;
				},
				setter:function(iY) {
					this.oEl.style.top = Math.round(iY) + "px";
				}
			}
		
		break;
		case "opacity":
		
			return {
				getter:function() {	
					var iOpac = this.oEl.style.opacity;
					return iOpac == "" || isNaN(iOpac) ? 100 : iOpac * 100;
				},
				setter:function(iOpacity) {
					Element.setOpacity(this.oEl,iOpacity);
				}
			}
		
		break;
		case "scrollLeft":
		
			return {
				getter:function() {	
					return this.oEl.scrollLeft;
				},
				setter:function(iScrollLeft) {
					this.oEl.scrollLeft = iScrollLeft;
				}
			}
		
		break;
		case "scrollTop":
		
			return {
				getter:function() {	
					return this.oEl.scrollTop;
				},
				setter:function(iScrollTop) {
					return this.oEl.scrollTop = iScrollTop;
				}
			}
		case "background":
		
			return {
				getter:function() {	
					return this.toRGB(this.oEl.style.backgroundColor);
				},
				setter:function(aRGB) {
					this.oEl.style.backgroundColor = this.toHex(aRGB);
					return aRGB
				}
			}
		
		break;
		default:
			return false;
		break
	
	}
}

Effect.prototype.toRGB = function (hex) {
	hexInt = parseInt(hex.substr(1), 16);
	return [ hexInt >> 16, hexInt >> 8 & 255, hexInt & 255 ];
}

Effect.prototype.toHex = function (rgb) {
	var hex = '#';
	for (var i = 0; i < 3; i++) {
		rgb[i] = Math.min(255, Math.max(0, Math.round(rgb[i]))).toString(16);
		while (rgb[i].length < 2) {
			rgb[i] = '0' + rgb[i];
		}
		hex += rgb[i];
	}

	return hex;
}

/*-------------------------------------------------------------------
Easing
-------------------------------------------------------------------*/
var Easing = {};

Easing.easeNone =  function (t, b, c, d) {
   	return c*t/d + b;
}

Easing.backEaseIn = function(t,b,c,d,a,p){
	if (s == undefined) var s = 1.70158;
	return c*(t/=d)*t*((s+1)*t - s) + b;
}
Easing.backEaseOut = function(t,b,c,d,a,p){
	if (s == undefined) var s = 1.70158;
	return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
}
Easing.backEaseInOut = function(t,b,c,d,a,p){
	if (s == undefined) var 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;
}
Easing.elasticEaseIn = 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;
	
}
Easing.elasticEaseOut = 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);
	}
Easing.elasticEaseInOut = function (t,b,c,d,a,p){
	if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) var p=d*(.3*1.5);
	if (!a || a < Math.abs(c)) {var 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;
}

Easing.bounceEaseOut = 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;
	}
}
Easing.bounceEaseIn = function(t,b,c,d){
	return c - Easing.bounceEaseOut (d-t, 0, c, d) + b;
	}
Easing.bounceEaseInOut = function(t,b,c,d){
	if (t < d/2) return Easing.bounceEaseIn (t*2, 0, c, d) * .5 + b;
	else return Easing.bounceEaseOut (t*2-d, 0, c, d) * .5 + c*.5 + b;
	}

Easing.strongEaseInOut = function(t,b,c,d){
	return c*(t/=d)*t*t*t*t + b;
	}

Easing.regularEaseIn = function(t,b,c,d){
	return c*(t/=d)*t + b;
	}
Easing.regularEaseOut = function(t,b,c,d){
	return -c *(t/=d)*(t-2) + b;
	}

Easing.regularEaseInOut = function(t,b,c,d){
	if ((t/=d/2) < 1) return c/2*t*t + b;
	return -c/2 * ((--t)*(t-2) - 1) + b;
	}
Easing.strongEaseIn = function(t,b,c,d){
	return c*(t/=d)*t*t*t*t + b;
	}
Easing.strongEaseOut = function(t,b,c,d){
	return c*((t=t/d-1)*t*t*t*t + 1) + b;
	}

Easing.strongEaseInOut = 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;
	}


