/************************************************************************
 *
 * $Id: xhttp.js,v 1.0 2006-03-14 11:00:00 massimo viti$
 *          derived by xhr.js  Paul Spencer (pspencer@dmsolutions.ca)
 *
 * purpose: a simple cross-browser XmlHttpRequest interface that adds
 *          support for multiple, concurrent requests. 
 *
 * author: Massimo Viti (mviti@lamma.rete.toscana.it)
 *
 * todo:
 *   - retrieve XL data by reponseXML
 *
 * changed:
 *   - call() function name: now if callxhttp()
 *   - POST parameter in callxhttp() function
 * 
 **********************************************************************/

/************************************************************************
 * 
 * prevenire il caching di richieste 
 *
 * How will caching be controlled?
 * 
 * It's possible that an XMLHttpRequest response will be cached by the browser. Sometimes, 
 * that's what you want and sometimes it's not, so you need to exert some control over caching.
 * With cache control, we're talking about "GET" based requests. 
 * Use "GET" for read-only queries and other request types for operations that affect server state. 
 * If you use "POST" to get information, that information won't be cached. Likewise, if you use "GET" 
 * to change state, you run the risk that the call won't always reach the server, because the browser 
 * will cache the call locally. There are other reasons to follow this advice too; see RESTful Service.
 * 
 * Often, you want to suppress caching in order to get the latest server information, in which case a 
 * few techniques are relevant.
 * Since browsers and servers vary in their behaviour, the standard advice is spread the net as wide 
 * as possible, by combining all of these techniques: 
 * 
 * 
 * 
 * at the request:
 * 
 * 1 - you can add a header to the request: 
 *     xhReq.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2005 00:00:00 GMT");
 * 
 * 2 - you can make the URL unique by appending a timestamp: 
 *     var aTimestamp = "timestamp=" + new Date().getTime();
 *     var url = url_orig + {&|?} + aTimestampa;
 * 
 * 
 * In the web service, set response headers to suppress caching. In PHP, for example: 
 * 
 * header("Expires: Sat, 1 Jan 2005 00:00:00 GMT");
 * header("Last-Modified: ".gmdate( "D, d M Y H:i:s")."GMT");
 * header("Cache-Control: no-cache, must-revalidate");
 * header("Pragma: no-cache");
 *
 **********************************************************************/




/**********************************************************************
// Prefisso che indica che e' avvenuto un errore
// Se la stringa passata alla afunzione callback inizia con questa stringa
// significa che si e' verificato un errore nell'uso di XMLHttpRequest
// es: 
function callbak_function(str)
{
	if (typeof(str) == "string" && str.indexOf(preAJAXError) == -1)
	{
		// OK non ci son errori 
	}
}
**********************************************************************/
//var preAJAXError = '\x80' + "AJAX xhttp Error Code ";
var preAJAXError = "AJAX Error Code n.: ";


// pefisso con cui inizia la stringa di risposta di un file ajax nel caso l'operazione
// richiesta sia stata completata con successo
var XHTTP_JSDATA = "/* XHTTP JAVASCRIPT DATA */ ";
 // pefisso con cui termina stringa di risposta di un file ajax
var XHTTP_JSDATA_END = "/* XHTTP JAVASCRIPT DATA END */ ";


/***********************************************************************
 *  
 * il prefisso 'preAJAXError' indica che ci sono stati degi errori a livello di sistema
 * ad esempio il file richiesto non e' stato trovato, invece la mancanza del 
 * prefisso XHTTP_JSDATA indica che e' avvenuto un errore nel file php lanciato via
 * xhttp, ad esempio l'uso di una variabile non referenziata
 * 
 **********************************************************************/


/*
 * rende true se l'url indicato e' riferito ad un URL esistente
 */
function urlExists(url)
{
	var xmlhttp = getXMLHTTP();
	xmlhttp.open("HEAD", url, false);
	xmlhttp.send(null);
 	return xmlhttp.status != 404;
}





// array multidimensionale in cui ogni elemento rappresenta callxhttp.
// Ogni elemento e' formato da 4 item:
// 		aXmlHttp[n][0]: oggetto XMLHttpRequest
// 		aXmlHttp[n][1]: oggetto di callback
// 		aXmlHttp[n][2]: funzione o metodo di callback. Se si tratta di una funzione, aXmlHttp[n][1] dovra' essere null;
// 		aXmlHttp[n][3]: target URL privo di eventuali parametri passati via 'GET'
//

