/**
 * Initializes ajax support
 * @param isDev Whether or not this is development environment
 * @param url Url to retrieve content from
 */
function initAjax(isDev, url) {
    // validate url
    if (url == null || url == 'undefined' || url.length <= 0) {
        processError("No remove url specified", null, isDev);
        return;
    }

    // initialize xml http
    var xmlHttp = initialize();
    if (xmlHttp == null) {
        processError("Unable to set ActiveX object", null, isDev);
        return;
    }

    // async
    processAsync(xmlHttp, url, null, isDev);
}

/**
 * Retrieves content from remote url
 * @param divId Element to insert retrieved content
 * @param isDev Whether or not this is development environment
 * @param url Url to retrieve content from
 */
function getContent(divId, isDev, url) {
    // set div
    var div;
    if (divId != null && 0 < divId.length) {
        div = document.getElementById(divId);
    }

    // validate div
    if (div == null || div == 'undefined') {
        processError(("No element found with id " + divId), div, isDev);
        return;
    }

    // validate url
    if (url == null || url == 'undefined' || url.length <= 0) {
        processError("No remove url specified", div, isDev);
        return;
    }

    // initialize xml http
    var xmlHttp = initialize();
    if (xmlHttp == null) {
        processError("Unable to set ActiveX object", div, isDev);
        return;
    }

    // sync - temporary and should be moved to server side ASAP
    // will lock up some browsers until response is back
    processSync(xmlHttp, url, div, isDev);
}

/**
 * Initialize xml http object
 */
function initialize() {
    var xmlHttp;

    try {
        // Firefox, Opera 8.0+, Safari
        xmlHttp = new XMLHttpRequest();
    } catch (ex) {
        // Internet Explorer
        var HttpRequest;
        if (!HttpRequest) {
            HttpRequest = {};
        }
        if (!HttpRequest.prototype) {
            HttpRequest.prototype = {};
        }

        // Define a list of Microsoft XML HTTP ProgIDs.
        HttpRequest.prototype.MS_PROGIDS = new Array(
            "Microsoft.XMLHTTP",
            "Msxml2.XMLHTTP.6.0",
            "Msxml2.XMLHTTP.5.0",
            "Msxml2.XMLHTTP.4.0",
            "Msxml2.XMLHTTP.3.0",
            "Msxml2.XMLHTTP"
            );

        for (var i = 0; xmlHttp == null && i < HttpRequest.prototype.MS_PROGIDS.length; i++) {
            try  {
                xmlHttp = new ActiveXObject(HttpRequest.prototype.MS_PROGIDS[i]);
            } catch (e) {
                // ignore
            }
        }
    }

    return xmlHttp;
}

/**
 * Process request async
 * @param xmlHttp ActiveX object
 * @param url Remote url
 * @param div Div to insert result
 * @param isDev Dev flag
 */
function processAsync(xmlHttp, url, div, isDev) {
    var response;

    try {
        // background thread
        xmlHttp.onreadystatechange = function() {
            if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                if(div != null) {
                    // get and format response
                    response = getAndFormatResponse(xmlHttp, div, isDev);
                    // output response
                    div.innerHTML = response;
                    // force execute scripts
                    forceScripts(div, div, isDev);
                }
            }
        }

        // send request
        xmlHttp.open("GET", url, true);
        xmlHttp.send();
    } catch(e) {
        processError(e, div, isDev);
    }
}

/**
 * Process request sync
 * @param xmlHttp ActiveX object
 * @param url Remote url
 * @param div Div to insert result
 * @param isDev Dev flag
 */
function processSync(xmlHttp, url, div, isDev) {
    var timeout_;
    var response;
    
    try {
        // set a timeout
        timeout_ = setTimeout(function(){stopSync(xmlHttp)}, 5000);
        // send request
        xmlHttp.open("GET", url, false);
        xmlHttp.send(null);
        // get and format response
        response = getAndFormatResponse(xmlHttp, div, isDev);
        // output response
        document.write(response);
        // clear timeout
        clearTimeout(timeout_);
    } catch(e) {
        processError(e, div, isDev);
    }
}

/**
 * Stop the sync request after specified amount of time
 * @param xmlHttp Active X object
 * @param div Div
 * @param isDev Dev flag
 */
function stopSync(xmlHttp, div, isDev) {
    xmlHttp.abort();
    processError("Aborted", div, isDev);
}

/**
 * Get and format response from active x object
 * @param xmlHttp Active X object
 * @param div Div for error handling
 * @param isDev Dev flag
 */
function getAndFormatResponse(xmlHttp, div, isDev) {
    var response;

    try {
        // get response
        response = xmlHttp.responseText;
        // format response
        response = response.replace(/<noscript>.*?<\/noscript>/ig, ""); // duhhh! javascript is already running...
    } catch(e) {
        processError(e, div, isDev);
    }

    return response;
}

/**
 * Force run all scripts
 * @param element Element to run scripts on
 * @param div Original div element
 * @param isDev Dev flag
 */
function forceScripts(element, div, isDev) {
    // validate element
    if (element.nodeType != 1) {
        return;
    }

    if (element.tagName.toLowerCase() == 'script') {
        if (element.src) {
            try {
                eval(element);
            } catch (ex) {
                processError(ex, div, isDev);
            }
        }
        if (element.text) {
            if (/\.[\s|\+|\'|\"]*?write[\s|\+|\'|\"]*?\(/ig.test(element.text)) {
                processError("*.write not supported", div, isDev);
            } else {
                try {
                    eval(element.text); // run the script
                } catch (ex) {
                    processError(ex, div, isDev);
                }
            }
        }
    } else {
        var child = element.firstChild;
        while (child) {
            if (child.nodeType == 1) {
                forceScripts(child, div, isDev);
            }
            child = child.nextSibling;
        }
    }
}

/**
 * Displayes exception if development mode
 * @param error Exception to display
 * @param div Div element to display on
 * @param isDev Whether or not this is development mode
 */
function processError(error, div, isDev) {
    document.cookie = "js=0; path=/";
    
    if (isDev) {
        if (div == null) {
            alert(error);
        } else {
            div.innerHTML = "<b class='red'>"+error+"</b>";
        }
    }
}
