/*
 *
 */
 
function XML()
{	
	// CREACION DEL OBJETO

	this.xmlhttp = null;

	try
	{
		// If the user is using Mozilla/Firefox/Safari

		this.xmlhttp = new XMLHttpRequest();
		this.xmlhttp.overrideMimeType('text/xml');
		this.xmlParser = new DOMParser();
	}
	catch (e)
	{
	}

	if (this.xmlhttp == null)
	{
		try
		{
			// If the user is using IE

			this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
			this.xmlparser = null;	// MUST USE A NEW ONE EACH TIME
		}
		catch (e)
		{
		}
	}

	if (this.xmlhttp == null)
	{
		try
		{
			// If the user is using IE

			this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
			this.xmlparser = null;	// MUST USE A NEW ONE EACH TIME
		}
		catch (e)
		{
		}
	}
	
	this.connectToUrl = XML_connectToUrl;
	this.parseString = XML_parseString;
	this.nodeToAttributeTree = XML_NodeToAttributeTree ;
}

/* 
 *
 */
function XML_parseString(xmlString)
{
	var xml, xmlRootNode, parseError, bKeep ;
	
	// CREACION DEL OBJETO
	
	bKeep = true
	
	try
	{
		// If the user is using Mozilla/Firefox/Safari

		xml = this.xmlParser.parseFromString(xmlString, "text/xml");					
		parseError = XML_checkForMozillaParseError(xml);
		if (parseError.errorCode == 0)
		{
			return xml.documentElement;
		}
		else
		{
			return parseError.reason + '\r\n' + parseError.srcText ;
		}
		
		xml = null ;
		bKeep = false;
	}
	catch(e)
	{
	}
	
	if (bKeep)
	{		
		try
		{
			 var xml = new ActiveXObject("Microsoft.XMLDOM");
		   xml.async = false;
		   xml.loadXML(xmlString);
		    
		   if (xml.parseError.errorCode != 0)
		    	return (xml.parseError.reason + " " + xml.parseError.line);
	
		    return xml.documentElement;
	   }
	   catch(e)
	   {
	   }
	}
}

/*
 *
 */
function XML_checkForMozillaParseError(xmlDocument) 
{
	var errorNamespace = 'http://www.mozilla.org/newlayout/xml/parsererror.xml' ;
	var documentElement = xmlDocument.documentElement ;
	var parseError = { errorCode : 0 };
	
	if (documentElement.nodeName == 'parsererror' &&
		documentElement.namespaceURI == errorNamespace) 
	{
		parseError.errorCode = 1;
      var sourceText = documentElement.getElementsByTagNameNS(errorNamespace, 'sourcetext')[0];
      if (sourceText != null) 
      {
        parseError.srcText = sourceText.firstChild.data
    	}
      parseError.reason = documentElement.firstChild.data;
    }
    return parseError ;
}


	
/*
 *
 */
 
var XML_REQUEST_RESULT_TIMEOUT_BEFORE_CONNECT = "1";
var XML_REQUEST_RESULT_TIMEOUT_AFTER_CONNECT = "2";
var XML_REQUEST_RESULT_OK = "3";
var XML_REQUEST_RESULT_ERROR_BEFORE_CONNECT = "4";
var XML_REQUEST_RESULT_ERROR_AFTER_CONNECT = "5";
var XML_REQUEST_RESULT_CANCELLED_BEFORE_CONNECT = "6";
var XML_REQUEST_RESULT_CANCELLED_AFTER_CONNECT = "7";
var XML_REQUEST_RESULT_UNAUTHORIZED = "8";

var XML_REQUEST_RESULT_TYPE_XML = "1";
var XML_REQUEST_RESULT_TYPE_TEXT = "2";

//--------------------------------------------

