/**
* Filename................: mel.animation.js
* Project.................: web pages SDK
* Last Modified...........: $Date: 13/9/2007 13:58:33 $
* CVS Revision............: $Revision: 0.0.15 $
* Idea and Developed by...: Maxim Bulygin (sailormax@gmail.com)
*/

if (typeof MEL == "undefined") var MEL = {};

MEL.animation = {

	in_use: [],
	cfg: {
		TIME: 150,
		DELAY: 15,
		PARAMS: []
	},

	__addParam: function()	// [obj], name, from, till, step, counter
	{
		var i, piece_step, args = MEL.to_array(arguments);
		var obj = (typeof args[0] == "object" ? args.shift() : this.obj);
		var name = args[0],
			from = args[1],
			till = args[2],
			step = args[3] || "1px",
			counter = args[4],
			cps = MEL.to_array(args[5]);
		var params = this.cfg.PARAMS;
		if (!from.match && isNaN(from)) return;

		var prefix = "", postfix = "";
		if (from.match)
		{
			if (from.match(/^#(.+)$/))
			{
				from = MEL.rgba2arr(RegExp.$1);
				if (till.match && till.match(/^#(.+)$/)) till = RegExp.$1;
				till = MEL.rgba2arr(till);

				for (i in from)
					this.addParam(obj, (i=="0"?"+":""), from[i], till[i], step, counter, cps);
				params.push({obj:obj, name:name, from:0, cur:0, till:0, step:0, counter:null, cps:null, prefix:"#", postfix:""});
				return this;
			}
			else if (from.match(/^([a-z]+\()([^\)]+)\)$/i))
			{
				prefix = RegExp.$1;
				from = RegExp.$2.replace(/ +/g, "").split(",");
				if (till.match && (till.match(/^[a-z]+\(([^\)]+)\)$/i))) till = RegExp.$1;
				till = till.replace(/ +/g, "").split(",");

				for (i in from)
					this.addParam(obj, (i=="0"?"+":""), from[i], till[i], step, counter, cps);
				params.push({obj:obj, name:name, from:0, cur:0, till:0, step:0, counter:null, cps:null, prefix:prefix, postfix:")"});
				return this;
			}
		}

		var re = new RegExp("^([0-9]+)([a-z%]+)$", "i");
		if (step.match && step.match(re)) {	step = RegExp.$1; postfix = RegExp.$2; }
		step -= 0;
		if (from.match && from.match(re)) { from = RegExp.$1; postfix = RegExp.$2; }
		from -= 0;
		if (till.match && till.match(re)) { till = RegExp.$1; postfix = RegExp.$2; }
		till -= 0;

		if (((from < till) && (step < 0)) || (from > till) && (step > 0)) step *= -1;
		if (typeof counter != "function")
		{
			if (!cps.length && counter && counter.pop) cps = counter;
			counter = MEL.animation.__counter;
		}
		params.push({obj:obj, name:name, from:from, cur:from, till:till, step:step, counter:counter, cps:cps, prefix:"", postfix:postfix});
		return this;
	},

	__clearParams: function()
	{
		this.cfg.PARAMS = [];
		this.scenes = [];
	},

	__animatePredefInit: function(args)
	{
		var obj;
		if (typeof args[0] == "object")
			obj = args.shift();

		if (isNaN(this.__idx))
			animate = this.Create(obj);
		else
		{
			animate = this;
			if (!obj) obj = this.obj;
		}

		return [animate, obj];
	},

	animateOpacity: function()	// [obj], from, to, step
	{
		var args = MEL.to_array(arguments);
		var tmp = this.__animatePredefInit(args);
		var obj = tmp[1], animate = tmp[0];

		var from = args[0];	if (isNaN(from)) from = 0;
		var to = args[1];	if (isNaN(to)) to = 1;
		var step = args[2];	if (isNaN(step)) step = 0.1;
		if ((from > 1) || (to > 1))
		{
			from /= 100;
			to /= 100;
			step /= 100;
		}

		if (MEL.ua.ie)
			animate.addParam(obj, "filters.alpha.opacity", from*100, to*100, step*100);
		else
			animate.addParam(obj, "style.opacity", from, to, step);
		return animate;
	},

	animateSize: function()	// [obj], w_from, w_to, h_from, h_to, step
	{
		var params, i, from, to, step, sh = 0;
		var args = MEL.to_array(arguments);
		var tmp = this.__animatePredefInit(args);
		var obj = tmp[1], animate = tmp[0];

		step = args[4] || "5px";
		params = ["width", "height"];
		for (i in params)
		{
			from = args[0+sh];
			to = args[1+sh];

			if (from != to)
			{
				if (isNaN(from)) from = (i=="1" ? obj.scrollHeight : obj.scrollWidth);
				if (isNaN(to)) to = (i=="1" ? obj.scrollHeight : obj.scrollWidth);
				animate.addParam(obj, "style." + params[i], from, to, step);
			}
			sh += 2;
		}
		return animate;
	},

	animatePosition: function()	// [obj], x_from, y_from, x_to, y_to, step, x_cps, y_cps
	{
		var params, i, from, to, step, sh = 0;
		var args = MEL.to_array(arguments);
		var tmp = this.__animatePredefInit(args);
		var obj = tmp[1], animate = tmp[0];

		step = args[4] || "5px";
		if (MEL.dom.getStyle(obj, "position") == "absolute")
			params = ["left", "top"];
		else
			params = ["marginLeft", "marginTop"];

		for (i in params)
		{
			from = args[0+sh];
			to = args[2+sh];

			if (isNaN(from)) from = MEL.dom.getStyle(obj, params[i]);
			if (isNaN(to)) to = MEL.dom.getStyle(obj, params[i]);

			if ((from != to) || args[5+sh])
				animate.addParam(obj, "style." + params[i], from, to, step, args[5+sh]);
			sh++;
		}
		return animate;
	},


	__newScene: function()
	{
		if (this.cfg.PARAMS.length)
		{
			this.scenes.push(this.cfg);
			this.cfg = { TIME: this.cfg.TIME, DELAY: this.cfg.DELAY, PARAMS: [] };
		}
	},

	__reverse: function()
	{
		var i, j, tmp, params, scenes = this.scenes;
		for (i in scenes)
		{
			params = scenes[i].PARAMS;
			for (j in params)
			{
				tmp = params[j].from;
				params[j].from = params[j].till;
				params[j].till = tmp;
			}
		}

		this.scenes = scenes.reverse();
	},

	__start: function()
	{
		var i, j, steps, attr, val_el, vals, max_steps = 0;
		if (this.__status > 0) this.stop();
		if (!this.__scene_idx && this.cfg.PARAMS.length) this.newScene();
		this.cfg = this.scenes[this.__scene_idx++];
		if (!this.cfg)
		{
			this.__scene_idx = 0;
			this.cfg = { TIME: 150, DELAY: 15, PARAMS: [] };

			if (this.onFinish) this.onFinish();
			return;
		}
		if (this.onStart) this.onStart();
		var params = this.cfg.PARAMS;
		this.__start_time = new Date()-0;
		var prev_name = "-";

		for (i=0; i<params.length; i++)
		{
			attr = params[i];
			attr.cur = attr.from;
			steps = (attr.step ? Math.abs((attr.till - attr.cur) / attr.step) : 0);
			attr.next_step = 0;
			attr.left_steps = Math.floor(steps) || -1;
			if (steps > max_steps) max_steps = steps;
		}
		this.__max_steps = this.__elapsed = Math.round(max_steps);
		this.__interval = (this.cfg.TIME ? Math.round(this.cfg.TIME / max_steps) : this.cfg.DELAY);

		this.__status = 1;
		this.__step(true);
	},

	__stop: function()
	{
		MEL.timer.clearTimeout(this.__timer);
		this.__status = -1;
		this.__scene_idx = 0;

		var params = this.cfg.PARAMS;
		for (i=0; i<params.length; i++)
			params[i].cur = params[i].from;
		if (this.onFinish) this.onFinish();
	},

	__counter: function(prms, t)
	{
		if (prms.cps.length)
		{
			if (prms.cps.length > 1)
				prms.cur = Math.pow(1-t, 3)*prms.from + 3*t*Math.pow(1-t, 2)*prms.cps[0] + 3*Math.pow(t,2)*(1-t)*prms.cps[1] + Math.pow(t, 3)*prms.till;
			else
				prms.cur = Math.pow(1-t, 2)*prms.from + 2*t*(1-t)*prms.cps[0] + Math.pow(t, 2)*prms.till;
		}
		else
			prms.cur = (1-t)*prms.from + t*prms.till;
	},


	__step: function(first)
	{
		if (this.onStep) this.onStep();

		var i, j, attr, left_steps = 0;
		var val_el, vals, updated = 0;
		var prev_name = "-";
		var params = this.cfg.PARAMS;
		var finish = Math.ceil(--this.__elapsed) < 1;
		var ela = this.__elapsed;
		var now = new Date()-0;
		var t, time = this.cfg.TIME;
		if (time)
			t = (now - this.__start_time) / time;
		else
			t = (this.__max_steps - ela) / this.__max_steps;
		if (t >= 1) finish = t = 1;

		for (i=0; i<params.length; i++)
		{
			attr = params[i];
			if (time || (attr.left_steps && ((attr.next_step >= ela) || first)))
			{
				if (!first && attr.counter) attr.counter(attr, t);
				if (attr.name && (attr.name != "+") && prev_name && (prev_name != "+"))
					MEL.dom.setAttribute(attr.obj, attr.name, attr.prefix + (finish ? attr.till : attr.cur) + attr.postfix);

				if (!time) attr.left_steps--;
				updated++;

				if (!time && ((attr.left_steps > 0) || ela))
					attr.next_step = ela - (ela / (attr.left_steps > 0 ? attr.left_steps : ela));
			}

			if (updated && i && attr.name && (attr.name != "+") && (!prev_name || prev_name == "+"))
			{
				vals = [];
				j = i;
				if (val_el = params[--j])
				do { vals.push(val_el.prefix + (finish ? val_el.till : val_el.cur) + val_el.postfix) } while ((val_el.name != "+") && (val_el = params[--j]));
				MEL.dom.setAttribute(attr.obj, attr.name, attr.prefix + (attr.prefix=="#" ? MEL.arr2rgba(vals.reverse()) : vals.reverse().join(", ")) + attr.postfix);
				updated = 0;
			}

			if (attr.left_steps) left_steps++;
			prev_name = attr.name;
		}

		finish = finish || !left_steps || !this.__status;
		if (finish)
		{
			MEL.timer.clearTimer(this.__timer);
			this.__timer = null;

			if (this.__status > 0)
			{
				this.__status = 0;
				this.start();
			}
			else
				if (this.onFinish) this.onFinish();
		}
		else if (!this.__timer)
			this.__timer = MEL.timer.setInterval(this, "__step", this.__interval);
	},


	Create: function(obj, time, delay)
	{
		var anim = {};

		if (typeof obj == "string") obj = MEL.d.getEl(obj);
		anim.obj = obj;
		anim.cfg = MEL.getCopy(this.cfg);

		if (typeof time != "undefined") anim.cfg.TIME = time;
		if (typeof delay != "undefined") anim.cfg.DELAY = delay;

		anim.scenes = [];
		anim.__animatePredefInit = this.__animatePredefInit;

		var i;
		var h_func = ["stop", "start", "addParam", "clearParams", "reverse", "newScene"];
		var p_func = ["animateOpacity", "animateSize", "animatePosition"];
		for (i in h_func) anim[h_func[i]] = this["__"+h_func[i]];
		for (i in p_func) anim[p_func[i]] = this[p_func[i]];

		var p_null = ["event_args", "onStart", "onStep", "onFinish"];
		for (i in p_null) anim[p_null[i]] = null;

		anim.__step = this.__step;

		var h_zero = ["__interval", "__max_steps", "__elapsed", "__timer", "__start_time", "__scene_idx", "__status"];
		for (i in h_zero) anim[h_zero[i]] = 0;

		anim.__idx = MEL.animation.in_use.length;
		MEL.animation.in_use.push(anim);

		return anim;
	}
}

