//  mozilla extension
if (document.implementation.hasFeature("XPath", "3.0")) {
    XMLDocument.prototype.selectNodes = function(cXPathString, xNode) {
        if (!xNode) { xNode = this; }
        var oNSResolver = this.createNSResolver(this.documentElement)
        var aItems = this.evaluate(cXPathString, xNode, oNSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
        var aResult = [];
        for (var i = 0; i < aItems.snapshotLength; i++) {
            aResult[i] =  aItems.snapshotItem(i);
        }
        return aResult;
    }
    XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode) {
        if (!xNode) { xNode = this; }

        var xItems = this.selectNodes(cXPathString, xNode);
        if (xItems.length > 0) {
            return xItems[0];
        } else {
            return null;
        }
    }

    Element.prototype.selectNodes = function(cXPathString) {
        if (this.ownerDocument.selectNodes) {
            return this.ownerDocument.selectNodes(cXPathString, this);
        } else {
            throw "For XML Elements Only";
        }
    }

    Element.prototype.selectSingleNode = function(cXPathString) {
        if (this.ownerDocument.selectSingleNode) {
            return this.ownerDocument.selectSingleNode(cXPathString, this);
        } else {
            throw "For XML Elements Only";
        }
    }
}

function https(number_of_objects) { this.init(number_of_objects); };

https.prototype = {
    https: [], // array of xml_http_request instances
    init: function(number_of_objects) {
        if (number_of_objects) {
            for (var i = 0; i < number_of_objects; i++) {
                this.add_new_object();
            }
        }
    },

    get_free_object_index: function() {
        for (var i = 0; i < this.https.length; i++) {
            if (!this.https[i].is_busy()) {
                return i;
            }
        }
        this.add_new_object();  //  all objects are busy now, new object is required
        return this.https.length - 1;
    },

    add_new_object: function() {
        this.https.push(new xml_http_request("automatically created object #" + this.https.length, this));
    },

    get_object: function(params) {
        if (!params.type) {
            params.type = "text2js";
        }
        if (!params.method) {
            params.method = "get";
        }
        if (!params.arguments) {
            params.arguments = [];
        }
        if (!/^(text|xml2js|xml|xslt|text2js)$/.test(params.type)) {
            alert("Unsupported object_name: " + params.type);
            return;
        }
        if (!/^(get|post)$/i.test(params.method)) {
            alert("Unsupported method: " + params.method);
        }
        var object_index = this.get_free_object_index();
        var url = params.url;
        if (!url) {
//          url = this.get_url(params.type, params.call_id, params.parameters, params.query);
            url = this.get_url(params.type, params.call_id);
        }
        var data = this.get_data(params.parameters, params.query);
        this.https[object_index].top_arguments = [ ];
        if (params.arguments) {
            this.https[object_index].top_arguments = params.arguments;
        }
        this.https[object_index].name = params.name;
        return this.https[object_index].get_object({type: params.type, url: url, method: params.method, data: data, callback: params.callback, callback_object: params.callback_object});
    },

    abort: function(names) {
        if (names && typeof names != "object") {
            names = [ names ];
        }
        for (var i = 0; i < this.https.length; i++) {
            if (this.https[i].is_busy() && (!names || (names && array_search(this.https[i].name, names) !== false))) {
                this.https[i].abort();
            }
        }
    },

    get_url: function(type, call_id) {
        if (!/^(text|xml2js|xml|xslt|text2js)$/.test(type)) {
            alert("Unsupported type: " + type);
            return false;
        }
        var parameters = {};
        if (type == "text2js") {
            parameters.page = "json";
        } else if (type == "xml") {
            parameters.page = "xml";
        } else if (type == "xslt") {
            parameters.page = "xslt";
        }
        parameters.call_id = call_id;
        var url = "index.php?" + generate_post_query(parameters);
        return url;
    },

    get_data: function(parameters, query) {
        if (parameters == undefined) {
            parameters = {};
        }
        var data = generate_post_query(parameters);
        if (query) {
            data = data + "&" + query;
        }
        return data;
    },

    get_number_of_busy_objects: function() {
        var result = 0;
        for (var i = 0; i < this.https.length; i++) {
            if (this.https[i].is_busy()) {
                result++;
            }
        }
        return result;
    },

    update_indicator: function() {
        if (this.indicator) {
            var number_of_busy_objects = this.get_number_of_busy_objects();
            if (number_of_busy_objects) {
                this.indicator.title = language.requests_in_progress + " " + number_of_busy_objects;
                this.indicator.alt = number_of_busy_objects;
                this.indicator.style.visibility = "visible";
            } else {
                this.indicator.style.visibility = "hidden";
            }
        }
    }

};

