/**
 * Javascript cLibrary
 * cLibrary.js
 * ------------------------------------------------------------------------------------------------------------
 * @author ciscodelgado http://www.ciscodelgado.com
 * @version 2.0
 * 
 */

var cLibrary = {
	Version : '2.0'
};

/**
 * Clase Ajax
 * 
 */
function Ajax()
{
	this.container = null;
}

/**
 * Crea y devuelve un objeto XMLHttpRequest
 */
Ajax.prototype.httpRequest = function()
{	
	var http_request = false;
	if (window.XMLHttpRequest) 
	{ 
		// Mozilla, Safari,...
	    http_request = new XMLHttpRequest();
	    if (http_request.overrideMimeType) 
		{
	    //    http_request.overrideMimeType('text/xml');
	    }
	} 
	else if (window.ActiveXObject) 
	{ 
		// IE
	    try {
	        http_request = new ActiveXObject("Msxml2.XMLHTTP");
	    } catch (e) {
	        try{ 
	            http_request = new ActiveXObject("Microsoft.XMLHTTP");
	        } catch (e) {}
	    }
	}	
	return http_request;
};

/**
 * Realiza una consulta al servidor
 * @param {String} theUrl
 * @param {Object} [options]
 */
Ajax.prototype.request = function(theUrl)
{
	try {
		var url = theUrl; 
		var sendData = null;
		var isUpdate = !(this.container === null);
		var options = arguments[1] || {};
		var rMethod = options.method || "get";
		var asynchronous = options.asynchronous || true;
		var execScripts = options.execScripts || false;
		var nocache = encodeURIComponent("r")+"="+encodeURIComponent(hex_md5(Math.random(999)));
		var oXmlHttp = this.httpRequest();
		
		if(rMethod == "post")
		{
			sendData = (options.params) ? options.params+"&" : "";
			sendData += nocache;
		}
		else if(rMethod == "get")
		{
			sendData = (options.params) ? "?"+options.params+"&" : "?";
			url += sendData + nocache;
		}
		var onLoading = options.onLoading || null;
		var onError = options.onError || null;
		var onComplete = (typeof options.onComplete == "function") ? function(r){ options.onComplete(r); } : null;

		oXmlHttp.open(rMethod, url, asynchronous);
			
		if(rMethod == "post")
		{
			oXmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		}	
		var oSelf = this;
	    oXmlHttp.onreadystatechange = function ()
		{
	        try {
				if (oXmlHttp.readyState == 1) // Cargando
				{
					if(typeof onLoading == "function") { onLoading(); }
				}
	            else if (oXmlHttp.readyState == 4) // Completado
				{
	                if (oXmlHttp.status == 200) // Recurso encontrado
					{ 	
						var sResponse = oXmlHttp.responseText.replace(/\n|\r|\t/g, '');
						var oHead = document.getElementsByTagName('head')[0];
					//	if(execScripts)
					//	{
							var script = "(?:<script.*?>)((\n|\r|\t|.)*?)(?:<\/script>)";
							var matchScripts = new RegExp(script, "g");	
							var matches = null;
							var aScripts = [];
							while(matches = matchScripts.exec(sResponse))
							{
								var sScriptTag = matches[0] || '';
								var sScript = matches[1] || '';
								
								var sIdScript = ".*id=[\"\']([a-zA-Z0-9_ ]+)[\"\'](.*)";
								var oRegExpId = new RegExp(sIdScript, "i");
								var aMatchesId = oRegExpId.exec(sScriptTag);
								var sScriptId = null;
								if(aMatchesId != null)
								{
									sScriptId = aMatchesId[1];
								}
								var bCreateScript = true;
								for(i = 0; i < oHead.childNodes.length; i++)
								{
									if(oHead.childNodes[i].id && oHead.childNodes[i].id == sScriptId)
									{
										bCreateScript = false;
									}
								}
								var oNewScript = document.createElement('script');
								oNewScript.text = sScript;
								oNewScript.type = 'text/javascript';
								oNewScript.id = sScriptId;
								var sSrcScript = ".*src=[\"\']([a-zA-Z0-9\-\.\/:_ ]+)[\"\'](.*)";
								var oRegExpSrc = new RegExp(sSrcScript, "g");
								var aMatchesSrc = oRegExpSrc.exec(sScriptTag);
								if(aMatchesSrc != null)
								{
									oNewScript.src = aMatchesSrc[1];
								}
								if(bCreateScript)
								{
								//	oHead.appendChild(oNewScript);
									oNewScript.sMode = 'append';
								}
								else
								{
								//	oHead.replaceChild(oNewScript, $(sScriptId));
									oNewScript.sMode = 'replace';
								}
								aScripts.push(oNewScript);
								sResponse = sResponse.replace(sScriptTag, '');
							}
						//}
						var styles = "(?:<link)(.*?)(\/>)";	    
						var matchStyles = new RegExp(styles, "g");	
						var matches = null;
						while(matches = matchStyles.exec(sResponse))
						{
							var sStyleTag = matches[0] || '';
							var sStyle = matches[1] || '';
							var sIdStyle = ".*id=[\"\']([a-zA-Z0-9_ ]+)[\"\'](.*)";
							var oRegExpId = new RegExp(sIdStyle, "i");
							var aMatchesId = oRegExpId.exec(sStyle);
							var sStyleId = null;
							if(aMatchesId != null)
							{
								sStyleId = aMatchesId[1];
							}
							var bCreateStyle = true;
							for(i = 0; i < oHead.childNodes.length; i++)
							{
								if(oHead.childNodes[i].id && oHead.childNodes[i].id == sStyleId)
								{
									bCreateStyle = false;
								}
								
							}
							
							var oNewStyle = document.createElement('link');
							oNewStyle.rel = 'stylesheet';
							oNewStyle.type = 'text/css';
							oNewStyle.id = sStyleId;
							var sSrcStyle = ".*href=[\"\']([a-zA-Z0-9\-\.\/:_ ]+)[\"\'](.*)";
							var oRegExpSrc = new RegExp(sSrcStyle, "g");
							var aMatchesSrc = oRegExpSrc.exec(sStyle);
							if(aMatchesSrc != null)
							{
								oNewStyle.href = aMatchesSrc[1];
							}
							if(bCreateStyle)
							{
								oHead.appendChild(oNewStyle);
							}
							else
							{
								oHead.replaceChild(oNewStyle, $(sStyleId));
							}
							sResponse = sResponse.replace(sStyleTag, '');
						}           
						if(isUpdate) 
						{ 
							oSelf.container.update(sResponse);
						}	
						
						if(typeof onComplete == "function")
						{
							options.onComplete(oXmlHttp);	
						}   
						
						oSelf.appendScripts(oHead, aScripts);
	                }
					else
					{
						if(typeof onError == "function")
						{
							onError();	
						}
					} 
	            }
	        } catch (oException) {
	            if(typeof onError == "function")
				{
					onError();	
				}
	        }
	    };
		if(rMethod == "post")
		{
	    	oXmlHttp.send(sendData);
		}
		else if(rMethod == "get")
		{
			oXmlHttp.send(null);
		}
	} catch(oException) {}
};

/**
 * Realiza una consulta al servidor y actualiza el contenedor con la respuesta
 * @param {String} theUrl
 * @param {Object} oContainer
 */
Ajax.prototype.update = function(theUrl, oContainer)
{
	this.container = oContainer;
	var options = arguments[2] || {};
	this.request(theUrl, options);
};

/**
 * Realiza una consulta al servidor y devuelve la respuesta en formato XML
 * @param {String} theUrl
 */
Ajax.prototype.xml = function (theUrl)
{
	var options = arguments[1] || {};

	var oXmlDom = zXmlDom.createDocument();
	oXmlDom.onreadystatechange = function()
	{
		if(oXmlDom.readyState == 4)
		{
			
			if(oXmlDom.parseError.errorCode === 0)
			{
				
				options.onComplete(oXmlDom);
			}
			else
			{
				if(typeof options.onError == "function")
				{
					options.onError(oXmlDom);	
				}	
			}
		}
	};
	oXmlDom.load(theUrl);
};

Ajax.prototype.appendScripts = function(oNode, aScripts)
{
	for(i = 0; i < aScripts.length; i++)
	{
		if(aScripts[i].sMode == 'append')
		{
			oNode.appendChild(aScripts[i]);
		}
		else if(aScripts[i].sMode == 'replace')
		{
			oNode.replaceChild(aScripts[i], $(aScripts[i].id));
		}
	}
}
// Fin clase Ajax

/**
 * @classDescription Crea un objeto Elements
 * 
 */
function Elements(sElementID)
{
	this.obj = $(sElementID);
	this.obj_id = sElementID;
}

/**
 * Devuelve las coordenadas absolutas del objeto
 * @return {Array} 
 */
Elements.prototype.getPosition = function() 
{
	if(!$(this.obj_id)) {return;}
	var iCurrentLeft = 0, iCurrentTop = 0;
	var obj = this.obj;
	if (obj.offsetParent) 
	{
		while (obj) 
		{
			iCurrentLeft += obj.offsetLeft || 0;
			iCurrentTop += obj.offsetTop || 0;
			obj = obj.offsetParent;
		}
	}
	return [iCurrentLeft, iCurrentTop];
};

Elements.prototype.setMousePosition = function(oTarget) 
{
	var oEvent = oTarget.oEvent;
	this.move_to(oEvent.pageX, oEvent.pageY);
};

/**
 * Devuelve la posicion en pixeles del borde izquierdo
 * @return {Number} 
 */
Elements.prototype.getLeft = function()
{
	if(!$(this.obj_id)) {return;}
	return this.getPosition()[0];
};

/**
 * Devuelve la posicion en pixeles del borde derecho
 * @return {Number} 
 */
Elements.prototype.getRight = function()
{
	if(!$(this.obj_id)) {return;}
	return this.getLeft() + this.getWidth();
};

/**
 * Devuelve la posicion en pixeles del borde superior
 * @return {Number} 
 */
Elements.prototype.getTop = function()
{
	if(!$(this.obj_id)) {return;}
	return this.getPosition()[1];
};

/**
 * Devuelve la posicion en pixeles del borde inferior
 * @return {Number} 
 */