/*****************************************************************************************
 *
 * XMLHttpRequest()->readyState
 *    0 = uninitialized
 *    1 = loading
 *    2 = loaded
 *    3 = interactive
 *    4 = complete
 *
 *
 * XMLHttpRequest()->status
 *		100 Continue
 *		101 Switching protocols
 *		200 OK
 *		201 Created
 *		202 Accepted
 *		203 Non-Authoritative Information
 *		204 No Content
 *		205 Reset Content
 *		206 Partial Content
 *		300 Multiple Choices
 *		301 Moved Permanently
 *		302 Found
 *		303 See Other
 *		304 Not Modified
 *		305 Use Proxy
 *		307 Temporary Redirect
 *		400 Bad Request
 *		401 Unauthorized
 *		402 Payment Required
 *		403 Forbidden
 *		404 Not Found
 *		405 Method Not Allowed
 *		406 Not Acceptable
 *		407 Proxy Authentication Required
 *		408 Request Timeout
 *		409 Conflict
 *		410 Gone
 *		411 Length Required
 *		412 Precondition Failed
 *		413 Request Entity Too Large
 *		414 Request-URI Too Long
 *		415 Unsupported Media Type
 *		416 Requested Range Not Suitable
 *		417 Expectation Failed
 *		500 Internal Server Error
 *		501	Not Implemented
 *		502	Bad Gateway
 *		503 Service Unavailable
 *		504 Gateway Timeout
 *		505 HTTP Version Not Supported*
 *
 *****************************************************************************************/
var aXmlHttp = new Array();

// indica se siamo con un browser MS-InternetExplorer
var xml_IsIE = false;

function xmlResult()
{
	var xmlBol = false;
	if (arguments.length >= 1 && arguments[0] == 'xml') 
	{
      xmlBol= true;
    }
    
    try {
		for(var i=0; i < aXmlHttp.length; i++)
		{
			if (aXmlHttp[i][0] && aXmlHttp[i][0].readyState == 4)
			{
				if (typeof(aXmlHttp[i][0].status) == "undefined")
				{
					var ajError = preAJAXError + "undefined:\n" + aXmlHttp[i][3] + "\n" + aXmlHttp[i][0].statusText;
					alert(ajError);
					
					var f = aXmlHttp[i][2];
					var o = aXmlHttp[i][1];
					var s = undefined; //ajError;
					aXmlHttp.splice(i--, 1);					
					if (f)
						f.apply(o, [s]);
				}
				else
				{
					if (!aXmlHttp[i][0].status || aXmlHttp[i][0].status ==  0 || aXmlHttp[i][0].status == 200 || aXmlHttp[i][0].status == 304)
					{
						{
							var f = aXmlHttp[i][2];
							var o = aXmlHttp[i][1];
							//var s = (aXmlHttp[i][0].responseXML) ? aXmlHttp[i][0].responseXML : aXmlHttp[i][0].responseText + '';
							var s = aXmlHttp[i][0].responseText + '';
//alert("xmlHttp.responseText:\n0: " + s.charCodeAt(0) + "\n" + "1: " + s.charCodeAt(1));
							s = s.trim(); // effettua il trimming del testo restituito via ajax
							aXmlHttp.splice(i--, 1);
							o = o || this;
							if (f)
								f.call(o, s);					
						}
					}
					else  
					{
						var ajError = preAJAXError + aXmlHttp[i][0].status + ":\n" + aXmlHttp[i][3] + "\n" + aXmlHttp[i][0].statusText;
						alert(ajError);
						
						var f = aXmlHttp[i][2];
						var o = aXmlHttp[i][1];
						var s = undefined; //ajError;
						aXmlHttp.splice(i--, 1);					
						if (f)
							f.apply(o, [s]);
					}
				}
			}
		}
	}
	catch (e) { alert(e); }
}

/**************************************************************************************
 *
 * chiamata asincrona di XMLHttpRequest 
 *
 * url -> url[?get_data] (es: http://www.pippo.com?var1=data1&var2=data2)
 * o -> object (can be null) to invoke function on
 * f -> callback function
 * p -> optional argument to specify POST (es: "var1=data1&var2=data2")
 *
 **************************************************************************************/