var https = new https();

//  main ajax class
function xml_http_request(object_name, parent) { this.set_object_name(object_name); this.set_parent(parent); this.init(); };
xml_http_request.prototype = {
    xmlhttp: null,
    xml: null,
    busy: false,
    current_url: "",
    object_name: "",
    parent: null,   //  points to the objects container, may be null

    set_object_name: function(object_name) {
        if (object_name == "" || object_name == undefined || object_name == null) {
            object_name = "anonymous";
        }
        this.object_name = object_name;
    },

    set_parent: function(parent) {
        if (parent) {
            this.parent = parent;
        }
    },

    check_busy: function() {
        if (this.busy) {
            alert("DEBUG: the object " + this.object_name + " is busy now with " + this.current_url);
        }
    },

    is_busy: function() {
        return this.busy;
    },

    set_busy: function(url) {
        this.busy = true;
        this.current_url = url;
        this.update_indicator();
    },

    clear_busy: function() {
        this.busy = false;
        this.update_indicator();
    },

    update_indicator: function() {
        if (this.parent) {
            this.parent.update_indicator();
        }
    },

    init: function() {
        this.check_busy();
        this.clear_busy();
        if (this.xmlhttp) {
            delete this.xmlhttp;
        }
        try {
            this.xmlhttp = new XMLHttpRequest();
        } catch (e) {
            var msxml_xmlhttp_progids = new Array(
                    "Microsoft.XMLHTTP"
                    );
            for (var i = 0; i< msxml_xmlhttp_progids.length; i++) {
                try {
                    this.xmlhttp = new ActiveXObject(msxml_xmlhttp_progids[i]);
                } catch (e) { }
            }
        }
    },

    abort: function() {
        this.clear_busy();
        if (this.xmlhttp) {
            this.xmlhttp.abort();
        }
    },

    node2array: function (node) {
        if (node == undefined || node == null) node = this.xml;
        var child_node;
        var has_text = false;
        for (var i = 0; i < node.childNodes.length; i++) {
            var child_node = node.childNodes[i];
            if (child_node.nodeType == 3) has_text = true;
        }
        if (has_text || node.childNodes.length == 0) {
            if (node.hasChildNodes && node.firstChild) value = node.firstChild.nodeValue;
            else value = "";
            return value;
        }
        var result = new Array();
        for (var i = 0; i < node.childNodes.length; i++) {
            var child_node = node.childNodes[i];
            var map = new Object();
            for (var j = 0; j < child_node.childNodes.length; j++) {
                var sub_child_node = child_node.childNodes[j];
                map[sub_child_node.nodeName] = this.node2array(sub_child_node);
            }
            result[result.length] = map;
        }
        return result;
    },

    callback: function(params) {
        var top_arguments = this.top_arguments;
        var callback_arguments = [];
        var callback_arguments_string = [];
        if (params.exception) {
            callback_arguments[0] = false;
        } else if (params.type == "text") {
            callback_arguments[0] = this.xmlhttp.responseText;
        } else if (params.type == "xml2js") {
            callback_arguments[0] = this.node2array(this.xmlhttp.responseXML.documentElement);
        } else if (params.type == "xml") {
            callback_arguments[0] = this.xmlhttp.responseXML;
        } else if (params.type == "xslt") {
            callback_arguments[0] = xslt_from_xml(this.xmlhttp.responseXML);
        } else if (params.type == "text2js") {
            try {
                eval("callback_arguments[0] = " + this.xmlhttp.responseText);
            } catch(e) {
                alert(e + "\n\nSource code is:\n\n" + this.xmlhttp.responseText);
                return;
            }
        } else {
            alert("There's an error in javascript code!\nUnknown type: " + type);
            return;
        }
        if (!params.callback) {
            return callback_arguments[0];
        }

        for (var i = 0; i < top_arguments.length; i++) {
            callback_arguments[callback_arguments.length] = top_arguments[i];
        }
        for (var i = 0; i < callback_arguments.length; i++) {
            callback_arguments_string[callback_arguments_string.length] = "callback_arguments[" + i + "]";
        }
        this.clear_busy();
        if (!params.callback_object) {
            eval("params.callback(" + callback_arguments_string.join(", ") + ")");
        } else {
            eval("params.callback_object['" + params.callback + "'](" + callback_arguments_string.join(", ") + ")");
        }
    },

    get_object: function(params) {
        this.init();
        this.set_busy(params.url);
        if (params.data === "" || params.data === undefined) {
            params.data = null;
        }
        params.method = params.method.toUpperCase();
        if (params.method == "GET" && params.data !== null) {
            params.url = params.url + "?" + params.data;
            params.data = null;
        }
        //alert(params.url);
        if (!params.callback) {
            this.xmlhttp.open(params.method, params.url, false);
            this.xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            this.xmlhttp.send(params.data);
            this.clear_busy();
            if (this.xmlhttp.readyState == 4 && this.xmlhttp.status == 200)
                return this.callback(params);
            else
                return null;
        } else {
            this.xmlhttp.open(params.method, params.url, true);
            this.xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            var th = this;
            this.xmlhttp.onreadystatechange = function() {
                var ready = false;
                var error = false;
                try {
                    if (th.xmlhttp.readyState == 4 && th.xmlhttp.status == 200) {
                        ready = true;
                    }
                } catch (e) {
                    error = e;
                }
                if (ready) { th.callback(params); }
                else if (error) { params.exception = error; th.callback(params); }
            }
            this.xmlhttp.send(params.data);
        }
    }
}

