/****
    xmlDownloader.js - Xiaowei Li - 2006/09/14
    Description:
        This file contains XMLDownloader and correlated classes
    Dependencies: 
        encryption.js
        exceptionHandler.js
        Microsoft.XMLHTTP
****/


/**
    XMLDownloader class - Xiaowei Li - 2006/08/14
    Purose: 
        XMLDownloader handles synchronouse and asynchronouse XML downloading.
    Inputs: 
        N/A
    Notes:
        XMLDownloader exposes 2 methods, download() and asyncDownload(), they handle
        synchronouse and asynchronouse XML downloading correspondingly.
    Outputs/Returns:
        N/A. If there's any error, an exception will be thrown.
**/
var XMLDownloader = (
    function XMLDownloader() {
        try {
            // CONSTRUCTOR

            function XMLDownloader() {}
            
            // CONSTANTS
            
            var MAX_RETRYS = 3;
            var READYSTATE_COMPLETED = 4;
            var HTTP_STATUS_OK = 200;
            
            // PUBLIC METHODS
            
            /**
                XMLDownloader.download() - Xiaowei Li - 2006/08/14
                Purose: 
                    Download XML synchronously
                Inputs: 
                    sUrl - XML URL
                Notes:
                    The function download XML from the given URL synchronously by using XMLHTTP.
                Outputs/Returns:
                    XMLDOM object. If there's any error, like HTTP status code is not 200 or XML
                    parsing error, an exception will be thrown.
            **/
            XMLDownloader.download = 
            function XMLDownloader_download(sUrl, bRawText, oPostData) {
                try {
                    bRawText = (true == bRawText);
                    
                    oPostData = oPostData ? oPostData : null;
                    
                    var sRequestMethod = oPostData ? "POST" : "GET";
                    var sPostData = oPostData ? oPostData.join("&") : null;
                    
                    // send request to server
                    var oHttpRequest = createXMLHttpRequest();
                    oHttpRequest.open(sRequestMethod, sUrl, false);
                    
                    // if post, need to set the correct headers
                    if (sRequestMethod.toUpperCase() == "POST"){
						oHttpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
						oHttpRequest.setRequestHeader("Content-length", sPostData.length);
						oHttpRequest.setRequestHeader("Connection", "close");
					}
					
                    oHttpRequest.send(sPostData);
                    
                    // validate response xml, an exception will be thrown if there's any error
                    _validateResponse(oHttpRequest, bRawText);

                    var oResponse = null;
                    if (bRawText) {
                        oResponse = oHttpRequest.responseText;
                    } else {                        
                        oResponse = oHttpRequest.responseXML;
                        //alert((new XMLSerializer()).serializeToString(oResponse));
                        //alert(oHttpRequest.responseText);
                        //alert(oResponse.xml);
                    }
                    
                    // dispose
                    oHttpRequest = null;
                    
                    return oResponse;
                } catch (e) {
                    //ExceptionHandler.handle(e);
                }
            }
            
            /**
                XMLDownloader.asyncDownload() - Xiaowei Li - 2006/08/14
                Purose: 
                    Download XML asynchronously
                Inputs: 
                    sUrl - XML URL
                    fnSucceededCallback - on download succeeded callback function
                    fnFailedCallback - on download failed callback function
                Notes:
                    The function download XML from the given URL asynchronously by using XMLHTTP.
                    If download succeeded, the callback function will be called, and XMLDOM object
                    will be passed in as parameter. If there's any connection error or XML parsing
                    error, 2 more retries will take place. If still fail after retries, on failed
                    callback function will be invoked and error message will be passed as parameter.
                Outputs/Returns:
                    The return value will always be null. If there's any error, an exception will
                    be thrown.
            **/
            XMLDownloader.asyncDownload = 
            function XMLDownloader_asyncDownload(sUrl, bRawText, fnSucceededCallback, fnFailedCallback, fnDataParser) {
                try {
                    var arCurUrl = document.location.href.match(new RegExp('^([a-z]+)://([^/]+)(.*)', 'i'));
                    var arReqUrl = sUrl.match(new RegExp('^([a-z]+)://([^/]+)(.*)', 'i'));
                    
                    if (arReqUrl && arCurUrl) {
                        if (arReqUrl[1].toLowerCase() == arCurUrl[1].toLowerCase() &&
                            arReqUrl[2].toLowerCase() == arCurUrl[2].toLowerCase())
                        {
                            // send request to server thru XmlHttpRequest
                            (new AsyncDownloader(sUrl, bRawText, fnSucceededCallback, fnFailedCallback, fnDataParser)).start();
                        } else {
                            // send request to server thru ScriptRequest
                            ScriptRequest.send(sUrl, bRawText, fnSucceededCallback, fnFailedCallback, fnDataParser);
                        }
                    } else {
                        throw ("Error: Invalid URL.");
                    }
                    
                    // return immediately if is async downloading
                    return null;
                } catch (e) {
                    //ExceptionHandler.handle(e);
                }
            }

            // PRIVATE METHODS
            
            /**
                _validateResponse() - Xiaowei Li - 2006/08/14
                Purose: 
                    Evaluates HTTP status code and XML parsing error.
                Inputs: 
                    oHttpRequest - XMLHTTP object
                Notes:
                    Caller should have a try..catch statement surround this function, so it can
                    capture any exception thrown by this function.
                Outputs/Returns:
                    N/A. If there's any error, an exception will be thrown.
            **/
            var _validateResponse =
            function XMLDownloader_validateResponse(oHttpRequest, bRawText) {
                // check status code
                if (oHttpRequest.status != HTTP_STATUS_OK) {
                    throw ("Error: " + oHttpRequest.status + ", " + oHttpRequest.statusText);
                }
                
                if (true != bRawText) {
                    // validate XML TODO: Firefox
//                    var iErrCode = oHttpRequest.responseXML.parseError.errorCode;
//                    if (0 != iErrCode) {
//                        throw ("Error: " + iErrCode + ", Download Failed.");
//                    }
                }
            }
            
            /**
                _onAsyncDownloadFinished() - Xiaowei Li - 2006/08/14
                Purose: 
                    This function will be called by AsyncDownloader when download finish.
                Inputs: 
                    oAsyncDownloader - AsyncDownloader object
                Notes:
                    The AsyncDownloader will invoke this function, and pass in itself as
                    paramenter.
                Outputs/Returns:
                    N/A. If there's any error, an exception will be thrown.
            **/
            var _onAsyncDownloadFinished =
            function XMLDownloader_onAsyncDownloadFinished(oAsyncDownloader) {
                try {
                    var oHttpRequest = oAsyncDownloader.httpRequest;
                    var fnSucceededCallback = oAsyncDownloader.fnSucceededCallback;
                    var fnFailedCallback = oAsyncDownloader.fnFailedCallback;
                    var fnDataParser = oAsyncDownloader.fnDataParser;
                    
                    // check status code
                    try {
                        // validateResponseXml will throw exception if there's any error
                        _validateResponse(oHttpRequest, oAsyncDownloader.isRawText);
                        
                        // get to this point means no http error or parsing error
                        try {
                            if (fnSucceededCallback) {
                                if (oAsyncDownloader.isRawText) {
                                    fnSucceededCallback(oHttpRequest.responseText);
                                } else {
                                    if (null != fnDataParser) {
                                        fnSucceededCallback(fnDataParser(oHttpRequest.responseXML));
                                    } else {
                                        fnSucceededCallback(oHttpRequest.responseXML);
                                    }
                                }
                            }
                        } finally {
                            oAsyncDownloader.dispose();
                        }
                    } catch (e) {
                        // retry 3 times
                        if (MAX_RETRYS > oAsyncDownloader.retryAttempts) {
                            oAsyncDownloader.start();
                            return;
                        }
                        
                        // invoke onFailed callback after 3 retries
                        try {
                            if (fnFailedCallback) { fnFailedCallback(e); }
                        } finally {
                            oAsyncDownloader.dispose();
                        }
                    }
                } catch (e) {
                    //ExceptionHandler.handle(e);
                }
            }
            
            // SUB CLASS
            
            /**
                AsyncDownloader class - Xiaowei Li - 2006/08/14
                Purose: 
                    AsyncDownloader handles asynchronouse downloading.
                Inputs: 
                    sUrl - URL of the XML to be downloaded
                    fnSucceededCallback - on download succeeded callback
                    fnFailedCallback - on download failed callback
                    fnDataParser - function to parse XML data
                Notes:
                    AsyncDownloader consumer need to dispose AsyncDownloader explicitly by invoking
                    dispose() method.
                    AsyncDownloader will not invoke fnSucceededCallback or fnFailedCallback when
                    download finish. XMLDownloader._onAsyncDownloadFinished is the one to be called
                    fnSucceededCallback and fnFailedCallback are just stored here to be used by
                    XMLDownloader._onAsyncDownloadFinished.
                    AsyncDownloader also maintains a retry attempts counter.
                Outputs/Returns:
                    N/A. If there's any error, an exception will be thrown.
            **/
            function AsyncDownloader(sUrl, bRawText, fnSucceededCallback, fnFailedCallback, fnDataParser) {
                try {
                    // PRIVATE PROPERTIES
                    
                    var me = this;  // alias of this object
                    
                    // PUBLIC PROPERTIES
                    
                    me.httpRequest = null;
                    me.url = sUrl;
                    me.isRawText = (true == bRawText);
                    me.fnSucceededCallback = fnSucceededCallback;   // callback
                    me.fnFailedCallback = fnFailedCallback;         // callback
                    me.fnDataParser = fnDataParser;             // data prasing function
                    me.retryAttempts = 0;                       // retry attempts counter
                    
                    // PUBLIC METHODS
                    
                    /**
                        AsyncDownloader.start() - Xiaowei Li - 2006/08/14
                        Purose: 
                            Start he async downloading
                        Inputs: 
                            N/A
                        Notes:
                            This function will append current timestamp into URL for retries, so
                            it can actually redo the downloading.
                        Outputs/Returns:
                            N/A. If there's any error, an exception will be thrown.
                    **/
                    this.start = 
                    function AsyncDownloader_start() {
                        try {
                            // send request to server
                            if (!me.httpRequest) {
                                me.httpRequest = createXMLHttpRequest();
                            }
                            
                            // form URL for retries, not the first time
                            var sUrl = me.url;
                            if (me.retryAttempts > 0) {
                                sUrl += (me.url.match(/\?/) == null ? "?" : "&") + new Date().getTime();
                            }
                            
                            me.httpRequest.open("GET", sUrl, true);
                            me.httpRequest.onreadystatechange = _onReadyStateChange;
                            me.httpRequest.send(null);

                            // increase retry counter
                            me.retryAttempts++;
                            
                            // return immediately if is async downloading
                            return null;
                        } catch (e) {
                            //ExceptionHandler.handle(e);
                        }
                    }
                    
                    /**
                        AsyncDownloader.dispose() - Xiaowei Li - 2006/08/14
                        Purose: 
                            Dispose XMLHTTP object
                        Inputs: 
                            N/A
                        Notes:
                            AsyncDownloader consumer need to dispose AsyncDownloader explicitly
                            by invoking this method. This function will release memory used by
                            XMLHTTP object.
                        Outputs/Returns:
                            N/A. No exception will be thrown.
                    **/
                    this.dispose =
                    function AsyncDownloader_dispose() {
                        try {
                            if (me.httpRequest) {
                                me.httpRequest = null;
                            }
                        } catch (e) {
                            ; // ignore
                        }
                    }

                    // PRIVATE METHODS
                    
                    /**
                        _onReadyStateChange() - Xiaowei Li - 2006/08/14
                        Purose: 
                            On ready state change callback of the XMLHTTP object.
                        Inputs: 
                            N/A
                        Notes:
                            This function will invoke XMLDownloader._onAsyncDownloadFinished when
                            downloading is completed.
                        Outputs/Returns:
                            N/A. An exception will be thrown if there's any error.
                    **/
                    var _onReadyStateChange =
                    function AsyncDownloader_onReadyStateChange() {
                        try {
	                        if (me.httpRequest.readyState == READYSTATE_COMPLETED) {
	                            _onAsyncDownloadFinished(me);
	                        }
	                    } catch (e) {
	                        //ExceptionHandler.handle(e);
	                    }
                    }
                } catch (e) {
                    //ExceptionHandler.handle(e);
                }
            }
            
            
            return XMLDownloader;
        } catch (e) {
            //ExceptionHandler.handle(e);
        }
    }
)();

