/*======================= copyrights ============================
    FiberCMS
    version 2.0
    Copyright (c) Carlo Meijer 2008
    All rights reserved
  ======================= file info =============================
    @name tween.js
    @desc object tweening support file
    creation date January 29 2009
  ===============================================================
*/

var FADE_TIME = 700;
var RESIZE_TIME = 500;
var MOTION_TIME = 500;

function tween(obj)
{
	var cls = this;
	this.obj = obj;
	this.obj.tween = this;

	this.fadeStart; // timestamp at start|
	this.fadeTime;
	this.fadeOpcStart; // opacity value at start
	this.fadeOpcEnd; // opacity value at end
	this.fadeTimer; // timeout object for next frame
	this.onsetopacity; // callback function on setting opacity
	this.onfadefinish;

	this.resizeStart; // timestamp at start
	this.resizeTime;
	this.resizeWidthStart; // width at start
	this.resizeWidthEnd; // width at end
	this.resizeHeightStart; // height at start
	this.resizeHeightEnd; // height at end
	this.resizeTimer; // timeout object
	this.onresize; // callback function on resize
	this.onresizefinish; // callback function on finishing resize
	this.resizePower = 1;

	this.motionStart;
	this.motionTime;
	this.motionXStart;
	this.motionXEnd;
	this.motionYStart;
	this.motionYEnd;
	this.motionTimer;
	this.onsetposition;
	this.onmotionfinish;
	this.motionPower = 1;

	/**
		tween.fade(opacity)
		@access public
		@description
			fades opacity of obj from current to opacity in FADE_TIME ms
	*/
	this.fade = function(opacity, ftime)
	{
		if(typeof(opacity) != 'undefined') // first call, do some initialisations
		{
// 			if(typeof(cls) != 'object')
// 				cls = this;
			// if there is another thread pending, cancel it
			if(cls.fadeTimer)
				window.clearTimeout(cls.fadeTimer);
			// set start time
			cls.fadeStart = new Date().getTime();
			if(typeof(ftime) == 'number')
				cls.fadeTime = ftime;
			else
				cls.fadeTime = FADE_TIME;
			// set start opacity
			cls.fadeOpcStart = cls.getOpacity();
			cls.fadeOpcEnd = opacity;
			// done setting configuration, detach from here
			if(cls.fadeTimer)
			{
				window.clearTimeout(cls.fadeTimer);
				cls.fadeTimer = null;
			}
			cls.fadeTimer = window.setTimeout(function(){cls.fade(undefined,undefined/*,cls*/);},10);
		}
		// calculate scaler based on current time
		var time = new Date().getTime();
		if(cls.fadeOpcStart != cls.fadeOpcEnd)
			var scaler = (time - cls.fadeStart) / cls.fadeTime;
		else
			var scaler = 1;
		scaler = Math.max(Math.min(scaler, 1), 0);
		// calculate new opacity
		var newOpc = ((cls.fadeOpcEnd - cls.fadeOpcStart) * scaler) + cls.fadeOpcStart;
		cls.setOpacity (newOpc);
		if(scaler < 1) // not done yet, create a new thread in 10ms
			cls.fadeTimer = window.setTimeout(function(){cls.fade(undefined,undefined/*,cls*/);}, 10);
		else if (typeof(cls.onfadefinish) == 'function')
		{
			//cls.onfadefinish()
			window.setTimeout(cls.onfadefinish, 1);
			cls.onfadefinish = undefined;
		}
	}

	/**
		tween.resize(width, height, rtime)
		@access public
		@description
			smoothly resizes obj to width x height in rtime (or RESIZE_TIME) ms
	*/
	this.resize = function(width, height, rtime)
	{
// 		var cls = this;
		if(typeof(width) != 'undefined')
		{
			if(this.resizeTimer)
				window.clearTimeout(this.resizeTimer);
			var dims = this.getDimensions();
			this.resizeStart = new Date().getTime();
			if(typeof(rtime) == 'number')
				this.resizeTime = rtime;
			else
				this.resizeTime = RESIZE_TIME;
			this.resizeWidthStart = dims.width;
			this.resizeHeightStart = dims.height;
			this.resizeWidthEnd = width;
			this.resizeHeightEnd = height;
			if(this.resizeTimer)
			{
				window.clearTimeout(this.resizeTimer);
				this.resizeTimer = null;
			}
			this.resizeTimer = window.setTimeout(function(){cls.resize();},10);
			return;
		}
		var time = new Date().getTime();
		if(!((this.resizeWidthStart == this.resizeWidthEnd) && (this.resizeHeightStart == this.resizeHeightEnd)))
			var scaler = (time - this.resizeStart) / this.resizeTime;
		else
			var scaler = 1;
		scaler = Math.max(Math.min(scaler, 1), 0);
		scaler = Math.pow(scaler, Math.max(this.resizePower, 1)); // makes slope curvy, so the effect is more dramatically
		var newWidth = ((this.resizeWidthEnd - this.resizeWidthStart) * scaler) + this.resizeWidthStart;
		var newHeight = ((this.resizeHeightEnd - this.resizeHeightStart) * scaler) + this.resizeHeightStart;
		this.setDimensions (newWidth, newHeight);
		if(scaler < 1)
			this.resizeTimer = window.setTimeout(function(){cls.resize();}, 10);
		else if (typeof(this.onresizefinish) == 'function')
		{
			window.setTimeout(cls.onresizefinish, 1);
			//cls.onresizefinish();
			this.onresizefinish = undefined;
		}
	}

	/**
		tween.motion(x, y, mtime)
		@access public
		@description
			smoothly moves obj to x,y in mtime (or MOTION_TIME) ms
	*/
	this.motion = function(x, y, mtime/*, cls*/)
	{
		if(typeof(x) != 'undefined')
		{
// 			if(typeof(cls) != 'object')
// 				cls = this;
			if(cls.motionTimer)
				window.clearTimeout(cls.motionTimer);
			var pos = cls.getPosition();
			cls.motionStart = new Date().getTime();
			if(typeof(mtime) == 'number')
				cls.motionTime = mtime;
			else
				cls.motionTime = MOTION_TIME;
			cls.motionXStart = pos.x;
			cls.motionYStart = pos.y;
			cls.motionXEnd = x;
			cls.motionYEnd = y;
			if(cls.motionTimer)
			{
				window.clearTimeout(cls.motionTimer);
				cls.motionTimer = null;
			}
			cls.motionTimer = window.setTimeout(function(){cls.motion(undefined,undefined,undefined/*,cls*/);},10);
			return;
		}
		var time = new Date().getTime();
		if(!((cls.motionXStart == cls.motionXEnd) && (cls.motionYStart == cls.motionYEnd)))
			var scaler = (time - cls.motionStart) / cls.motionTime;
		else
			var scaler = 1;
		scaler = Math.max(Math.min(scaler, 1), 0);
		scaler = Math.pow(scaler, Math.max(cls.motionPower, 1));
		var newX = ((cls.motionXEnd - cls.motionXStart) * scaler) + cls.motionXStart;
		var newY = ((cls.motionYEnd - cls.motionYStart) * scaler) + cls.motionYStart;
		cls.setPosition (newX, newY);
		if(scaler < 1)
			cls.motionTimer = window.setTimeout(function(){cls.motion(undefined,undefined,undefined/*,cls*/);}, 10);
		else if (typeof(cls.onmotionfinish) == 'function')
		{
			window.setTimeout(cls.onmotionfinish, 1);
			//cls.onmotionfinish();
			cls.onmotionfinish = undefined;
		}
	}

	this.getScroll = function()
	{
		var out = new Object();
		out.x = 0;
		out.y = 0;
		var elem = this.obj;
		do
		{
			out.x += elem.scrollLeft || 0;
			out.y += elem.scrollTop || 0;
		}
		while(elem = elem.parentNode);
		return out;
	}

	/**
		tween.getPosition()
		@access public
		@description
			returns position of object within document
	*/
	this.getPosition = function(absolute)
	{
		var out = new Object();
		out.x = 0;
		out.y = 0;
		var elem = this.obj;
		do
		{
// 			if(!absolute && (elem != this.obj) && (/^\s*(absolute|relative)\s*$/i.test(curCSS(elem, 'position'))))
// 				break;
			out.x += elem.offsetLeft || 0;
			out.y += elem.offsetTop  || 0;
		}
		while (elem = elem.offsetParent);
		return out;
	}

	/**
		tween.setPosition()
		@access public
		@description
			sets position of object within document
	*/
	this.setPosition = function(x, y)
	{
		this.obj.style.position = 'absolute';
		var xpos = 0;
		var ypos = 0;
		var tmp = this.obj;
		while(tmp = tmp.offsetParent)
		{
// 		var add = null;
// 		do
// 		{
// 			if(!add && (tmp != this.obj) && /absolute|relative/i.test(curCSS(tmp, 'position')))
// 				add = {x: xpos, y:ypos};
			xpos += tmp.offsetLeft || 0;
			ypos += tmp.offsetTop  || 0;
		}
// 		} while(tmp = tmp.offsetParent);
// 		if(!add) add = {x: 0, y:0};
		// hack: some browsers don't and some do take borders into account
// 		var curLeft = parseInt(curCSS(this.obj, 'left'));
// 		var curTop =  parseInt(curCSS(this.obj, 'top'));
		//var delta = isNaN(curLeft) ? 0 : curLeft - this.obj.offsetLeft;
		this.obj.style.left = (x - xpos /*+ add.x*/ /* + delta*/) + 'px';
		//var delta = isNaN(curTop) ? 0 : curTop - this.obj.offsetTop;
		this.obj.style.top = (y - ypos /*+ add.y*//* + delta*/) + 'px';
		if(typeof(this.onsetposition) == 'function')
			this.onsetposition(x, y);
	}

	/**
		tween.getDimensions()
		@access public
		@description
			returns dimensions of obj in an object
	*/
	this.getDimensions = function(borders)
	{
		var dims = new Object();
		dims.width  = this.obj.offsetWidth;
		dims.height = this.obj.offsetHeight;
		if(!borders)
		{
			dims.width -= parseInt(curCSS(this.obj, 'padding-left')) || 0;
			dims.width -= parseInt(curCSS(this.obj, 'padding-right')) || 0;
			dims.width -= parseInt(curCSS(this.obj, 'border-left-width')) || 0;
			dims.width -= parseInt(curCSS(this.obj, 'border-right-width')) || 0;
			dims.height -= parseInt(curCSS(this.obj, 'padding-top')) || 0;
			dims.height -= parseInt(curCSS(this.obj, 'padding-bottom')) || 0;
			dims.height -= parseInt(curCSS(this.obj, 'border-top-width')) || 0;
			dims.height -= parseInt(curCSS(this.obj, 'border-bottom-width')) || 0;
		}
		return dims;
	}

	/**
		tween.setDimensions(width, height)
		@access public
		@description
			sets width and height of obj to width and height respectively
	*/
	this.setDimensions = function(width, height)
	{
		this.obj.style.width = width + 'px';
		this.obj.style.height = height + 'px';
		if(typeof(this.onresize) == 'function')
			this.onresize(width, height);
	}

	/**
		tween.setOpacity(opacity)
		@access public
		@description
			sets opacity of obj to opacity
	*/
	this.setOpacity = function(opacity)
	{
		if (this.obj.filters != undefined)
			this.obj.style.filter = 'alpha(opacity=' + parseInt(opacity * 100) + ')';
		else if (this.obj.style.MozOpacity != undefined)
			this.obj.style.MozOpacity = opacity;
		else if (this.obj.style.KhtmlOpacity != undefined)
			this.obj.style.KhtmlOpacity = opacity;
		else if (this.obj.style.opacity != undefined)
			this.obj.style.opacity = opacity;
		if(typeof(this.onsetopacity) == 'function')
			this.onsetopacity(opacity);
	}

	/**
		tween.getOpacity()
		@access public
		@description
			returns a float containing the opacity of obj
	*/
	this.getOpacity = function()
	{
		if ((this.obj.filters != undefined) && (this.obj.filters.alpha != undefined)) // ie
			return (parseInt(this.obj.filters.alpha.opacity) / 100);
		else if (this.obj.style.MozOpacity) // mozilla
			return parseFloat(this.obj.style.MozOpacity);
		else if (this.obj.style.KhtmlOpacity) // khtml/webkit
			return parseFloat(this.obj.style.KhtmlOpacity);
		else if (this.obj.style.opacity) // w3c
			return parseFloat(this.obj.style.opacity);
		else
			return 1.0;
	}
}