function xslt_from_xml(xml) {
    if (document.all) {
        var xslt = new ActiveXObject("MSXML2.FreeThreadedDOMDocument");
        xslt.async = false;
        xslt.loadXML(xml.xml);
        return xslt;
    } else {
        return xml;
    }
}

//  perfoming xslt transformation
function xslt_transform(xml_source, xslt_source, container, parameters) {
    if (!xml_source) {
//      alert("[xslt_transform] Error: xml cannot be null");
        return;
    }
    if (!xslt_source) {
//      alert("[xslt_transform] Error: xslt cannot be null");
        return;
    }
    var temp = document.getElementById("temp_container");
    if (!temp) {
        alert("[xslt_transform] Error: temp container is missing");
        return;
    }
    try {
        var xsl_template = new ActiveXObject("MSXML2.XSLTemplate");
        xsl_template.stylesheet = xslt_source;
        var xslt_processor = xsl_template.createProcessor();
        xslt_processor.input = xml_source;
        for (var property in parameters) {
            var parameter = parameters[property];
            xslt_processor.addParameter(property, parameter, "");
        }
        xslt_processor.transform();
        temp.innerHTML = xslt_processor.output;
        if (container.innerHTML != temp.innerHTML) {
            container.innerHTML = temp.innerHTML;
        }
        temp.innerHTML = "";
    } catch (e) {
        try {
            var xslt_processor = new XSLTProcessor();
            xslt_processor.importStylesheet(xslt_source);
            for (var property in parameters) {
                var parameter = parameters[property];
                xslt_processor.setParameter(null, property, parameter);
            }
            var result = xslt_processor.transformToFragment(xml_source, document);
//          container.appendChild(result);

            temp.innerHTML = "";
            temp.appendChild(result);
            if (container.innerHTML != temp.innerHTML) {
                container.innerHTML = temp.innerHTML;
            }
            temp.innerHTML = "";
        } catch (e) {
            //alert("Your browser still not supported");
        }
    }
}

