/**********************************************************************************************************************
 * Author: Joshua Carmody
 * Last Modified: 2009-04-22
 **********************************************************************************************************************
 * This file contains a number of functions designed to allow for asynchronous HTTP requests using Javascript.
 * For the most part, applications will need to call only one function, the startAjaxGetRequest function. 
 * Most of the other functions here are call each other and do not need to be used from the outside. An
 * example of simple usage is as follows:
 *
 * function handleMyRequestResponse(requestData, xmlHttpRequestObject)
 * {
 *     alert("The response was: " + xmlHttpRequestObject.responseText);
 * }
 *
 * startAjaxGETRequest("myrequest", "/ajaxpages/getinfo.cfm?id=1", null, handleMyRequestResponse);
 *
 * The code above will start a request to /ajaxpages/getinfo.cfm?id=1 , and call handleMyRequestResponse when done.
 * More info on this function can be found below, by the function definition.
 **********************************************************************************************************************/
 

var outstandingAjaxRequests = new Array(); // This variable maintains a queue of http requests that have not yet completed



/**********************************************************************************************************************
 * Provides a cross-browser way of obtaining an XMLHttpRequest object.
 **********************************************************************************************************************/
function getAjaxRequestObject() {
    var xmlHttp = null;
    try {
        xmlHttp = new XMLHttpRequest();
    }
    catch (e) {
        try {
            xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch (e2) {
            try {
                xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e3) {
                // Ajax attempt failure!
            }
        }
    }
    return xmlHttp;
}




/**********************************************************************************************************************
* This function is called first whenever and XMLHttpRequest returns. It checks all outstanding requests
* to see if they've completed successfully, and calls the appropriate response function if they have.
**********************************************************************************************************************/
// TODO: Add error handling code. What happens when a request ISN'T successful? Right now, nothing.
function handleCompletedAjaxRequest() {
    for (var i = 0; i < outstandingAjaxRequests.length; i++) {
        if (outstandingAjaxRequests[i].ajaxRequest.readyState == 4 && outstandingAjaxRequests[i].ajaxRequest.status == 200) {
            var handlerFunction = outstandingAjaxRequests[i].handlerFunction;
            var requestData = outstandingAjaxRequests[i].requestData;
            var ajaxRequest = outstandingAjaxRequests[i].ajaxRequest;
            outstandingAjaxRequests.splice(i, 1);
            i--;
            handlerFunction(requestData, ajaxRequest);
        }
    }
}



/**********************************************************************************************************************
* Adds an HTTP request to the list of incomplete requests.
**********************************************************************************************************************/
function addToAjaxQueue(ajaxRequestObject, requestName, requestData, handlerFunction) {
    outstandingRequest = new Object();
    outstandingRequest.requestName = requestName;
    outstandingRequest.requestData = requestData;
    outstandingRequest.handlerFunction = handlerFunction;
    outstandingRequest.ajaxRequest = ajaxRequestObject;
    var arrayIndex = outstandingAjaxRequests.length;
    if (requestName.length > 0) {
        for (var i = 0; i < outstandingAjaxRequests.length; i++) {
            if (outstandingAjaxRequests[i].requestName == requestName) {
                arrayIndex = i;
                outstandingAjaxRequests[i].ajaxRequest.onreadystatechange = null;
                outstandingAjaxRequests[i].ajaxRequest.abort();
            }
        }
    }
    outstandingAjaxRequests[arrayIndex] = outstandingRequest;
}



/**********************************************************************************************************************
* Add's a meaningless random parameter to the URL passed to it. Adding random data to URLs in this fashion prevents
* browsers, most notable Internet Explorer, from caching pages and returning old data.
**********************************************************************************************************************/
function uniqueUrl(passedUrl) {
    if (passedUrl.indexOf("?") > 0) {
        return passedUrl + "&ajaxcache=" + Math.floor(Math.random() * 100000000);
    }
    else {
        return passedUrl + "?ajaxcache=" + Math.floor(Math.random() * 100000000);
    }
}



/**********************************************************************************************************************
* Starts an HTTP Request using the GET protocol. This is the function that other scripts will call most often.
* Arguments:
*    requestName - If passed a string, the generated HTTP request will cancel and replace any existing unfinished 
*                   request with the same requestName value. This will prevent redundant or conflicting requests from
*                   interfering with each other. If this parameter is null, the new request will simple be added to
*                   the exisiting queue of requests without interfering with any current running ones.
*    url - The URL the request is sent to. Required.
*    requestData - This parameter doesn't do anything in this function, but it is passed back as the first parameter
*                   to the function specified in handlerFunction. If you need to pass something to your handler 
*                   (for example, a reference to a DOM element you want to affect with the results of the HTTP request),
*                   you can include it here. This parameter may be null, and in fact usually will be.
*    handlerFunction - A reference to a function that will be called when the HTTP request completes. This function 
*                       must accept 2 values. The first is the requestData object mentioned above. The second is the
*                       XMLHttpRequest object used to make the completed request. Most handler functions will use
*                       the XMLHttpRequest object's responseText or responseXML properties as their primary data source.
**********************************************************************************************************************/
function startAjaxGETRequest(requestName, url, requestData, handlerFunction) {
    var ajaxRequestObject = getAjaxRequestObject();
    if (ajaxRequestObject) {
        ajaxRequestObject.onreadystatechange = handleCompletedAjaxRequest;
        ajaxRequestObject.open("GET", uniqueUrl(url), true);
        addToAjaxQueue(ajaxRequestObject, requestName, requestData, handlerFunction);
        ajaxRequestObject.send(null);
    }
    else {
        alert("Looks like your browser isn't AJAX-capable.\nThis page may not function properly.");
    }
}




/**********************************************************************************************************************
* This function sends a POST HTTP request asynchronously. Otherwise, it works pretty much like the 
* startAjaxGetRequest function above, with one more parameter required - formElement.
* formElement is a reference to a FORM element in the DOM tree. The function will automatically traverse
* the DOM of any form element passed, and add all form fields as parameters to the request, in much the same
* way that an HTML INPUT[type=submit] element would function.
**********************************************************************************************************************/
function startAjaxPOSTRequestUsingForm(requestName, url, formElement, requestData, handlerFunction) {
    var ajaxRequestObject = getAjaxRequestObject();
    if (ajaxRequestObject) {
        var formData = "";
        if (formElement) {
            if (formElement.elements.length) {
                for (var i = 0; i < formElement.elements.length; i++) {
                    if (formElement.elements[i].type == "text" || formElement.elements[i].type == "hidden" || formElement.elements[i].type == "textarea") {
                        formData += (formData.length > 0 ? "&" : "") + escape(formElement.elements[i].name) + "=" + escape(formElement.elements[i].value);
                    }
                    else if (formElement.elements[i].type == "radio" || formElement.elements[i].type == "checkbox") {
                        if (formElement.elements[i].checked) {
                            formData += (formData.length > 0 ? "&" : "") + escape(formElement.elements[i].name) + "=" + escape(formElement.elements[i].value);
                        }
                    }
                    else if (formElement.elements[i].type == "select" || formElement.elements[i].type == "select-one") {
                        formData += (formData.length > 0 ? "&" : "") + escape(formElement.elements[i].name) + "=" + escape(formElement.elements[i].options[formElement.elements[i].options.selectedIndex].value);
                    }
                    else if (formElement.elements[i].type == "submit" || formElement.elements[i].type == "button" || formElement.elements[i].type == "image" || formElement.elements[i].type == "reset") {
                        // We're not doing anything with these at the moment, but you never know.
                    }
                    else {
                        alert("Warning: Support needed for " + formElement.elements[i].type + " form elements");
                    }
                }
            }
        }

        ajaxRequestObject.open("POST", uniqueUrl(url), true);
        ajaxRequestObject.onreadystatechange = handleCompletedAjaxRequest;
        ajaxRequestObject.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        ajaxRequestObject.setRequestHeader("Content-length", formData.length);
        ajaxRequestObject.setRequestHeader("Connection", "close");
        addToAjaxQueue(ajaxRequestObject, requestName, requestData, handlerFunction);
        ajaxRequestObject.send(formData);
    }
    else {
        alert("Looks like your browser isn't AJAX-capable.\nThis page may not function properly.");
    }
}




