/**
 * This file must be included to support animation. The element style is changed through a tween process. The style may be anything
 * from opacity to width / height / position / etc. See also 
 * <a href="http://projects.ischool.washington.edu/tabrooks/343INFO/JSMotionTween/jsMotionTween.htm">JS Motion Tween</a>
 * 
 * @version 2009-09-30
 * @author <a href="mailto:r.tennapel@griponservice.nl?SUBJECT=animation.js">R. ten Napel, ing.</a>
 * @author <a href="mailto:k.hogeling@griponservice.nl?SUBJECT=animation.js">K. Hogeling, ing.</a>
 **/

/**
 * An animation class that handles element animation.
 * 
 * @param element			The element to animate.
 * @param property			The property of the element style to animate.
 * @param begin				The start value of the element style property.
 * @param end				The end value of the element style property.
 * @param suffix			The style property value suffix.
 * @param duration			The duration of the motion tween.
 * @param type				The animation core routine (type).
 * @param fps				The amount of frames per second to animate.
 * @param startTime			The time offset in ms to start the animtion from.
 * @param formatter			The value formatter to format the property value.
 **/
function Tween(element, property, begin, end, suffix, duration, type, fps, startTime, formatter) {
	// Detect if Tween is a color Tween:
	if (property.toLowerCase().contains("color")) {
		begin 		= GlobalKit.h2d(begin);
		end 		= GlobalKit.h2d(end);
		fps 		= 30;
		formatter 	= Tween.colorFormatter;
	}
	
	this.element 		= element;
	this.property 		= property;
	this.begin 			= begin;
	this.end 			= end;
	this.suffix 		= suffix;
	this.duration 		= (duration == null) ? 1000 : duration;
	this.animator 		= (type == null) ? Tween.linear : type;
	this.fps 			= (fps == null) ? Math.abs(this.end - this.begin) * 1000 / duration : fps;
	this.startTime		= (startTime == null) ? 0 : startTime;
	this.formatter 		= (formatter == null) ? Tween.defaultFormatter : formatter;
	
	// Working variables:
	this.timers 		= new Array();
	this.difference 	= Math.abs(this.end - this.begin);
	this.totalFrames 	= Math.ceil(this.duration * this.fps / 1000);
	this.timePerFrame 	= 1000 / this.fps;
	this.mul 			= this.difference / this.totalFrames;
	this.startFrame 	= this.startTime / this.timePerFrame;
}

/**
 * The default value formatter which formats sizes (...px), locations (...px) & percentages (...%). Percentages won't be
 * mathimatically round to integers.
 * 
 * @param value				The value to format.
 * @param tween				The tween to use for formatting.
 * 
 * @return					The formatted value.
 **/
Tween.defaultFormatter = function(value, tween) {
	// If suffix is not set:
	if (tween.suffix == null) {
		return Math.round(value);
	} else if (tween.suffix != "%") {
		value = Math.round(value);
	}
	
	return value + tween.suffix;
};

/**
 * The color value formatter which formats color values (#......).
 * 
 * @param value				The value to format.
 * @param tween				The tween to use for formatting.
 * 
 * @return					The formatted value.
 **/
Tween.colorFormatter = function(value, tween) {
	// Calculate the begin and end values:
	var begin = GlobalKit.d2h(tween.begin).pad(-6, "0");
	var end = GlobalKit.d2h(tween.end).pad(-6, "0");
	
	// Calculate the percentage, start & end RGBs:
	var percent = value;
	if (tween.begin <= tween.end) {
		percent -= tween.begin;
		
		var r1 = GlobalKit.h2d(begin.slice(0, 2));
		var g1 = GlobalKit.h2d(begin.slice(2, 4));
		var b1 = GlobalKit.h2d(begin.slice(4, 6));
		
		var r2 = GlobalKit.h2d(end.slice(0, 2));
		var g2 = GlobalKit.h2d(end.slice(2, 4));
		var b2 = GlobalKit.h2d(end.slice(4, 6));
	} else {
		percent -= tween.end;
		
		var r1 = GlobalKit.h2d(end.slice(0, 2));
		var g1 = GlobalKit.h2d(end.slice(2, 4));
		var b1 = GlobalKit.h2d(end.slice(4, 6));
		
		var r2 = GlobalKit.h2d(begin.slice(0, 2));
		var g2 = GlobalKit.h2d(begin.slice(2, 4));
		var b2 = GlobalKit.h2d(begin.slice(4, 6));
	}
	percent /= tween.difference;
	
	// Calculate the new RGB:
    var r = Math.floor(r1 + (percent * (r2 - r1)) + .5);
    var g = Math.floor(g1 + (percent * (g2 - g1)) + .5);
    var b = Math.floor(b1 + (percent * (b2 - b1)) + .5);
	
	return ("#" + GlobalKit.d2h(r).pad(-2, "0") + GlobalKit.d2h(g).pad(-2, "0") + GlobalKit.d2h(b).pad(-2, "0"));
};

