// CROSS-APPLICATION FILE
// -----------------------------------------------------------------------------
// Utility functions for making requests and receiving responses via
// "XMLHttp" - so-called "AJAX".
// (This should probably have been implemented as a js "class"?)
// (*) NB: Although communication is always asynchronous, any page using these
//     functions must make ONLY ONE REQUEST AT A TIME - i.e. it must "chain"
//		 requests if more than one is needed at any point.
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// Set up globals:
// -----------------------------------------------------------------------------
// (*) Some basic "private" globals that this script needs internally:
var ajax_objRequest = null; // An XMLHttpRequest object or similar.
var ajax_fnResponseHandler; // The external function that will handle the
							// callback.
var ajax_processing = false; // So external code can warn user it is still
								// waiting for response if need be.
// (*) The following are all required internally so that we can keep an eye on
// requests - i.e. check if any seem to have gone unanswered, so we can kill
// them off an make a repeat request etc:
var ajax_maxWaitBeforeRepeatRequest_seconds = 2; // External code can
													// override this.
var ajax_maxNumberOfRepeatRequests = 2; // External code can override this.
// Remember how the last ajax request was made in case we need to re-try it:
var ajax_requestMadeAt = null; // Will be a js date object.
var ajax_currentRepeatRequestNumber = 0;
var ajax_timer = null;
var ajax_requestMethod = ""; // "GET" or "POST"
var ajax_URL = "";
var ajax_params = ""; // Only relevant if request method is POST
var ajax_currentRequestAborted = false;
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// "Public" functions, i.e. ones designed to be called by external code:
// -----------------------------------------------------------------------------
// This function is for making requests via "GET":
function ajax_getResponse(strURL, fnResponseHandler, bIsRepeatRequest)
{
	ajax_getResponse_internal("GET", strURL, "", fnResponseHandler, false)
}

// (*) This function is for making requests via "POST" (not sure of the
// advantage - can handle
// more, and more complex, data, possibly?):
function ajax_getResponse_POST(strURL, strParams, fnResponseHandler,
		bIsRepeatRequest)
{
	ajax_getResponse_internal("POST", strURL, strParams, fnResponseHandler,
			false)
}

// This function should be used when decoding a response from server if it is
// a json-encoded object / variable. If the response cannot be interpreted as
// javascript (usually the case if the server script had a problem and returned
// an error message), it will be alerted to user - potentially useful for debug:
function ajax_tryDecodeResponse(strCode)
{
	var retVal = null;
	try
	{
		retVal = eval("(" + strCode + ")");
		return retVal;
	} catch (e)
	{
		// The response was probably an error message from script on server,
		// rather than a json-ified object:
		alert("Unexpected response from server: " + strCode);
	}
}

// This functions is so external code knows if a request to server is still in
// progress:
function ajax_isProcessing()
{
	return ajax_processing;
}
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// "Private" functions, i.e. ones not designed to be accessed by external code:
// -----------------------------------------------------------------------------
function ajax_getResponse_internal(strMethod, strURL, strParams,
		fnResponseHandler, bIsRepeatRequest)
{
	// (*) Set globals vars to preserve the info for the call in case we need to
	// re-try it:
	ajax_requestMadeAt = new Date();
	ajax_requestMethod = strMethod;
	ajax_URL = strURL;
	ajax_params = strParams;
	if (typeof (bIsRepeatRequest) == "undefined")
		bIsRepeatRequest = false;
	if (!bIsRepeatRequest)
		ajax_currentRepeatRequestNumber = 0;
	ajax_fnResponseHandler = fnResponseHandler;

	// (*) If method is GET, add a timestamp to the URL QS to avoid cacheing
	// issues:
	if (strMethod == "GET")
	{
		var objDate = new Date();
		var strTimeStamp = objDate.getTime();
		strURL +=
				(strURL.indexOf("?") > -1 ? "&" : "?") + "_ajax_ts="
						+ strTimeStamp;
	}

	// (*) Get a request object suitable for the browser:
	ajax_getValidRequestObj();

	// (*) Use it to make the request:
	if (ajax_objRequest != null)
	{
		// Now set up a handler for the response - use the onreadystatechange
		// event:
		ajax_objRequest.onreadystatechange = _ajax_handleReadyStateChange;
		ajax_objRequest.open(strMethod, strURL, true)
		if (strMethod == "POST")
		{
			ajax_objRequest.setRequestHeader("Content-type",
					"application/x-www-form-urlencoded");
			ajax_objRequest.send(strParams);
		} else
			ajax_objRequest.send(null);
		// Register the fact a request is underway, and monitor it:
		ajax_processing = true;
		/*
		if (ajax_timer == null)
			ajax_timer = window.setInterval("_ajax_checkResponseTime()", 1000);
		*/
	}
}

function ajax_getValidRequestObj()
{
	// The exact object type to use for making the request will depened on
	// browser. This
	// code was stolen from a popular web page - tested on various browsers on
	// windows and mac:
	try
	{
		// Opera 8.0+, Firefox, Safari...
		ajax_objRequest = new XMLHttpRequest();
	} catch (e)
	{
		// Bang! So probably using Internet Explorer:
		try
		{
			ajax_objRequest = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e)
		{
			try
			{
				ajax_objRequest = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e)
			{
				alert("The browser, or current browser settings, do not support XMLHTTP. Operation denied.");
			}
		}
	}
}

function _ajax_handleReadyStateChange()
{
	// If the response is complete (and the request has not been flagged as
	// aborted), process it:
	if (ajax_objRequest.readyState == 4 && !ajax_currentRequestAborted) // 4 =
																		// load
																		// complete
	{
		ajax_processing = false;
		// Preserve the response text as we are about to nullify the request
		// object:
		var strResponseText = ajax_objRequest.responseText;
		ajax_objRequest = null;
		window.clearInterval(ajax_timer);
		ajax_fnResponseHandler(strResponseText);
	}
}

function _ajax_checkResponseTime()
{
	// This function will be called by a timer. It will check to see if a
	// current
	// request is underway, and if so, if it has taken too long for comfort:
	var objDate = new Date();
	if (ajax_objRequest != null
			&& (objDate - ajax_requestMadeAt) > ajax_maxWaitBeforeRepeatRequest_seconds * 1000)
	{
		// The abort method seems to result in the readyStateChange event being
		// fired, so we need to let our
		// code that handles that event know that the request that fired it is
		// one we want it to ignore:
		ajax_currentRequestAborted = true;
		// Use try...catch as only modern browsers support .abort():
		try
		{
			ajax_objRequest.abort();
		} catch (e)
		{
			// Don't alert error, but we need to be aware that we have not
			// aborted the current request and we are about to make a new one!
			// we will be nullifying the current request object, but will this
			// ensure it does not receive the pending response if it
			// comes late? It appears so, which is good!
		}
		ajax_currentRequestAborted = false;
		ajax_currentRepeatRequestNumber++;
		/*
		 * if (ajax_currentRepeatRequestNumber > ajax_maxNumberOfRepeatRequests) {
		 * ajax_objRequest = null; window.clearInterval(ajax_timer); alert("An
		 * attempt has been made to contact the server " +
		 * ajax_currentRepeatRequestNumber + " times with no success. It might
		 * be possible to resolve the problem by refreshing the page in your
		 * browser."); } else { // No longer making repeat requests - too
		 * dangerous! //ajax_getResponse_internal(ajax_requestMethod, ajax_URL,
		 * ajax_params, ajax_fnResponseHandler, true); }
		 */
	}
}
// -----------------------------------------------------------------------------


