/*
================================================================================
 .............     ......                                                       
    ....   .....     ....                                                       
    ....    ....     ....                                                       
    ....     ....    ....                                                       
    ....     ....    ....                                                       
    ....     ....    ....                                                       
    ....    ....     ....      ......        ......... .....  .....     ...  .  
    ....   ....      ....    ..   ....     .... ....    ....   ....    ..  ...  
    ..........       ....   ...    ....   ....   ....   ....   ....   ...   ..  
    ....   .....     ....   ....   ....   ....   ....   ....   ....   ....   .  
    ....     ....    ....    ..    ....   ....   ....   ....   ....   ......    
    ....     .....   ....        ......   ....   ....   ....   ....    ......   
    ....     .....   ....      ..  ....   ....   ...    ....   ....      .....  
    ....     .....   ....    ...   ....    .... ...     ....   ....       ..... 
    ....     .....   ....   ....   ....     ......      ....   ....   .    .... 
    ....    .....    ....   ....  .....    .            ....  .....   ..    ... 
    ....   .....     ....   ...... .....  ..            ...........   ...   ..  
 .............     ........  ....   ...   ..........     ..... .....  .  ....   
                                          ............                          
                                            ...........                         
                                           ..        ..                         
                                           ..        ..                         
                                           ...      ..                          
                                             .......                            
================================================================================
H T T P       R e q u e s t       f o r        A J A X       o p e r a t i o n s
================================================================================
Usage: To begin the sequence, call requestDocument with your XML/text generating 
application. executeOnReady is the handling function for the response.  
executeOnReady should make a call to getDocument to get the response document.
postMethod is the method of the request, like the method attribute of an form 
element in a page.

<!> executeOnReady must be declared without quotes (e.g. myFunction) if no 
    parameter is declared. If a parameter must be declared, or if you want use 
    your function with parentesis (e.g. "myFunction();" or 
                                       "myFunction('my parameters goes here');")

This section of code must be thought of as a concurrent environment.  We only 
want one XMLHttpRequest running at a time, otherwise requests can get mixed up.
A binary semaphore is used to attempt to ensure this.  
                                        
Sample 1 (great for deal with XML objects):
ŻŻŻŻŻŻŻŻ
function requestData(){
	requestDocument("myXML.xml",executeWhenDone);
}
function executeWhenDone() {
	var requestedXML = getDocument(); 
	if ( requestedXML != null ) {
		alert(requestedXML.getElementsByTagName("result").length); // your code...
	}
}
                                        
Sample 2 (great to dinamically insert contents in a rendered page):
ŻŻŻŻŻŻŻŻ
function requestData(){
	requestDocument("myHTML.html",executeWhenDone);
}
function executeWhenDone() {
	var requestedText = getDocument("text"); // "text" is used to parse the
	                                         // incoming stream into a plan one 
	                                         // (not a XML-tree)
	if ( requestedText != null ) {
		myDiv.innerHTML = requestedText; // your code...
	}
}
                                        
Sample 3 (performing a function with parameters):
ŻŻŻŻŻŻŻŻ
function requestData(){
	requestDocument("myXML.xml","executeWhenDone('my parameter')");
}
function executeWhenDone(incomingParameter) {
	var requestedXML = getDocument(); 
	if ( requestedXML != null ) {
		alert(requestedXML.getElementsByTagName("result").length); // your code...
		alert(incomingParameter); // will show a alert box with "my parameter"
	}
}
                                        
Sample 4 (performing post requests):
ŻŻŻŻŻŻŻŻ
function requestData(){
	requestDocument("myXML.xml?x=1&y=2",executeWhenDone, "post");
}
function executeWhenDone() {
	var requestedXML = getDocument(); 
	if ( requestedXML != null ) {
		alert(requestedXML.getElementsByTagName("result").length); // your code...
	}
}
________________________________________________________________________________
ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
*/
// Library settings
// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
var httpRequestDebugMode =           0; // debug mode for this lib (only 0 or 1)
var httprequestHighlightColor = "#57b"; // hrTrace highlight color

// Global variables (not for edition)
// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
var requestQueue   =       new Array(); // queue of requests
var noRequests     =              true; // binary semaphore
var hrTraceMessage =             false; // display enableHRmessage

// Enable/disable this lib tracing
if (httpRequestDebugMode) { 
	hrTrace = trace; 
} else { 
	hrTrace = function(){
		if (!hrTraceMessage) {
			var enableHRmessage = "";
			enableHRmessage += "HTTP Request debug messages are disabled. ";
			enableHRmessage += "Click ";
			enableHRmessage += "<a href=\"javascript:void(hrTrace=trace);void(httpRequestDebugMode=1);\">";
			enableHRmessage += "here";
			enableHRmessage += "</a>";
			enableHRmessage += " to enable then.";
			trace(enableHRmessage);
			hrTraceMessage = true;
		}
	}; 
}

