/*	Este archivo se encarga de los efectos gráficos (animaciones).
	La clase XObject alamacena un div en su interior - con todas sus propiedade - y es capaz de modificarlo de forma gradual.
	
	Por un lado, la clase incluye métodos para averiguar y modificar las propiedades de los contenedores (setStyle getStyle).

	Los cambios de estilo se realizarán de la siguiente forma:
		La clase calcula todos los números intermedios entre dos cantidades, con un incremento constante y cada paso dado en un intervalo de tiempo:

		0 --> 100 INC = 10
		0 - 10 - 20 - 30 - 40 -50 -60 - 70 - 80 - 90 -100

		En cada paso intermedio se llama a una función con ese valor como parámetro.
		Dicha función será la que cambie el estilo que deseemos:

		1 - 100 Width

		width = 0;
		width = 10
		....


		También se pueden hacer loops, bucles para que cuando acabe, vuelva al principio

		0 --> 100 --> 0 --> 100

		Con ello, se consigue un efecto como el del crono, que parpadea.

	Efectos de 'Rebote' (setPercEffect). Para este tipo de efectos, creamos una cola interna
	en la que se añaden nuevos cambios de estilo para dar sensación de que el elemento llega al final, vuelve en un tanto por ciento 
	de nuevo al final (y esto repetido una serie de veces) 							(ej. 10px -> 200px:   200px -> 190px -> 200px -> 195px -> 200px).
*/ 


var HEX_TABLE =
	{
		'0': 0,
		'1': 1,
		'2': 2,
		'3': 3,
		'4': 4,
		'5': 5,
		'6': 6,
		'7': 7,
		'8': 8,
		'9': 9,
		'A': 10,
		'B': 11,
		'C': 12,
		'D': 13,
		'E': 14,
		'F': 15
	};
		

/* Constructor
 * Inicializa todos los valores internos (style)
 * content:		Div que tratamos.
 *
 */

function XObject(content)
{
	var res = false;

	if(content)
	{
		this.div_cont = content;
	
		this.getCSS();
		this.style =  
		{
			opacity:				this.getStyle("opacity"),
			
			width:					this.getStyle("width"),
			height:					this.getStyle("height"),

			abs_pos_x:				this.getStyle("abs_pos_x"),
			abs_pos_y:				this.getStyle("abs_pos_y"),

			rel_pos_x:				this.getStyle("rel_pos_x"),
			rel_pos_y:				this.getStyle("rel_pos_y"),

			backgroundColor:		this.getStyle("backgroundColor"),
			borderColor:			this.getStyle("borderColor"),
			borderWidth:			this.getStyle("borderWidth"),

			color:					this.getStyle("color")
		};

		// Timeouts de todas las animaciones, cuando se llama una, si hay otra antes la para.
		this.timeouts = 
		{
			"opacity":				false,
			
			"width":				false,
			"height":				false,

			"abs_pos_x":			false,
			"abs_pos_y":			false,

			"rel_pos_x":			false,
			"rel_pos_y":			false,

			"backgroundColor":		false,
			"borderColor":			false,
			"borderWidth":			false,
			
			"color":				false
		}

		res = true;
	
		/* DEFAULTS */
		// Tiempo de los intervalos.
		this.SCALE_TIME = 10;


	}

	return res;
}