Elements.prototype.getBottom = function()
{
	if(!$(this.obj_id)) {return;}
	return this.getTop() + this.getHeight();
};

/**
 * Devuelve el ancho en pixeles del objeto
 * @return {Number} 
 */
Elements.prototype.getWidth = function()
{ 
	if(!$(this.obj_id)) {return;}
	return this.obj.offsetWidth; 
};

/**
 * Devuelve el alto en pixeles del objeto
 * @return {Number} 
 */
Elements.prototype.getHeight = function()
{
	if(!$(this.obj_id)) {return;}
	return $(this.obj_id).offsetHeight;
};

/**
 * Devuelve true si el objeto es visible y false si no lo es
 * @return {Bool}
 */
Elements.prototype.isVisible = function()
{
	if(!$(this.obj_id)) {return;}
	return (this.obj.style.visibility != 'hidden');
};

Elements.prototype.getDisplay = function()
{
	if(!$(this.obj_id)) {return;}
	return this.obj.style.display;
};

Elements.prototype.setCssStyle = function(oStyle) 
{
	if(!$(this.obj_id)) {return;}
    for (var sStyleName in oStyle)
	{
      	this.obj.style[sStyleName] = oStyle[sStyleName];
	}
};
/**
 * Actualiza el contenido de un elemento
 * @param {String} sContent Nuevo contenido
 */
Elements.prototype.update = function(sContent)
{
	if(!$(this.obj_id)) { return; }
	this.obj.innerHTML = sContent;
};

/**
 * Añade contenido a un elemento
 * @param {String} sContent Nuevo contenido
 */
Elements.prototype.addContent = function(sContent)
{
	if(!$(this.obj_id)) {return;}
	var sCurrentContent = this.obj.innerHTML;
	this.obj.innerHTML = sCurrentContent + sContent;
}
/*
function MoverTexto(id,d){

 var bloque=document.getElementById(id);

 var x=(document.defaultView && document.defaultView.getComputedStyle) ?
        document.defaultView.getComputedStyle(bloque,'').getPropertyValue("top") :
        bloque.currentStyle ? bloque.currentStyle.top : "";
 var y=(document.defaultView && document.defaultView.getComputedStyle) ?
        document.defaultView.getComputedStyle(bloque,'').getPropertyValue("left") :
        bloque.currentStyle ? bloque.currentStyle.left : "";

 x= parseInt(x);
 y= parseInt(y);

switch(d){
 case "A":  x -=10; break;
 case "B":  x +=10; break;
 case "D":  y +=10; break;
 case "I":  y -=10; break;
}
bloque.style.top=x+"px";
bloque.style.left=y+"px";
}
*/
/**
 * Devuelve el estilo z-index del objeto
 * @return {Number} 
 */
Elements.prototype.getZIndex = function()
{
	if(!$(this.obj_id)) {return;}
	if(this.obj.style.zIndex){
		return this.obj.style.zIndex;
	} else { 
		return 0; 
	}
};


Elements.prototype.setTop = function(iTop)
{
	if(!$(this.obj_id)) {return;}
	this.obj.style.top = iTop+'px';
}

Elements.prototype.setLeft = function(iLeft)
{
	if(!$(this.obj_id)) {return;}
	this.obj.style.left = iLeft+'px';
}

Elements.prototype.setBottom = function(iBottom)
{
	if(!$(this.obj_id)) {return;}
	this.obj.style.bottom = iBottom+'px';
}

Elements.prototype.setRight = function(iRight)
{
	if(!$(this.obj_id)) {return;}
	this.obj.style.right = iRight+'px';
}
Elements.prototype.move_to = function(iLeft, iTop)
{
	if(!$(this.obj_id)) {return;}
	this.setTop(iTop);
	this.setLeft(iLeft);
};

Elements.prototype.setAlpha = function(iAlpha)
{
	if(!$(this.obj_id)) {return;}
	iAlpha = parseInt(iAlpha, 10);
	if(iAlpha < 0 || iAlpha > 10) { return; }
	if(isIE)
	{
		this.obj.style.filter = 'alpha(opacity=' + iAlpha*10 + ')';
	}
	else
	{
		this.obj.style.opacity = iAlpha/10;
	}
};

Elements.prototype.slideTo = function(iEndLeft, iEndTop)
{
	var oOptions = arguments[2] || {};
	var bReturn = oOptions.bReturn || false;
	var iDelay = oOptions.iDelay || 4;
	var sMode = oOptions.sMode || 'top';
	iDelay *= 1000;
	//
	var iCurrentLeft = this.getLeft();
	var iCurrentTop = this.getTop();
	if (this.obj.moving) 
	{
		clearTimeout(this.obj.moving);
	}
	else
	{
		if(oOptions.bReturn)
		{
			this.obj.iOriginalLeft = iCurrentLeft;
			this.obj.iOriginalTop = iCurrentTop;
		}
	}

	var iTopIncrement = Math.ceil((Math.abs(iEndTop) - iCurrentTop)/100);
	var iLeftIncrement = Math.ceil((Math.abs(iEndLeft) - iCurrentLeft)/100);
	
	var iNewTop = (iEndTop < 0) ? iCurrentTop - iTopIncrement : iCurrentTop + iTopIncrement;
	var iNewLeft = (iEndLeft < 0) ? iCurrentLeft - iLeftIncrement : iCurrentLeft + iLeftIncrement;
	//
	this.move_to(iNewLeft, iNewTop);
	var oSelf = this;
	//
	if (iNewTop == iEndTop && iNewLeft == iEndLeft) 
	{
		this.obj.isMoving = false;
	//	this.updatePosition(sMode);
		if(oOptions.bReturn)
		{
			var fnReturn = function()
			{
				oSelf.slideTo(oSelf.obj.iOriginalLeft, oSelf.obj.iOriginalTop, {bReturn:false});
			}
			setTimeout(fnReturn, iDelay);
		}
		return;
	}
	else
	{
		this.obj.isMoving = true;
	}
	var fnRepeat = function() 
	{
		oSelf.slideTo(iEndLeft, iEndTop, oOptions);
	};
	this.obj.moving = setTimeout(fnRepeat,10);
	
};

Elements.prototype.updatePosition = function(sMode)
{
	if(!this.obj || this.obj.isMoving) { return; }	
	if(sMode == 'top')
	{
		this.setLeft(getScrollLeft());
    	this.setTop(getScrollTop());
	}  
	else if(sMode == 'bottom')
	{  
   		var iBottom = getScrollTop() * (-1);
		this.setRight(0);
		this.setBottom(iBottom);    
	}
};

Elements.prototype.fadeIn = function() 
{
	var fnCallback = arguments[0] || null;
	if(!$(this.obj_id)) {return;}
	if(!this.isVisible()){ this.show(); }
	if (this.obj._fadeIn) 
	{ 
		clearTimeout(this.obj._fadeIn);
		this.obj._i = this.obj._i + 1;	
	}
	else
	{
		this.obj._tweening = true;
		this.obj._i = 0;
	}
	if(this.obj._i > 10) 
	{ 
		this.obj._i = null;
		this.obj._tweening = false;
		if(fnCallback) { fnCallback.apply(this);}
		return; 
	} 
	this.setAlpha(this.obj._i);	
	
	var oSelf = this;
	var repeat = function()	{ oSelf.fadeIn(fnCallback); };
	this.obj._fadeIn = setTimeout(repeat, 100);
}

Elements.prototype.fadeOut = function() 
{
	if(!$(this.obj_id)) {return;}
	var fnCallback = arguments[0] || null;
	if (this.obj._fadeOut) {
		clearTimeout(this.obj._fadeOut);
		this.obj._i = this.obj._i - 1;
	}
	else
	{
		this.obj._tweening = true;
		this.obj._i = 10;
	}
	if(this.obj._i < 0) 
	{
		this.obj._i = null;
		this.obj._tweening = false;
		if(fnCallback) { fnCallback.apply(this);}
		return; 
	}
	this.setAlpha(this.obj._i);	
	var oSelf = this;
	var repeat = function() { oSelf.fadeOut(fnCallback); };
	this.obj._fadeOut = setTimeout(repeat, 100);
}

Elements.prototype.effect = function(sEffect)
{
	if(!$(this.obj_id)) {return;}
	var iDelay = 0;
	var bCallback = false;
	var fnCallback = null;
	
	if(arguments.length > 1)
	{
		if(typeof arguments[1] == 'number')	{ 
			iDelay = parseInt(arguments[1], 10);
			if(arguments.length > 2)
			{
				if(typeof arguments[2] == 'function')
				{
					bCallback = true;
					fnCallback = arguments[2];
				}
			}	
		} else if(typeof arguments[1] == 'function') {
			iDelay = 0;
			bCallback = true;
			fnCallback = arguments[1];
		}
	}
	iDelay *= 1000;
	var oSelf = this;
	var fnEffect = function()
	{
		switch(sEffect)
		{
			case 'fadeIn':
				oSelf.fadeIn(fnCallback);
				break;
			case 'fadeOut':
				oSelf.fadeOut(fnCallback);
				break;
			default: break;
		}
	}
	setTimeout(fnEffect, iDelay);
}

/**
 * Añadir eventos a un elemento
 * @param {String} sEventType Tipo de evento (click, mouseOut,...)
 * @param {Object} oObj Nombre del objeto asociado
 * @return {void}
 */
Elements.prototype.addEventHandler = function(sEventType, fnHandler)
{
	if(!$(this.obj_id)) {return;}
	if(this.obj.addEventListener)
	{
		this.obj.addEventListener(sEventType, fnHandler, false);
	}
	else if(this.obj.attachEvent)
	{
		this.obj.attachEvent("on"+sEventType, fnHandler);
	}
	else
	{
		this.obj["on"+sEventType] = fnHandler;		
	}
	this.attachedEvent = fnHandler;
};

/**
 * Añade eventos a un elemento
 * @param {String} sEventType Tipo de evento (click, mouseOut,...)
 * @param {Object} oObj Nombre del objeto asociado
 * @param {Function} fnObjMethod Nombre del metodo asociado
 * @param {String, Number} [...] Argumentos que se pasan al metodo
 * @return {void}
 */	
