if (ajax && events) {

ajax.READY_STATE_UNINITIALIZED = 0;
ajax.READY_STATE_LOADING = 1;
ajax.READY_STATE_LOADED = 2;
ajax.READY_STATE_INTERATCIVE = 3;
ajax.READY_STATE_COMPLETE = 4;

ajax.onloadHandlers = new Array();

xml /*:Object*/ = {
    useActiveX: (typeof ActiveXObject != "undefined"),
    useDom: document.implementation && document.implementation.createDocument,
    useXmlHttp: (typeof XMLHttpRequest != "undefined")
};

xml.XMLHTTP_VERS /*:Array*/ = ["MSXML2.XmlHttp.6.0","MSXML2.XmlHttp.3.0"];

xml.DOM_VERS /*:Array*/ = ["MSXML2.DOMDocument.6.0","MSXML2.DOMDocument.3.0"];

ajax.createXHR = function()
{
    if (typeof XMLHttpRequest != "undefined") {
        return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        var aVersions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0"];
        var sVersion = null;
        for (var i = 0; i < aVersions.length; i++) {
            try {
                sVersion = aVersions[i];
                new ActiveXObject(sVersion);
                break;
            } catch (oError) {}
        }
        if (sVersion) {
            return new ActiveXObject(sVersion);
        } else {
            throw new Error("Невозможно создать объект XMLHttp");
        }
    }
    throw new Error("Ваш браузер не поддерживает объект XMLHttp");
}



/**
 * Static class for handling XML DOM creation.
 * @class
 */
xml.XMLDocument = function() {

}

/**
 * Creates an XML DOM document.
 * @return An XML DOM document.
 */
xml.createDocument = function () /*:XMLDocument*/{
    if (xml.useDom) {
        var oDom = document.implementation.createDocument("", "", null);
        oDom.parseError = {
            valueOf: function () { return this.errorCode; },
            toString: function () { return this.errorCode.toString() }
        };
        oDom.__initError__();
        oDom.addEventListener("load", function () {
            this.__checkForErrors__();
            this.__changeReadyState__(4);
        }, false);

        return oDom;
    } else if (xml.useActiveX) {
        if (! xml.DOM_VER) {
            for (var i = 0; i < xml.DOM_VERS.length; i++) {
                try {
                    new ActiveXObject(xml.DOM_VERS[i]);
                    xml.DOM_VER = xml.DOM_VERS[i];
                    break;
                } catch (oError) {}
            }
        }
        if (xml.DOM_VER) {
            return new ActiveXObject(xml.DOM_VER);
        } else {
            throw new Error("Невозможно создать XML DOM документ.");
        }
    } else {
        throw new Error("Ваш браузер не поддерживает XML DOM документ.");
    }
}

/**
 * Indicates if an XML DOM is available.
 * @return True if XML DOM is available, false if not.
 */
xml.XMLDocument.isSupported = function ()/*:Boolean*/ {
    return xml.useDom || xml.useActiveX;
};

//Code to make Mozilla DOM documents act more like MS DOM documents.
var oMozDocument = null;
if (typeof XMLDocument != "undefined") {
    oMozDocument = XMLDocument;
} else if (typeof Document != "undefined") {
    oMozDocument = Document;
}

if (oMozDocument && ! window.opera) {
    oMozDocument.prototype.readyState = 0;
    oMozDocument.prototype.onreadystatechange = null;
    oMozDocument.prototype.__changeReadyState__ = function (iReadyState) {
        this.readyState = iReadyState;
        if (typeof this.onreadystatechange == "function") {
            this.onreadystatechange();
        }
    };
    oMozDocument.prototype.__initError__ = function () {
        this.parseError.errorCode = 0;
        this.parseError.filepos = -1;
        this.parseError.line = -1;
        this.parseError.linepos = -1;
        this.parseError.reason = null;
        this.parseError.srcText = null;
        this.parseError.url = null;
    };

    oMozDocument.prototype.__checkForErrors__ = function () {
        if (this.documentElement.tagName == "parsererror") {
            var reError = />([\s\S]*?)Location:([\s\S]*?)Line Number (\d+), Column (\d+):<sourcetext>([\s\S]*?)(?:\-*\^)/;
            reError.test(this.xml);
            this.parseError.errorCode = -999999;
            this.parseError.reason = RegExp.$1;
            this.parseError.url = RegExp.$2;
            this.parseError.line = parseInt(RegExp.$3);
            this.parseError.linepos = parseInt(RegExp.$4);
            this.parseError.srcText = RegExp.$5;
        }
    };

    oMozDocument.prototype.loadXML = function (sXml) {
        this.__initError__();
        this.__changeReadyState__(1);
        var oParser = new DOMParser();
        var oXmlDom = oParser.parseFromString(sXml, "text/xml");
        while (this.firstChild) {
            this.removeChild(this.firstChild);
        }
        for (var i=0; i < oXmlDom.childNodes.length; i++) {
            var oNewNode = this.importNode(oXmlDom.childNodes[i], true);
            this.appendChild(oNewNode);
        }
        this.__checkForErrors__();
        this.__changeReadyState__(4);
    };

    oMozDocument.prototype.__load__ = oMozDocument.prototype.load;

    oMozDocument.prototype.load = function (sURL) {
        this.__initError__();
        this.__changeReadyState__(1);
        this.__load__(sURL);
    };

    Node.prototype.__defineGetter__("xml", function () {
        var oSerializer = new XMLSerializer();
        return oSerializer.serializeToString(this, "text/xml");
    });

    Node.prototype.__defineGetter__("text", function () {
        var sText = "";
        for (var i = 0; i < this.childNodes.length; i++) {
            if (this.childNodes[i].hasChildNodes()) {
                sText += this.childNodes[i].text;
            } else {
                sText += this.childNodes[i].nodeValue;
            }
        }
        return sText;
    });
}

/*
   ===================================================================
	ajax.PriorityQueue

    _items

    PriorityQueue(fnCompare)
    order()
    get()
    item(iPos)
    peek()
    put(oValue)
    size()
    remove(oValue)

    _compare(oValue1, oValue2)
   ===================================================================
*/
ajax.PriorityQueue = function(fnCompare)
{
    this._items = new Array();
    if (typeof fnCompare == "function") {
        this._compare = fnCompare;
    }
}

ajax.PriorityQueue.prototype = {
    order: function() { this._items.sort(this._compare); },
    get: function() { return this._items.shift(); },
    item: function(iPos) { return this._items[iPos]; },
    peek: function() { return this._items[0]; },
    put: function(oValue) { this._items.push(oValue); this.order(); },
    size: function() { return this._items.length; },
    remove: function (oValue) {
        for (var i = 0; i < this._items.length; i++) {
            if (this._items[i] === oValue) {
                this._items.splice(i, 1);
                return true;
            }
        }
        return false;
    },
    _compare: function(oValue1, oValue2) {
        if (oValue1 < oValue2) {
            return -1;
        } else if (oValue1 > oValue2) {
            return 1;
        }
        return 0;
    }
}

/*
   ===================================================================
	ajax.Request

    url
    owner
    priority
    method
    onsuccess
    onnotmodified
    onfailure
    oncancel
    active
    age
    xhr
   ===================================================================
*/

ajax.Request = function(url, owner, priority, method, onsuccess, onnotmodified, onfailure, oncancel)
{
    if (! url) return;
//    this.owner = owner;
    this.url = url;
    this.priority = (priority) ? priority : 1;
    this.method = (method) ? method : "get";
    this.onsuccess = new Publisher();
    this.onnotmodified = new Publisher();
    this.onfailure = new Publisher();
    this.oncancel = new Publisher();
    if (onsuccess) onsucces.subscribe(this.onsuccess, owner);
    if (onnotmodified) onnotmodified.subscribe(this.onnotmodified, owner);
    if (onfailure) onfailure.subscribe(this.onfailure, owner);
    if (oncancel) oncancel.subscribe(this.oncancel, owner);

    this.active = false;
    this.xml = true;
}

/*
   ===================================================================
	ajax.RequestManager
   ===================================================================
*/
ajax.RequestManager = (function() {
    var oManager = {
        DEFAULT_PRIORITY: 10,
        MONITOR_INTERVAL: 250,
        AGE_LIMIT: 60000,

        _active: new Array(),
        _pending: new ajax.PriorityQueue(function(oReq1, oReq2) {return oReq1.priority - oReq2.priority;}),

        _sendNext: function()
        {
            if (this._active.length >= 2) return;
            var oRequest = this._pending.get();
            if (! oRequest) return;
            this._active.push(oRequest);
            oRequest.xhr = ajax.createXHR();
            oRequest.xhr.open(oRequest.method, oRequest.url, true);
            oRequest.xhr.send(oRequest.data);
            oRequest.active = true;
        },

        _monitor: function()
        {
            var oRequest = null;
            var oXHR = null;
            var success = false;

            for (var i = this._active.length - 1; i >= 0; i--) {
                oRequest = this._active[i];
                oXHR = oRequest.xhr;
                if (oXHR.readyState != 4) continue;
                oRequest.active = false;
                this._active.splice(i, 1);
                var publisher = null;
                if ((oXHR.status >= 200 && oXHR.status < 300) || oXHR.status == 0) {
                    publisher = oRequest.onsuccess;
                    success = true;
                } else if (oXHR.status == 304) {
//                    publisher = this.onnotmodified;
                    publisher = oRequest.onsuccess;
                    success = true;
                } else {
                    publisher = oRequest.onfailure;
                }
                if (publisher != null) {
                    var oResponse = { status : oXHR.status, data : oXHR.responseText, request : oRequest };
                    if (success && oRequest.xml) {
                        try {
                            oResponse.xml = xml.createDocument();
                            oResponse.xml.loadXML(oXHR.responseText);
                        } catch(oError) {
                            oResponse.xml = null;
                        }
                    }
                    publisher.deliver(oResponse, oXHR);
/*                    setTimeout((function (fnCallback, oRequest, oXHR) {
                        return function (){
                            fnCallback.call(oRequest.owner || window, oResponse);
                            }})(fnCallback, oRequest, oXHR), 1);*/
                }
            }
        },

        _agePromote : function() {
            for (var i = 0; i < this._pending.size(); i++) {
                var oRequest = this._pending.item(i);
                oRequest.age += this.MONITOR_INTERVAL;
                if (oRequest.age >= this.AGE_LIMIT) {
                    oRequest.age = 0;
                    oRequest.priority--;
                }
            }
            this._pending.order();
        },

        send: function(oRequest)
        {
            if (typeof oRequest.priority != "number") {
                oRequest.priority = this.DEFAULT_PRIORITY;
            }
            oRequest.active = false;
            this._pending.put(oRequest);
        },

        cancel: function(oRequest)
        {
            if (! this._pending.remove(oRequest)) {
                oRequest.xhr.abort();
                if (this._active[0] === oRequest) {
                    this._active.shift();
                } else if (this._active[1] === oRequest) {
                    this._active.pop();
                }
//                if (typeof oRequest.oncancel == "function") {
                this.oncancel.deliver({request: oRequest});
//                    oRequest.oncancel.call(oRequest.owner || window, {request: oRequest});
//                }
            }
        }
    };
    setInterval(function() {
        ajax.RequestManager._monitor();
        ajax.RequestManager._sendNext();
        ajax.RequestManager._agePromote();
    }, oManager.MONITOR_INTERVAL);
    return oManager;
})();

}