var ScriptRequest = (
    function ScriptRequest() {
        try {
            function ScriptRequest() {}
            
            // global variables
            
            var me = this;  // alias of this object
            var m_arPendingRequests = {};
            var m_iCounter = 0;
            
            var m_oHead = document.getElementsByTagName("head").item(0);
            
            ScriptRequest.send = 
            function ScriptRequest_send(sUrl, bRawText, fnSucceededCallback, fnFailedCallback, fnDataParser) {
                var iId = (new Date()).getTime() + "" + m_iCounter++;
                
                var oScript = _buildScriptTag(sUrl, iId);
                
                m_arPendingRequests[iId] = {isRawText: bRawText,
                                            succeededCallback: fnSucceededCallback,
                                            failedCallback: fnFailedCallback, 
                                            scriptTag: oScript,
                                            dataParser: fnDataParser
                                           };
                m_oHead.appendChild(oScript);
            }
            
            ScriptRequest.onDataReady = 
            function ScriptRequest_onDataReady(iId, sResponse) {
                try {
                    var oPendingRequest = m_arPendingRequests[iId];
                    delete m_arPendingRequests[iId]
                    
                    // retrieve callback
                    var fnSucceededCallback = oPendingRequest.succeededCallback;
                    if (fnSucceededCallback) {
                        if (oPendingRequest.isRawText) {
                            fnSucceededCallback(sResponse);
                        } else {
                            var oXml = _loadXmlText(sResponse);
                            
                            if (null != oPendingRequest.dataParser) {
                                fnSucceededCallback(oPendingRequest.dataParser(oXml));
                            } else {
                                fnSucceededCallback(oXml);
                            }
                        }
                    }
                    
                    if (null != oPendingRequest.scriptTag) {
//                        m_oHead.removeChild(oPendingRequest.scriptTag);
                    }
                } catch (e) {
                    //ExceptionHandler.handle(e);
                }
            }
            
            var _loadXmlText =
            function ScriptRequest_loadXmlText(sXmlText) {
                var oXml = null;
                
                if (window.ActiveXObject) {
                    // code for IE
                    oXml = new ActiveXObject("Microsoft.XMLDOM");
                    oXml.async = "false";
                    oXml.validateOnParse = false;
                    oXml.resolveExternals = false;
                    oXml.loadXML(sXmlText);
                } else {
                    // code for Mozilla, Firefox
                    var oParser=new DOMParser();
                    
                    oXml = oParser.parseFromString(sXmlText, "text/xml");
                }
                
                return oXml;
            }

            
            var _buildScriptTag = 
            function ScriptRequest_buildScriptTag(sUrl, iId) {
                try {
                    if (sUrl.indexOf("?") >= 0) {
                        sUrl = sUrl + '&srid=';
                    } else {
                        sUrl = sUrl + '?srid=';
                    }
                    
                    sUrl += iId;
                    
                    var oSpan = document.createElement("SPAN");
                    
                    // Create the script tag
                    var oScript = document.createElement("script");
                    
                    // Add script object attributes
                    oScript.setAttribute("type", "text/javascript");
                    oScript.setAttribute("src", sUrl);
                    oScript.setAttribute("id", iId);
                    
                    return oScript;
                } catch (e) {
                    //ExceptionHandler.handle(e);
                }
            }
            
            var _removeScriptTag =
            function ScriptRequest_removeScriptTag(oScript) {
                try {
                    m_oHead.removeChild(oScript);
                } catch (e) {
                    //ExceptionHandler.handle(e);
                }
            };
            
            return ScriptRequest;
        } catch (e) {
            //ExceptionHandler.handle(e);
        }
    }
) ();

var xmlDownloader_scriptload = true; // define variable for ScriptChecker check.