XObject.prototype = {

	// Esta función coge todos los estilos.
	getStyle: function(attribute)
	{
		var res = 0;
		var style_src = this.css // this.css && false? this.css : this.div_cont.style;

		switch(attribute)
		{
			case "opacity":
				if(navigator.userAgent.toLowerCase().indexOf('msie') == -1)
					res = style_src.opacity * 100;
				else
					res = style_src.filter;

				if(!res)
					res = "100"

				break;

			case "width":
				res = style_src.width && style_src.width != "auto" ? style_src.width : "0px";
	
				break;

			case "height":
				res = style_src.height && style_src.height != "auto" ? style_src.height : "0px";

				break;

			case "abs_pos_x":
				res = style_src.left && style_src.left != "auto" ? style_src.left : "0px";

				break;

			case "abs_pos_y":
				res = style_src.top && style_src.top != "auto" ? style_src.top : "0px";

				break;

			case "rel_pos_x":
				res = style_src.marginLeft && style_src.marginLeft != "auto" ? style_src.marginLeft : "0px";

				break;

			case "rel_pos_y":
				res = style_src.marginTop && style_src.marginTop != "auto" ? style_src.marginTop : "0px";

				break;

			case "backgroundColor":
			case "borderColor":
			case "color":
				var res_rgb = this.getRGB(style_src[attribute]);
				res = res_rgb ? res_rgb : "rgb(255, 255, 255)";

				// Parche Chrome, Safari: rgba(n, n, n, n):

				if(res.match(/rgba./g))
				{
					var cols = this.getNumeric(res);
					res = "rgb(" + cols[0] + ", " + cols[1] + ", " + cols[2] + ")";
				}

				break;

			case "borderWidth":
				if(style_src.borderWidth)
				{
					switch(style_src.borderWidth)
					{
						case "thin":
							res = "1px";
							break;

						case "medium":
							res = "2px";
							break;

						case "thick":
							res = "3px";
							break;
		
						default:
							res = style_src.borderWidth;
					}
				}
				else
					res = "0px";
				
				break;

			default:
				res = style_src[attribute] ? style_src[attribute] : "0px";

				break;

		}

		return res;
	},


	// ESta función pone estilo en cada atributo.
	setStyle: function(attribute, value)
	{
		var style_src = this.div_cont.style; // this.css && false ? this.css : this.div_cont.style;

		if(value || value == 0)
		{
			this.style[attribute] = value;

			switch(attribute)
			{
				case "opacity":
					if(navigator.userAgent.toLowerCase().indexOf('msie') == -1)
						style_src.opacity = value / 100.0;
					else
						style_src.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=" + String(value) + ")";

					break;

				case "abs_pos_x":
					style_src.left = value;
		
					break;

				case "abs_pos_y":
					style_src.top = value;
		
					break;

				case "rel_pos_x":
					style_src.marginLeft = value;
		
					break;

				case "rel_pos_y":
					style_src.marginTop = value;
		
					break;

				default:
					style_src[attribute] = value;
	
					break;
			}
		}

	},

	// Emepiza una animación.
	startAnimation: function(attribute, final_value, step, func, fifo, loop)
	{
		if(this.timeouts[attribute])
			window.clearTimeout(this.timeouts[attribute]);

		var value = this.getNumeric(this.style[attribute]);

		if(loop == true)
			loop = value;
		else
			loop = -1;

		if(final_value - value < 0)
			step = Number(-step);

		var self = this;

		if(step != 0 && ((final_value - value) / step) > 0)
		{
			this.timeouts[attribute] = setTimeout(function()
												{
													self.getNextStep(attribute, value, final_value, step, func, fifo, loop);
												}, this.SCALE_TIME);
		}
		else
		{
			if(fifo)
				fifo.next();
		}
	},

	// Calcula el siguiente paso, si no ha acabado. Si está en un loop vuelve al principio cuando acaba.
	getNextStep: function(attribute, value, final_value, step, func, fifo, loop_value)
	{
		var cont = ((final_value - value) / step);
		var self = this;
		
		if(cont > 0)
		{
			var next_value;

			if(cont >= 1)
				next_value = Number(Number(value) + Number(step));
			else
				next_value = Number(final_value);
	
			func.call(this, next_value);

			if(this.timeouts[attribute])
			{
				this.timeouts[attribute] = setTimeout(function()
													{
														self.getNextStep(attribute, next_value, final_value, step, func, fifo, loop_value);
													}, this.SCALE_TIME);
			}			
		}
		else
		{
			if(loop_value != -1)
			{
				self = this;

				if(this.timeouts[attribute])
				{
					this.timeouts[attribute] = setTimeout(function()
														{
															self.getNextStep(attribute, value, loop_value, -step, func, fifo, value);
														}, this.SCALE_TIME);
				}
			}
			else
			{
				this.timeouts[attribute] = false;
				if(fifo)
					fifo.next();
			}
		}
	},	


	// Lo mismo que startAnimation, pero donde todso los valores (final_values, steps...) son VECTORES:
	// [10, 20, 30] --> [50, 50, 50]:  [11, 21, 31] - [12, 22, 32] .....
	startAnimationV: function(attribute, final_values, steps, func, fifo, loop)
	{
		if(this.timeouts[attribute])
			window.clearTimeout(this.timeouts[attribute]);

		var values = new Array();
		values = this.getNumeric(this.style[attribute]);

		if(loop == true)
		{
			loop = new Array();
			for(var l = 0; l < values.length; l++)
				loop[l] = values[l];
		}
		else
			loop = -1;

		if(final_values && typeof(final_values) == "object" && steps && typeof(steps) == "object" && final_values.length == steps.length && steps.length == values.length)
		{
			var res = true;

			for(var s in steps)
			{
				if(steps[s] == 0)	
					res = false;
				else if(final_values[s] - values[s] < 0)
					steps[s] = Number(-steps[s]);
			}

			if(res)
			{	
				var self = this;
				this.timeouts[attribute] = setTimeout(function()
													{
														self.getNextStepV(attribute, values, final_values, steps, func, fifo, loop);
													}, this.SCALE_TIME);
			}
			else
			{
				if(fifo)
					fifo.next();
			}
		}
		else
		{
			if(fifo)
				fifo.next();
		}

	},

	// Lo mismo que nextStep, pero con vectores.
	getNextStepV: function(attribute, values, final_values, steps, func, fifo, loop_values)
	{
		var end = 0;
		var next_values = new Array(values.length);
		var self = this;

		for(var i = 0; i < values.length; i++)
		{
			var cont = ((final_values[i] - values[i]) / steps[i]);

			if(cont > 0)
			{
				if(cont >= 1)
					next_values[i] = Number(Number(values[i]) + Number(steps[i]));
				else
					next_values[i] = Number(final_values[i]);
			}
			else
			{
				next_values[i] = values[i];
				end ++;
			}
		}

		if(end != 3)
		{
			func.call(this, next_values);

			self = this;
			if(this.timeouts[attribute])
			{
				this.timeouts[attribute] = setTimeout(function()
														{
															self.getNextStepV(attribute, next_values, final_values, steps, func, fifo, loop_values);
														}, this.SCALE_TIME);
			}
		}
		else
		{
			if(loop_values != -1)
			{
				if(this.timeouts[attribute])
				{
					for(var s = 0; s < steps.length; s++)
						steps[s] = -steps[s];

					this.timeouts[attribute] = setTimeout(function()
															{
																self.getNextStepV(attribute, values, loop_values, steps, func, fifo, values);
															}, this.SCALE_TIME);
				}
			}
			else
			{
				this.timeouts[attribute] = false;
				if(fifo)
					fifo.next();
			}
		}
	},		

	// Efecto de rebote:
	//  Animación hasta el final,
	// Animación hasta un poco antes,
	//  Animación hasta el final.
	startPercEffect: function(attribute, final_value, step, perc, func, fifo, loop)
	{
		if(perc > 0)
		{
			var perc_fifo = new xFuncFIFO;
			var self = this;
			
			var new_final_value = (final_value - (step * perc));

			perc_fifo.add(function() { self.startAnimation(attribute, final_value, step, func, perc_fifo) }, self);
			perc_fifo.add(function() { self.startAnimation(attribute, new_final_value, step, func, perc_fifo); }, self);
			perc_fifo.add(function() { self.startAnimation(attribute, final_value, step, func, fifo, loop) }, self);
			perc_fifo.next();
		}
		else
			this.startAnimation(attribute, final_value, step, func, fifo, loop);
	},

	// Función principal, esta llama startAnimation(V) según los datos introducidos - analizados.
	start: function(attribute, final_value, scale, perc, fifo, loop)
	{
		var value_n = this.getNumeric(this.style[attribute]);
		var self = this;

		if(navigator.userAgent.toLowerCase().indexOf('msie') != -1)
			scale = parseInt(scale / 2);

		if(value_n && value_n.length && value_n.length > 1)
		{
			var steps = new Array()
			
			for(var i = 0; i < value_n.length; i++)
			{
				steps[i] = parseInt((this.getNumeric(final_value[i]) - value_n[i]) / scale);
				if(steps[i] < 0)
					steps[i] = -steps[i];

				if(steps[i] < 1)
					steps[i] = 1;
			}

			this.startAnimationV(attribute, final_value, steps,
				function(value) 
				{ 
					// RGB
					self.setStyle(attribute, "rgb(" + value[0] + ", " + value[1] + ", " + value[2] + ")");
				}, fifo, loop);
		}
		else
		{
			var final_value_n = this.getNumeric(final_value);

			var step = parseInt((final_value_n - value_n) / scale);
			if(step < 0)
				step = -step;
			
			if(step < 1)
				step = 1;

			this.startPercEffect(attribute, final_value_n, step, perc,
					function(value)
					{
						value = value >= 0 ? value : 0;
						self.setStyle(attribute, value + self.getUnit(final_value));
					}, fifo, loop);

		}
	},

	// FUNCIONES 'PUBLIC' DESDE LAS QUE EL USUARIO CREA LA ANIMACIÓN.

	moveRelX: function(final_x, scale, perc, fifo, loop)
	{
		this.start("rel_pos_x", final_x, scale, perc, fifo, loop);
	},

	moveRelY: function(final_y, scale, perc, fifo, loop)
	{
		this.start("rel_pos_y", final_y, scale, perc, fifo, loop);
	},

	moveRel: function(final_x, final_y, scale, perc, fifo, loop)
	{
		this.start("rel_pos_x", final_x, scale, perc, fifo, loop);
		this.start("rel_pos_y", final_y, scale, perc, false, loop);
	},


	moveAbsX: function(final_x, scale, perc, fifo, loop)
	{
		this.start("abs_pos_x", final_x, scale, perc, fifo, loop);
	},

	moveAbsY: function(final_y, scale, perc, fifo, loop)
	{
		this.start("abs_pos_y", final_y, scale, perc, fifo, loop);
	},

	moveAbs: function(final_x, final_y, scale, perc, fifo, loop)
	{
		this.start("abs_pos_x", final_x, scale, perc, fifo, loop);
		this.start("abs_pos_y", final_y, scale, perc, false, loop);
	},

	align: function(position, scale, perc, fifo)
	{
        var obj_perc_x = 0;
        var obj_perc_y = 0;
        var center_pos_x = 0;
        var center_pos_y = 0;
		
		var height = this.getNumeric(this.style.height);
		var width = this.getNumeric(this.style.width);

		switch(position)
		{
			case "top":
				this.moveAbsY("0%", scale, perc, fifo);
				break;

			case "bottom":
				obj_perc_y = parseInt((height / screen.availHeight) * 100);
				pos_y = parseInt(100 - obj_perc_y) + "%";
				this.moveAbsY(pos_y, scale, perc, fifo);
				break;

			case "left":
				this.moveAbsX("0%", scale, perc, fifo);
				break;

			case "center":
				obj_perc_x = parseInt((width / screen.availWidth) * 100);
				center_pos_x = parseInt(50 - parseInt(obj_perc_x / 2)) + "%";

				obj_perc_y = parseInt((height / screen.availHeight) * 100);
				center_pos_y = parseInt(50 - parseInt(obj_perc_y / 2)) + "%";

				if(scale != 1)
					this.moveAbs(center_pos_x, center_pos_y, scale, perc, fifo);
				else
				{
					this.setStyle("abs_pos_x", center_pos_x);
					this.setStyle("abs_pos_y", center_pos_y);
				}
				
				break;

			case "center_x":
				obj_perc_x = parseInt((width / screen.availWidth) * 100);
				center_pos_x = parseInt(50 - parseInt(obj_perc_x / 2)) + "%";

				this.moveAbsX(center_pos_x, scale, perc, fifo);
				break;

			case "center_y":
				obj_perc_y = parseInt((height / screen.availHeight) * 100);
				center_pos_y = parseInt(50 - parseInt(obj_perc_y / 2)) + "%";

				this.moveAbsY(center_pos_y, scale, perc, fifo);
				break;

			case "right":
				obj_perc_x = parseInt((height / screen.availWidth) * 100);
				pos_x = parseInt(100 - obj_perc_x) + "%";

				this.moveAbsX(pos_x, scale, perc, fifo);
				break;
		}

	},

	alignRel: function(position, scale, perc, fifo)
	{
        var obj_perc_x = 0;
        var obj_perc_y = 0;
        var center_pos_x = 0;
        var center_pos_y = 0;
		
		switch(position)
		{
			case "top":
				this.moveAbsY("0%", scale, perc, fifo);
				break;

			case "bottom":
				obj_perc_y = parseInt((this.div_cont.offsetHeight / screen.availHeight) * 100);
				pos_y = parseInt(100 - obj_perc_y) + "%";
				this.moveRelY(pos_y, scale, perc, fifo);
				break;

			case "left":
				this.moveRelX("0%", scale, perc, fifo);
				break;

			case "center":
				obj_perc_x = parseInt((this.div_cont.offsetWidth / screen.availWidth) * 100);
				center_pos_x = parseInt(50 - parseInt(obj_perc_x / 2)) + "%";

				obj_perc_y = parseInt((this.div_cont.offsetHeight / screen.availHeight) * 100);
				center_pos_y = parseInt(50 - parseInt(obj_perc_y / 2)) + "%";

				if(scale != 1)
					this.moveRel(center_pos_x, center_pos_y, scale, perc, fifo);
				else
				{
					this.setStyle("abs_rel_x", center_pos_x);
					this.setStyle("abs_rel_y", center_pos_y);
				}
				
				break;

			case "center_x":
				obj_perc_x = parseInt((this.div_cont.offsetWidth / screen.availWidth) * 100);
				center_pos_x = parseInt(50 - parseInt(obj_perc_x / 2)) + "%";

				this.moveRelX(center_pos_x, scale, perc, fifo);
				break;

			case "center_y":
				obj_perc_y = parseInt((this.div_cont.offsetHeight / screen.availHeight) * 100);
				center_pos_y = parseInt(50 - parseInt(obj_perc_y / 2)) + "%";

				this.moveRelY(center_pos_y, scale, perc, fifo);
				break;

			case "right":
				obj_perc_x = parseInt((this.div_cont.offsetWidth / screen.availWidth) * 100);
				pos_x = parseInt(100 - obj_perc_x) + "%";

				this.moveRelX(pos_x, scale, perc, fifo);
				break;
		}
	},

	
	setWidth: function(final_w, scale, perc, fifo, loop)
	{
		this.start("width", final_w, scale, perc, fifo, loop);
	},

	setHeight: function(final_w, scale, perc, fifo, loop)
	{
		this.start("height", final_w, scale, perc, fifo, loop);
	},
		
	resize: function(final_w, final_h, scale, perc, fifo, loop)
	{
		this.start("width", final_w, scale, perc, fifo, loop);
		this.start("height", final_h, scale, perc, false, loop);
	},

	setBorderColor: function(final_colors, scale, fifo, loop)
	{
		if(typeof(final_colors) == "string")
		{
			var rgb = this.getRGB(final_colors);
			final_colors = this.getNumeric(rgb);
		}
		this.start("borderColor", final_colors, scale, 0, fifo, loop);
	},

	setBackgroundColor: function(final_colors, scale, fifo, loop)
	{
		if(typeof(final_colors) == "string")
		{
			var rgb = this.getRGB(final_colors);
			final_colors = this.getNumeric(rgb);
		}

		this.start("backgroundColor", final_colors, scale, 0, fifo, loop);
	},

	setBorderWidth:	function(final_w, scale, fifo, loop)
	{
		this.start("borderWidth", final_w, scale, 0, fifo, loop);
	},

	setOpacity:	function(final_o, scale, fifo, loop)
	{
		this.start("opacity", final_o, scale, 0, fifo, loop);
	},

	setColor:	function(final_c, scale, fifo, loop)
	{
		if(typeof(final_c) == "string")
		{
			var rgb = this.getRGB(final_c);
			final_c = this.getNumeric(rgb);
		}

		this.start("color", final_c, scale, 0, fifo, loop);
	},

	stop: function(attribute)
	{
		if(this.timeouts[attribute])
		{
			window.clearTimeout(this.timeouts[attribute]);
			this.timeouts[attribute] = false;
		}
	},

	// Parar todas las animaciones (útil para loops).
	stopAll: function()
	{
		for(var t in this.timeouts)
		{
			window.clearTimeout(this.timeouts[t]);
			this.timeouts[t] = false;
		}
	},

	// Destruye el div, haciéndolo transparente y borrándolo.
	destroy: function(scale)
	{
		if(!scale)
			scale = 20;

		var fifo = new xFuncFIFO;
		var self = this;

		fifo.add(function() { self.setOpacity("0", scale, fifo, false) }, this);
		fifo.add(function() 
		{ 
			this.div_cont.parentNode.removeChild(this.div_cont);
		}, this);

		fifo.next();
	},

	// CSS
	getCSS: function()
	{
		this.css = false;
	
		if(this.div_cont && this.div_cont.currentStyle)
		{
			this.css = this.div_cont.currentStyle;
		}
		else if(window.getComputedStyle)
		{
			this.css = window.getComputedStyle(this.div_cont, null);
		}
	},

	// A partir de un color (HEX, RGB) -> RGB
	getRGB: function(color)
	{
		var res = false;

		if(color)
		{				
			if(color.match("rgb"))
			{
				res = color;
			}
			else if(color.match("#"))
			{
				var rgb = new Array(3);

				var hex_str = new Array();
				hex_str = color.match(/[^#]/g);

				var i, j;
				for(i = 0, j = 0; i < 3; i ++, j += 2)
				{
					rgb[i] = (this.getHEX2RGB(hex_str[j]) * 16 + this.getHEX2RGB(hex_str[j + 1]) );

				}

				res = "rgb(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ")";
			}
			else
				res = "rgb(0, 0, 0)";
	
		}

		return res;
	},

	getHEX2RGB: function(hex)
	{
		res = hex ? Number(HEX_TABLE[hex.toUpperCase()]) : 0;
		
		return res;
	},


	// Parte numérica de un valor ([1] -- px)
	getNumeric: function(string)
	{
		return String(string).match(/[0-9]+/g);
	},

	// Unidad de un valor (1 -- [px])
	getUnit: function(string)
	{
		return String(string).match(/[^,\.0-9]+/g);
	}
}

// XLayer es una 'capa' que oscurece el fondo, basado en XObject.
function XLayer(div_cont, class_name, opacity, fifo)
{
	var div = document.createElement("div");
	div.className = class_name;

	div.style.position = "absolute";
	document.body.style.overflow = "hidden";

	this.xdiv = new XObject(div);
	
	this.xdiv.setStyle("width", "100%");
	this.xdiv.setStyle("height", "100%");

	this.xdiv.setStyle("opacity", opacity);
	//this.xdiv.setOpacity(opacity, 1, fifo);

	div_cont.appendChild(div);
}

XLayer.prototype =
	{
		stop:	function(scale)
		{
			document.body.style.overflowY = "auto";

			this.xdiv.destroy(scale);
		}
	};