/*
________________________________________________________________________________
ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
Purpose: Add an XMLHttpRequest to the queue.  Please use this rather than 
         loadDocument directly (Unless you want weird behaviour where your 
         requests get mixed up.)
Inputs:  requestedURL:   A valid URL to call, e.g.: http://domain/myfile.xml
         executeOnReady: A function to be called when the requested URL is ready.
                         Read more about it above.
         postMethod:     string, get|post. "get" is the default value.
Return:  none
*/
function requestDocument(requestedURL, executeOnReady, postMethod) {
	hrTrace("Function requestDocument(" + getInfo(arguments) + ")", httprequestHighlightColor);
	requestQueue.push({requestedURL: requestedURL, executeOnReady: executeOnReady, postMethod:postMethod});
	hrTrace("requestQueue.length: <b>" + requestQueue.length + "</b>");
	// Check to see if you're the first to arrive, and the waiting room is empty.
	if (noRequests) {
		var thisRequest = requestQueue.pop();
		loadDocument(thisRequest.requestedURL, thisRequest.executeOnReady, thisRequest.postMethod);
	}
} // requestDocument
/*
________________________________________________________________________________
ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
Purpose: Make an XMLHttpRequest call if possible (depends on the browser).
Inputs:  requestedURL:   URL of the xml file. Can be relative. 
                         e.g.: requestxml_full.xml
         executeOnReady: A function to be called when the requested URL is ready.
                         Read more about it above.
         postMethod:     string, get|post. "get" is the default value.
Return:  none
*/
function loadDocument(requestedURL, executeOnReady, postMethod) {
	hrTrace("Function loadDocument(" + getInfo(arguments) + ")", httprequestHighlightColor);
	noRequests = false;
	
	// default posting method
	if (postMethod == undefined) {
		postMethod = "get";
	} else if (postMethod.toLowerCase() == "post") {
		postMethod = "post";
	} else {
		postMethod = "get";
	}
	hrTrace("postMethod: <b>" + getInfo(postMethod) + "</b>");
	
	if (postMethod == "post") {
		hrTrace("Use <b>post</b> method");
		if ( requestedURL.indexOf("?") > -1 ) { 
			// retrieve querystring from the requested URL to the post variable
			hrTrace("There <b>is</b> querystring data");
			var postData = requestedURL.substr(requestedURL.indexOf("?")+1);
			hrTrace("postData: <b>" + getInfo(postData) + "</b>");

			// remove querystring from the requested URL
			requestedURL = requestedURL.substr(0, requestedURL.indexOf("?"));
			hrTrace("requestedURL: <b>" + getInfo(requestedURL) + "</b>");
		} else {
			hrTrace("There <b>is not</b> querystring data");
		}
	} else {
		hrTrace("Use <b>get</b> method");
	}
	
	// To prevent IE from cacheing the XML document returned from this, append
	// a timestamp to the URL to ensure that it is unique.  The page itself 
	// doesn't actually use the time for anything.
	var failsafeRandom = "rnd=" + Math.floor(Math.random() * 0xFFFFFF).toString(16);
	
	if ( requestedURL.indexOf("?") < 0 ) { 
		hrTrace("Add \"<b>?</b>\" to the URL");
		var finalURL = requestedURL + "?" + failsafeRandom; 
	} else { 
		hrTrace("Add \"<b>&</b>\" to the URL");
		var finalURL = requestedURL + "&" + failsafeRandom; 
	}

	hrTrace("requestedURL: <b>" + getInfo(requestedURL) + "</b>");
	hrTrace("finalURL: <b>" + getInfo(finalURL) + "</b>");
	hrTrace("File requested: <b><a href=\"" + finalURL + "\" target=\"_blank\">" + finalURL + "</a></b>" ); 
 
	if (window.XMLHttpRequest) {
		// branch for native XMLHttpRequest object
		hrTrace("branch for <b>native XMLHttpRequest object</b>");
		httpRequest = new XMLHttpRequest();
		
		// execute the onready function
		hrTrace("typeof(executeOnReady): <b>" + typeof(executeOnReady) + "</b>");
		if ( typeof(executeOnReady) == "function" ) {
			httpRequest.onreadystatechange = executeOnReady;
		} else if ( typeof(executeOnReady) == "string" ) {
			httpRequest.onreadystatechange = function () { eval(executeOnReady); };
		}
		
		// send the request
		if (postMethod == "post") {
			hrTrace("<b>Send</b> request via <b>post</b>");
			httpRequest.open("POST", finalURL, true);
			httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			httpRequest.send(postData);
		} else {
			hrTrace("<b>Send</b> request via <b>get</b>");
			httpRequest.open("GET", finalURL, true);
			httpRequest.send(null);
		}

	} else if (window.ActiveXObject) {
		// branch for IE/Windows ActiveX version
		hrTrace("branch for <b>IE/Windows ActiveX version</b>");
		httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
		if (httpRequest) {
			
			// execute the onready function
			hrTrace("typeof(executeOnReady): <b>" + typeof(executeOnReady) + "</b>");
			if ( typeof(executeOnReady) == "function" ) {
				httpRequest.onreadystatechange = executeOnReady;
			} else if ( typeof(executeOnReady) == "string" ) {
				httpRequest.onreadystatechange = function () { eval(executeOnReady); };
			}
			
			// send the request
			if (postMethod == "post") {
				hrTrace("<b>Send</b> request via <b>post</b>");
				httpRequest.open("POST", finalURL, true);
				httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
				httpRequest.send(postData);
			} else {
				hrTrace("<b>Send</b> request via <b>get</b>");
				httpRequest.open("GET", finalURL, true);
				httpRequest.send(null);
			}
		}
	} else {
		window.alert("This browser does not support HTTP requests" + "\n" + "Please contact technical support");
	}
} // loadDocument
/*
________________________________________________________________________________
ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
Purpose: Return the HTTP response.
Inputs:  forceMIMEto: if ommited, will retun a XML object.
                      if "text", will return a text string containing the 
                      requested file contents
Return:  - null if it is not yet ready or if there is 
                an error in the request/response or;
         - an XML object or a text string containing the requested file contents
*/
function getDocument(forceMIMEto) {
	hrTrace("Function getDocument(" + getInfo(arguments) + ")", httprequestHighlightColor);
	if (httpRequest.readyState == 0) { hrTrace("Request state: <b>0: uninitialized...</b>"); }
	if (httpRequest.readyState == 1) { hrTrace("Request state: <b>1: loading...</b>");       }
	if (httpRequest.readyState == 2) { hrTrace("Request state: <b>2: loaded...</b>");        }
	if (httpRequest.readyState == 3) { hrTrace("Request state: <b>3: interactive...</b>");   }
	if (httpRequest.readyState == 4) { hrTrace("Request state: <b>4: complete...</b>");      }
	if (httpRequest.readyState == 4) { 
		// Ensures that we return this request rather than some other one
		// that slips through the semaphore between the semaphore release
		// and the return (basically returning the same request twice and
		// skipping this one).
		var tempHttpRequest = httpRequest;
		if (requestQueue.length > 0) {
			var thisRequest = requestQueue.pop();
			loadDocument(thisRequest.requestedURL, thisRequest.executeOnReady);
		} else {
			noRequests = true;
		}
		
		hrTrace("tempHttpRequest.status: <b>" + tempHttpRequest.status + "</b>");
		hrTrace("tempHttpRequest.statusText: <b>" + tempHttpRequest.statusText + "</b>");
		hrTrace("tempHttpRequest.getAllResponseHeaders(): <br /><b>" + tempHttpRequest.getAllResponseHeaders() + "</b>");
		
		if (tempHttpRequest.status) { 
			
			hrTrace(
				"Requested file contents:" +
				"<code>" + 
				tempHttpRequest.responseText.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\t/g, " ") + 
				"</code>"
			);
			
			// get http status
			var tempHttpRequestStatus = tempHttpRequest.status;
			// process the request even if errors occours
			var processAnyway = false;

			if (tempHttpRequestStatus != 200) {
				// it may be a 404 or 403 or 500... who knows ?
				var alertMessage = "";
				alertMessage += "An error occurred while processing request:" + "\n";
				alertMessage += "HTTP" + tempHttpRequest.status;
				// Some browsers (as Opera) does not have the statusText property
				if ( tempHttpRequest.statusText != undefined ) {
					alertMessage += ": " + tempHttpRequest.statusText;
				}
				alertMessage += "\n";
				alertMessage += "Please contact technical support";
				window.alert( alertMessage );
				// show message if debug is enabled
				if (httpRequestDebugMode) {processAnyway = true;}
			}
			
			// Finish the job
			if (tempHttpRequestStatus == 200 || processAnyway) {
				hrTrace("Inserting requested file contents");
				// process request
				if ( forceMIMEto == "text" ) {
					// return a text
					return tempHttpRequest.responseText;
				} else {
					// return a XML
					return tempHttpRequest.responseXML.documentElement;
				}
			} else {
					return "";
			}
		}
	}
	
	// if not ready yet...
	return null;
} // getDocument
/*
________________________________________________________________________________
ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
*/