function callxhttp(url, o, f)
{
try {
	var method = "GET";
	var dat = null;
	if (arguments.length >= 4)
	{
		method = "POST";
		// keyname_1=valore[&keyname_2=valore]...[&keyname_n=valore]
		dat = arguments[3];
	}
	if (getXMLHTTP)
	{
		var idx = aXmlHttp.length;
		aXmlHttp[idx] = new Array(4);
		if(aXmlHttp[idx])
		{
			aXmlHttp[idx][0] = getXMLHTTP();
			aXmlHttp[idx][1] = o;
			aXmlHttp[idx][2] = f;
			
			var tmp = splitURIComponent(url); // separa l'indirizzo dai parametri
			var hRef = tmp[0]; // indirizzo URL interessato
			// aggiunge il parametro: timestamp=<...> per evitare il caching
			url = joinURIComponent(hRef, tmp[1], getURIPram("timestamp", encodeURIComponent(new Date().getTime())));			
			aXmlHttp[idx][3] = hRef; // imposta l'elemento dell'array globale con l'indirizzo
		
			aXmlHttp[idx][0].onreadystatechange = xmlResult;
			aXmlHttp[idx][0].open(method, url, true);
			if(method == "POST")
			{
				aXmlHttp[idx][0].setRequestHeader("Content-Type", "application/x-www-form-urlencoded");	
				aXmlHttp[idx][0].send(dat);
			}
			else if(method =="GET")
			{ 
				aXmlHttp[idx][0].setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2005 00:00:00 GMT"); // per evitare il caching
				if (xml_IsIE)
					aXmlHttp[idx][0].send();
				else
					aXmlHttp[idx][0].send(null);
			}
		}
	}	
	else
	{
		alert("Sorry, your browser isn\'t equiped to work with XMLHttpRequest.");
	}
}
catch (excp)
{}
}


/**************************************************************************************
 *
 * chiamata sincrona di XMLHttpRequest 
 *
 * url -> url[?get_data] (es: http://www.pippo.com?var1=data1&var2=data2)
 * p -> optional argument to specify POST (es: "var1=data1&var2=data2")
 *
 * la funzione rende responseText o responseXML se tutto OK, altrimenti rende null
 *
 **************************************************************************************/
function callxhttp_sync(url)
{
	var xmlHttp = null;
	var method = "GET";
	var dat = null;
	var retval = null;
	
 try {	
	if (arguments.length >= 2)
	{
		method = "POST";		
		dat = arguments[1];
	}
	
	var tmp = splitURIComponent(url);
	var hRef = tmp[0]; // ricava l'indirizzo interessato
	// aggiunge il parametro: timestamp=<...> per evitare il caching
	url = joinURIComponent(hRef, tmp[1], getURIPram("timestamp", encodeURIComponent(new Date().getTime())));
	
	var xmlHttp = getXMLHTTP();	
	xmlHttp.open(method, url, false);
	if(method == "POST")
	{
		xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");	
		try {	
			xmlHttp.send(dat);
		} catch (e) {}
 	}
	else if(method =="GET")
	{ 
		xmlHttp.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2005 00:00:00 GMT"); // per evitare il caching
		if (xml_IsIE)
			xmlHttp.send();
		else
			xmlHttp.send(null);
	}
	
	if (xmlHttp.status != 200)
	{
		var ajError = preAJAXError + xmlHttp.status + ":\n" + hRef + "\n" + xmlHttp.statusText;
		alert(ajError);
		retval = undefined;
	 }
	 else
	 {     
		//retval = xmlHttp.responseXML ?  xmlHttp.responseXML : xmlHttp.responseText + "";
		retval = xmlHttp.responseText + "";
//alert("retval:\n0: " + retval.charCodeAt(0) + "\n" + "1: " + retval.charCodeAt(1));
		retval = retval.trim();  // effettua il trimming del testo restituito via ajax
 	}
 }
 catch (excp) {}
 
 return retval;
}

