/**
 * This file provides basic asynchronous Ajax functionality.  'doAjaxCall()'
 * is called to fire off an Ajax call, and the 'callback' parameter is the
 * function that is called when the response is received.  There is an option
 * to parse the response into a two-dimentional array, for easier reading.
 */

/**
 * Pass one of these to 'doAjaxCall()' to specify the expected response format.
 * The "XML" and "TEXT" formats correspond directly to the 'responseText' and
 * 'responseXML' properties of the XMLHttpRequest interface.
 *
 * The "ARRAY" format parses the response text much like CSV input, according
 * to an predefined format, with each line becoming a first-level element in
 * a two-dimensional array, which makes it easy for the callback to deal with
 * it.  Each line may contain its own number of elements, a la JavaScript, so
 * don't expect it to be uniform as in Java.  An extra value may be added to
 * certain lines for populating a SELECT, for example, which could indicate
 * which OPTIONs are to be selected.
 */
var AJAX_RESPONSE_XML = 1;
var AJAX_RESPONSE_TEXT = 2;
var AJAX_RESPONSE_ARRAY = 3;
var ERROR_FLAG = "<!-- ERROR -->";
var ERROR_URL = "error";



/**
 * Submit an asynchronous AJAX request to the given URL, returning immediately.
 * The response is parsed according to 'responseFormat', and an appropriate
 * object is passed to the specified callback method.  The default format (if
 * 'responseFormat' is omitted) is the array, in which case an array is passed
 * to the callback.
 *
 * @param url the URL for the AJAX call
 * @param callback the JavaScript callback function to use
 * @param responseFormat one of the "constant" format codes defined above
 */
function doAjaxCall(url, callback, responseFormat) {
    var ajaxRequest = null;
    if (window.XMLHttpRequest)
        ajaxRequest = new XMLHttpRequest();
    else if (window.ActiveXObject)
        ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
    var ajaxResponseFormat = responseFormat ? responseFormat : AJAX_RESPONSE_ARRAY;
    ajaxRequest.open("GET", url, true);
    ajaxRequest.onreadystatechange = function doAjaxResponse() {
        if (!ajaxRequest)
            throw "An AJAX callback was received, but the global AJAX request object was null.";
        if (ajaxRequest.readyState != 4)
            return;
        if (ajaxRequest.status != 200)
            throw "An AJAX call to '" + url + "' failed with HTTP status " + ajaxRequest.status + ".";
        if (!testForError(ajaxRequest) && callback)
            switch (ajaxResponseFormat) {
                case AJAX_RESPONSE_XML:
                    callback(ajaxRequest.responseXML);
                    break;
                case AJAX_RESPONSE_TEXT:
                    callback(ajaxRequest.responseText);
                    break;
                case AJAX_RESPONSE_ARRAY:
                    callback(parseArray(ajaxRequest.responseText));
                    break;
                default:
                    throw "An AJAX request specified an unknown format, '" + responseFormat + "'.";
            }
    };
    ajaxRequest.send(null);
}

/**
 * Examine the text of the Ajax response for a certain error flag, and return
 * true iff it is found.  When this happens, the callback method is not
 * invoked, and instead the browser is sent to an error page.  I'm not sure
 * this is safe generally, since reading the text may not allow us to read the
 * XML afterward.
 */
function testForError(ajaxRequest) {
    var text = ajaxRequest.responseText;
    if ((text.length >= ERROR_FLAG.length)
            && (text.substring(text.length - ERROR_FLAG.length) == ERROR_FLAG)) {
//        window.top.document.location = ERROR_URL;
        window.document.location = ERROR_URL;
        return true;
    }
    return false;
}


/**
 * Parse the text of an AJAX response into an array of strings, according to
 * a predefined format.  This is used for the AJAX_RESPONSE_ARRAY format.
 *
 * @param text the raw text of the AJAX response (or null if the it was empty)
 */
function parseArray(text) {
    var array = new Array();
    if (text) {
        var lines = text.split("\n");
        var index1 = 0;
        var index2 = 0;
        for (var i = 0; i < lines.length; i++) {
            lines[i] = trimTrailingWhitespace(lines[i]);
            if (lines[i].length == 0)
                continue;
            array[i] = new Array();
            index1 = 0;
            index2 = 0;
            for (var j = 0; index2 != -1; j++) {
                index2 = lines[i].indexOf("|", index1);
                if (index2 == -1) {
                    array[i][j] = lines[i].substring(index1);
                    index2 = -1;
                }
                else {
                    array[i][j] = lines[i].substring(index1, index2);
                    index1 = index2 + 1;
                }
            }
        }
    }
    return array;
}


/**
 * Trim off any trailing whitespace in 'value' and return the result.  This
 * is needed because a) IEeeeee! seems to include the carriage returns of lines
 * split using the built-in 'split()' function and b), I can't find a working
 * 'trim()' function in JavaScript.
 *
 * @param value any string
 * @return the string without any trailing whitespace
 */
function trimTrailingWhitespace(value) {
    if (value.length < 1)
        return value;
    var lastChar = value.charAt(value.length - 1);
    if ((lastChar != '\n') && (lastChar != '\r'))
        return value;
    return trimTrailingWhitespace(value.substring(0, value.length - 1));
}
