    /**
     * @class Ext
     *
     * The Ext namespace (global object) encapsulates all classes, singletons, and
     * utility methods provided by Sencha's libraries.
     *
     * Most user interface Components are at a lower level of nesting in the namespace,
     * but many common utility functions are provided as direct properties of the Ext namespace.
     *
     * Also many frequently used methods from other classes are provided as shortcuts
     * within the Ext namespace. For example {@link Ext#getCmp Ext.getCmp} aliases
     * {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
     *
     * Many applications are initiated with {@link Ext#onReady Ext.onReady} which is
     * called once the DOM is ready. This ensures all scripts have been loaded,
     * preventing dependency issues. For example:
     *
     *     Ext.onReady(function(){
     *         new Ext.Component({
     *             renderTo: document.body,
     *             html: 'DOM ready!'
     *         });
     *     });
     *
     * For more information about how to use the Ext classes, see:
     *
     * - <a href="http://www.sencha.com/learn/">The Learning Center</a>
     * - <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
     * - <a href="http://www.sencha.com/forum/">The forums</a>
     *
     * @singleton
     */
    Ext.apply(Ext, {
        userAgent: navigator.userAgent.toLowerCase(),
        cache: {},
        idSeed: 1000,
        windowId: 'ext-window',
        documentId: 'ext-document',

        /**
         * True when the document is fully initialized and ready for action
         */
        isReady: false,

        /**
         * True to automatically uncache orphaned Ext.Elements periodically
         */
        enableGarbageCollector: true,

        /**
         * True to automatically purge event listeners during garbageCollection.
         */
        enableListenerCollection: true,

        addCacheEntry: function(id, el, dom) {
            dom = dom || el.dom;

            //<debug>
            if (!dom) {
                // Without the DOM node we can't GC the entry
                Ext.Error.raise('Cannot add an entry to the element cache without the DOM node');
            }
            //</debug>

            var key = id || (el && el.id) || dom.id,
                entry = Ext.cache[key] || (Ext.cache[key] = {
                    data: {},
                    events: {},

                    dom: dom,

                    // Skip garbage collection for special elements (window, document, iframes)
                    skipGarbageCollection: !!(dom.getElementById || dom.navigator)
                });

            if (el) {
                el.$cache = entry;
                // Inject the back link from the cache in case the cache entry
                // had already been created by Ext.fly. Ext.fly creates a cache entry with no el link.
                entry.el = el;
            }

            return entry;
        },

        /**
         * Generates unique ids. If the element already has an id, it is unchanged
         * @param {HTMLElement/Ext.Element} [el] The element to generate an id for
         * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
         * @return {String} The generated Id.
         */
        id: function(el, prefix) {
            var me = this,
                sandboxPrefix = '';
            el = Ext.getDom(el, true) || {};
            if (el === document) {
                el.id = me.documentId;
            }
            else if (el === window) {
                el.id = me.windowId;
            }
            if (!el.id) {
                if (me.isSandboxed) {
                    sandboxPrefix = Ext.sandboxName.toLowerCase() + '-';
                }
                el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed);
            }
            return el.id;
        },

        escapeId: (function(){
            var validIdRe = /^[a-zA-Z_][a-zA-Z0-9_\-]*$/i,
                escapeRx = /([\W]{1})/g,
                leadingNumRx = /^(\d)/g,
                escapeFn = function(match, capture){
                    return "\\" + capture;
                },
                numEscapeFn = function(match, capture){
                    return '\\00' + capture.charCodeAt(0).toString(16) + ' ';
                };

            return function(id) {
                return validIdRe.test(id)
                    ? id
                    // replace the number portion last to keep the trailing ' '
                    // from being escaped
                    : id.replace(escapeRx, escapeFn)
                        .replace(leadingNumRx, numEscapeFn);
            };
        }()),

        /**
         * Returns the current document body as an {@link Ext.Element}.
         * @return Ext.Element The document body
         */
        getBody: (function() {
            var body;
            return function() {
                return body || (body = Ext.get(document.body));
            };
        }()),

        /**
         * Returns the current document head as an {@link Ext.Element}.
         * @return Ext.Element The document head
         * @method
         */
        getHead: (function() {
            var head;
            return function() {
                return head || (head = Ext.get(document.getElementsByTagName("head")[0]));
            };
        }()),

        /**
         * Returns the current HTML document object as an {@link Ext.Element}.
         * @return Ext.Element The document
         */
        getDoc: (function() {
            var doc;
            return function() {
                return doc || (doc = Ext.get(document));
            };
        }()),

        /**
         * This is shorthand reference to {@link Ext.ComponentManager#get}.
         * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
         *
         * @param {String} id The component {@link Ext.Component#id id}
         * @return Ext.Component The Component, `undefined` if not found, or `null` if a
         * Class was found.
        */
        getCmp: function(id) {
            return Ext.ComponentManager.get(id);
        },

        /**
         * Returns the current orientation of the mobile device
         * @return {String} Either 'portrait' or 'landscape'
         */
        getOrientation: function() {
            return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
        },

        /**
         * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
         * DOM (if applicable) and calling their destroy functions (if available).  This method is primarily
         * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of
         * {@link Ext.util.Observable} can be passed in.  Any number of elements and/or components can be
         * passed into this function in a single call as separate arguments.
         *
         * @param {Ext.Element/Ext.Component/Ext.Element[]/Ext.Component[]...} args
         * An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy
         */
        destroy: function() {
            var ln = arguments.length,
            i, arg;

            for (i = 0; i < ln; i++) {
                arg = arguments[i];
                if (arg) {
                    if (Ext.isArray(arg)) {
                        this.destroy.apply(this, arg);
                    }
                    else if (Ext.isFunction(arg.destroy)) {
                        arg.destroy();
                    }
                    else if (arg.dom) {
                        arg.remove();
                    }
                }
            }
        },

        /**
         * Execute a callback function in a particular scope. If no function is passed the call is ignored.
         *
         * For example, these lines are equivalent:
         *
         *     Ext.callback(myFunc, this, [arg1, arg2]);
         *     Ext.isFunction(myFunc) && myFunc.apply(this, [arg1, arg2]);
         *
         * @param {Function} callback The callback to execute
         * @param {Object} [scope] The scope to execute in
         * @param {Array} [args] The arguments to pass to the function
         * @param {Number} [delay] Pass a number to delay the call by a number of milliseconds.
         */
        callback: function(callback, scope, args, delay){
            if(Ext.isFunction(callback)){
                args = args || [];
                scope = scope || window;
                if (delay) {
                    Ext.defer(callback, delay, scope, args);
                } else {
                    callback.apply(scope, args);
                }
            }
        },

        /**
         * Alias for {@link Ext.String#htmlEncode}.
         * @inheritdoc Ext.String#htmlEncode
         */
        htmlEncode : function(value) {
            return Ext.String.htmlEncode(value);
        },

        /**
         * Alias for {@link Ext.String#htmlDecode}.
         * @inheritdoc Ext.String#htmlDecode
         */
        htmlDecode : function(value) {
             return Ext.String.htmlDecode(value);
        },

        /**
         * Alias for {@link Ext.String#urlAppend}.
         * @inheritdoc Ext.String#urlAppend
         */
        urlAppend : function(url, s) {
            return Ext.String.urlAppend(url, s);
        }
    });

    Ext.ns = Ext.namespace;

    // for old browsers
    window.undefined = window.undefined;

    /**
     * @class Ext
     */
    (function(){
    /*
    FF 3.6      - Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17
    FF 4.0.1    - Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
    FF 5.0      - Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0

    IE6         - Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;)
    IE7         - Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1;)
    IE8         - Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)
    IE9         - Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)

    Chrome 11   - Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.60 Safari/534.24

    Safari 5    - Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1

    Opera 11.11 - Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11
    */
        var check = function(regex){
                return regex.test(Ext.userAgent);
            },
            isStrict = document.compatMode == "CSS1Compat",
            version = function (is, regex) {
                var m;
                return (is && (m = regex.exec(Ext.userAgent))) ? parseFloat(m[1]) : 0;
            },
            docMode = document.documentMode,
            isOpera = check(/opera/),
            isOpera10_5 = isOpera && check(/version\/10\.5/),
            isChrome = check(/\bchrome\b/),
            isWebKit = check(/webkit/),
            isSafari = !isChrome && check(/safari/),
            isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
            isSafari3 = isSafari && check(/version\/3/),
            isSafari4 = isSafari && check(/version\/4/),
            isSafari5_0 = isSafari && check(/version\/5\.0/),
            isSafari5 = isSafari && check(/version\/5/),
            isIE = !isOpera && check(/msie/),
            isIE7 = isIE && ((check(/msie 7/) && docMode != 8 && docMode != 9) || docMode == 7),
            isIE8 = isIE && ((check(/msie 8/) && docMode != 7 && docMode != 9) || docMode == 8),
            isIE9 = isIE && ((check(/msie 9/) && docMode != 7 && docMode != 8) || docMode == 9),
            isIE6 = isIE && check(/msie 6/),
            isGecko = !isWebKit && check(/gecko/),
            isGecko3 = isGecko && check(/rv:1\.9/),
            isGecko4 = isGecko && check(/rv:2\.0/),
            isGecko5 = isGecko && check(/rv:5\./),
            isGecko10 = isGecko && check(/rv:10\./),
            isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
            isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
            isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
            isWindows = check(/windows|win32/),
            isMac = check(/macintosh|mac os x/),
            isLinux = check(/linux/),
            scrollbarSize = null,
            chromeVersion = version(true, /\bchrome\/(\d+\.\d+)/),
            firefoxVersion = version(true, /\bfirefox\/(\d+\.\d+)/),
            ieVersion = version(isIE, /msie (\d+\.\d+)/),
            operaVersion = version(isOpera, /version\/(\d+\.\d+)/),
            safariVersion = version(isSafari, /version\/(\d+\.\d+)/),
            webKitVersion = version(isWebKit, /webkit\/(\d+\.\d+)/),
            isSecure = /^https/i.test(window.location.protocol),
            nullLog;

        // remove css image flicker
        try {
            document.execCommand("BackgroundImageCache", false, true);
        } catch(e) {}

        //<debug>
        var primitiveRe = /string|number|boolean/;
        function dumpObject (object) {
            var member, type, value, name,
                members = [];

            // Cannot use Ext.encode since it can recurse endlessly (if we're lucky)
            // ...and the data could be prettier!
            for (name in object) {
                if (object.hasOwnProperty(name)) {
                    value = object[name];

                    type = typeof value;
                    if (type == "function") {
                        continue;
                    }

                    if (type == 'undefined') {
                        member = type;
                    } else if (value === null || primitiveRe.test(type) || Ext.isDate(value)) {
                        member = Ext.encode(value);
                    } else if (Ext.isArray(value)) {
                        member = '[ ]';
                    } else if (Ext.isObject(value)) {
                        member = '{ }';
                    } else {
                        member = type;
                    }
                    members.push(Ext.encode(name) + ': ' + member);
                }
            }

            if (members.length) {
                return ' \nData: {\n  ' + members.join(',\n  ') + '\n}';
            }
            return '';
        }

        function log (message) {
            var options, dump,
                con = Ext.global.console,
                level = 'log',
                indent = log.indent || 0,
                stack,
                out,
                max;

            log.indent = indent;

            if (typeof message != 'string') {
                options = message;
                message = options.msg || '';
                level = options.level || level;
                dump = options.dump;
                stack = options.stack;

                if (options.indent) {
                    ++log.indent;
                } else if (options.outdent) {
                    log.indent = indent = Math.max(indent - 1, 0);
                }

                if (dump && !(con && con.dir)) {
                    message += dumpObject(dump);
                    dump = null;
                }
            }

            if (arguments.length > 1) {
                message += Array.prototype.slice.call(arguments, 1).join('');
            }

            message = indent ? Ext.String.repeat(' ', log.indentSize * indent) + message : message;
            // w/o console, all messages are equal, so munge the level into the message:
            if (level != 'log') {
                message = '[' + level.charAt(0).toUpperCase() + '] ' + message;
            }

            // Not obvious, but 'console' comes and goes when Firebug is turned on/off, so
            // an early test may fail either direction if Firebug is toggled.
            //
            if (con) { // if (Firebug-like console)
                if (con[level]) {
                    con[level](message);
                } else {
                    con.log(message);
                }

                if (dump) {
                    con.dir(dump);
                }

                if (stack && con.trace) {
                    // Firebug's console.error() includes a trace already...
                    if (!con.firebug || level != 'error') {
                        con.trace();
                    }
                }
            } else {
                if (Ext.isOpera) {
                    opera.postError(message);
                } else {
                    out = log.out;
                    max = log.max;

                    if (out.length >= max) {
                        // this formula allows out.max to change (via debugger), where the
                        // more obvious "max/4" would not quite be the same
                        Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); // keep newest 75%
                    }

                    out.push(message);
                }
            }

            // Mostly informational, but the Ext.Error notifier uses them:
            ++log.count;
            ++log.counters[level];
        }

        function logx (level, args) {
            if (typeof args[0] == 'string') {
                args.unshift({});
            }
            args[0].level = level;
            log.apply(this, args);
        }

        log.error = function () {
            logx('error', Array.prototype.slice.call(arguments));
        };
        log.info = function () {
            logx('info', Array.prototype.slice.call(arguments));
        };
        log.warn = function () {
            logx('warn', Array.prototype.slice.call(arguments));
        };

        log.count = 0;
        log.counters = { error: 0, warn: 0, info: 0, log: 0 };
        log.indentSize = 2;
        log.out = [];
        log.max = 750;
        log.show = function () {
            window.open('','extlog').document.write([
                '<html><head><script type="text/javascript">',
                    'var lastCount = 0;',
                    'function update () {',
                        'var ext = window.opener.Ext,',
                            'extlog = ext && ext.log;',
                        'if (extlog && extlog.out && lastCount != extlog.count) {',
                            'lastCount = extlog.count;',
                            'var s = "<tt>" + extlog.out.join("~~~").replace(/[&]/g, "&amp;").replace(/[<]/g, "&lt;").replace(/[ ]/g, "&#160;").replace(/\\~\\~\\~/g, "<br/>") + "</tt>";',
                            'document.body.innerHTML = s;',
                        '}',
                        'setTimeout(update, 1000);',
                    '}',
                    'setTimeout(update, 1000);',
                '</script></head><body></body></html>'].join(''));
        };
        //</debug>

        nullLog = function () {};
        nullLog.info = nullLog.warn = nullLog.error = Ext.emptyFn;

        Ext.setVersion('extjs', '4.1.0');
        Ext.apply(Ext, {
            /**
             * @property {String} SSL_SECURE_URL
             * URL to a blank file used by Ext when in secure mode for iframe src and onReady src
             * to prevent the IE insecure content warning (`'about:blank'`, except for IE
             * in secure mode, which is `'javascript:""'`).
             */
            SSL_SECURE_URL : isSecure && isIE ? 'javascript:\'\'' : 'about:blank',

            /**
             * @property {Boolean} enableFx
             * True if the {@link Ext.fx.Anim} Class is available.
             */

            /**
             * @property {Boolean} scopeResetCSS
             * True to scope the reset CSS to be just applied to Ext components. Note that this
             * wraps root containers with an additional element. Also remember that when you turn
             * on this option, you have to use ext-all-scoped (unless you use the bootstrap.js to
             * load your javascript, in which case it will be handled for you).
             */
            scopeResetCSS : Ext.buildSettings.scopeResetCSS,

            /**
             * @property {String} resetCls
             * The css class used to wrap Ext components when the {@link #scopeResetCSS} option
             * is used.
             */
            resetCls: Ext.buildSettings.baseCSSPrefix + 'reset',

            /**
             * @property {Boolean} enableNestedListenerRemoval
             * **Experimental.** True to cascade listener removal to child elements when an element
             * is removed. Currently not optimized for performance.
             */
            enableNestedListenerRemoval : false,

            /**
             * @property {Boolean} USE_NATIVE_JSON
             * Indicates whether to use native browser parsing for JSON methods.
             * This option is ignored if the browser does not support native JSON methods.
             *
             * **Note:** Native JSON methods will not work with objects that have functions.
             * Also, property names must be quoted, otherwise the data will not parse.
             */
            USE_NATIVE_JSON : false,

            /**
             * Returns the dom node for the passed String (id), dom node, or Ext.Element.
             * Optional 'strict' flag is needed for IE since it can return 'name' and
             * 'id' elements by using getElementById.
             *
             * Here are some examples:
             *
             *     // gets dom node based on id
             *     var elDom = Ext.getDom('elId');
             *     // gets dom node based on the dom node
             *     var elDom1 = Ext.getDom(elDom);
             *
             *     // If we don&#39;t know if we are working with an
             *     // Ext.Element or a dom node use Ext.getDom
             *     function(el){
             *         var dom = Ext.getDom(el);
             *         // do something with the dom node
             *     }
             *
             * **Note:** the dom node to be found actually needs to exist (be rendered, etc)
             * when this method is called to be successful.
             *
             * @param {String/HTMLElement/Ext.Element} el
             * @return HTMLElement
             */
            getDom : function(el, strict) {
                if (!el || !document) {
                    return null;
                }
                if (el.dom) {
                    return el.dom;
                } else {
                    if (typeof el == 'string') {
                        var e = Ext.getElementById(el);
                        // IE returns elements with the 'name' and 'id' attribute.
                        // we do a strict check to return the element with only the id attribute
                        if (e && isIE && strict) {
                            if (el == e.getAttribute('id')) {
                                return e;
                            } else {
                                return null;
                            }
                        }
                        return e;
                    } else {
                        return el;
                    }
                }
            },

            /**
             * Removes a DOM node from the document.
             *
             * Removes this element from the document, removes all DOM event listeners, and
             * deletes the cache reference. All DOM event listeners are removed from this element.
             * If {@link Ext#enableNestedListenerRemoval Ext.enableNestedListenerRemoval} is
             * `true`, then DOM event listeners are also removed from all child nodes.
             * The body node will be ignored if passed in.
             *
             * @param {HTMLElement} node The node to remove
             * @method
             */
            removeNode : isIE6 || isIE7 || isIE8
                ? (function() {
                    var d;
                    return function(n){
                        if(n && n.tagName.toUpperCase() != 'BODY'){
                            (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);

                            var cache = Ext.cache,
                                id = n.id;

                            if (cache[id]) {
                                delete cache[id].dom;
                                delete cache[id];
                            }

                            // removing an iframe this way can cause severe leaks
                            // fixes leak issue with htmleditor in themes example
                            if (n.tagName.toUpperCase() != 'IFRAME') {
                                if (isIE8 && n.parentNode) {
                                    n.parentNode.removeChild(n);
                                }
                                d = d || document.createElement('div');
                                d.appendChild(n);
                                d.innerHTML = '';
                            }
                        }
                    };
                }())
                : function(n) {
                    if (n && n.parentNode && n.tagName.toUpperCase() != 'BODY') {
                        (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);

                        var cache = Ext.cache,
                            id = n.id;

                        if (cache[id]) {
                            delete cache[id].dom;
                            delete cache[id];
                        }

                        n.parentNode.removeChild(n);
                    }
                },

            isStrict: isStrict,

            isIEQuirks: isIE && !isStrict,

            /**
             * True if the detected browser is Opera.
             * @type Boolean
             */
            isOpera : isOpera,

            /**
             * True if the detected browser is Opera 10.5x.
             * @type Boolean
             */
            isOpera10_5 : isOpera10_5,

            /**
             * True if the detected browser uses WebKit.
             * @type Boolean
             */
            isWebKit : isWebKit,

            /**
             * True if the detected browser is Chrome.
             * @type Boolean
             */
            isChrome : isChrome,

            /**
             * True if the detected browser is Safari.
             * @type Boolean
             */
            isSafari : isSafari,

            /**
             * True if the detected browser is Safari 3.x.
             * @type Boolean
             */
            isSafari3 : isSafari3,

            /**
             * True if the detected browser is Safari 4.x.
             * @type Boolean
             */
            isSafari4 : isSafari4,

            /**
             * True if the detected browser is Safari 5.x.
             * @type Boolean
             */
            isSafari5 : isSafari5,

            /**
             * True if the detected browser is Safari 5.0.x.
             * @type Boolean
             */
            isSafari5_0 : isSafari5_0,

            /**
             * True if the detected browser is Safari 2.x.
             * @type Boolean
             */
            isSafari2 : isSafari2,

            /**
             * True if the detected browser is Internet Explorer.
             * @type Boolean
             */
            isIE : isIE,

            /**
             * True if the detected browser is Internet Explorer 6.x.
             * @type Boolean
             */
            isIE6 : isIE6,

            /**
             * True if the detected browser is Internet Explorer 7.x.
             * @type Boolean
             */
            isIE7 : isIE7,

            /**
             * True if the detected browser is Internet Explorer 8.x.
             * @type Boolean
             */
            isIE8 : isIE8,

            /**
             * True if the detected browser is Internet Explorer 9.x.
             * @type Boolean
             */
            isIE9 : isIE9,

            /**
             * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
             * @type Boolean
             */
            isGecko : isGecko,

            /**
             * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
             * @type Boolean
             */
            isGecko3 : isGecko3,

            /**
             * True if the detected browser uses a Gecko 2.0+ layout engine (e.g. Firefox 4.x).
             * @type Boolean
             */
            isGecko4 : isGecko4,

            /**
             * True if the detected browser uses a Gecko 5.0+ layout engine (e.g. Firefox 5.x).
             * @type Boolean
             */
            isGecko5 : isGecko5,

            /**
             * True if the detected browser uses a Gecko 5.0+ layout engine (e.g. Firefox 5.x).
             * @type Boolean
             */
            isGecko10 : isGecko10,

            /**
             * True if the detected browser uses FireFox 3.0
             * @type Boolean
             */
            isFF3_0 : isFF3_0,

            /**
             * True if the detected browser uses FireFox 3.5
             * @type Boolean
             */
            isFF3_5 : isFF3_5,

            /**
             * True if the detected browser uses FireFox 3.6
             * @type Boolean
             */
            isFF3_6 : isFF3_6,

            /**
             * True if the detected browser uses FireFox 4
             * @type Boolean
             */
            isFF4 : 4 <= firefoxVersion && firefoxVersion < 5,

            /**
             * True if the detected browser uses FireFox 5
             * @type Boolean
             */
            isFF5 : 5 <= firefoxVersion && firefoxVersion < 6,

            /**
             * True if the detected browser uses FireFox 10
             * @type Boolean
             */
            isFF10 : 10 <= firefoxVersion && firefoxVersion < 11,

            /**
             * True if the detected platform is Linux.
             * @type Boolean
             */
            isLinux : isLinux,

            /**
             * True if the detected platform is Windows.
             * @type Boolean
             */
            isWindows : isWindows,

            /**
             * True if the detected platform is Mac OS.
             * @type Boolean
             */
            isMac : isMac,

            /**
             * The current version of Chrome (0 if the browser is not Chrome).
             * @type Number
             */
            chromeVersion: chromeVersion,

            /**
             * The current version of Firefox (0 if the browser is not Firefox).
             * @type Number
             */
            firefoxVersion: firefoxVersion,

            /**
             * The current version of IE (0 if the browser is not IE). This does not account
             * for the documentMode of the current page, which is factored into {@link #isIE7},
             * {@link #isIE8} and {@link #isIE9}. Thus this is not always true:
             *
             *     Ext.isIE8 == (Ext.ieVersion == 8)
             *
             * @type Number
             */
            ieVersion: ieVersion,

            /**
             * The current version of Opera (0 if the browser is not Opera).
             * @type Number
             */
            operaVersion: operaVersion,

            /**
             * The current version of Safari (0 if the browser is not Safari).
             * @type Number
             */
            safariVersion: safariVersion,

            /**
             * The current version of WebKit (0 if the browser does not use WebKit).
             * @type Number
             */
            webKitVersion: webKitVersion,

            /**
             * True if the page is running over SSL
             * @type Boolean
             */
            isSecure: isSecure,

            /**
             * URL to a 1x1 transparent gif image used by Ext to create inline icons with
             * CSS background images. In older versions of IE, this defaults to
             * "http://sencha.com/s.gif" and you should change this to a URL on your server.
             * For other browsers it uses an inline data URL.
             * @type String
             */
            BLANK_IMAGE_URL : (isIE6 || isIE7) ? '/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',

            /**
             * Utility method for returning a default value if the passed value is empty.
             *
             * The value is deemed to be empty if it is:
             *
             * - null
             * - undefined
             * - an empty array
             * - a zero length string (Unless the `allowBlank` parameter is `true`)
             *
             * @param {Object} value The value to test
             * @param {Object} defaultValue The value to return if the original value is empty
             * @param {Boolean} [allowBlank=false] true to allow zero length strings to qualify as non-empty.
             * @return {Object} value, if non-empty, else defaultValue
             * @deprecated 4.0.0 Use {@link Ext#valueFrom} instead
             */
            value : function(v, defaultValue, allowBlank){
                return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
            },

            /**
             * Escapes the passed string for use in a regular expression.
             * @param {String} str
             * @return {String}
             * @deprecated 4.0.0 Use {@link Ext.String#escapeRegex} instead
             */
            escapeRe : function(s) {
                return s.replace(/([-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
            },

            /**
             * Applies event listeners to elements by selectors when the document is ready.
             * The event name is specified with an `@` suffix.
             *
             *     Ext.addBehaviors({
             *         // add a listener for click on all anchors in element with id foo
             *         '#foo a@click' : function(e, t){
             *             // do something
             *         },
             *
             *         // add the same listener to multiple selectors (separated by comma BEFORE the @)
             *         '#foo a, #bar span.some-class@mouseover' : function(){
             *             // do something
             *         }
             *     });
             *
             * @param {Object} obj The list of behaviors to apply
             */
            addBehaviors : function(o){
                if(!Ext.isReady){
                    Ext.onReady(function(){
                        Ext.addBehaviors(o);
                    });
                } else {
                    var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
                        parts,
                        b,
                        s;
                    for (b in o) {
                        if ((parts = b.split('@'))[1]) { // for Object prototype breakers
                            s = parts[0];
                            if(!cache[s]){
                                cache[s] = Ext.select(s);
                            }
                            cache[s].on(parts[1], o[b]);
                        }
                    }
                    cache = null;
                }
            },

            /**
             * Returns the size of the browser scrollbars. This can differ depending on
             * operating system settings, such as the theme or font size.
             * @param {Boolean} [force] true to force a recalculation of the value.
             * @return {Object} An object containing scrollbar sizes.
             * @return.width {Number} The width of the vertical scrollbar.
             * @return.height {Number} The height of the horizontal scrollbar.
             */
            getScrollbarSize: function (force) {
                if (!Ext.isReady) {
                    return {};
                }

                if (force || !scrollbarSize) {
                    var db = document.body,
                        div = document.createElement('div');

                    div.style.width = div.style.height = '100px';
                    div.style.overflow = 'scroll';
                    div.style.position = 'absolute';

                    db.appendChild(div); // now we can measure the div...

                    // at least in iE9 the div is not 100px - the scrollbar size is removed!
                    scrollbarSize = {
                        width: div.offsetWidth - div.clientWidth,
                        height: div.offsetHeight - div.clientHeight
                    };

                    db.removeChild(div);
                }

                return scrollbarSize;
            },

            /**
             * Utility method for getting the width of the browser's vertical scrollbar. This
             * can differ depending on operating system settings, such as the theme or font size.
             *
             * This method is deprected in favor of {@link #getScrollbarSize}.
             *
             * @param {Boolean} [force] true to force a recalculation of the value.
             * @return {Number} The width of a vertical scrollbar.
             * @deprecated
             */
            getScrollBarWidth: function(force){
                var size = Ext.getScrollbarSize(force);
                return size.width + 2; // legacy fudge factor
            },

            /**
             * Copies a set of named properties fom the source object to the destination object.
             *
             * Example:
             *
             *     ImageComponent = Ext.extend(Ext.Component, {
             *         initComponent: function() {
             *             this.autoEl = { tag: 'img' };
             *             MyComponent.superclass.initComponent.apply(this, arguments);
             *             this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
             *         }
             *     });
             *
             * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
             *
             * @param {Object} dest The destination object.
             * @param {Object} source The source object.
             * @param {String/String[]} names Either an Array of property names, or a comma-delimited list
             * of property names to copy.
             * @param {Boolean} [usePrototypeKeys] Defaults to false. Pass true to copy keys off of the
             * prototype as well as the instance.
             * @return {Object} The modified object.
             */
            copyTo : function(dest, source, names, usePrototypeKeys){
                if(typeof names == 'string'){
                    names = names.split(/[,;\s]/);
                }

                var n,
                    nLen = names.length,
                    name;

                for(n = 0; n < nLen; n++) {
                    name = names[n];

                    if(usePrototypeKeys || source.hasOwnProperty(name)){
                        dest[name] = source[name];
                    }
                }

                return dest;
            },

            /**
             * Attempts to destroy and then remove a set of named properties of the passed object.
             * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
             * @param {String...} args One or more names of the properties to destroy and remove from the object.
             */
            destroyMembers : function(o){
                for (var i = 1, a = arguments, len = a.length; i < len; i++) {
                    Ext.destroy(o[a[i]]);
                    delete o[a[i]];
                }
            },

            /**
             * Logs a message. If a console is present it will be used. On Opera, the method
             * "opera.postError" is called. In other cases, the message is logged to an array
             * "Ext.log.out". An attached debugger can watch this array and view the log. The
             * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 250).
             * The `Ext.log.out` array can also be written to a popup window by entering the
             * following in the URL bar (a "bookmarklet"):
             *
             *    javascript:void(Ext.log.show());
             *
             * If additional parameters are passed, they are joined and appended to the message.
             * A technique for tracing entry and exit of a function is this:
             *
             *      function foo () {
             *          Ext.log({ indent: 1 }, '>> foo');
             *
             *          // log statements in here or methods called from here will be indented
             *          // by one step
             *
             *          Ext.log({ outdent: 1 }, '<< foo');
             *      }
             *
             * This method does nothing in a release build.
             *
             * @param {String/Object} message The message to log or an options object with any
             * of the following properties:
             *
             *  - `msg`: The message to log (required).
             *  - `level`: One of: "error", "warn", "info" or "log" (the default is "log").
             *  - `dump`: An object to dump to the log as part of the message.
             *  - `stack`: True to include a stack trace in the log.
             *  - `indent`: Cause subsequent log statements to be indented one step.
             *  - `outdent`: Cause this and following statements to be one step less indented.
             *
             * @method
             */
            log :
                //<debug>
                log ||
                //</debug>
                nullLog,

            /**
             * Partitions the set into two sets: a true set and a false set.
             *
             * Example 1:
             *
             *     Ext.partition([true, false, true, true, false]);
             *     // returns [[true, true, true], [false, false]]
             *
             * Example 2:
             *
             *     Ext.partition(
             *         Ext.query("p"),
             *         function(val){
             *             return val.className == "class1"
             *         }
             *     );
             *     // true are those paragraph elements with a className of "class1",
             *     // false set are those that do not have that className.
             *
             * @param {Array/NodeList} arr The array to partition
             * @param {Function} truth (optional) a function to determine truth.
             * If this is omitted the element itself must be able to be evaluated for its truthfulness.
             * @return {Array} [array of truish values, array of falsy values]
             * @deprecated 4.0.0 Will be removed in the next major version
             */
            partition : function(arr, truth){
                var ret = [[],[]],
                    a, v,
                    aLen = arr.length;

                for (a = 0; a < aLen; a++) {
                    v = arr[a];
                    ret[ (truth && truth(v, a, arr)) || (!truth && v) ? 0 : 1].push(v);
                }

                return ret;
            },

            /**
             * Invokes a method on each item in an Array.
             *
             * Example:
             *
             *     Ext.invoke(Ext.query("p"), "getAttribute", "id");
             *     // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
             *
             * @param {Array/NodeList} arr The Array of items to invoke the method on.
             * @param {String} methodName The method name to invoke.
             * @param {Object...} args Arguments to send into the method invocation.
             * @return {Array} The results of invoking the method on each item in the array.
             * @deprecated 4.0.0 Will be removed in the next major version
             */
            invoke : function(arr, methodName){
                var ret  = [],
                    args = Array.prototype.slice.call(arguments, 2),
                    a, v,
                    aLen = arr.length;

                for (a = 0; a < aLen; a++) {
                    v = arr[a];

                    if (v && typeof v[methodName] == 'function') {
                        ret.push(v[methodName].apply(v, args));
                    } else {
                        ret.push(undefined);
                    }
                }

                return ret;
            },

            /**
             * Zips N sets together.
             *
             * Example 1:
             *
             *     Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
             *
             * Example 2:
             *
             *     Ext.zip(
             *         [ "+", "-", "+"],
             *         [  12,  10,  22],
             *         [  43,  15,  96],
             *         function(a, b, c){
             *             return "$" + a + "" + b + "." + c
             *         }
             *     ); // ["$+12.43", "$-10.15", "$+22.96"]
             *
             * @param {Array/NodeList...} arr This argument may be repeated. Array(s)
             * to contribute values.
             * @param {Function} zipper (optional) The last item in the argument list.
             * This will drive how the items are zipped together.
             * @return {Array} The zipped set.
             * @deprecated 4.0.0 Will be removed in the next major version
             */
            zip : function(){
                var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
                    arrs = parts[0],
                    fn = parts[1][0],
                    len = Ext.max(Ext.pluck(arrs, "length")),
                    ret = [],
                    i,
                    j,
                    aLen;

                for (i = 0; i < len; i++) {
                    ret[i] = [];
                    if(fn){
                        ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
                    }else{
                        for (j = 0, aLen = arrs.length; j < aLen; j++){
                            ret[i].push( arrs[j][i] );
                        }
                    }
                }
                return ret;
            },

            /**
             * Turns an array into a sentence, joined by a specified connector - e.g.:
             *
             *     Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin'
             *     Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin'
             *
             * @param {String[]} items The array to create a sentence from
             * @param {String} connector The string to use to connect the last two words.
             * Usually 'and' or 'or' - defaults to 'and'.
             * @return {String} The sentence string
             * @deprecated 4.0.0 Will be removed in the next major version
             */
            toSentence: function(items, connector) {
                var length = items.length,
                    head,
                    tail;

                if (length <= 1) {
                    return items[0];
                } else {
                    head = items.slice(0, length - 1);
                    tail = items[length - 1];

                    return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
                }
            },

            /**
             * @property {Boolean} useShims
             * By default, Ext intelligently decides whether floating elements should be shimmed.
             * If you are using flash, you may want to set this to true.
             */
            useShims: isIE6
        });
    }());

    /**
     * Loads Ext.app.Application class and starts it up with given configuration after the page is ready.
     *
     * See Ext.app.Application for details.
     *
     * @param {Object} config
     */
    Ext.application = function(config) {
        Ext.require('Ext.app.Application');

        Ext.onReady(function() {
            new Ext.app.Application(config);
        });
    };