
// ======================================================================
// Class: CS_ScriptInclude - Makes HTTP request using dynamically generated script tags inserted into DOM.
//                           First construct object, then build script object, then add to DOM.
//                           When used with JSON, the returned javascript may contain a call back function
//                           wrapping the data, i.e. functionName({data});
//
// Public Methods:
//   - buildScriptTag: Generates script tag object.
//   - addScriptTag: Adds script object to DOM. Tag must be built first.
//   - removeScriptTag: Removes script object from DOM.
//
// === Constructor ===
// @param url Request url of remote javascript file.
function CS_ScriptInclude(url) {
    this.fullUrl = url; 
    this.noCacheIE = "?noCacheIE=" + (new Date()).getTime();         // Keep IE from caching requests
    this.headLoc = document.getElementsByTagName("head").item(0);    // Get the DOM location to put the script tag
    this.scriptId = "JscriptId" + CS_ScriptInclude.scriptCounter++;       // Generate a unique script tag id
}

// === Class Variables ===
CS_ScriptInclude.scriptCounter = 1;

// === Member Functions ===
// buildScriptTag - Generates script tag object
CS_ScriptInclude.prototype.buildScriptTag = function () {
    this.scriptObj = document.createElement("script");
    this.scriptObj.setAttribute("type", "text/javascript");
    this.scriptObj.setAttribute("charset", "utf-8");
    this.scriptObj.setAttribute("src", this.fullUrl + this.noCacheIE);
    this.scriptObj.setAttribute("id", this.scriptId);
}
 
// removeScriptTag - Removes script object from DOM.
CS_ScriptInclude.prototype.removeScriptTag = function () {
    this.headLoc.removeChild(this.scriptObj);  
}
 
// addScriptTag - Adds script object from DOM.
CS_ScriptInclude.prototype.addScriptTag = function () {
    this.headLoc.appendChild(this.scriptObj);
}
// ======================================================================


// === HTTP Request ===
var CS_AJAX_OBJECT_COUNTER = 0;

// CS_getAJAX - Generates an http call and hands off request object to the handler.
// @param url Url of http request.
// @param handler Function name that will handle http data. oHttp passed into handler. Handler should test readyState and status.
//                Data available using the oHttp.responseText/oHttp.responseXML.
// @return true/false Success value of http call.
function CS_getAJAX(url, handler) {
    var oHttp = CS_getHttpObject();
    if (!oHttp) { return false; }     // Request object failed to instantiate. Unsupported browser.

    CS_AJAX_OBJECT_COUNTER++;
    eval("oHttp.onreadystatechange = function() { " + handler + "(oHttp); }");
    oHttp.open("GET", url);
    oHttp.send(null);

    return true;
}

