/* (c) Aliaksei Maiseyeu, 2008 */
/* Ajax Query Class with JSON support */

function ajQuery() {
    this.transport = null;
    this.isWorking = false;

    /* properties initialization functions */
    this.clearParams = function() {
        if (!this.isWorking) {
            this.requestURL = "";
            this.requestMethod = "POST";
            this.requestPOSTMethod = 0; // 0 - multipart/form-data, 1- application/x-www-form-urlencoded
            this.async = true;
            this.timeOut = 8000;
            this.useTimeOut = true;
            this.answerMode = "JSON"; // may be JSON, TEXT, XML
        }
    }
    
    this.clearData = function() {
        if (!this.isWorking) {
            this.responseStatus = new Array(2);
            this.responseText = "";
            this.responseXML = "";
            this.isTimeOut = false;
            this.outData = null;
            this.inData = null;
            this.outData = new Object();
            this.inData = new Object();
        
            this.onSending = function() { }
            this.onSent = function() { }
            this.onReceiving = function() { }
            this.onReceived = function() { }
            this.onError = function() { }
            this.onFail = function() { }
        }
    }
    
    //create transport object
    this.createTransport = function() {
        if (typeof XMLHttpRequest != "undefined") // try compilant mode
            this.transport = new XMLHttpRequest();
        else { //try MS IE
            try { this.transport = new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch (e) {
                try { this.transport = new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch (e) {
                    try { this.transport = new ActiveXObject("Msxml2.XMLHTTP") } catch (e) {
                        try { this.transport = new ActiveXObject("Microsoft.XMLHTTP") } catch (e) { }
                    }                
                }
            }
        }

        if (this.transport) return true; else return false;
    };
    
    //set request value by name
    this.setData = function(vName, vValue) {
        if (!this.isWorking) this.outData[vName] = vValue;
    }
    
    //get response value by name
    this.getData = function(vName) {
        return ((this.inData[vName]) ? this.inData[vName] : "");
    }
    
    /* encoding request functions for different modes */
    this.createMIMEBoundary = function() {
        return "AJQUERYLIB" + Math.round(Math.random() * new Date().getTime() / 1024);
    }
    
    this.createURLEncodedData = function() {
        var uEData = new Array();
        for (key in this.outData) {
            uEData.push(encodeURIComponent(key) + "=" + encodeURIComponent(this.outData[key]));
        }
        var uEDataStr = uEData.join("&");
        return new Array(uEDataStr, uEDataStr.length);
    }
    
    this.createMIMEMultipartData = function() {
        var boundary = this.createMIMEBoundary();
        var mData = "--" + boundary;
        for (key in this.outData) {
            mData += "\n";
            mData += "Content-Disposition: form-data; name=\"" + key + "\"\n";
            mData += "\n";
            mData += this.outData[key] + "\n";
            mData += "--" + boundary;
        }
        mData += "--\n";
        return new Array(mData, mData.length, boundary);
    }
    
    this.processResponse = function() {
        this.responseStatus[0] = this.transport.status;
        this.responseStatus[1] = this.transport.statusText;
        this.responseText = this.transport.responseText;
        this.responseXML = this.transport.responseXML;
                        
        if (this.responseStatus[0] == "200") {
            if ( (this.answerMode == "JSON") && 
                 ( ((this.responseText.charAt(0) == "{") && (this.responseText.charAt(this.responseText.length - 1) == "}")) ||
                   ((this.responseText.charAt(0) == "[") && (this.responseText.charAt(this.responseText.length - 1) == "]")) )
               ) 
                this.inData = eval("(" + this.responseText + ")");
            this.onReceived();
        } else this.onError();
    }
    
    this.runQuery = function() {
        var vrnd = new Date().getTime();
        var reqURL = this.requestURL + "?" + "rndaquery=" + vrnd;
        var requestTimeout, encData, conType;
        
        if (!this.transport) this.onFail();
        else if (!this.isWorking) { 
            this.isWorking = true;
            
            if (this.requestMethod == "GET") {
                encData = this.createURLEncodedData();
                if (encData[0].length > 0) reqURL += "&" + encData[0];
                this.transport.open("GET", reqURL, this.async);
            } else if (this.requestMethod == "POST") {
                if (this.requestPOSTMethod == 0) {
                    encData = this.createMIMEMultipartData();
                    conType = "multipart/form-data; boundary=\"" + encData[2] + "\"";
                } else {
                    encData = this.createURLEncodedData();
                    conType = "application/x-www-form-urlencoded";
                }
                this.transport.open("POST", reqURL, this.async);
                try {
                    this.transport.setRequestHeader("Content-Type", conType);
                    //this.transport.setRequestHeader("Content-Length", encData[1]);
                    //this.transport.setRequestHeader("Connection", "close");
                } catch (e) { }
            }
            
            var father = this; //because name context @this@ changes in function, and it's different in diff versions
            this.transport.onreadystatechange = function() {
                switch (father.transport.readyState) {
                    case 1:
                        father.onSending();
                        break;
                    case 2:
                        father.onSent();
                        break;
                    case 3:
                        father.onReceiving();
                        break;
                    case 4:
                        if (father.useTimeOut) clearTimeout(father.requestTimeout);
                        father.processResponse();
                        father.isWorking = false;
                        break;
                }
            }
            
            //set timeout if needed and what to do after timeout
            if (this.useTimeOut) this.requestTimeout = setTimeout(function() {father.isTimeOut = true; father.transport.abort(); father.isWorking = false; father.onError() }, this.timeOut);
            
            if (this.requestMethod == "GET") this.transport.send(null);
            else if (this.requestMethod == "POST") this.transport.send(encData[0]);
            
            //process data in sync mode
            if (!this.async) {
                if ((this.useTimeOut) && (!this.isTimeOut)) clearTimeout(this.requestTimeout);
                this.processResponse();
                this.isWorking = false;
            }
        }
    }
    
    //initilizing
    this.clearParams();
    this.clearData();
    this.createTransport();
}
