function ClAjax()
{
    this.ajax = Array();
    
    this.initAjax = function()
    {
        for (var i = 0; i < this.ajax.length; i++)
        {
            if (this.ajax[i] == null)
            {
                this.ajax[i] = this.createAjax();
                
                return i;
            }
        }
        
        this.ajax[this.ajax.length] = this.createAjax();
        return this.ajax.length - 1;
    };
    
    this.createAjax = function()
    {
        var a;
        
        try
        {
            a = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch (e)
        {
            try
            {
                a = new XMLHttpRequest();
            }
            catch (e)
            {
                alert('Not supported');
            }
        }        
        return a;
    };
    
    this.closeAjax = function(index)
    {
        if (this.ajax.length > index && index >= 0)
        {
            this.ajax[index].abort();
            this.ajax[index] = null;
        }
    };
    
    this.processStateChange = function(index, callback, args, xml)
    {
        if (this.ajax[index].readyState == 4)
        {
            if (this.ajax[index].status == 200)
            {
                if (xml) callback(this.ajax[index].responseXML, args);
                else callback(this.ajax[index].responseText, args);
                
                this.closeAjax(index);
            }
            else
            {
                alert("Error requesting page: " + this.ajax[index].status);
            }
        }
    };
    
    this.SendGet = function(url, args, xml, callback, callArgs)
    {
        var i = this.initAjax();
        var a = this.ajax[i];
        
        if (args != '') args = '?' + args;
        a.open('GET', url + args, true);
        var self = this;
        a.onreadystatechange = function() {self.processStateChange(i, callback, callArgs, xml);}
        a.send(null);
    };
    
    this.SendPost = function(url, data, xml, callback, callArgs)
    {
        var i = this.initAjax();
        var a = this.ajax[i];
        var arg = this.getArgsString(data, '');

        a.open('POST', url, true);
        a.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;');
        a.setRequestHeader('Content-Length', arg.length);

        var self = this;
        a.onreadystatechange = function() {self.processStateChange(i, callback, callArgs, xml);}
        a.send(arg);
    };
    
    this.getArgsString = function(args, sep)
    {
        var s = "";
        
        for (var i = 0; i < args.length; i += 2)
        {
            if (s != "") s += '&';
            s += args[i] + "=" + encodeURIComponent(args[i+1]);
        }
        
        if (s != "") s = sep + s;

        return s;
    };
    
    this.GetForm = function(url, divFormID)
    {
        this.SendGet(url, '', false, this.GetFormResp, [divFormID]);
    };
    
    this.GetFormResp = function(d, formDivID)
    {
        var body = document.getElementsByTagName('body');
        body = body[0];
        
        var div = document.createElement('div');
        div.className = "FloatForm";
        div.innerHTML = d;
        div.id = formDivID[0];
        
        body.appendChild(div);
    };
    
    this.FormToQueryString = function(FormID)
    {
        var form = document.getElementById(FormID);
        var qs = '';
        
        // get input elements
        var elements = form.getElementsByTagName('input');
        for (var i = 0; i < elements.length; i++)
        {
            qs += elements[i].id + '=' + encodeURIComponent(elements[i].value) + "&";
        }
        
        // get select elements
        elements = form.getElementsByTagName('select');
        for (var i = 0; i < elements.length; i++)
        {
            qs += elements[i].id + '=' + encodeURIComponent(elements[i].value) + "&";
        }
        
        return qs.substr(0, qs.length - 1);
    };
    
    this.FormToClAjaxPost = function(form)
    {
        var elements = form.getElementsByTagName('input');
        var qs = new Array;
        for (var i = 0; i < elements.length; i++)
        {
            qs.push(elements[i].id);
            qs.push(elements[i].value);
        }
        
        return qs;
    };
    
    this.GetTable = function(url, queryString, parentNode, attribs, id, callFunc)
    {
        // remote existing table
        var exist = document.getElementById(id);        
        if (exist !== null)
        {
            var parExist = exist.parentNode;
            parExist.removeChild(exist);
        }

        // send request
        this.SendGet(url, queryString, true, this.GetTableResp, [parentNode, attribs, id, callFunc]);
    };
    
    this.GetTableResp = function(xml, args)
    {
        var parentNode = args[0];
        var attribs = args[1];
        var id = args[2];
        var callFunc = args[3];
        
        var from = xml.getElementsByTagName('table')[0];
        
        var dest = document.getElementById(parentNode);
        var table = document.createElement('table');
        table.id = id;
        
        // set table attributes
        for (var i = 0; i < attribs.length; i += 2)
        {
            if (attribs[i] == "cellspacing") table.cellSpacing = attribs[i+1];
            else
            {
                table.setAttribute(attribs[i], attribs[i+1]);
            }
        }
        
        copyElement(from, table);
        
        dest.appendChild(table);
        
        callFunc();
    };
    
    this.GetNode = function(url, queryString, parentNode, callFunc, includeRootTag)
    {
        // send request
        this.SendGet(url, queryString, true, this.GetNodeResp, [parentNode, callFunc, includeRootTag]);
    };
    
    this.GetNodeResp = function(xml, args)
    {
        var parentNode = args[0];
        var callFunc = args[1];
        var includeRootTag = args[2];

        var node = xml.firstChild;
        
        if (includeRootTag) copyElement(node, parentNode);
        else copyContents(node, parentNode);
        
        callFunc();
    };
    
    this.GetComboBox = function(url, queryString, id, callFunc)
    {
        // clear combo
        var combo = document.getElementById(id);
        combo.options.length = 0;
        combo.options[0] = new Option("Loading...", "-1");
        
        // send request
        this.SendGet(url, queryString, true, this.GetComboBoxResp, [id, callFunc]);
    }
    
    this.GetComboBoxResp = function(xml, args)
    {
        var id = args[0];
        var callFunc = args[1];
        
        var combo = document.getElementById(id);
        combo.options.length = 0;
        
        var res = xml.getElementsByTagName('option');
        // process entities
        for (i = 0; i < res.length; i++)
        {
            var n = res[i];
            
            var value = n.getAttribute("value");
            var text = n.firstChild.nodeValue;

            var opt = document.createElement("option");
            opt.appendChild(document.createTextNode(text));
            opt.setAttribute("value", value);
            combo.appendChild(opt);
        }
    }
    
    return true;
}

function copyElement(node, destCont)
{
    if (node.nodeType == '3') // text node
    {
        destCont.appendChild(document.createTextNode(decodeURIComponent(node.nodeValue)));
    }
    else if (node.childNodes.length == 0) // no children - no iteration
    {
        // create same element
        var newEl = document.createElement(node.nodeName);
        copyAttribs(node, newEl);

        //copyElement(node, newEl);
        
        destCont.appendChild(newEl);
    }
    else if (node.childNodes.length > 0) // element with children
    {
        var newEl = document.createElement(node.nodeName);
        copyAttribs(node, newEl);        
        
        var children = node.childNodes.length;

        for (var i = 0; i < children; i++)
        {
            copyElement(node.childNodes[i], newEl);
        }
        
        destCont.appendChild(newEl);
    }
}

function copyContents(srcCont, destCont)
{
    for (var i = 0; i < srcCont.childNodes.length; i++)
    {
        node = srcCont.childNodes[i];
        
        copyElement(node, destCont);
    }
}

function copyAttribs(src, dest)
{
    var atrSrc = src.attributes;
    var attrbCount = atrSrc.length;
    
    for (var a = 0; a < attrbCount; a++)
    {
        var atr = atrSrc[a];
        
        if (atr.name == 'class')
        {
            dest.setAttribute('class', atr.value);
            dest.setAttribute('className', atr.value);
        }
        else if (atr.value !== null)
        {
            dest.setAttribute(atr.name, atr.value);
        }
    }
}