// CS_getHttpObject - Attempts to create HTTP object in a variety of ways.
// @return XMLHTTP object.
function CS_getHttpObject() {
    var req = null;

    try {
        req = new XMLHttpRequest();
    } catch(e) {
        try {
            req = new ActiveXObject("Msxml2.XMLHTTP");
        } catch(e) {
            try {
                req = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (err3) {
            }
        }
    }

    return req;
}


// === XML to JSON Parsing ===
// parseXml - Takes xml (string format), parses, and returns a XML node.
// @param xml XML data as string.
// @return XML object.
function parseXml(xml) {
    var dom = null;
    if (window.DOMParser) {
        try { 
            dom = (new DOMParser()).parseFromString(xml, "text/xml"); 
        } catch (e) {
            dom = null;
        }
    } else if (window.ActiveXObject) {
        try {
            dom = new ActiveXObject('Microsoft.XMLDOM');
            dom.async = false;

            // parse error
            if (!dom.loadXML(xml)) { window.alert(dom.parseError.reason + dom.parseError.srcText); }
        } catch (e) {
            dom = null;
        }
    } else {
        alert("Can not parse xml string!");
    }

    return dom;
}

// xmlToJSON - Converts xml (string) to JSON data.
// @param xml XML data (as string).
// @param tab ???
// @return JSON object (as string).
function xmlToJSON(xml, tab) {
    var X = {
        // Transforms xml node to data array for JSON processing
        toObj: function(xml) {
            var o = {};
            if (xml.nodeType == 1) {             // element node ..
                if (xml.attributes.length) {     // element with attributes  ...
                    for (var i = 0; i < xml.attributes.length; i++) o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString();
                }
                if (xml.firstChild) {        // element has child nodes ...
                    var textChild = 0, cdataChild = 0, hasElementChild = false;
                    for (var n = xml.firstChild; n; n = n.nextSibling) {
                        if (n.nodeType == 1) {
                            hasElementChild = true;
                        } else if (n.nodeType == 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
                            textChild++;    // non-whitespace text
                        } else if (n.nodeType == 4) {
                            cdataChild++;    // cdata section node
                        }
                    }

                    if (hasElementChild) {
                        if (textChild < 2 && cdataChild < 2) { // structured element with evtl. a single text or/and cdata node ..
                            X.removeWhite(xml);
                            for (var n=xml.firstChild; n; n=n.nextSibling) {
                                if (n.nodeType == 3) {  // text node
                                    o["#text"] = X.escape(n.nodeValue);
                                } else if (n.nodeType == 4) { // cdata node
                                    o["#cdata"] = X.escape(n.nodeValue);
                                } else if (o[n.nodeName]) {  // multiple occurence of element ..
                                    if (o[n.nodeName] instanceof Array) {
                                        o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
                                    } else {
                                        o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
                                    }
                                } else {  // first occurence of element..
                                    o[n.nodeName] = X.toObj(n);
                                }
                             }
                        } else { // mixed content
                            if (!xml.attributes.length) {
                                o = X.escape(X.innerXml(xml));
                            } else {
                                o["#text"] = X.escape(X.innerXml(xml));
                            }
                        }
                    } else if (textChild) { // pure text
                        if (!xml.attributes.length) {
                            o = X.escape(X.innerXml(xml));
                        } else {
                            o["#text"] = X.escape(X.innerXml(xml));
                        }
                    } else if (cdataChild) { // cdata
                        if (cdataChild > 1) {
                            o = X.escape(X.innerXml(xml));
                        } else {
                            for (var n = xml.firstChild; n; n = n.nextSibling) o["#cdata"] = X.escape(n.nodeValue);
                        }
                    }
                }

                if (!xml.attributes.length && !xml.firstChild) o = null;
             } else if (xml.nodeType == 9) { // document.node
                 o = X.toObj(xml.documentElement);
             } else {
                 alert("unhandled node type: " + xml.nodeType);
             }

             return o;
        },

        // Transforms json data into final JSON object
        toJson: function(o, name, ind) {
            var json = name ? ("\"" + name + "\"") : "";
            if (o instanceof Array) {
                for (var i = 0, n = o.length; i < n; i++) o[i] = X.toJson(o[i], "", ind + "\t");
                json += (name ? ":[" : "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]";
            } else if (o == null) {
                json += (name && ":") + "null";
            } else if (typeof(o) == "object") {
                var arr = [];
                for (var m in o) arr[arr.length] = X.toJson(o[m], m, ind + "\t");
                json += (name ? ":{" : "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}";
            } else if (typeof(o) == "string") {
                json += (name && ":") + "\"" + o.toString() + "\"";
            } else {
                json += (name && ":") + o.toString();
            }

            return json;
        },


        // Returns back inner XML for an element as is (no more processing of xml subnodes).
        innerXml: function(node) {
            var s = "";
            if ("innerHTML" in node) {
                s = node.innerHTML;
            } else {
                var asXml = function(n) {
                                var s = "";
                                if (n.nodeType == 1) {
                                    s += "<" + n.nodeName;
                                    for (var i = 0; i < n.attributes.length; i++) s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\"";
                                    if (n.firstChild) {
                                        s += ">";
                                        for (var c = n.firstChild; c; c = c.nextSibling) s += asXml(c);
                                        s += "</" + n.nodeName + ">";
                                    } else {
                                        s += "/>";
                                    }
                                } else if (n.nodeType == 3) {
                                    s += n.nodeValue;
                                } else if (n.nodeType == 4) {
                                    s += "<![CDATA[" + n.nodeValue + "]]>";
                                }
                                return s;
                            };
                for (var c = node.firstChild; c; c = c.nextSibling) s += asXml(c);
            }
            return s;
        },

        // Replaces special characters with escape values. Protects from js retrieval.
        escape: function(txt) {
            return txt.replace(/[\"]/g, '\\"')
            .replace(/[\n]/g, '\\n')
            .replace(/[\r]/g, '\\r')
            .replace(/[\\]/g, "\\\\");
        },

        // Cleans out whitespace from XML object.
        removeWhite: function(e) {
           e.normalize();
           for (var n = e.firstChild; n; ) {
               if (n.nodeType == 3) {         // text node
                   // pure whitespace text node (garbage)
                   if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
                       var nxt = n.nextSibling;
                       e.removeChild(n);
                       n = nxt;
                   } else {
                       n = n.nextSibling;
                   }
               } else if (n.nodeType == 1) {  // element node
                   X.removeWhite(n);
                   n = n.nextSibling;
               } else {                       // all other nodes
                   n = n.nextSibling;
               }
            }

            return e;
        },

        // Debugging output of XML object.
        debug: function(e) {
            var output = "";
            while (e) {
                var nodeType = e.nodeType;
                var nodeName = e.nodeName;
                var nodeValue = "";

                if (nodeType == 1) {
                    var attrVals = "";
                    for (var i = 0; i < e.attributes.length; i++) {
                        var attrName = e.attributes[i].nodeName;
                        attrVals += " " + attrName + "=\"" + e.getAttribute(attrName) + "\""; 
                    }

                    output += "<" + e.nodeName + attrVals + ">";
                } else if (nodeType == 3) {
                    output += e.nodeValue;
                } else if (nodeType == 4) {
                    output += "<![CDATA[" + e.nodeValue + "]]>";
                } else {
                }

                if (e.hasChildNodes()) { output += X.debug(e.childNodes[0]); }

                if (nodeType == 1) { output += "</" + e.nodeName + ">"; }

                e = e.nextSibling;
            }
            return(output);
        }
    };

    if (xml.nodeType == 9) xml = xml.documentElement;    // document node

    var xml1 = X.removeWhite(xml)
    var obj1 = X.toObj(xml1);
    var json = X.toJson(obj1, xml.nodeName, "\t");
    
    var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
    return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
}