Elements.prototype.addObjEventListener = function(sEventType, oObj, fnObjMethod)
{
	if(!$(this.obj_id)) {return;}
	var oObjImpl = oObj;
	var fnObjMethodImpl = fnObjMethod;
	var oAdditionalArgs = null;

	if(arguments.length > 3)
	{
		var iAdditionalArgsLength = arguments.length - 3;
		oAdditionalArgs = [];
		for(var i=0; i<iAdditionalArgsLength; i++)
		{
			oAdditionalArgs[i] = arguments[i + 3];
		}
	}
	var oSelf = this;
	function EventListenerImpl()
	{
		oSelf.oEvent = oSelf.getEvent();
		var sExprToEval = 'fnObjMethodImpl.call(oObjImpl';
		if(oAdditionalArgs !== null)
		{
			for(var i=0; i<oAdditionalArgs.length; i++)
			{
				sExprToEval += ', oAdditionalArgs[' + i + ']';
			}
			sExprToEval += ');';
			eval(sExprToEval);
		}
		else
		{
			fnObjMethodImpl.call(oObjImpl);
		}
	}
	this.addEventHandler(sEventType, EventListenerImpl);
};

Elements.prototype.removeEvents = function(sEventType, fnFunction)
{
	if(!$(this.obj_id)) {return;}
	
	if(this.obj.removeEventListener)
	{
		this.obj.removeEventListener(sEventType, fnFunction, true);
	}
	else if(this.obj.detachEvent)
	{
		this.obj.detachEvent("on"+sEventType, fnFunction);
	}
	this.obj["on"+sEventType] = null;
};

Elements.prototype.getEvent = function()
{
	if(window.event)
	{
		return this.formatEvent(window.event);
	}
	else
	{
		return this.getEvent.caller.arguments[0];
	}
};

Elements.prototype.formatEvent = function(oEvent)
{
	if (isIE && isWin) 
	{
		oEvent.charCode = (oEvent.type == "keypress") ? oEvent.keyCode : 0;
		oEvent.isChar = (oEvent.charCode > 0);
		oEvent.pageX = oEvent.clientX + document.body.scrollLeft;
		oEvent.pageY = oEvent.clientY + document.body.scrollTop;
		oEvent.preventDefault = function ()
		{
			this.returnValue = false;
		};
		if (oEvent.type == "mouseout") 
		{
		  	oEvent.relatedTarget = oEvent.toElement;
		} 
		else if (oEvent.type == "mouseover") 
		{
		  	oEvent.relatedTarget = oEvent.fromElement;
		}
		oEvent.stopPropagation = function () 
		{
		  	this.cancelBubble = true;
		};
		oEvent.target = oEvent.srcElement;
		oEvent.time = new Date().getTime();
	}
	return oEvent;
};

/**
 * Añadir una clase a un elemento
 * @param {String} sClassName Nombre de la nueva clase
 * @return {Void}
 */
Elements.prototype.addClassName = function(sClassName)
{
	if(!$(this.obj_id)) {return;}
	if(!sClassName) { return; }
	var sCurrentClassName = this.obj.className;
	if(!sCurrentClassName)
	{
		this.obj.className = sClassName;
	}
	else
	{
		if(!sCurrentClassName.match(sClassName))
		{
			this.obj.className += " "+sClassName;
		}	 
	}
};

/**
 * Cambiar la clase de un elemento
 * @param {String} sNewClassName
 * @param {String} sOldClassName
 * @return {Void}
 */
Elements.prototype.replaceClassName = function(sNewClassName, sOldClassName)
{
	if(!$(this.obj_id)) {return;}
	if(!sNewClassName || !sOldClassName) { return; }
	var sCurrentClassName = this.obj.className;
	if(!sCurrentClassName || !sCurrentClassName.match(sOldClassName))
	{
		this.addClassName(sNewClassName);
	}
	else
	{
		this.removeClassName(sOldClassName);
		this.addClassName(sNewClassName);
	}
};

/**
 * Eliminar una clase de un elemento
 * @param {String} theClassName
 * @return {Void}
 */
Elements.prototype.removeClassName = function(sClassName)
{
	if(!$(this.obj_id)) {return;}
	if(!sClassName) { return; }
	var sCurrentClassName = this.obj.className;
	var sTmpClassName = null;
	if(!sCurrentClassName) { return; }
	
	if(sCurrentClassName.match(sClassName))
	{
		sTmpClassName = sCurrentClassName.replace(sClassName, '');
		this.obj.className = sTmpClassName;
	}
};

/**
 * Elimina un elemento
 * @return {Void}
 */
Elements.prototype.remove = function()
{
	if(!$(this.obj_id)) {return;}
	this.obj.parentNode.removeChild(this.obj);
	delete this.obj;
};
/**
 * Oculta un elemento
 * @return {Void}
 */
Elements.prototype.hide = function() 
{
	if(!$(this.obj_id)) {return;}
	this.obj.style.visibility = 'hidden';
};
/**
 * Muestra un elemento
 * @return {Void}
 */
Elements.prototype.show = function() 
{
	if(!$(this.obj_id)) {return;}
	this.setCssStyle({'visibility' : 'visible'});
};

/**
 * Inserta un nuevo elemento en la página y devuelve una referencia a éste
 * @param {String} sElement Etiqueta del nuevo elemento
 * @param {String} sId Id del nuevo elemento
 * @param {String} sPosition Posición del nuevo elemento respecto al objeto (before|after|inside)
 * @param {Object} [oAttributes] Atributos del nuevo elemento
 * @return {Object} 
 */
Elements.prototype.insert = function(sElement, sId, sPosition)
{
	if(!$(this.obj_id)) {return;}
	var oNewElement = document.createElement(sElement);

	if(arguments.length > 3)
	{
		var oAttributes = arguments[3];
		
		for(vAttribute in oAttributes)	
		{
			if(vAttribute == "class")
			{
				this.addClassName(oAttributes[vAttribute]);
			}
			else if(vAttribute == "style")
			{
				var aStyles = [];
				if(oAttributes[vAttribute].indexOf(";") > 0)
				{
					aStyles = oAttributes[vAttribute].split(";");
				}
				else
				{
					aStyles[0] = oAttributes[vAttribute];
				}
				for (var i = 0; i < aStyles.length; i++)
				{
					var aStyle = aStyles[i].split(":");
					if(aStyle[0] == "float")
					{
						oNewElement.setAttribute("style", "float:"+aStyle[1]);
					}
					else
					{
						oNewElement.style[aStyle[0]] = eval("'"+aStyle[1]+"'");
					}
				}
			}
			else
			{
				oNewElement.setAttribute(vAttribute, oAttributes[vAttribute]);
			}
		}
	}
	oNewElement.id = sId;
	
	switch(sPosition)
	{
		case "before": 
			this.obj.parentNode.insertBefore(oNewElement, this.obj); 
			break;
		case "after": 
			if(this.obj.nextSibling)
			{
				this.obj.parentNode.insertBefore(oNewElement, this.obj.nextSibling);
			}
			else
			{
				this.obj.parentNode.appendChild(oNewElement);	
			}
			break;
		case "inside":
			this.obj.appendChild(oNewElement);
			break;
		default: 
			this.obj.appendChild(oNewElement);
			break;
	}
	return $(sId);
};