// sistema e controlla la stringa di risposta da un file ajax
function adjustXhttpReturn(str)
{	
	var strdata = "";
	var strover = "";

	if (typeof(str) == "string")
	{
		// estrae la porzione che identifica delle istruzioni javascript 
		var startstrdata = str.indexOf(XHTTP_JSDATA);
		if (startstrdata >= 0)
		{
			var endstrdata = str.indexOf(XHTTP_JSDATA_END);
			if (endstrdata <= startstrdata)
				endstrdata = str.length;
			var len = endstrdata - startstrdata;
			strdata = trim(str.substr(startstrdata, len));
			
			var str_1 = (startstrdata > 0) ? str.substr(0, startstrdata) : "";
			var str_2 = (endstrdata < str.length) ? str.substr(endstrdata + XHTTP_JSDATA_END.length) : "";
			strover = trim(str_1) + "\n" + trim(str_2);
		}
	}
	var p = new Array();
	p['strdata'] = strdata;
	p['strover'] = strover;
	//return new Array(strdata, strover);
	return p;
}


/*********************************************************************************************************/
// classe che instazia il messaggio XML di ritorno da un file ajax
function XHTTPReturn(xmlDoc)
{
	this.STARTING_WARN = 10000;
	this.retcode = 0;	// codice di ritorno: 0 = OK, 1-9999 = codice d'errore, >= 10000 = warning
	this.message = "";  // messaggio
	this.unwmessage = ""; // messaggio non previsti. In genere si tratta di un messaggio d'errore
	
	this.parsingError = false; // indica che e' avvenuto un errore in fase di parsing dei dati XML
	
	this.decode = function (xmlDoc)
	{
		this.retcode = 0;
		this.message = "";
		this.unwmessage = "";
		this.parsingError = false;

		if (typeof(xmlDoc) == "string")
		{
			var doc=null;
			// code for IE
			if (window.ActiveXObject)
			{
				doc=new ActiveXObject("Microsoft.XMLDOM");
				doc.async="false";
				doc.loadXML(xmlDoc);
			}
			// code for Mozilla, Firefox, Opera, etc.
			else
			{
				var parser=new DOMParser();
				doc=parser.parseFromString(xmlDoc,"text/xml");
			}
			// documentElement always represents the root node
			//var x=doc.documentElement;
			xmlDoc = doc;
		}
		
		//return;
		if (typeof(xmlDoc) == "object" && xmlDoc instanceof XMLDocument)
		{
			var roottag = xmlDoc.documentElement;
			
			if ((roottag.tagName == "parserError") ||
				(roottag.namespaceURI == "http://www.mozilla.org/newlayout/xml/parsererror.xml"))
			{
				//alert("XML Parsing Error!");
				this.parsingError = true;
			}
			else
			{
				try {
					this.unwmessage = trim(roottag.firstChild.data);
				}
				catch (excp)
				{}
				
				try {
					elems = roottag.getElementsByTagName("retcode");
					this.retcode = elems[0].firstChild.data;	
				}
				catch (excp)
				{}
				
				try {
					elems = roottag.getElementsByTagName("message");
					this.message = elems[0].firstChild.data;	
				}
				catch (excp)
				{}
			}
		}
	}
	
	this.isOK = function ()
	{
		return this.retcode == 0 || this.retcode >= this.STARTING_WARN;
	}

	this.decode(xmlDoc);
}




/*********************************************************************************************************/
// instanzia e rende un nuovo oggetto XMLHttpRequest in base a quale browser e' in uso
function xmlHttpGen()
{
    if(window.XMLHttpRequest) 
	{
		xml_IsIE = false;
		return function(){ return new XMLHttpRequest(); }
	}
    else if(window.ActiveXObject)     
	{
		var msv= ["Msxml2.XMLHTTP.7.0", 
				  "Msxml2.XMLHTTP.6.0",
				  "Msxml2.XMLHTTP.5.0", 
				  "Msxml2.XMLHTTP.4.0", 
				  "MSXML2.XMLHTTP.3.0",
				  "MSXML2.XMLHTTP", 
				  "Microsoft.XMLHTTP"];
		for(j=0; j <= msv.length; j++)
		{	
			try
			{
				var A = new ActiveXObject(msv[j]);
				if(A)
				{
					xml_IsIE = true;
					return function(){ return new ActiveXObject(msv[j]); }
				}
			}
			catch(e) {}
		}
	}
	
	alert("Sorry, your browser isn\'t equiped to work with XMLHttpRequest.");
	return false;
}

// instanzia getXMLHTTP come alias di xmlHttpGen();
var getXMLHTTP = xmlHttpGen();

