﻿/**
 * Ajax
 * 
 * @author: Alexander Myadzel <mailbox@myadzel.ru>
 * @modified: 23/12/2009 16:34
 *
 */

function Ajax(sURL)
{
    this.aParameters = [];
    this.sParameters;

    this.fCallbackHandler;
    this.fCallbackErrorHandler;
    this.fCallbackLoadingHandler;
    
    this.sMethod = 'GET';
    
    this.iStartMsTime;
    this.iEndMsTime;

    this.sURL = sURL || null;
    this.oRequest = Ajax.getTransport();
}

Ajax.STATE_UNINITIALIZED = 0;
Ajax.STATE_LOADING = 1;
Ajax.STATE_LOADED = 2;
Ajax.STATE_INTERACTIVE = 3;
Ajax.STATE_COMPLETED = 4;

Ajax.HTTP_STATUS_OK = 200;
Ajax.HTTP_STATUS_FORBIDDEN = 403;
Ajax.HTTP_STATUS_NOT_FOUND = 404;

Ajax.getTransport = function()
{
    if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        try {
            return new ActiveXObject('Microsoft.XMLHTTP');
        } catch (oException) {
            return null;
        }
    } else {
        return null;
    }
}

Ajax.prototype.getMsTime = function()
{
    if (this.iStartMsTime && this.iEndMsTime) {
        return this.iEndMsTime - this.iStartMsTime;
    } else {
        return false;
    }
}

Ajax.prototype.clearMsTime = function()
{
    this.iStartMsTime = null;
    this.iEndMsTime = null;
}

Ajax.prototype.addParameter = function(sKey, sValue)
{
    this.aParameters.push({'key': sKey, 'value': sValue});
}

Ajax.prototype.removeParameter = function(sKey)
{
    for (var i = 0; i < this.aParameters.length; i++) {
		if (this.aParameters[i].key === sKey) {
			this.aParameters[i] = undefined;
		}
	}
}

Ajax.prototype.setMethod = function(sValue)
{
    var sValue = sValue.toUpperCase();
    this.sMethod = (sValue == 'POST') ? sValue : this.sMethod;
}

Ajax.prototype.setCallbackHandler = function(fValue)
{
    if (typeof fValue == 'function') {
        this.fCallbackHandler = fValue;
    }
}

Ajax.prototype.setCallbackErrorHandler = function(fValue)
{
    if (typeof fValue == 'function') {
        this.fCallbackErrorHandler = fValue;
    }
}    

Ajax.prototype.setCallbackLoadingHandler = function(fValue)
{
    if (typeof fValue == 'function') {
        this.fCallbackLoadingHandler = fValue;
    }
}

Ajax.prototype.setParameters = function()
{
	for (var i = 0, a = []; i < this.aParameters.length; i++) {
		var oParameter = this.aParameters[i];
		a.push(encodeURIComponent(oParameter.key) + '=' + encodeURIComponent(oParameter.value));
	}

	
    this.sParameters = a.join('&').replace(/%20/g, "+") || null;
}

Ajax.prototype.setURL = function()
{
    if (this.sParameters && this.sMethod == 'GET') {
        this.sURL += '?' + this.sParameters;
    } 
}

Ajax.prototype.proceedResponse = function()
{
    if (this.oRequest.readyState == Ajax.STATE_COMPLETED) {
        this.iEndMsTime = (new Date()).getTime();
        if (this.oRequest.status == Ajax.HTTP_STATUS_OK) {
            if (this.fCallbackHandler) {
                this.fCallbackHandler(this.oRequest, this.getMsTime());
            }
        } else {
            if (this.fCallbackErrorHandler) {
                this.fCallbackErrorHandler(this.oRequest);
            }
        }
    } else {
        if (this.fCallbackLoadingHandler) {
            this.fCallbackLoadingHandler(this.oRequest);
        }
    }
}

Ajax.prototype.transfer = function()
{
	this.clearMsTime();

    if (this.oRequest) {
        var oThis = this;
        
        this.setParameters();
        this.setURL();

        this.oRequest.open(this.sMethod, this.sURL, true);
        
        this.oRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
        this.oRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        this.oRequest.setRequestHeader('If-Modified-Since', 'Thu, 01 Jan 1970 00:00:00 GMT');

        /**
         * https://developer.mozilla.org/En/XMLHttpRequest
         * Warning: This must not be used from native code. 
         * You should also not use this with synchronous requests.
         */
        this.oRequest.onreadystatechange = function()
        {
            oThis.proceedResponse();
        }

        this.oRequest.send(this.sParameters);

        this.iStartMsTime = (new Date()).getTime();
    }
}

