    /*!
     * Ext JS Library 3.3.0
     * Copyright(c) 2006-2010 Ext JS, Inc.
     * licensing@extjs.com
     * http://www.extjs.com/license
     */
    /*
    * Portions of this file are based on pieces of Yahoo User Interface Library
    * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
    * YUI licensed under the BSD License:
    * http://developer.yahoo.net/yui/license.txt
    */
    Ext.lib.Ajax = function() {
        var activeX = ['Msxml2.XMLHTTP.6.0',
                       'Msxml2.XMLHTTP.3.0',
                       'Msxml2.XMLHTTP'],
            CONTENTTYPE = 'Content-Type';

        // private
        function setHeader(o) {
            var conn = o.conn,
                prop,
                headers = {};

            function setTheHeaders(conn, headers){
                for (prop in headers) {
                    if (headers.hasOwnProperty(prop)) {
                        conn.setRequestHeader(prop, headers[prop]);
                    }
                }
            }

            Ext.apply(headers, pub.headers, pub.defaultHeaders);
            setTheHeaders(conn, headers);
            delete pub.headers;
        }

        // private
        function createExceptionObject(tId, callbackArg, isAbort, isTimeout) {
            return {
                tId : tId,
                status : isAbort ? -1 : 0,
                statusText : isAbort ? 'transaction aborted' : 'communication failure',
                isAbort: isAbort,
                isTimeout: isTimeout,
                argument : callbackArg
            };
        }

        // private
        function initHeader(label, value) {
            (pub.headers = pub.headers || {})[label] = value;
        }

        // private
        function createResponseObject(o, callbackArg) {
            var headerObj = {},
                headerStr,
                conn = o.conn,
                t,
                s,
                // see: https://prototype.lighthouseapp.com/projects/8886/tickets/129-ie-mangles-http-response-status-code-204-to-1223
                isBrokenStatus = conn.status == 1223;

            try {
                headerStr = o.conn.getAllResponseHeaders();
                Ext.each(headerStr.replace(/\r\n/g, '\n').split('\n'), function(v){
                    t = v.indexOf(':');
                    if(t >= 0){
                        s = v.substr(0, t).toLowerCase();
                        if(v.charAt(t + 1) == ' '){
                            ++t;
                        }
                        headerObj[s] = v.substr(t + 1);
                    }
                });
            } catch(e) {}

            return {
                tId : o.tId,
                // Normalize the status and statusText when IE returns 1223, see the above link.
                status : isBrokenStatus ? 204 : conn.status,
                statusText : isBrokenStatus ? 'No Content' : conn.statusText,
                getResponseHeader : function(header){return headerObj[header.toLowerCase()];},
                getAllResponseHeaders : function(){return headerStr;},
                responseText : conn.responseText,
                responseXML : conn.responseXML,
                argument : callbackArg
            };
        }

        // private
        function releaseObject(o) {
            if (o.tId) {
                pub.conn[o.tId] = null;
            }
            o.conn = null;
            o = null;
        }

        // private
        function handleTransactionResponse(o, callback, isAbort, isTimeout) {
            if (!callback) {
                releaseObject(o);
                return;
            }

            var httpStatus, responseObject;

            try {
                if (o.conn.status !== undefined && o.conn.status != 0) {
                    httpStatus = o.conn.status;
                }
                else {
                    httpStatus = 13030;
                }
            }
            catch(e) {
                httpStatus = 13030;
            }

            if ((httpStatus >= 200 && httpStatus < 300) || (Ext.isIE && httpStatus == 1223)) {
                responseObject = createResponseObject(o, callback.argument);
                if (callback.success) {
                    if (!callback.scope) {
                        callback.success(responseObject);
                    }
                    else {
                        callback.success.apply(callback.scope, [responseObject]);
                    }
                }
            }
            else {
                switch (httpStatus) {
                    case 12002:
                    case 12029:
                    case 12030:
                    case 12031:
                    case 12152:
                    case 13030:
                        responseObject = createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false), isTimeout);
                        if (callback.failure) {
                            if (!callback.scope) {
                                callback.failure(responseObject);
                            }
                            else {
                                callback.failure.apply(callback.scope, [responseObject]);
                            }
                        }
                        break;
                    default:
                        responseObject = createResponseObject(o, callback.argument);
                        if (callback.failure) {
                            if (!callback.scope) {
                                callback.failure(responseObject);
                            }
                            else {
                                callback.failure.apply(callback.scope, [responseObject]);
                            }
                        }
                }
            }

            releaseObject(o);
            responseObject = null;
        }

        function checkResponse(o, callback, conn, tId, poll, cbTimeout){
            if (conn && conn.readyState == 4) {
                clearInterval(poll[tId]);
                poll[tId] = null;

                if (cbTimeout) {
                    clearTimeout(pub.timeout[tId]);
                    pub.timeout[tId] = null;
                }
                handleTransactionResponse(o, callback);
            }
        }

        function checkTimeout(o, callback){
            pub.abort(o, callback, true);
        }

        // private
        function handleReadyState(o, callback){
            callback = callback || {};
            var conn = o.conn,
                tId = o.tId,
                poll = pub.poll,
                cbTimeout = callback.timeout || null;

            if (cbTimeout) {
                pub.conn[tId] = conn;
                pub.timeout[tId] = setTimeout(checkTimeout.createCallback(o, callback), cbTimeout);
            }
            poll[tId] = setInterval(checkResponse.createCallback(o, callback, conn, tId, poll, cbTimeout), pub.pollInterval);
        }

        // private
        function asyncRequest(method, uri, callback, postData) {
            var o = getConnectionObject() || null;

            if (o) {
                o.conn.open(method, uri, true);

                if (pub.useDefaultXhrHeader) {
                    initHeader('X-Requested-With', pub.defaultXhrHeader);
                }

                if(postData && pub.useDefaultHeader && (!pub.headers || !pub.headers[CONTENTTYPE])){
                    initHeader(CONTENTTYPE, pub.defaultPostHeader);
                }

                if (pub.defaultHeaders || pub.headers) {
                    setHeader(o);
                }

                handleReadyState(o, callback);
                o.conn.send(postData || null);
            }
            return o;
        }

        // private
        function getConnectionObject() {
            var o;

            try {
                if (o = createXhrObject(pub.transactionId)) {
                    pub.transactionId++;
                }
            } catch(e) {
            } finally {
                return o;
            }
        }

        // private
        function createXhrObject(transactionId) {
            var http;

            try {
                http = new XMLHttpRequest();
            } catch(e) {
                for (var i = 0; i < activeX.length; ++i) {
                    try {
                        http = new ActiveXObject(activeX[i]);
                        break;
                    } catch(e) {}
                }
            } finally {
                return {conn : http, tId : transactionId};
            }
        }

        var pub = {
            request : function(method, uri, cb, data, options) {
                if(options){
                    var me = this,
                        xmlData = options.xmlData,
                        jsonData = options.jsonData,
                        hs;

                    Ext.applyIf(me, options);

                    if(xmlData || jsonData){
                        hs = me.headers;
                        if(!hs || !hs[CONTENTTYPE]){
                            initHeader(CONTENTTYPE, xmlData ? 'text/xml' : 'application/json');
                        }
                        data = xmlData || (!Ext.isPrimitive(jsonData) ? Ext.encode(jsonData) : jsonData);
                    }
                }
                return asyncRequest(method || options.method || "POST", uri, cb, data);
            },

            serializeForm : function(form) {
                var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
                    hasSubmit = false,
                    encoder = encodeURIComponent,
                    name,
                    data = '',
                    type,
                    hasValue;

                Ext.each(fElements, function(element){
                    name = element.name;
                    type = element.type;

                    if (!element.disabled && name) {
                        if (/select-(one|multiple)/i.test(type)) {
                            Ext.each(element.options, function(opt){
                                if (opt.selected) {
                                    hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
                                    data += String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
                                }
                            });
                        } else if (!(/file|undefined|reset|button/i.test(type))) {
                            if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
                                data += encoder(name) + '=' + encoder(element.value) + '&';
                                hasSubmit = /submit/i.test(type);
                            }
                        }
                    }
                });
                return data.substr(0, data.length - 1);
            },

            useDefaultHeader : true,
            defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
            useDefaultXhrHeader : true,
            defaultXhrHeader : 'XMLHttpRequest',
            poll : {},
            timeout : {},
            conn: {},
            pollInterval : 50,
            transactionId : 0,

    //  This is never called - Is it worth exposing this?
    //          setProgId : function(id) {
    //              activeX.unshift(id);
    //          },

    //  This is never called - Is it worth exposing this?
    //          setDefaultPostHeader : function(b) {
    //              this.useDefaultHeader = b;
    //          },

    //  This is never called - Is it worth exposing this?
    //          setDefaultXhrHeader : function(b) {
    //              this.useDefaultXhrHeader = b;
    //          },

    //  This is never called - Is it worth exposing this?
    //          setPollingInterval : function(i) {
    //              if (typeof i == 'number' && isFinite(i)) {
    //                  this.pollInterval = i;
    //              }
    //          },

    //  This is never called - Is it worth exposing this?
    //          resetDefaultHeaders : function() {
    //              this.defaultHeaders = null;
    //          },

            abort : function(o, callback, isTimeout) {
                var me = this,
                    tId = o.tId,
                    isAbort = false;

                if (me.isCallInProgress(o)) {
                    o.conn.abort();
                    clearInterval(me.poll[tId]);
                    me.poll[tId] = null;
                    clearTimeout(pub.timeout[tId]);
                    me.timeout[tId] = null;

                    handleTransactionResponse(o, callback, (isAbort = true), isTimeout);
                }
                return isAbort;
            },

            isCallInProgress : function(o) {
                // if there is a connection and readyState is not 0 or 4
                return o.conn && !{0:true,4:true}[o.conn.readyState];
            }
        };
        return pub;
    }();