function XML_connectToUrl(sUrl, aPostParams, processFunction,
												iMillisecondsTimeout, resultType)
{		
	// TIPO DE PETICION Y PARAMETROS POST
	
	var sPostData, i;
	
	sPostData = "" ;
	
	if (aPostParams == null)
		this.xmlhttp.open("GET", sUrl, true);
	else
	{
		this.xmlhttp.open("POST", sUrl, true);
		this.xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=ISO-8859-1');
		
		for (i=0 ; i<aPostParams.length ; i++)
		{
			sPostData += postEncode(aPostParams[i][0]) + "=" + postEncode(aPostParams[i][1]) + "&";
		}
	}
	
	// PREPARACION DE LA FUNCION QUE PROCESA LA RESPUESTA

	this.start = new Date() ;
	this.cancelConnectionRequested = false ;
	this.iMillisecondsTimeout = iMillisecondsTimeout ;
	this.processFunction = processFunction ;
	this.resultType = resultType ;
	
	this.id = addXMLObjectToSet(this);
	
	this.xmlhttp.send(sPostData);	
		
	this.cancelUrlConnection = function() { this.cancelConnectionRequested = true; } ;
	
	var xmlObj = this ;	
	var fn = function() 
	{				
		var currentState = xmlObj.xmlhttp.readyState ;
			
		if (xmlObj.cancelConnectionRequested)	// CHECK IF THE COMMUNICATION HAS BEEN ASKED TO CANCEL
		{
			removeXMLObjectFromSet(xmlObj.id);
			xmlObj.xmlhttp.abort();
			if (currentState == 0)
				xmlObj.processFunction(XML_REQUEST_RESULT_CANCELLED_BEFORE_CONNECT);
			else
				xmlObj.processFunction(XML_REQUEST_RESULT_CANCELLED_AFTER_CONNECT);
			return ;
		}
		
		if ((currentState < 4) && (currentState!="complete"))
		{
			// NOT FINISHED YET
			if (((new Date()) - xmlObj.start) > xmlObj.iMillisecondsTimeout)
			{
				// TIMEOUT
				
				removeXMLObjectFromSet(xmlObj.id);
				xmlObj.xmlhttp.abort();
				if (currentState == 0)
					xmlObj.processFunction(XML_REQUEST_RESULT_TIMEOUT_BEFORE_CONNECT);
				else
					xmlObj.processFunction(XML_REQUEST_RESULT_TIMEOUT_AFTER_CONNECT);
			}
			else	
			{
				// KEEP WAITING THE END
				setTimeout(fn, 500);
			}
		}
		else
		{
			// COMMUNICATION FINISHED
			
			removeXMLObjectFromSet(xmlObj.id);
			
			var result ;
			var statusCode = xmlObj.xmlhttp.status;
						
			if ((statusCode >= 200) && (statusCode <= 299))
			{
				// OK

				switch(xmlObj.resultType)
				{
					case XML_REQUEST_RESULT_TYPE_XML:
						result = xmlObj.xmlhttp.responseXML;
						break;
					default:
						result = xmlObj.xmlhttp.responseText;
						break;
				}
				xmlObj.processFunction(XML_REQUEST_RESULT_OK, result);
			}
			else
			{
				// ERROR
				
				//  Ante la duda, considero que el error es tras la conexion. Es mejor avisar de que puede haber 
				// habido proceso parcial de la operacion y que no lo haya habido, que avisar de que no ha habido
				// proceso en el servidor y luego que si haya habido
				
				result = XML_REQUEST_RESULT_ERROR_AFTER_CONNECT;
				
				if (((status >= 300) && (status <= 399)) ||
					 ((status >= 400) && (status <= 499)) ||
					 ((status >= 501) && (status <= 505)))
				{
					// 3XX - EL CLIENTE TIENE QUE LLEVAR A CABO ALGUNA ACCION PARA COMPLETAR EL PROCESO
					// EJEMPLOS: 300-Multiple Choices, 301-Moved Permanently, 302-Found, ...
					
					// 4xx - ERROR EN LA COMUNICACION. NO SE PRODUJO PROCESO EN EL SERVIDOR
					// EJEMPLOS: 400-Bad Request, 401-Unauthorized, 402-Payment Required, 403-Forbidden, 404-Not Found
					
					// 5xx - ERROR EN EL SERVIDOR. ALGUNOS IMPLICAN PROCESO PARCIAL EN SERVIDOR Y OTROS NO
					// EJEMPLOS: 500-Internal Server Error, 501-Not Implemented, 502-Bad Gateway, 503-Service Unavailable,
					//  504-Gateway Timeout. 
					//  COMO VEMOS, ALGUNOS SABEMOS QUE NO HUBO PROCESO, PERO OTROS, COMO EL 500, PUEDEN IMPLICAR PROCESO
				
					if (status == 401)
						result = XML_REQUEST_RESULT_UNAUTHORIZED;
					else
						result = XML_REQUEST_RESULT_ERROR_BEFORE_CONNECT;
				}
				
				xmlObj.processFunction(result, xmlObj.xmlhttp.statusText);
			}
		}
	}
	
	setTimeout(fn, 500);
}

/*
 *
 */
  
var XML_NODE_TYPE_ELEMENT = 1;
var XML_NODE_TYPE_TEXT = 3;
var XML_NODE_TYPE_CDATA = 4;

/* 
 *
 */