Elements.prototype.tooltip = function(sError, vCss)
{	
	if(!vCss)
	{
		var oStyle = {
			'border' : "1px solid #000000",
			'backgroundColor' : "#FFFFCC",
			'color' : "#000000",
			'padding' : "5px"
		}
		this.setCssStyle(oStyle);
	}
	else
	{
		this.addClassName(vCss);
	}
	oStyle = {
			'margin' : "10px",
			'position' : "absolute",
			'visibility' : 'hidden',
			'zIndex' : document.getNextZIndex()+10
		}
	this.setCssStyle(oStyle);
	this.update(sError);
	var oEventUtil = new EventUtil();
	oEventUtil.addObjEventListener(this.obj, 'mouseover', this, this.show);
	oEventUtil.addObjEventListener(this.obj, 'mouseout', this, this.hide);
	oEventUtil.addObjEventListener(document.body, "mousemove", this, this.setMousePosition, this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Fin clase Elements
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function DragAndDrop(oTarget)
{
	var oDrag = oTarget;
	var oDragable = null;
	if(!(oTarget instanceof Elements))
	{
		oDrag = new Elements(oTarget.id);
	}
	if(arguments[1])
	{
		oDragable = arguments[1];
		if(!(oDragable instanceof Elements))
		{
			oDragable = new Elements(oDragable.id);
		}
	}
	else
	{
		oDragable = oDrag;
	}
	this.oEventUtil = new EventUtil();
	this.oEventUtil.addObjEventListener(oTarget, 'mousedown', this, this.startDrag, oDrag, oDragable);
};

DragAndDrop.prototype.startDrag = function(oTarget, oDrag, oDragable)
{
	oDrag.setCssStyle({'cursor':'move'});
	var oOutLine = document.createElement("div");
	oOutLine.id = "outline_"+oDragable.obj_id;
	var oBody = document.getElementsByTagName('body')[0];
	oBody.appendChild(oOutLine);
	var iTop = oDragable.getTop();
	var iLeft = oDragable.getLeft();
	var iWidth = oDragable.getWidth() - 2;
	var iHeight = oDragable.getHeight() - 2;
	
	var oElOutLine = new Elements(oOutLine.id);
	oElOutLine.setCssStyle({'position':'absolute','zIndex':'10012', 'border':'1px dotted #333', 'width':iWidth+'px', 'height':iHeight+'px'});
	oElOutLine.setTop(iTop-1);
	oElOutLine.setLeft(iLeft-1);
	var iCurrentTop = oElOutLine.getTop();
	var iCurrentLeft = oElOutLine.getLeft();
	
	var aMousePosition = getMousePosition(oDrag.oEvent);
	var iLeftDist = aMousePosition[0]-iCurrentLeft;
	var iTopDist = aMousePosition[1]-iCurrentTop;
	
	this.oEventUtil.addObjEventListener(document.body, 'mouseup',   this, this.stopDrag, oElOutLine, oDrag, oDragable);
	this.oEventUtil.addObjEventListener(document.body, 'mousemove', this, this.drag,     oElOutLine, oDrag, oDragable, iLeftDist, iTopDist);
	
	oElOutLine.move_to(aMousePosition[0] - iLeftDist, aMousePosition[1] - iTopDist);
};

DragAndDrop.prototype.stopDrag = function(oTarget, oDrag, oDragable)
{
	oDrag.setCssStyle({'cursor':'default'});
	var iTargetTop = oTarget.getTop();
	var iTargetLeft = oTarget.getLeft();
	oDragable.move_to(iTargetLeft+1, iTargetTop+1);
	oTarget.remove();
};

DragAndDrop.prototype.drag = function(oTarget, oDrag, oDragable, iLeftDist, iTopDist)
{	
	var aMousePosition = getMousePosition(oTarget.oEvent);
	oTarget.setCssStyle({'cursor':'move'});
	oTarget.move_to(aMousePosition[0]-iLeftDist, aMousePosition[1]-iTopDist);
	oTarget.oEvent.stopPropagation();
	oTarget.oEvent.preventDefault();
	oDrag.oEvent.stopPropagation();
	oDrag.oEvent.preventDefault();
	
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Clase Forms
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function Forms(sForm)
{
	this.oOptions = arguments[1] || {};
	var bValidate = this.oOptions.bValidation || false;
	var aParamsValidation = (bValidate) ? this.oOptions.aParamsValidation || null : null;
	this.sDisplayErrors = this.oOptions.sDisplayErrors || 'alert';//'popup'
	
	this.aFields = [];
	if(bValidate)
	{
		this.prepareFields(aParamsValidation);
	}
	var obj_form = $(sForm);
	if(obj_form)
	{
		this.obj_form = obj_form; 
	}
	else
	{
		this.obj_form = document.forms[sForm];
		this.obj_form.id = sForm;
	}
	this.form_id = sForm;
	this.obj_form.reset();
}

/**
 * Da el foco al primer campo de texto o textarea del formulario
 */

Forms.prototype.focusOnFirst = function()
{
	var oField;
	for(var i = 0; i < this.obj_form.elements.length; i++)
	{
		oField = this.obj_form.elements[i];
		if (oField.type != 'hidden' 
			&& oField.type != 'checkbox'
			&& oField.type != 'radio'
			&& oField.nodeName.toLowerCase() != 'fieldset') 
		{
			oField.focus();
			return;
		}
	}
};

Forms.prototype.cancel = function()
{
	this.obj_form.reset();
	for(var i = 0; i < this.aFields.length; i++)
	{
		if(typeof this.aFields[i].obj.bValid == 'boolean')
		{
			this.aFields[i].reset();
		}	
	}
	this.focusOnFirst();
};

/**
 * Convierte las entradas de formulario en una cadena de consulta
 * @return {String} Cadena de consulta tipo variable1=valor1&variable2=valor2
 */
Forms.prototype.toQueryString = function()
{
	var aParams = [];
	var oField;
	var queryString = '';
	for(var i = 0; i < this.obj_form.elements.length; i++)
	{
		oField = this.obj_form.elements[i];
		if (oField.type != "button" 
			&& oField.type != "reset" 
			&& oField.type != "submit" 
			&& oField.nodeName.toLowerCase() != "fieldset")
		{
			inputValue = oField.value;
			if(oField.type == 'password')
			{
				inputValue = hex_md5(oField.value);
				queryString = encodeURIComponent(oField.id);
				queryString += "=";
				queryString += encodeURIComponent(inputValue);
			}
			else if(oField.type == 'checkbox')
			{
				if(oField.checked)
				{
					queryString = encodeURIComponent(oField.id);
					queryString += "=";
					queryString += encodeURIComponent(oField.value);
				}
				
			}
			else if(oField.type == 'radio')
			{
				for(var j = 0; j < this.obj_form[oField.name].length; j++)
				{
					if(this.obj_form[oField.name][j].checked)
					{
						queryString = encodeURIComponent(this.obj_form[oField.name][j].name);
						queryString += "=";
						queryString += encodeURIComponent(this.obj_form[oField.name][j].value);
						break;
					}
				}
				
			}
			else if(oField.nodeName.toLowerCase() == "select")
			{
				queryString = encodeURIComponent(oField.id);
				queryString += "=";
				queryString += encodeURIComponent(oField.options[oField.selectedIndex].value);
			}
			else
			{
				queryString = encodeURIComponent(oField.id);
				queryString += "=";
				queryString += encodeURIComponent(inputValue);
			}
			if(inputValue !== "" && queryString !== "")
			{
				aParams.push(queryString);
			}
			
		}
	}
	return aParams.join("&");
};

/**
 * Inicializa los campos de un formulario para ser validados
 * @param {Array} aParamsValidation Array con los datos de validación
 */
Forms.prototype.prepareFields = function(aParamsValidation)
{
	var aFieldsInfo = document.getElementsByClassName('fieldInfo', this.form_id);
	for(i = 0; i < aFieldsInfo.length; i++)
	{
		var oSpanInfo = new Elements(aFieldsInfo[i].id);
		if(oSpanInfo.oTooltip)
		{
			oSpanInfo.oTooltip.remove();
		}
		oSpanInfo.oTooltip = new Tooltip(oSpanInfo, oSpanInfo.obj_id+'_tt', false, oSpanInfo.obj.innerHTML);
	}
	for(var i = 0; i < aParamsValidation.length; i++)
	{
		var bRequired = aParamsValidation[i].bRequired;
		var sValidation = aParamsValidation[i].sValidation || 'none';
		var bPassword = aParamsValidation[i].bPassword || false;
		var oMessages = aParamsValidation[i].oErrorMsg || {};
		var sTypeError = oMessages.sType || '';
		var sPasswordError = oMessages.sPasswordError || '';
		var sRequired = oMessages.sRequired || '';
		var iMessagesMode = oMessages.iErrorMode || 2; // 1 = tooltip, 2 = inline, 3 = alert, 4 = popup
		
		this.aFields[i] = new Formelements(aParamsValidation[i].sId, bRequired, sValidation, sRequired, sTypeError, bPassword, sPasswordError, iMessagesMode);
		
		if(bPassword && i > 0)
		{
			if(this.aFields[i-1].bPassword)
			{
				this.aFields[i].aPreviousPass = this.aFields[i-1];
			}
		}
		
		this.aFields[i].addObjEventListener('change', this.aFields[i], this.aFields[i].validateField);
	}
};

/**
 * Devuelve true si el formulario es válido o false si no lo es
 * @return {Boolean}
 */
Forms.prototype.isFormValid = function()
{
	var bValid = true;
	for(var i = 0; i < this.aFields.length; i++)
	{
		this.aFields[i].validateField();
		if(typeof this.aFields[i].obj.bValid == "boolean")
		{
			bValid = bValid && this.aFields[i].obj.bValid;
		}
	}
	return bValid;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Fin clase Forms
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Clase Formelements
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function Formelements(sFormElement, bRequired, sValidation, sRequired, sTypeError, bPassword, sPasswordError, iMessagesMode)
{
	Elements.call(this, sFormElement);	
	this.bRequired = bRequired;
	this.sValidation = sValidation;
	this.sRequiredMsg = sRequired;
	this.sTypeMsg = sTypeError;	
	this.sPasswordErrorMsg = sPasswordError;	
	this.bPassword = bPassword;
	this.iMessagesMode = iMessagesMode;
};

Formelements.prototype = new Elements();

Formelements.prototype.validateField = function()
{
	var sLabelid = this.obj_id+'Label';
	this.oLabel = new Elements(sLabelid);
	this.obj.bValid = false;
	
	if(this.obj.bValidate === false)
	{
		this.reset();
		return false;
	}
	
	if(this.obj.type == 'password')
	{
		if(this.aPreviousPass)
		{
			if(this.aPreviousPass.obj.value != this.obj.value)
			{
				this.aPreviousPass.error(this.aPreviousPass.sPasswordErrorMsg, this.aPreviousPass.iMessagesMode);
				this.error(this.sPasswordErrorMsg, this.iMessagesMode);
				return false;
			}
			else
			{
				this.reset();
			}
		}
	}
	if((this.obj.type == 'text' ||
		this.obj.type == 'textarea' ||
		this.obj.type == 'password' ||
		this.obj.nodeName.toLowerCase() == 'select') &&
		this.bRequired)
	{
		if(this.isEmpty())
		{
			this.error(this.sRequiredMsg, this.iMessagesMode);
			return false;
		}
		else
		{
			this.reset();
		}
		
	}
	if(this.obj.type == 'checkbox' && this.bRequired)
	{
		if(!this.obj.checked)
		{
			this.error(this.sRequiredMsg, this.iMessagesMode);
			return false;
		}
		else
		{
			this.reset();
		}
	}
	if(this.sValidation == 'none')
	{
		this.ok();
		return false;
	}
	else if(this.sValidation == 'integer')
	{
		if(!this.isInteger(objInput.value))
		{
			this.errorField(objInput, sMsgHolder, sValErrorMsg);
			return false;
		}
		else
		{
			this.okField(objInput, sMsgHolder);
		}
	}
	else if(this.sValidation == 'mail')
	{
		if(!this.isMail())
		{
			this.error(this.sTypeMsg, this.iMessagesMode);
			return false;
		}
		else
		{
			this.ok();
			return;
		}
	}
	else if(this.sValidation == 'cif')
	{
		if(!this.isCIF())
		{
			this.error(this.sTypeMsg, this.iMessagesMode);
			return false;
		}
		else
		{
			this.ok();
			return;
		}
	}
	else if(this.sValidation == 'dni')
	{
		if(!this.isDNI())
		{
			this.error(this.sTypeMsg, this.iMessagesMode);
			return false;
		}
		else
		{
			this.ok();
			return;
		}
	}
	else if(this.sValidation == 'zip')
	{
		if(!this.isZipCode())
		{
			this.error(this.sTypeMsg, this.iMessagesMode);
			return false;
		}
		else
		{
			this.ok();
			return;
		}
	}
	else if(this.sValidation == 'password')
	{
		if(!this.isPassword())
		{
			this.error(this.sTypeMsg, this.iMessagesMode);
			return false;
		}
		else
		{
			this.ok();
		}
	}
	else if(this.sValidation == 'text')
	{
		if(!this.isText(objInput.value))
		{
			this.errorField(objInput, sMsgHolder, sValErrorMsg);
			return false;
		}
		else
		{
			this.okField(objInput, sMsgHolder);
		}
	}
	
};

Formelements.prototype.error = function(sError, iMessagesMode)
{
	var oStyle = {'border':'1px solid #FF0000'};

	var sFeedbackMsgid = this.obj_id+'Msg';
	var oFeedbackMsg = new Elements(sFeedbackMsgid);
	switch(iMessagesMode)
	{
		case 1: 						
			if(oFeedbackMsg.oTooltip)
			{
				oFeedbackMsg.oTooltip.remove();
			}
			oFeedbackMsg.oTooltip = new Tooltip(oFeedbackMsg, this.obj_id+'_tt', false, sError);
			this.oLabel.addClassName('warning');
			oFeedbackMsg.setCssStyle({'height':'18px', 'width':'1px'});
			break;
		case 2:
			oFeedbackMsg.update(sError);
			this.oLabel.addClassName('warning');
			break;
		case 3:
			this.oLabel.addClassName('warning');
			oFeedbackMsg.setCssStyle({'height':'18px', 'width':'1px', 'cursor': 'pointer', 'cursor': 'hand'});
			oFeedbackMsg.obj.onclick = function() {alert(sError);};
			break;
		case 4:
			alert(sError);
			break;
		default: break;
	}

	this.setCssStyle(oStyle);
	this.obj.bValid = false;
 
	this.obj.focus();
};

Formelements.prototype.ok = function()
{
	var oStyle = {'border':'1px solid #009900'};
	this.oLabel.removeClassName('warning');
	this.setCssStyle(oStyle);
	this.obj.bValid = true;
};

Formelements.prototype.reset = function()
{
	var oStyle = {'border':''};
	this.oLabel.removeClassName('warning');
	this.setCssStyle(oStyle);
	this.obj.bValid = true;
};
Formelements.prototype.disable = function()
{
	var oStyle = {'backgrounColor':'#999999'};
	this.obj.disabled = true;
	this.setCssStyle(oStyle);
}
Formelements.prototype.enable = function()
{
	var oStyle = {'backgrounColor':''};
	this.obj.disabled = false;
	this.setCssStyle(oStyle);
}
Formelements.prototype.isEmpty = function()
{
	if(this.obj.nodeName.toLowerCase() == 'select')
	{
		if(this.obj.selectedIndex == 0)
		{
			return true;
		}	
	}
	var str_value = this.obj.value.trim();
	if(str_value == "" || str_value == null)
	{
		return true;
	}
	return false;
}
Formelements.prototype.isPassword = function()
{
	var rePattern = /[A-Z0-9_\$]{8,}/i;
	return rePattern.test(this.obj.value.trim());
}
Formelements.prototype.isMail = function()
{
	var rePattern = /^(?:\w+\.?)*\w+@(?:\w+)+\.(?:[a-z]){2,4}$/;
	return rePattern.test(this.obj.value.trim());
};

Formelements.prototype.isNumber = function()
{

};

Formelements.prototype.isInteger = function()
{
	var sValue = this.obj.value.trim();
	var bInteger = true;
	for(i = 0; i < sValue.length; i++)
	{
		sCurrentChar = sValue.charAt(i);
		bInteger = bInteger && !(isNaN(sCurrentChar));
	}
	return bInteger;
}

Formelements.prototype.isDNI = function()
{
	var sValue = this.obj.value.trim();
	var sChars = "TRWAGMYFPDXBNJZSQVHLCKET"; 
	var rePattern = /^[Xx]?(\d{8})([A-Za-z]{1})$/;
	if(rePattern.test(sValue))
	{
		var sPosition = parseInt(RegExp.$1, 10) % 23;
		var sChar = sChars.substring(sPosition, sPosition + 1);
		if(sChar == RegExp.$2.toUpperCase())
		{
			this.obj.value = sValue.toUpperCase();
			return true;
		}
	}
	return false;
}
Formelements.prototype.isCIF = function()
{
	var sValue = this.obj.value.trim();
	sValue = sValue.toUpperCase();
	if(sValue.length !== 9)
	{
		return false;
	}
	var reCifPattern = /^([ABCDEFGHKLMNPQS]{1})(\d{7})([A-J0-9]){1}$/;
	var sChar = 'KPQS';
	var sNum = 'ABEH';
	var sVar = 'CDFGLMN';
	if(reCifPattern.test(sValue))
	{
		var sValidChars = 'JABCDEFGHI';
		var iA = 0;
		var iB = 0;
		var aOddChars = [];
		
		for(i = 1; i <= RegExp.$2.length; i++)
		{
			var iCurrentChar = parseInt(RegExp.$2.charAt(i-1), 10);
			if(i % 2 == 0)
			{
				iA += iCurrentChar;
			}
			else
			{
				aOddChars.push(iCurrentChar * 2);
			}
		}
		for(i = 0; i < aOddChars.length; i++)
		{
			aOddChars[i] = aOddChars[i].toString();
			iB += (parseInt(aOddChars[i].charAt(0), 10) + (parseInt(aOddChars[i].charAt(1), 10) || 0));
		}
		var iC = iA + iB;
		var sC = iC.toString();
		var iControlDigit = 10 - parseInt(sC.charAt(1), 10);
		
		if((sValidChars.charAt(iControlDigit) == RegExp.$3 && sChar.indexOf(RegExp.$1) > -1)
		 	|| (iControlDigit == RegExp.$3 && sNum.indexOf(RegExp.$1) > -1)
		   )
		{
			this.obj.value = sValue.toUpperCase();
			return true;
		}
		else if((sValidChars.charAt(iControlDigit) == RegExp.$3 || iControlDigit == RegExp.$3)
				&& sVar.indexOf(RegExp.$1) > -1
		   	)
		{
			this.obj.value = sValue.toUpperCase();
			return true;
		}
	}
	return false;
}
Formelements.prototype.isZipCode = function()
{
	var sValue = this.obj.value.trim();	
	var rePattern = /\d{5}/;
	return rePattern.test(sValue);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Fin clase Formelements
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Clase Popup
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function Popup(sID)
{
	var oBody = (document.body) ? document.body : document.getElementsByTagName("body")[0];
	this.oNewPopUp = document.createElement('div');
	if($(sID))
	{
		sID += "_"+random_str();
		alert(sID);
	}
	this.oNewPopUp.id = sID;
	oBody.appendChild(this.oNewPopUp);
	
	Elements.call(this, sID);
}
Popup.prototype = new Elements();


Popup.prototype.create = function(sMode, sContent, sIcon)
{
	if(sMode == 'top')
	{
		this.oPopupContent = new Elements(this.insert('div', this.obj_id+'content'));
		this.oPopupHeader = new Elements(this.insert('div', this.obj_id+'header'));
	}
	else if(sMode == 'bottom')
	{
		this.oPopupHeader = new Elements(this.insert('div', this.obj_id+'header'));
		this.oPopupContent = new Elements(this.insert('div', this.obj_id+'content'));
	}
	
	this.oCloseButton = new Elements(this.oPopupHeader.insert('div', this.obj_id+'close'));
	this.oPopupContent.setCssStyle({'padding' : '0 10px 10px 10px'});
	this.oPopupHeader.setCssStyle({'padding' : '3px'});
	if(sIcon)
	{
		this.oPopupContent.setCssStyle({'background' : 'url('+sIcon+') no-repeat 5px 5px'});
		this.oPopupContent.setCssStyle({'padding' : '5px 10px 10px 58px'});
	}
	this.oCloseButton.setCssStyle({'display':'block', 'background' : 'url(images/close.png) no-repeat left top', 'padding' : '0', 'width' :  '12px', 'height' :  '12px', 'right': '0'});
	if(isIE){this.oCloseButton.setCssStyle({'cursor':'hand'})}
	else{this.oCloseButton.setCssStyle({'cursor':'pointer'})}
	this.oCloseButton.addObjEventListener('click', this, this.remove);
	
	var oStyles =  {
				'position' : 'absolute',
				'padding' : '0',
				'background' : '#F7F7F7 url(images/popup_bg.jpg) repeat-x left top',
				'color' : '#100085',
				'border' : '2px solid #100085',
				'margin' : '3px',
				'zIndex' : document.getNextZIndex()
				};
	
	var oAditionalStyles =  arguments[3] || {};
	for(vStyle in oAditionalStyles)
	{
		oStyles[vStyle] = oAditionalStyles[vStyle];
	}
	this.addClassName('popup');
	this.setCssStyle(oStyles);

	this.oPopupContent.update(sContent);	
	var oSelf = this;  
	var fnUpdate = function()
				{
					oSelf.updatePosition(sMode);
				}
	window.onresize = fnUpdate;		
	window.onscroll = fnUpdate;
	this.updatePosition(sMode);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Fin clase Popup
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Clase Tooltip
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function Tooltip(oTarget, sId, vCss, sText)
{	
	var oBody = document.getElementsByTagName('body')[0];
	var oTooltip  = document.createElement('div');
	oTooltip.id = sId;
	oBody.appendChild(oTooltip);
	
	Elements.call(this, sId);
	oTooltip.style.zIndex = document.getNextZIndex()+10;
	
	this.move_to(0, 0);
	var padding = 5;
	if(!vCss)
	{
		var oStyle = {
			'border' : "1px solid #000000",
			'backgroundColor' : "#FFFFCC",
			'color' : "#000000",
			'padding' : "5px"
		}
		this.setCssStyle(oStyle);
	}
	else
	{
		this.addClassName(vCss);
	}
	oStyle = {
			'margin' : "10px",
			'position' : "absolute",
			'visibility' : 'hidden'
		}
	this.setCssStyle(oStyle);
	this.update(sText);

	oTarget.addObjEventListener("mouseover", this, this.show);
	oTarget.addObjEventListener("mousemove", this, this.setMousePosition, oTarget);
	oTarget.addObjEventListener("mouseout", this, this.hide);

};

Tooltip.prototype = new Elements();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Fin clase Tooltip
////////////////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Clase Arrangetable
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * Ordenación de tablas por el valor de sus columnas 
 * @param {String} sTableID Id de la tabla
 */
function Arrangetable(sTableID)
{
	this.oTable = $(sTableID);
	this.sTableID = sTableID;
	var oSelf = this;
	
	/**
	 * Prepara la tabla para que sea ordenable 
	 * @return {Void}
	 */
	var __prepare__ = function()
	{
		var oHead = oSelf.oTable.tHead;
		var aHeaderCells = oHead.rows[0].cells;
		
		/**
		 * Genera una funcion de comparación de valores que se pasará a la función sort()
		 * @param {Number} iCol Numero de indice de la columna
		 * @param {String} sDataType [sDataType] Tipo de dato por el que se hara la ordenación
		 * @return {Function}
		 */
		var __generateCompareTRs__ = function(iCol, sDataType) 
		{
			return function __compareTRs__(oTR1, oTR2) 
			{
				var vValue1, vValue2;
				if (oTR1.cells[iCol].getAttribute("value")) 
				{
					vValue1 = setType(oTR1.cells[iCol].getAttribute("value"), sDataType);
					vValue2 = setType(oTR2.cells[iCol].getAttribute("value"), sDataType);
				}
				else 
				{
					vValue1 = setType(oTR1.cells[iCol].firstChild.nodeValue, sDataType);
					vValue2 = setType(oTR2.cells[iCol].firstChild.nodeValue, sDataType);
				}
				if (vValue1 < vValue2) 
				{
					return -1;
				} 
				else if (vValue1 > vValue2) 
				{
					return 1;
				} 
				else 
				{
					return 0;
				}
			};
		};
		
		/**
		 * Ordena una tabla por los datos y el numero de columna dados
		 * @param {Number} iCol Numero de indice de columna
		 * @param {String} [sDataType] Tipo de dato por el que se hara la ordenacion
		 * @return {Void}
		 */
		var __sortTable__ = function(iCol, sDataType) 
		{
			var oTBody = oSelf.oTable.tBodies[0];
			var colDataRows = oTBody.rows;
			var aTRs = [];
			for (var i=0; i < colDataRows.length; i++) 
			{
				aTRs[i] = colDataRows[i];
			}
			if (oSelf.oTable.sortCol == iCol) 
			{
				aTRs.reverse();
			} 
			else 
			{		
				aTRs.sort(__generateCompareTRs__(iCol, sDataType));
			}
			var oFragment = document.createDocumentFragment();
			for (var i=0; i < aTRs.length; i++) 
			{
				oFragment.appendChild(aTRs[i]);
			}
			oTBody.appendChild(oFragment);
			oSelf.oTable.sortCol = iCol;
		};
		
		for(i = 0; i < aHeaderCells.length; i++)
		{
			if(!aHeaderCells[i].id)
			{
				aHeaderCells[i].id = 'th'+oSelf.sTableID+'_'+i;
			}
			if(aHeaderCells[i].getAttribute('value'))
			{
				var sDataType = aHeaderCells[i].getAttribute('value');
			}
			var oTh = new Elements(aHeaderCells[i].id);
			oTh.addObjEventListener('click', __sortTable__, __sortTable__, i, sDataType);
		}
	};
	__prepare__();
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Fin clase Arrangetable
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Añado métodos a la clase Array
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * Devuelve el indice de la primera ocurrencia del elemento pasado dentro del array, si no se encuentra devuelve -1
 * @param {String, Object, Number, Array} vItem
 * @return {Number} 
 */
Array.prototype.indexOf = function (vItem) 
{
	for (var i=0; i < this.length; i++) 
	{
		if (vItem === this[i]) 
		{
			return i;
		}
	}
	return -1;
};
/**
 * Comprueba si el elemento pasado existe en el array. Devuelve true si lo encuentra o false en caso contrario
 * @param {String, Object, Number, Array} vItem
 * @return {Boolean}
 */
Array.prototype.inArray = function(vItem)
{
	return (this.indexOf(vItem) > -1);
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Añado métodos a la clase String
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * Decodifica una cadena en formato URL
 * @return {String} Cadena decodificada
 */
String.prototype.urldecode = function()
{
	var sInputString = unescape(this);
	return sInputString.replace(/\+/gi," "); // Se substituye el signo + que deja unescape por espacio en blanco
};

/**
 * Elimina los espacios en blanco de una cadena
 */
String.prototype.trim = function () 
{
	var reExtraSpace = /^\s*(.*?)\s*$/;
	return this.replace(reExtraSpace, "$1");
};

String.prototype.toObject = function()
{
	var oObject = new Object();
	var aStringParts = this.split('&');
	var aPairs = null;
	for(i = 0; i < aStringParts.length; i++)
	{
		aPairs = aStringParts[i].split('=');
		oObject[aPairs[0]] = aPairs[1];
	}
	return oObject;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// * Funciones globales
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * Devuelve la/s referencia/s al/los objeto/s
 * @param {String} Id del objeto (atributo id)
 * @return {String || Array} 
 */
function $()
{
	var element;
	var idElements = [];
	for(var i=0; i<arguments.length; i++)
	{
		if(typeof arguments[i] == "string")
		{
			element = document.getElementById(arguments[i]);
		}
		else if(typeof arguments[i] == "object")
		{
			element = arguments[i];
		}
		idElements.push(element);
	}
	if(idElements.length === 0)
	{
		return false;
	}
	else
	{
		return (idElements.length < 2) ? idElements[0] : idElements;
	}
}

function $V(vElement)
{
	return $(vElement).value.trim();
}
/**
 * Convierte el tipo de dato al valor que se le pasa
 * @param {String} sValue Valor que se quiere convertir
 * @param {String} [sDataType] Tipo de conversion
 * @return {String, Number, Object}
 */
function setType(sValue, sDataType) 
{
	switch(sDataType) 
	{
		case "int":
		return parseInt(sValue, 10);
		case "float":
		return parseFloat(sValue);
		case "date":
		return new Date(Date.parse(sValue));
		default:
		return sValue.toString();
	}
}

function getScrollLeft()
{
	return ( document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft );    		
}

function getScrollTop()
{
	return ( document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop );
}

var nodeTree = [];
document.getElementsByClassName = function(aClass)
{
	nodeTree = [];
	var classNames = [];
	var element = null;
	var oParent = arguments[1] || null;
	
	if(oParent && !oParent.id)
	{
		oParent.id = 'obj_'+aClass+'_id';
	}
	oParent = $(oParent) || (document.body) ? document.body : document.getElementsByTagName("body")[0];
	
	getDOMTree(oParent);
	aClass = aClass.trim();
	for(var i=0; i<nodeTree.length; i++)	
	{
		element = nodeTree[i];
		
		if(element.className.indexOf(aClass) > -1)
		{
			classNames.push(element);
		}
	}
	if(classNames.length > 0) 
	{
		return classNames;
	} 
	return false;
};

document.getNextZIndex = function()
{
	nodeTree = [];
	var element = null;
	var aParent = (document.body) ? document.body : document.getElementsByTagName("body")[0];
	getDOMTree(aParent);
	
	var z = nodeTree.length + 1;
	
	return z;
};
document.processing = false;
document.setProcessing = function(bProcess)
{
	document.processing = bProcess;
	var docBody = (document.body) ? document.body : document.getElementsByTagName("body")[0];
	docBody.style.cursor = (bProcess) ? "wait" : "";
};

document.wWidth = function()
{
  var iWidth;
 
  if(typeof(window.innerWidth) == 'number') {
    // No es IE
    iWidth = window.innerWidth;
  } else if(document.documentElement && document.documentElement.clientWidth) {
    //IE 6 en modo estandar (no quirks)
    iWidth = document.documentElement.clientWidth;
  } else if(document.body && document.body.clientWidth) {
    //IE en modo quirks
    iWidth = document.body.clientWidth;
  }
   return iWidth;
};

document.wHeight = function()
{
  var iHeight;
 
  if(typeof(window.innerHeight) == 'number') {
    // No es IE
    iHeight = window.innerHeight;
  } else if(document.documentElement && document.documentElement.clientHeight) {
    //IE 6 en modo estandar (no quirks)
    iHeight = document.documentElement.clientHeight;
  } else if(document.body && document.body.clientHeight) {
    //IE en modo quirks
    iHeight = document.body.clientHeight;
  }
   return iHeight;
}

/**
 */
function getDOMTree(nRoot)
{
	for(var i=0; i<nRoot.childNodes.length; i++)
	{
		if(nRoot.childNodes[i].nodeType == 1)
		{
			nodeTree.push(nRoot.childNodes[i]);
			if(nRoot.childNodes[i].hasChildNodes())
			{
				getDOMTree(nRoot.childNodes[i]);
			}	
		}
	}	
}

function random_str()
{
	var aChars = ["a", "b", "c", "d", "e", "f", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
	var sRandomChar = "";
	var iMax = aChars.length - 1;
	var iMin = 0;
	var iIndex;
	for(i=0; i<32; i++)
	{
		iIndex = Math.floor(Math.random() * (iMmax - iMin + 1)) + iMin;
		sRandomChar += aChars[iIndex];
	}
	return sRandomChar;
}

function getMousePosition(oEvent) 
{
	var iMouseLeft = (oEvent && oEvent.clientX) ? oEvent.clientX : window.event.clientX;
	var iMouseTop = (oEvent && oEvent.clientY) ? oEvent.clientY : window.event.clientY;
	var aMousePosition = [iMouseLeft, iMouseTop];
	return aMousePosition;
}

function EventUtil() {};

EventUtil.prototype.addEventHandler = function (oTarget, sEventType, fnHandler) {
	if (oTarget.addEventListener) { 
	oTarget.addEventListener(sEventType, fnHandler, false);
	} else if (oTarget.attachEvent) { 
	oTarget.attachEvent("on" + sEventType, fnHandler);
	} else { 
	oTarget["on" + sEventType] = fnHandler;
	}
};
EventUtil.prototype.removeEventHandler = function (oTarget, sEventType, fnHandler) {
	if (oTarget.removeEventListener) {
	oTarget.removeEventListener(sEventType, fnHandler, false);
	} else if (oTarget.detachEvent) { 
	oTarget.detachEvent("on" + sEventType, fnHandler);
	} else { 
	oTarget["on" + sEventType] = null;
	}
};
/**
 * Formatea un evento segun el navegador
 * @param oEvent Evento
 * @return {Object}
 */
EventUtil.prototype.__formatEvent__ = function (oEvent) 
{
	if (isIE && isWin) 
	{
		oEvent.charCode = (oEvent.type == "keypress") ? oEvent.keyCode : 0;
		oEvent.eventPhase = 2;
		oEvent.isChar = (oEvent.charCode > 0);
		oEvent.pageX = oEvent.clientX + document.body.scrollLeft;
		oEvent.pageY = oEvent.clientY + document.body.scrollTop;
		oEvent.preventDefault = function () 
		{
			this.returnValue = false;
		};
		if (oEvent.type == "mouseout") 
		{
			oEvent.relatedTarget = oEvent.toElement;
		} 
		else if (oEvent.type == "mouseover") 
		{
			oEvent.relatedTarget = oEvent.fromElement;
		}
		oEvent.stopPropagation = function () 
		{
			this.cancelBubble = true;
		};
		oEvent.target = oEvent.srcElement;
		oEvent.time = (new Date).getTime();
	}
	return oEvent;
};
/**
 * Devuelve el evento recibido por el objeto formateado
 */
EventUtil.prototype.__getEvent__ = function()
{
	if(window.event)
	{
		return this.__formatEvent__(window.event);
	}
	else
	{
		return this.__getEvent__.caller.arguments[0];
	}
};

/**
 * Añade eventos a un elemento
 * @param {Object} oTarget Objeto que recibe el envento
 * @param {String} sEventType Tipo de evento (click, mouseOut,...)
 * @param {Object} oObj Nombre del objeto asociado
 * @param {Function} fnObjMethod Nombre del metodo asociado
 * @param {String, Number} [...] Argumentos que se pasan al metodo
 * @return {void}
 */	
EventUtil.prototype.addObjEventListener = function(oTarget, sEventType, oObj, fnObjMethod)
{
	var oObjImpl = oObj;
	var fnObjMethodImpl = fnObjMethod;
	var oAdditionalArgs = null;

	if(arguments.length > 4)
	{
		var iAdditionalArgsLength = arguments.length - 4;
		oAdditionalArgs = [];
		for(var i=0; i<iAdditionalArgsLength; i++)
		{
			oAdditionalArgs[i] = arguments[i + 4];
		}
	}
	var oSelf = this;
	function EventListenerImpl()
	{
		var oEvent = oSelf.__getEvent__();
		var sExprToEval = 'fnObjMethodImpl.call(oObjImpl, oEvent';
		if(oAdditionalArgs !== null)
		{
			for(var i=0; i<oAdditionalArgs.length; i++)
			{
				sExprToEval += ', oAdditionalArgs[' + i + ']';
			}
			sExprToEval += ');';
			eval(sExprToEval);
		}
		else
		{
			fnObjMethodImpl.call(oObjImpl);
		}
	}
	this.addEventHandler(oTarget, sEventType, EventListenerImpl);
};

/**
 ************************************************************************************************
 */

/**
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}
/*
 * Calculate the MD5 of an array of little-endian words, and a bit length
 */
function core_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);

}
/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
/*
 * Calculate the HMAC-MD5, of a key and some data
 */
function core_hmac_md5(key, data)
{
  var bkey = str2binl(key);
  if(bkey.length > 16){ bkey = core_md5(bkey, key.length * chrsz); }

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
  return core_md5(opad.concat(hash), 512 + 128);
}
/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}
/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}
/*
 * Convert a string to an array of little-endian words
 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
 */
function str2binl(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz){
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
	}
  return bin;
}
/*
 * Convert an array of little-endian words to a string
 */
function binl2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz) {
    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
 	}
  return str;
}
/*
 * Convert an array of little-endian words to a hex string.
 */
function binl2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
  }
  return str;
}
/*
 * Convert an array of little-endian words to a base-64 string
 */
function binl2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32){ str += b64pad; }
      else { str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);}
    }
  }
  return str;
}