//  xml manipulations
function remove_node_from_xml(xml, name) {
    if (!xml) {
        return false;
    }
    var root = xml.documentElement;
    if (!root) {
        root = xml;
    }
    if (name == undefined) {
        name = "js";
    }
    var old_node = root.selectSingleNode(name);
    if (old_node) {
        old_node.parentNode.removeChild(old_node);
    }
    return true;
}

function add_js2xml(xml, js, name) {
    if (name == undefined) {
        name = "js";
    }
    if (!xml) {
        return null;
    }
    var root = xml.documentElement;
    if (!root) {
        root = xml;
    }
    root.appendChild(object2node(xml, js, name));
}

function replace_js_in_xml(xml, js, name) {
    if (name == undefined) {
        name = "js";
    }
    remove_node_from_xml(xml, name);
    add_js2xml(xml, js, name);
}

function object2node(xml, obj, name) {
    var root = xml.documentElement;
    if (!root) {
        root = xml;
    }
    var node = root.ownerDocument.createElement(name);

    if (obj === undefined || obj === null || obj === false) {
        obj = "";
    } else if (obj === true) {
        obj = "1";
    }

    var type = typeof obj;
    if (type == "object") {
        if (typeof obj.join == "function") {    //  array
            for (var i in obj) {
                for (var j in obj[i]) {
                    node.appendChild(object2node(xml, obj[i][j], j));
                }
            }
        } else {    //  object
            for (var i in obj) {
                node.appendChild(object2node(xml, obj[i], i));
            }
        }
    } else {
        node.appendChild(root.ownerDocument.createTextNode(obj));
    }
    return node;
}

function add_options2xml(xml, options, name) {
    if (!remove_node_from_xml(xml, name)) {
        return;
    }
    var root = xml.documentElement;
    if (!root) {
        root = xml;
    }
    var js_node = root.ownerDocument.createElement(name);
    var temp_node;
    var attribute;
    for (var i in options) {
        temp_node = xml.createElement("option");
        attribute = xml.createAttribute("value");
        attribute.value = i;
        temp_node.attributes.setNamedItem(attribute);
        temp_node.appendChild(root.ownerDocument.createTextNode(options[i]));
        js_node.appendChild(temp_node);
    }
    root.appendChild(js_node);
}

function xml2string(xml) {
    if (xml == null || xml == undefined) {
        return null;
    }
    if (document.all) {
        return xml.xml;
    } else {
        try {
            return (new XMLSerializer()).serializeToString(xml);
        } catch (e) {
            return false;
        }
    }
}

function node2string(node) {
    if (node == null || node == undefined) {
        return null;
    }
    if (document.all) {
        return node.outerHTML;
    } else {
        try {
            return (new XMLSerializer()).serializeToString(node);
        } catch (e) {
            return false;
        }
    }
}

function string2xml(string) {
    var xml;
    if (document.all) {
        xml = new ActiveXObject("Msxml2.DOMDocument.3.0");
        xml.loadXML(string);
    } else {
        var dom_parser = new DOMParser();
        xml = dom_parser.parseFromString(string, "application/xml");
    }
    return xml;
}