function XML_NodeToAttributeTree(xmlNode)
{
  	var i, tree, sText, subtree ;
  	
	if (!xmlNode)
		return null ;
	
	if (("" + xmlNode.documentElement) != "undefined")
		xmlNode = xmlNode.documentElement ;
		
	tree = new AttributeTree(xmlNode.nodeName) ;
	
	// ATTRIBUTES

	if (xmlNode.attributes)
		for (i=0; i<xmlNode.attributes.length ; i++)
			tree.setAttribute(xmlNode.attributes[i].name, xmlNode.attributes[i].value);
	
	bAnyNonTextChildNode = false ;
	sText = null ;
	
	// CHILD NODES

	if (xmlNode.childNodes)
	{
		var sValue = "";
		var bHasSubnodes = false ;
		
		/* PROCESO LOS SUBNODOS DE ESTE NODO. SI TIENE SUBNODOS, ENTONCES LOS PROCESO, Y ENTIENDO QUE LOS BLOQUES 
		   DE TEXTO (Y CDATA, ETC) QUE CONTENGA NO SE INTRODUCEN COMO ATRIBUTO SINO COMO SUBNODOS, DE NOMBRE #text	
		   Y CUYO UNICO ATRIBUTO SE LLAMA #value Y CONTIENE EL TEXTO EN CUESTION. SI EL NODO SOLO TUVIERA TEXTO,
		   ENTONCES SE INTERPRETA COMO ATRIBUTO DIRECTAMENTE, DE NOMBRE #value.
		   
		   <a>
		     texto_1
		     <b></b>
		     texto_2
		   </a>
		   
		   SE INTERPRETA COMO:
		   
		   <a>
		    <#text #value="texto_1"></#text>
		    <b></b>
		    <#text #value="texto_1"></#text>
		   </a>
		   
		   Y SIN EMBARGO:
		   
		   <a>
		     texto_1
		   </a>
		   
		   SE INTERPRETA COMO:
		   
         <a #value="texto_1"></a>
		  
		*/	   
		
		for (i=0; i<xmlNode.childNodes.length ; i++)
		{	
			if (xmlNode.childNodes[i].nodeType == XML_NODE_TYPE_ELEMENT)
			{
				bHasSubnodes = true ;
				if (sValue.length > 0) 
				{
					// EL TEXTO ACUMULADO DESDE EL ANTERIOR NODO (texts, cdatas, ...) LO INTRODUZCO COMO NODO TEXTO
					textSubnode = new AttributeTree("#text");
					textSubnode.setAttribute("#value", sValue);
					tree.addChild(textSubnode);
					sValue = "" ;
				}
				tree.addChild(XML_NodeToAttributeTree(xmlNode.childNodes[i]));
			}
			else ((xmlNode.childNodes[i].nodeType == XML_NODE_TYPE_TEXT) ||
					(xmlNode.childNodes[i].nodeType == XML_NODE_TYPE_CDATA))
			{
				if (xmlNode.childNodes[i].nodeValue)
				{
					sValue += xmlNode.childNodes[i].nodeValue.replace(/^\s*|\s*$/g,"");
				}
			}
		}
		
		// PROCESO EL RESTO DEL TEXTO
		if (bHasSubnodes)
		{
			// EL TEXTO ACUMULADO DESDE EL ANTERIOR NODO (texts, cdatas, ...) LO INTRODUZCO COMO NODO TEXTO
			if (sValue.length > 0)
			{
				textSubnode = new AttributeTree("#text");
				textSubnode.setAttribute("#value", sValue);
				tree.addChild(textSubnode);
			}
		}
		else
		{
			tree.setAttribute("#value", sValue);
		}
	}
	
	return tree ;
}

  	/*
  	 *
  	 */
   
   function XML_encode(text)
   {
   	var i, encoded, c, ascCode;
   	
   	if (text == null)
   		return null ;
   		
   	encoded = "" ;
   	for (i=0; i<text.length ; i++)
   	{
   		c = text.charAt(i);
   		switch(c)
   		{
   			case "%":	c = "&#37;"; break ;
   			case "&":	c = "&amp;"; break ;
   			case "<":	c = "&lt;"; break ;
   			case ">":	c = "&gt;"; break ;
   			case "!":	c = "&#x21;"; break ;
   			case "[":	c = "&#x5B;"; break ;
   			case "]":	c = "&#x5D;"; break ;
   			case "+":	c = "&plus;"; break ;
   			default:
   				ascCode = c.charCodeAt(0);
   				if (ascCode >= 256)
   					c = "&#" + ascCode + ";";
   				   			
   		}
   		encoded += c ;
		}
   	return encoded ;
	}
	
	/*
	 *
	 */ 
	function postEncode(str)
	{
		return escape(str);
	}
	
	/*
	 *
	 */
	var XML_OBJECTS = new Array();
	var XML_OBJECTS_COUNT = 0;

	
	/*
	 *
	 */
	 
	function addXMLObjectToSet(obj)
	{
		var index;
		index = XML_OBJECTS_COUNT;
		XML_OBJECTS_COUNT++;
		XML_OBJECTS[index] = obj;
		return index;
	}
	
	/*
	 *
	 */
	 
	function removeXMLObjectFromSet(index)
	{
		var obj;
		XML_OBJECTS_COUNT--;
		XML_OBJECTS[index] = XML_OBJECTS[XML_OBJECTS_COUNT];
		XML_OBJECTS[XML_OBJECTS_COUNT] = null;
	}
	
	/*
	 *
	 */
	 if (window.onUnloadWork == undefined) window.onUnloadWork = [];
	window.onUnloadWork[window.onUnloadWork.length] = new function() 
	{
		for (i=0; i<XML_OBJECTS_COUNT; i++)
			if (XML_OBJECTS[i] != null)
				if (XML_OBJECTS[i].abortUrlConnection)
					XML_OBJECTS[i].abortUrlConnection();
	}
  