/**
 ************************************************************************************************
 */
/** 
   Copyright (c) 2005, Brad Neuberg, bkn3@columbia.edu
   http://codinginparadise.org
   
   Permission is hereby granted, free of charge, to any person obtaining 
   a copy of this software and associated documentation files (the "Software"), 
   to deal in the Software without restriction, including without limitation 
   the rights to use, copy, modify, merge, publish, distribute, sublicense, 
   and/or sell copies of the Software, and to permit persons to whom the 
   Software is furnished to do so, subject to the following conditions:
   
   The above copyright notice and this permission notice shall be 
   included in all copies or substantial portions of the Software.
   
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 
   OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 
   THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   
   The JSON class near the end of this file is
   Copyright 2005, JSON.org
*/

/** An object that provides DHTML history, history data, and bookmarking 
    for AJAX applications. */
window.dhtmlHistory = {
   	initialize: function() {
      if (this.isInternetExplorer() == false) {
         return;
      }
      if (historyStorage.hasKey("DhtmlHistory_pageLoaded") == false) {
         this.fireOnNewListener = false;
         this.firstLoad = true;
         historyStorage.put("DhtmlHistory_pageLoaded", true);
      }
      else {
         this.fireOnNewListener = true;
         this.firstLoad = false;   
      }
   },
   addListener: function(callback) {
      this.listener = callback;
      if (this.fireOnNewListener == true) {
         this.fireHistoryEvent(this.currentLocation);
         this.fireOnNewListener = false;
      }
   },
    add: function(newLocation, historyData) {
      var self = this;
      var addImpl = function() {
         if (self.currentWaitTime > 0)
            self.currentWaitTime = self.currentWaitTime - self.WAIT_TIME;
         newLocation = self.removeHash(newLocation);
         var idCheck = document.getElementById(newLocation);
         if (idCheck != undefined || idCheck != null) {
            var message = 
               "Exception: History locations can not have "
               + "the same value as _any_ id's "
               + "that might be in the document, "
               + "due to a bug in Internet "
               + "Explorer; please ask the "
               + "developer to choose a history "
               + "location that does not match "
               + "any HTML id's in this "
               + "document. The following ID "
               + "is already taken and can not "
               + "be a location: " 
               + newLocation;
            throw message; 
         }
         historyStorage.put(newLocation, historyData);
         self.ignoreLocationChange = true;
         this.ieAtomicLocationChange = true;
         self.currentLocation = newLocation;
         window.location.hash = newLocation;
         if (self.isInternetExplorer())
            self.iframe.src = "blank.html?" + newLocation;
         this.ieAtomicLocationChange = false;
      };
      window.setTimeout(addImpl, this.currentWaitTime);
      this.currentWaitTime = this.currentWaitTime + this.WAIT_TIME;
   },
    isFirstLoad: function() {
      if (this.firstLoad == true) {
         return true;
      }
      else {
         return false;
      }
   },
    isInternational: function() {
      return false;
   },
    getVersion: function() {
      return "0.05";
   },
   getCurrentLocation: function() {
      var currentLocation = this.removeHash(window.location.hash);
         
      return currentLocation;
   },
    currentLocation: null,
    listener: null,
    iframe: null,
    ignoreLocationChange: null,
    WAIT_TIME: 200,
	currentWaitTime: 0,
   fireOnNewListener: null,
   firstLoad: null,
   ieAtomicLocationChange: null,          
   create: function() {
      var initialHash = this.getCurrentLocation();
      this.currentLocation = initialHash;
      if (this.isInternetExplorer()) {
         document.write("<iframe style='border: 0px; width: 1px; "
                               + "height: 1px; position: absolute; bottom: 0px; "
                               + "right: 0px; visibility: visible;' "
                               + "name='DhtmlHistoryFrame' id='DhtmlHistoryFrame' "
                               + "src='blank.html?" + initialHash + "'>"
                               + "</iframe>");
         this.WAIT_TIME = 400;
      }
      var self = this;
      window.onunload = function() {
         self.firstLoad = null;
      };
      if (this.isInternetExplorer() == false) {
         if (historyStorage.hasKey("DhtmlHistory_pageLoaded") == false) {
            this.ignoreLocationChange = true;
            this.firstLoad = true;
            historyStorage.put("DhtmlHistory_pageLoaded", true);
         }
         else {
            this.ignoreLocationChange = false;
            this.fireOnNewListener = true;
         }
      }
      else { 
         this.ignoreLocationChange = true;
      }
      if (this.isInternetExplorer()) {
            this.iframe = document.getElementById("DhtmlHistoryFrame");
      }
      var self = this;
      var locationHandler = function() {
         self.checkLocation();
      };
      setInterval(locationHandler, 100);
   },
    fireHistoryEvent: function(newHash) {
      var historyData = historyStorage.get(newHash);   
      this.listener.call(null, newHash, historyData);
   },
   checkLocation: function() {
      if (this.isInternetExplorer() == false
         && this.ignoreLocationChange == true) {
         this.ignoreLocationChange = false;
         return;
      }
      if (this.isInternetExplorer() == false
          && this.ieAtomicLocationChange == true) {
         return;
      }
      var hash = this.getCurrentLocation();
      if (hash == this.currentLocation)
         return;
      this.ieAtomicLocationChange = true;
      if (this.isInternetExplorer()
          && this.getIFrameHash() != hash) {
         this.iframe.src = "blank.html?" + hash;
      }
      else if (this.isInternetExplorer()) {
         return;
      }
      this.currentLocation = hash;
      this.ieAtomicLocationChange = false;
      this.fireHistoryEvent(hash);
   },
   getIFrameHash: function() {
      var historyFrame = document.getElementById("DhtmlHistoryFrame");
      var doc = historyFrame.contentWindow.document;
      var hash = new String(doc.location.search);
      if (hash.length == 1 && hash.charAt(0) == "?")
         hash = "";
      else if (hash.length >= 2 && hash.charAt(0) == "?")
         hash = hash.substring(1); 
    	return hash;
   },          
    removeHash: function(hashValue) {
      if (hashValue == null || hashValue == undefined)
         return null;
      else if (hashValue == "")
         return "";
      else if (hashValue.length == 1 && hashValue.charAt(0) == "#")
         return "";
      else if (hashValue.length > 1 && hashValue.charAt(0) == "#")
         return hashValue.substring(1);
      else
         return hashValue;     
   },          
   
   /** For IE, says when the hidden iframe has finished loading. */
   /** private */ iframeLoaded: function(newLocation) {
      if (this.ignoreLocationChange == true) {
         this.ignoreLocationChange = false;
         return;
      }
      var hash = new String(newLocation.search);
      if (hash.length == 1 && hash.charAt(0) == "?")
         hash = "";
      else if (hash.length >= 2 && hash.charAt(0) == "?")
         hash = hash.substring(1);
      if (this.pageLoadEvent != true) {
         window.location.hash = hash.urldecode();
      }
      this.fireHistoryEvent(hash.urldecode());
   },
   isInternetExplorer: function() {
      var userAgent = navigator.userAgent.toLowerCase();
      if (document.all && userAgent.indexOf('msie')!=-1) {
         return true;
      }
      else {
         return false;
      }
   }
};
window.historyStorage = {
   debugging: false,
   storageHash: new Object(),
   hashLoaded: false, 
   put: function(key, value) {
       this.assertValidKey(key);
       if (this.hasKey(key)) {
         this.remove(key);
       }
       this.storageHash[key] = value;
       this.saveHashTable(); 
   },
   
   get: function(key) {
      this.assertValidKey(key);
      this.loadHashTable();
      var value = this.storageHash[key];
      if (value == undefined)
         return null;
      else
         return value; 
   },
   remove: function(key) {
      this.assertValidKey(key);
      this.loadHashTable();
      delete this.storageHash[key];
      this.saveHashTable();
   },
   reset: function() {
      this.storageField.value = "";
      this.storageHash = new Object();
   },
   hasKey: function(key) {
      this.assertValidKey(key);
      this.loadHashTable();
      if (typeof this.storageHash[key] == "undefined")
         return false;
      else
         return true;
   },
    isValidKey: function(key) {
      return (typeof key == "string");
      /*
      if (typeof key != "string")
         key = key.toString();
      
      
      var matcher = 
         /^[a-zA-Z0-9_ \!\@\#\$\%\^\&\*\(\)\+\=\:\;\,\.\/\?\|\\\~\{\}\[\]]*$/;
                     
      return matcher.test(key);*/
   },
   storageField: null,
   init: function() {
      var styleValue = "position: absolute; top: -1000px; left: -1000px;";
      if (this.debugging == true) {
         styleValue = "width: 30em; height: 30em;";
      }   
      var newContent =
         "<form id='historyStorageForm' " 
               + "method='GET' "
               + "style='" + styleValue + "'>"
            + "<textarea id='historyStorageField' "
                      + "style='" + styleValue + "'"
                              + "left: -1000px;' "
                      + "name='historyStorageField'></textarea>"
         + "</form>";
      document.write(newContent);
      this.storageField = document.getElementById("historyStorageField");
   },
   assertValidKey: function(key) {
      if (this.isValidKey(key) == false) {
         throw "Please provide a valid key for "
               + "window.historyStorage, key= "
               + key;
       }
   },
   loadHashTable: function() {
      if (this.hashLoaded == false) {
         var serializedHashTable = this.storageField.value;
         if (serializedHashTable != "" &&
             serializedHashTable != null) {
            this.storageHash = eval('(' + serializedHashTable + ')');  
         }
         this.hashLoaded = true;
      }
   },
   saveHashTable: function() {
      this.loadHashTable();
      var serializedHashTable = JSON.stringify(this.storageHash);
      this.storageField.value = serializedHashTable;
   }   
};
/** The JSON class is copyright 2005 JSON.org. */
Array.prototype.______array = '______array';
var JSON = {
    org: 'http://www.JSON.org',
    copyright: '(c)2005 JSON.org',
    license: 'http://www.crockford.com/JSON/license.html',
    stringify: function (arg) {
        var c, i, l, s = '', v;
        switch (typeof arg) {
        case 'object':
            if (arg) {
                if (arg.______array == '______array') {
                    for (i = 0; i < arg.length; ++i) {
                        v = this.stringify(arg[i]);
                        if (s) {
                            s += ',';
                        }
                        s += v;
                    }
                    return '[' + s + ']';
                } else if (typeof arg.toString != 'undefined') {
                    for (i in arg) {
                        v = arg[i];
                        if (typeof v != 'undefined' && typeof v != 'function') {
                            v = this.stringify(v);
                            if (s) {
                                s += ',';
                            }
                            s += this.stringify(i) + ':' + v;
                        }
                    }
                    return '{' + s + '}';
                }
            }
            return 'null';
        case 'number':
            return isFinite(arg) ? String(arg) : 'null';
        case 'string':
            l = arg.length;
            s = '"';
            for (i = 0; i < l; i += 1) {
                c = arg.charAt(i);
                if (c >= ' ') {
                    if (c == '\\' || c == '"') {
                        s += '\\';
                    }
                    s += c;
                } else {
                    switch (c) {
                        case '\b':
                            s += '\\b';
                            break;
                        case '\f':
                            s += '\\f';
                            break;
                        case '\n':
                            s += '\\n';
                            break;
                        case '\r':
                            s += '\\r';
                            break;
                        case '\t':
                            s += '\\t';
                            break;
                        default:
                            c = c.charCodeAt();
                            s += '\\u00' + Math.floor(c / 16).toString(16) +
                                (c % 16).toString(16);
                    }
                }
            }
            return s + '"';
        case 'boolean':
            return String(arg);
        default:
            return 'null';
        }
    },
    parse: function (text) {
        var at = 0;
        var ch = ' ';
        function error(m) {
            throw {
                name: 'JSONError',
                message: m,
                at: at - 1,
                text: text
            };
        }
        function next() {
            ch = text.charAt(at);
            at += 1;
            return ch;
        }
        function white() {
            while (ch != '' && ch <= ' ') {
                next();
            }
        }
        function str() {
            var i, s = '', t, u;

            if (ch == '"') {
				outer:   while (next()) {
                    if (ch == '"') {
                        next();
                        return s;
                    } else if (ch == '\\') {
                        switch (next()) {
                        case 'b':
                            s += '\b';
                            break;
                        case 'f':
                            s += '\f';
                            break;
                        case 'n':
                            s += '\n';
                            break;
                        case 'r':
                            s += '\r';
                            break;
                        case 't':
                            s += '\t';
                            break;
                        case 'u':
                            u = 0;
                            for (i = 0; i < 4; i += 1) {
                                t = parseInt(next(), 16);
                                if (!isFinite(t)) {
                                    break outer;
                                }
                                u = u * 16 + t;
                            }
                            s += String.fromCharCode(u);
                            break;
                        default:
                            s += ch;
                        }
                    } else {
                        s += ch;
                    }
                }
            }
            error("Bad string");
        }
        function arr() {
            var a = [];

            if (ch == '[') {
                next();
                white();
                if (ch == ']') {
                    next();
                    return a;
                }
                while (ch) {
                    a.push(val());
                    white();
                    if (ch == ']') {
                        next();
                        return a;
                    } else if (ch != ',') {
                        break;
                    }
                    next();
                    white();
                }
            }
            error("Bad array");
        }
        function obj() {
            var k, o = {};

            if (ch == '{') {
                next();
                white();
                if (ch == '}') {
                    next();
                    return o;
                }
                while (ch) {
                    k = str();
                    white();
                    if (ch != ':') {
                        break;
                    }
                    next();
                    o[k] = val();
                    white();
                    if (ch == '}') {
                        next();
                        return o;
                    } else if (ch != ',') {
                        break;
                    }
                    next();
                    white();
                }
            }
            error("Bad object");
        }
        function num() {
            var n = '', v;
            if (ch == '-') {
                n = '-';
                next();
            }
            while (ch >= '0' && ch <= '9') {
                n += ch;
                next();
            }
            if (ch == '.') {
                n += '.';
                while (next() && ch >= '0' && ch <= '9') {
                    n += ch;
                }
            }
            if (ch == 'e' || ch == 'E') {
                n += 'e';
                next();
                if (ch == '-' || ch == '+') {
                    n += ch;
                    next();
                }
                while (ch >= '0' && ch <= '9') {
                    n += ch;
                    next();
                }
            }
            v = +n;
            if (!isFinite(v)) {
                error("Bad number");
            } else {
                return v;
            }
        }
        function word() {
            switch (ch) {
                case 't':
                    if (next() == 'r' && next() == 'u' && next() == 'e') {
                        next();
                        return true;
                    }
                    break;
                case 'f':
                    if (next() == 'a' && next() == 'l' && next() == 's' &&
                            next() == 'e') {
                        next();
                        return false;
                    }
                    break;
                case 'n':
                    if (next() == 'u' && next() == 'l' && next() == 'l') {
                        next();
                        return null;
                    }
                    break;
            }
            error("Syntax error");
        }
        function val() {
            white();
            switch (ch) {
                case '{':
                    return obj();
                case '[':
                    return arr();
                case '"':
                    return str();
                case '-':
                    return num();
                default:
                    return ch >= '0' && ch <= '9' ? num() : word();
            }
        }
        return val();
    }
};

/** Initialize all of our objects now. */
window.historyStorage.init();
window.dhtmlHistory.create();
/**
 *	BrowserDetect by Peter-Paul Koch
 * 	http://www.quirksmode.org/js/detect.html
 **/
var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
	},
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1) { return data[i].identity; }
			}
			else if (dataProp)
			{
				return data[i].identity;
			}
		}
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) { return; }
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	dataBrowser: [
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]

};
/**
 * Navegador
 */
BrowserDetect.init();  // Detectar el navegador
var isIE = (BrowserDetect.browser == "Explorer");
var iBrowserVer = BrowserDetect.version;
var isOpera = (BrowserDetect.browser == "Opera");
var isWin = (BrowserDetect.OS == "Windows");
//