//  real browser emulation
function htmlspecialchars(str) {
    if (typeof str == "undefined") {
        str = "";
    }
    return str.replace(/\"/g, "&quot;");
}

function form2query(form) {
    var element;
    var successfull_elements = [];
    for (var i = 0; i < form.elements.length; i++) {
        element = form.elements[i];
        if (element.type.toLowerCase() == "checkbox" && !element.checked) {
            continue;
        }
        if (element.disabled) {
//          continue;
        }
        successfull_elements[successfull_elements.length] = element.name + "=" + encodeURIComponent(element.value);
    }
    return successfull_elements.join("&");
}

function generate_post_query(par, add_brackets) {
    if (add_brackets == undefined) {
        add_brackets = false;
    }
    if (!add_brackets && par.length != undefined) {
        add_brackets = true;
    }
    var pairs = [];
    var pair_name;
    for (var name in par) {
        var type = typeof par[name];
        if (type == "string" || type == "number") {
            pair_name = name;
            if (add_brackets) {
                pair_name = pair_name + "[]";
            }
            pairs[pairs.length] = pair_name + "=" + encodeURIComponent(par[name]);
        } else {
            pairs[pairs.length] = generate_post_query(par[name], add_brackets);
        }
    }

    return pairs.join("&");
}


var load_object_params;

function load_objects(result, object_name, callback, object_index) {
    if (result == null && object_name == null) {    //  initial call
        for (var i = 0; i < load_object_params.length; i++) {
            https.get_object({type: load_object_params[i].type, call_id: load_object_params[i].call_id, callback: load_objects, arguments: [load_object_params[i].object, callback, i]});
        }
    } else {
        window[object_name] = result;
        load_object_params[object_index].loaded = true;
        for (var i = 0; i < load_object_params.length; i++) {
            if (!load_object_params[i].loaded) {
                return;
            }
        }
        callback();
    }
}

function xml2object(xml) {
    var result = new Object();
    var node_name;
    var node_value;
    var child_node;

    for (var i = 0; i < xml.attributes.length; i++) {
        if (!result.attributes) {
            result.attributes = new Object();
        }
        result.attributes[xml.attributes[i].nodeName] = xml.attributes[i].nodeValue;
    }

    for (var i = 0; i < xml.childNodes.length; i++) {
        child_node = xml.childNodes[i];
        node_name = child_node.nodeName;
        node_value = child_node.nodeValue;

        if (child_node.nodeType == 1) { //  element
            node_value = xml2object(child_node);
            if (!result.content) {
                result.content = new Object();
            }
            if (result.content[node_name] === undefined && typeof result.content.join != "function") {
                result.content[node_name] = node_value;
            } else if (typeof result.content.join == "function") {
                result.content.push(node_value);
            } else {
                result.content = new Array(result.content[node_name], node_value);
            }
//      } else if (child_node.nodeType == 2) {  //  attribute
//          if (!result.attributes) {
//              result.attributes = new Object();
//          }
//          result.attributes[node_name] = node_value;
        } else if (child_node.nodeType == 3) {  //  text
            result.value = node_value;
        }
    }
    return result;
}


// -*S*- by Haritonov Sanya
function ajax_getParams(url) {
    var pos = url.indexOf("?", url);
    return pos<0 ? "" : url.substring(pos+1);
}
function ajax_getUrl(url) {
    var pos = url.indexOf("?", url);
    return pos<0 ? url : url.substring(0, pos);
}
function ajax_getParamsAsObject(params) {
    oParams={};
    if(params) {
        var aParams = params.split("&");
        for(var i=0;i<aParams.length;i++) {
            a = aParams[i].split("=");
            oParams[a[0]] = a.length>1 ? a[1] : "";
        }
    }
    return oParams;
}
function ajax(urlAndParams, callback_function, method) {
//alert(urlAndParams);
    var url = ajax_getUrl(urlAndParams);
    var oParams = ajax_getParamsAsObject(ajax_getParams(urlAndParams));
    if(!callback_function) callback_function = ajax_empty_callback;
    var oConfig = {
        url: url,
        method: (method ? method : "get"),
        parameters: oParams,
        callback: eval(callback_function)
    }
    https.get_object(oConfig);
}
//function ajax_empty_callback(o){alert("Empty callback")}
function ajax_empty_callback(o){}
// -*S*-