/**
 * The linear tween animation type.
 * 
 * @param tween				The tween to animate.
 * 
 * @return					The new property value (unformatted).
 **/
Tween.linear = function(tween) {
	return tween.currentFrame * tween.mul;
};

/**
 * The bounce out tween animation type.
 * 
 * @param tween				The tween to animate.
 * 
 * @return					The new property value (unformatted).
 **/
Tween.bounceOut = function(tween) {
	// The domain:
	var domain = Math.PI;
	var domainMul = domain / tween.totalFrames;
	
	return ((Math.sin(tween.currentFrame * domainMul) * tween.totalFrames / 2 + (tween.currentFrame)) * tween.mul);
};

/**
 * The ease tween animation type.
 * 
 * @param tween				The tween to animate.
 * 
 * @return					The new property value (unformatted).
 **/
Tween.ease = function(tween) {
	// Maximum reach of a tangens x and the graph shift:
	var domain = 16;
	var domainShift = -domain / 2;
	var domainMul =  domain / tween.totalFrames;
	
	// Maximum reach of a tangens y and the graph shift:
	var reach = Math.PI * 11 / 12;
	var reachShift = reach / 2;
	var reachMul = tween.difference / reach;
	
	return (Math.atan(tween.currentFrame * domainMul + domainShift) + reachShift) * reachMul;
};

/**
 * The sinus tween animtion type.
 * 
 * @param tween				The tween to animate.
 * 
 * @return					The new property value (unformatted).
 **/
Tween.sinus = function(tween) {
	// The domain:
	var domain = Math.PI * 2;
	var domainMul = domain / tween.totalFrames;
	
	return ((Math.sin(tween.currentFrame * domainMul) * tween.totalFrames / 2 + tween.currentFrame) * tween.mul);
};

/**
 * Start the animation.
 **/
Tween.prototype.start = function() {
	
	
	// Do each frame:
	for (var i = this.startFrame; i < this.totalFrames; i++) {
		this.currentFrame = i;
		
		// Retrieve the value calculated by the animator:
		var value = this.animator(this);
		
		// Check if smaller or greater:
		if (this.currentFrame == this.startFrame) {
			value = this.begin;
		} else if (this.end >= this.begin) {
			value += this.begin;
		} else if (this.end < this.begin) {
			value = this.begin - value;
		}
		
		// Finish the value:
		value = this.formatter(value, this);
		
		// Add the frame:
		this.timeout(value, (this.currentFrame - this.startFrame) * this.timePerFrame);
	}
	
	// Finish the value:
	value = this.formatter(this.end, this);
	
	// Add the frame:
	this.timeout(value, this.duration - this.startTime);
};

/**
 * Stop the animation by clearing all timers (element threads).
 **/
Tween.prototype.stop = function() {
	// Clear all threads:
	for (var i = 0; i < this.timers.length; i++) {
		clearTimeout(this.timers[i]);
	}
	
	this.timers.clear();
	
	return this;
};

/**
 * Add a new frame to the animation which shows after a specific time, the timer is stored in the elements thread pool.
 * 
 * @param value				The formatted value of the property to set.
 * @param time				The time it takes untill the frame is shown.
 **/
Tween.prototype.timeout = function(value, time) {
	var slide = this;
	
	//this.timers.add(setTimeout(function (e){ '$("' + slide.element.id + '").setStyle("' + slide.property + '", "' + value + '")' }, time));
	this.timers.add(setTimeout(function (e){
										 $(slide.element.id).setStyle(slide.property,value); 
										 }, time));
	
};

// ANIMATION ROTATE

function Rotate(element,parameters){	
	this.length= 1;
	this.element = document.getElementById(element);
	if (this.length===0||typeof parameters=="undefined") return;
	if (typeof parameters=="number") parameters={angle:parameters};
	var returned=[];
	for (var i=0,i0=this.length;i<i0;i++)
	{
		if (typeof element.Wilq32 == "undefined") {
		
			returned.push($(this.ImageRotate(parameters)));
		}
		else  {
		
            element.Wilq32.PhotoEffect._handleRotation(parameters);
		}
	}
	return returned;
	
}


Rotate.prototype.ImageRotate = function(parameters){
	
	// If this element is already a Wilq32.PhotoEffect object, skip creation
	if (this.Wilq32&&this.Wilq32.PhotoEffect) return;
	// parameters might be applied to many objects - so because we use them later - a fresh instance is needed 
	var paramClone = parameters;  
	return (new Wilq32.PhotoEffect(this.element,paramClone))._rootObj;
	
};



