
(function(Ext) {
Ext.sandboxName = 'Ext5';
Ext.isSandboxed = true;
Ext.buildSettings = { baseCSSPrefix: "x5-", scopeResetCSS: true };
/*
This file is part of Ext JS 5.1.0.107

Copyright (c) 2011-2014 Sencha Inc

Contact:  http://www.sencha.com/contact

GNU General Public License Usage
This file may be used under the terms of the GNU General Public License version 3.0 as
published by the Free Software Foundation and appearing in the file LICENSE included in the
packaging of this file.

Please review the following information to ensure the GNU General Public License version 3.0
requirements will be met: http://www.gnu.org/copyleft/gpl.html.

If you are unsure which license is appropriate for your use, please contact the sales department
at http://www.sencha.com/contact.

Version: 5.1.0.107 Build date: 2014-12-10 14:09:03 (2e9aed02ab6d3ef7b0ec406edea1e8581413381e)

*/




var Ext = Ext || {};



Ext.Boot = Ext.Boot || (function (emptyFn) {

    var doc = document,
        apply = function (dest, src, defaults) {
            if (defaults) {
                apply(dest, defaults);
            }

            if (dest && src && typeof src == 'object') {
                for (var key in src) {
                    dest[key] = src[key];
                }
            }
            return dest;
        },
        _config = {
            
            disableCaching: (/[?&](?:cache|disableCacheBuster)\b/i.test(location.search) ||
                !(/http[s]?\:/i.test(location.href)) ||
                /(^|[ ;])ext-cache=1/.test(doc.cookie)) ? false :
                true,

            
            disableCachingParam: '_dc',

            
            loadDelay: false,

            
            preserveScripts: true,

            
            charset: undefined
        },

        cssRe = /\.css(?:\?|$)/i,
        resolverEl = doc.createElement('a'),
        isBrowser = typeof window !== 'undefined',
        _environment = {
            browser: isBrowser,
            node: !isBrowser && (typeof require === 'function'),
            phantom: (typeof phantom !== 'undefined' && phantom.fs)
        },
        _tags = (Ext.platformTags = {}),

    
        _debug = function (message) {
            
        },
    
        _apply = function (object, config, defaults) {
            if (defaults) {
                _apply(object, defaults);
            }
            if (object && config && typeof config === 'object') {
                for (var i in config) {
                    object[i] = config[i];
                }
            }
            return object;
        },
    
        Boot = {
            loading: 0,
            loaded: 0,
            env: _environment,
            config: _config,

            
            
            scripts: {
                
            },

            
            currentFile: null,
            suspendedQueue: [],
            currentRequest: null,

            
            
            syncMode: false,

            
            
            debug: _debug,
            

            
            useElements: true,

            listeners: [],

            Request: Request,

            Entry: Entry,

            
            detectPlatformTags: function () {
                var ua = navigator.userAgent,
                    isMobile = _tags.isMobile = /Mobile(\/|\s)/.test(ua),
                    isPhone, isDesktop, isTablet, touchSupported, isIE10, isBlackberry,
                    element = document.createElement('div'),
                    uaTagChecks = [
                        'iPhone',
                        'iPod',
                        'Android',
                        'Silk',
                        'Android 2',
                        'BlackBerry',
                        'BB',
                        'iPad',
                        'RIM Tablet OS',
                        'MSIE 10',
                        'Trident',
                        'Chrome',
                        'Tizen',
                        'Firefox',
                        'Safari',
                        'Windows Phone'
                    ],
                    isEventSupported = function(name, tag) {
                        if (tag === undefined) {
                            tag = window;
                        }

                        var eventName = 'on' + name.toLowerCase(),
                            isSupported = (eventName in element);

                        if (!isSupported) {
                            if (element.setAttribute && element.removeAttribute) {
                                element.setAttribute(eventName, '');
                                isSupported = typeof element[eventName] === 'function';

                                if (typeof element[eventName] !== 'undefined') {
                                    element[eventName] = undefined;
                                }

                                element.removeAttribute(eventName);
                            }
                        }

                        return isSupported;
                    },
                    uaTags = {},
                    len = uaTagChecks.length, check, c;

                for (c = 0; c < len; c++) {
                    check = uaTagChecks[c];
                    uaTags[check] = new RegExp(check).test(ua);
                }

                isPhone =
                    (uaTags.iPhone || uaTags.iPod) ||
                    (!uaTags.Silk && (uaTags.Android && (uaTags['Android 2'] || isMobile))) ||
                    ((uaTags.BlackBerry || uaTags.BB) && uaTags.isMobile) ||
                    (uaTags['Windows Phone']);

                isTablet =
                    (!_tags.isPhone) && (
                    uaTags.iPad ||
                    uaTags.Android ||
                    uaTags.Silk ||
                    uaTags['RIM Tablet OS'] ||
                    (uaTags['MSIE 10'] && /; Touch/.test(ua))
                    );

                touchSupported =
                    
                    
                    isEventSupported('touchend') ||
                    
                    
                    
                    navigator.maxTouchPoints ||
                    
                    navigator.msMaxTouchPoints;

                isDesktop = !isPhone && !isTablet;
                isIE10 = uaTags['MSIE 10'];
                isBlackberry = uaTags.Blackberry || uaTags.BB;

                apply(_tags, Boot.loadPlatformsParam(), {
                    phone: isPhone,
                    tablet: isTablet,
                    desktop: isDesktop,
                    touch: touchSupported,
                    ios: (uaTags.iPad || uaTags.iPhone || uaTags.iPod),
                    android: uaTags.Android || uaTags.Silk,
                    blackberry: isBlackberry,
                    safari: uaTags.Safari && !isBlackberry,
                    chrome: uaTags.Chrome,
                    ie10: isIE10,
                    windows: isIE10 || uaTags.Trident,
                    tizen: uaTags.Tizen,
                    firefox: uaTags.Firefox
                });
            },

            
            loadPlatformsParam: function () {
                
                var paramsString = window.location.search.substr(1),
                    paramsArray = paramsString.split("&"),
                    params = {}, i,
                    platforms = {},
                    tmpArray, tmplen, platform, name, enabled;

                for (i = 0; i < paramsArray.length; i++) {
                    tmpArray = paramsArray[i].split("=");
                    params[tmpArray[0]] = tmpArray[1];
                }

                if (params.platformTags) {
                    tmpArray = params.platform.split(/\W/);
                    for (tmplen = tmpArray.length, i = 0; i < tmplen; i++) {
                        platform = tmpArray[i].split(":");
                        name = platform[0];
                        if (platform.length > 1) {
                            enabled = platform[1];
                            if (enabled === 'false' || enabled === '0') {
                                enabled = false;
                            } else {
                                enabled = true;
                            }
                        }
                        platforms[name] = enabled;
                    }
                }
                return platform;
            },

            filterPlatform: function (platform) {
                platform = [].concat(platform);
                var len, p, tag;

                for (len = platform.length, p = 0; p < len; p++) {
                    tag = platform[p];
                    if (_tags.hasOwnProperty(tag)) {
                        return !!_tags[tag];
                    }
                }
                return false;
            },

            init: function () {
                var scriptEls = doc.getElementsByTagName('script'),
                    len = scriptEls.length,
                    re = /\/ext(\-[a-z\-]+)?\.js$/,
                    entry, script, src, state, baseUrl, key, n, origin;

                
                
                
                for (n = 0; n < len; n++) {
                    src = (script = scriptEls[n]).src;
                    if (!src) {
                        continue;
                    }
                    state = script.readyState || null;

                    
                    if (!baseUrl) {
                        if (re.test(src)) {
                            Boot.hasReadyState = ("readyState" in script);
                            Boot.hasAsync = ("async" in script) || !Boot.hasReadyState;
                            baseUrl = src;
                        }
                    }

                    if (!Boot.scripts[key = Boot.canonicalUrl(src)]) {
                        
                        _debug("creating entry " + key + " in Boot.init");
                        
                        entry = new Entry({
                            key: key,
                            url: src,
                            done: state === null ||  
                                state === 'loaded' || state === 'complete', 
                            el: script,
                            prop: 'src'
                        });
                    }
                }

                if (!baseUrl) {
                    script = scriptEls[scriptEls.length - 1];
                    baseUrl = script.src;
                    Boot.hasReadyState = ('readyState' in script);
                    Boot.hasAsync = ("async" in script) || !Boot.hasReadyState;
                }

                Boot.baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
                origin = window.location.origin ||
                    window.location.protocol +
                    "//" +
                    window.location.hostnaBoot +
                    (window.location.port ? ':' + window.location.port: '');
                Boot.origin = origin;

                Boot.detectPlatformTags();
                Ext.filterPlatform = Boot.filterPlatform;
            },

            
            canonicalUrl: function (url) {
                
                
                resolverEl.href = url;

                var ret = resolverEl.href,
                    dc = _config.disableCachingParam,
                    pos = dc ? ret.indexOf(dc + '=') : -1,
                    c, end;

                
                
                if (pos > 0 && ((c = ret.charAt(pos - 1)) === '?' || c === '&')) {
                    end = ret.indexOf('&', pos);
                    end = (end < 0) ? '' : ret.substring(end);
                    if (end && c === '?') {
                        ++pos; 
                        end = end.substring(1); 
                    }
                    ret = ret.substring(0, pos - 1) + end;
                }

                return ret;
            },

            
            getConfig: function (name) {
                return name ? Boot.config[name] : Boot.config;
            },

            
            setConfig: function (name, value) {
                if (typeof name === 'string') {
                    Boot.config[name] = value;
                } else {
                    for (var s in name) {
                        Boot.setConfig(s, name[s]);
                    }
                }
                return Boot;
            },

            getHead: function () {
                return Boot.docHead ||
                    (Boot.docHead = doc.head ||
                        doc.getElementsByTagName('head')[0]);
            },

            create: function (url, key, cfg) {
                var config = cfg || {};
                config.url = url;
                config.key = key;
                return Boot.scripts[key] = new Entry(config);
            },

            getEntry: function (url, cfg) {
                var key = Boot.canonicalUrl(url),
                    entry = Boot.scripts[key];
                if (!entry) {
                    entry = Boot.create(url, key, cfg);
                }
                return entry;
            },

            processRequest: function(request, sync) {
                request.loadEntries(sync);
            },

            load: function (request) {
                
                _debug("Boot.load called");
                
                var request = new Request(request);

                if (request.sync || Boot.syncMode) {
                    return Boot.loadSync(request);
                }

                
                
                if (Boot.currentRequest) {
                    
                    _debug("current active request, suspending this request");
                    
                    
                    
                    
                    request.getEntries();
                    Boot.suspendedQueue.push(request);
                } else {
                    Boot.currentRequest = request;
                    Boot.processRequest(request, false);
                }
                return Boot;
            },

            loadSync: function (request) {
                
                _debug("Boot.loadSync called");
                
                var request = new Request(request);

                Boot.syncMode++;
                Boot.processRequest(request, true);
                Boot.syncMode--;
                return Boot;
            },

            loadBasePrefix: function(request) {
                request = new Request(request);
                request.prependBaseUrl = true;
                return Boot.load(request);
            },

            loadSyncBasePrefix: function(request) {
                request = new Request(request);
                request.prependBaseUrl = true;
                return Boot.loadSync(request);
            },

            requestComplete: function(request) {
                var next;

                if (Boot.currentRequest === request) {
                    Boot.currentRequest = null;
                    while(Boot.suspendedQueue.length > 0) {
                        next = Boot.suspendedQueue.shift();
                        if(!next.done) {
                            
                            _debug("resuming suspended request");
                            
                            Boot.load(next);
                            break;
                        }
                    }
                }
                if (!Boot.currentRequest && Boot.suspendedQueue.length == 0) {
                    Boot.fireListeners();
                }
            },

            isLoading: function () {
                return !Boot.currentRequest && Boot.suspendedQueue.length == 0;
            },

            fireListeners: function () {
                var listener;
                while (Boot.isLoading() && (listener = Boot.listeners.shift())) {
                    listener();
                }
            },

            onBootReady: function (listener) {
                if (!Boot.isLoading()) {
                    listener();
                } else {
                    Boot.listeners.push(listener);
                }
            },

            
            getPathsFromIndexes: function (indexMap, loadOrder) {
                return Request.prototype.getPathsFromIndexes(indexMap, loadOrder);
            },

            createLoadOrderMap: function(loadOrder) {
                return Request.prototype.createLoadOrderMap(loadOrder);
            },

            fetch: function(url, complete, scope, async) {
                async = (async === undefined) ? !!complete : async;

                var xhr = new XMLHttpRequest(),
                    result, status, content, exception = false,
                    readyStateChange = function () {
                        if (xhr && xhr.readyState == 4) {
                            status = (xhr.status === 1223) ? 204 :
                                (xhr.status === 0 && ((self.location || {}).protocol === 'file:' ||
                                    (self.location || {}).protocol === 'ionp:')) ? 200 : xhr.status;
                            content = xhr.responseText;
                            result = {
                                content: content,
                                status: status,
                                exception: exception
                            };
                            if (complete) {
                                complete.call(scope, result);
                            }
                            xhr = null;
                        }
                    };

                if (async) {
                    xhr.onreadystatechange = readyStateChange;
                }

                try {
                    
                    _debug("fetching " + url + " " + (async ? "async" : "sync"));
                    
                    xhr.open('GET', url, async);
                    xhr.send(null);
                } catch (err) {
                    exception = err;
                    readyStateChange();
                    return result;
                }

                if (!async) {
                    readyStateChange();
                }

                return result;
            },

            notifyAll: function(entry) {
                entry.notifyRequests();
            }
        };

    
    function Request(cfg) {
        if(cfg.$isRequest) {
            return cfg;
        }

        var cfg = cfg.url ? cfg : {url: cfg},
            url = cfg.url,
            urls = url.charAt ? [ url ] : url,
            charset = cfg.charset || Boot.config.charset;

        _apply(cfg, {
            urls: urls,
            charset: charset
        });
        _apply(this, cfg);
    };
    Request.prototype = {
        $isRequest: true,

        
        createLoadOrderMap: function (loadOrder) {
            var len = loadOrder.length,
                loadOrderMap = {},
                i, element;

            for (i = 0; i < len; i++) {
                element = loadOrder[i];
                loadOrderMap[element.path] = element;
            }

            return loadOrderMap;
        },

        
        getLoadIndexes: function (index, indexMap, loadOrder, includeUses, skipLoaded) {
            var item = loadOrder[index],
                len, i, reqs, entry, stop, added, idx, ridx, url;

            if (indexMap[index]) {
                
                return indexMap;
            }

            indexMap[index] = true;

            stop = false;
            while (!stop) {
                added = false;

                
                
                for (idx in indexMap) {
                    if (indexMap.hasOwnProperty(idx)) {
                        item = loadOrder[idx];
                        if (!item) {
                            continue;
                        }
                        url = this.prepareUrl(item.path);
                        entry = Boot.getEntry(url);
                        if (!skipLoaded || !entry || !entry.done) {
                            reqs = item.requires;
                            if (includeUses && item.uses) {
                                reqs = reqs.concat(item.uses);
                            }
                            for (len = reqs.length, i = 0; i < len; i++) {
                                ridx = reqs[i];
                                
                                
                                
                                
                                if (!indexMap[ridx]) {
                                    indexMap[ridx] = true;
                                    added = true;
                                }
                            }
                        }
                    }
                }

                
                
                if (!added) {
                    stop = true;
                }
            }

            return indexMap;
        },

        getPathsFromIndexes: function (indexMap, loadOrder) {
            var indexes = [],
                paths = [],
                index, len, i;

            for (index in indexMap) {
                if (indexMap.hasOwnProperty(index) && indexMap[index]) {
                    indexes.push(index);
                }
            }

            indexes.sort(function (a, b) {
                return a - b;
            });

            
            for (len = indexes.length, i = 0; i < len; i++) {
                paths.push(loadOrder[indexes[i]].path);
            }

            return paths;
        },

        expandUrl: function (url, indexMap, includeUses, skipLoaded) {
            if (typeof url == 'string') {
                url = [url];
            }

            var me = this,
                loadOrder = me.loadOrder,
                loadOrderMap = me.loadOrderMap;

            if (loadOrder) {
                loadOrderMap = loadOrderMap || me.createLoadOrderMap(loadOrder);
                me.loadOrderMap = loadOrderMap;
                indexMap = indexMap || {};
                var len = url.length,
                    unmapped = [],
                    i, item;

                for (i = 0; i < len; i++) {
                    item = loadOrderMap[url[i]];
                    if (item) {
                        me.getLoadIndexes(item.idx, indexMap, loadOrder, includeUses, skipLoaded);
                    } else {
                        unmapped.push(url[i]);
                    }
                }


                return me.getPathsFromIndexes(indexMap, loadOrder).concat(unmapped);
            }
            return url;
        },

        expandUrls: function (urls, includeUses) {
            if (typeof urls == "string") {
                urls = [urls];
            }

            var expanded = [],
                expandMap = {},
                tmpExpanded,
                len = urls.length,
                i, t, tlen, tUrl;

            for (i = 0; i < len; i++) {
                tmpExpanded = this.expandUrl(urls[i], {}, includeUses, true);
                for (t = 0, tlen = tmpExpanded.length; t < tlen; t++) {
                    tUrl = tmpExpanded[t];
                    if (!expandMap[tUrl]) {
                        expandMap[tUrl] = true;
                        expanded.push(tUrl);
                    }
                }
            }

            if (expanded.length == 0) {
                expanded = urls;
            }

            return expanded;
        },

        expandLoadOrder: function () {
            var me = this,
                urls = me.urls,
                expanded;

            if (!me.expanded) {
                expanded = this.expandUrls(urls, true);
                me.expanded = true;
            } else {
                expanded = urls;
            }

            me.urls = expanded;

            
            
            if (urls.length != expanded.length) {
                me.sequential = true;
            }

            return me;
        },

        getUrls: function () {
            this.expandLoadOrder();
            return this.urls;
        },

        prepareUrl: function(url) {
            if(this.prependBaseUrl) {
                return Boot.baseUrl + url;
            }
            return url;
        },

        getEntries: function () {
            var me = this,
                entries = me.entries,
                i, entry, urls, url;
            if (!entries) {
                entries = [];
                urls = me.getUrls();
                for (i = 0; i < urls.length; i++) {
                    url = me.prepareUrl(urls[i]);
                    entry = Boot.getEntry(url, {
                        buster: me.buster,
                        charset: me.charset
                    });
                    entry.requests.push(me);
                    entries.push(entry);
                }
                me.entries = entries;
            }
            return entries;
        },

        loadEntries: function(sync) {
            var me = this,
                entries = me.getEntries(),
                len = entries.length,
                start = me.loadStart || 0,
                continueLoad, entry, i;

            if(sync !== undefined) {
                me.sync = sync;
            }

            me.loaded = me.loaded || 0;
            me.loading = me.loading || len;

            for(i = start; i < len; i++) {
                entry = entries[i];
                if(!entry.loaded) {
                    continueLoad = entries[i].load(me.sync);
                } else {
                    continueLoad = true;
                }
                if(!continueLoad) {
                    me.loadStart = i;
                    entry.onDone(function(){
                        me.loadEntries(sync);
                    });
                    break;
                }
            }
            me.processLoadedEntries();
        },

        processLoadedEntries: function () {
            var me = this,
                entries = me.getEntries(),
                len = entries.length,
                start = me.startIndex || 0,
                i, entry;

            if (!me.done) {
                for (i = start; i < len; i++) {
                    entry = entries[i];

                    if (!entry.loaded) {
                        me.startIndex = i;
                        return;
                    }

                    if (!entry.evaluated) {
                        entry.evaluate();
                    }

                    if (entry.error) {
                        me.error = true;
                    }
                }
                me.notify();
            }
        },

        notify: function () {
            var me = this;
            if (!me.done) {
                var error = me.error,
                    fn = me[error ? 'failure' : 'success'],
                    delay = ('delay' in me)
                        ? me.delay
                        : (error ? 1 : Boot.config.chainDelay),
                    scope = me.scope || me;
                me.done = true;
                if (fn) {
                    if (delay === 0 || delay > 0) {
                        
                        setTimeout(function () {
                            fn.call(scope, me);
                        }, delay);
                    } else {
                        fn.call(scope, me);
                    }
                }
                me.fireListeners();
                Boot.requestComplete(me);
            }
        },

        onDone: function(listener) {
            var me = this,
                listeners = me.listeners || (me.listeners = []);
            if(me.done) {
                listener(me);
            } else {
                listeners.push(listener);
            }
        },

        fireListeners: function() {
            var listeners = this.listeners,
                listener;
            if(listeners) {
                
                _debug("firing request listeners");
                
                while((listener = listeners.shift())) {
                    listener(this);
                }
            }
        }
    };

    
    function Entry(cfg) {
        if(cfg.$isEntry) {
            return cfg;
        }

        
        _debug("creating entry for " + cfg.url);
        

        var charset = cfg.charset || Boot.config.charset,
            manifest = Ext.manifest,
            loader = manifest && manifest.loader,
            cache = (cfg.cache !== undefined) ? cfg.cache : (loader && loader.cache),
            buster, busterParam;

        if(cache === undefined) {
            cache = !Boot.config.disableCaching;
        }

        if(cache === false) {
            buster = +new Date();
        } else if(cache !== true) {
            buster = cache;
        }

        if(buster) {
            busterParam = (loader && loader.cacheParam) || Boot.config.disableCachingParam;
            buster = busterParam + "=" + buster;
        };

        _apply(cfg, {
            charset: charset,
            buster: buster,
            requests: []
        });
        _apply(this, cfg);
    };
    Entry.prototype = {
        $isEntry: true,
        done: false,
        evaluated: false,
        loaded: false,

        isCrossDomain: function() {
            var me = this;
            if(me.crossDomain === undefined) {
                
                _debug("checking " + me.getLoadUrl() + " for prefix " + Boot.origin);
                
                me.crossDomain = (me.getLoadUrl().indexOf(Boot.origin) !== 0);
            }
            return me.crossDomain;
        },

        isCss: function () {
            var me = this;
            if (me.css === undefined) {
                me.css = me.url && cssRe.test(me.url);
            }
            return this.css;
        },

        getElement: function (tag) {
            var me = this,
                el = me.el;
            if (!el) {
                
                _debug("creating element for " + me.url);
                
                if (me.isCss()) {
                    tag = tag || "link";
                    el = doc.createElement(tag);
                    if(tag == "link") {
                        el.rel = 'stylesheet';
                        me.prop = 'href';
                    } else {
                        me.prop="textContent";
                    }
                    el.type = "text/css";
                } else {
                    tag = tag || "script";
                    el = doc.createElement(tag);
                    el.type = 'text/javascript';
                    me.prop = 'src';
                    if (Boot.hasAsync) {
                        el.async = false;
                    }
                }
                me.el = el;
            }
            return el;
        },

        getLoadUrl: function () {
            var me = this,
                url = Boot.canonicalUrl(me.url);
            if (!me.loadUrl) {
                me.loadUrl = !!me.buster
                    ? (url + (url.indexOf('?') === -1 ? '?' : '&') + me.buster)
                    : url;
            }
            return me.loadUrl;
        },

        fetch: function (req) {
            var url = this.getLoadUrl(),
                async = !!req.async,
                complete = req.complete;

            Boot.fetch(url, complete, this, async);
        },

        onContentLoaded: function (response) {
            var me = this,
                status = response.status,
                content = response.content,
                exception = response.exception,
                url = this.getLoadUrl();
            me.loaded = true;
            if ((exception || status === 0) && !_environment.phantom) {
                me.error =
                    
                    ("Failed loading synchronously via XHR: '" + url +
                        "'. It's likely that the file is either being loaded from a " +
                        "different domain or from the local file system where cross " +
                        "origin requests are not allowed for security reasons. Try " +
                        "asynchronous loading instead.") ||
                    
                    true;
                me.evaluated = true;
            }
            else if ((status >= 200 && status < 300) || status === 304
                || _environment.phantom
                || (status === 0 && content.length > 0)
                ) {
                me.content = content;
            }
            else {
                me.error =
                    
                    ("Failed loading synchronously via XHR: '" + url +
                        "'. Please verify that the file exists. XHR status code: " +
                        status) ||
                    
                    true;
                me.evaluated = true;
            }
        },

        createLoadElement: function(callback) {
            var me = this,
                el = me.getElement(),
                readyStateChange = function(){
                    if (this.readyState === 'loaded' || this.readyState === 'complete') {
                        if(callback) {
                            callback();
                        }
                    }
                },
                errorFn = function() {
                    me.error = true;
                    if(callback) {
                        callback();
                    }
                };
            me.preserve = true;
            el.onerror = errorFn;
            if(Boot.hasReadyState) {
                el.onreadystatechange = readyStateChange;
            } else {
                el.onload = callback;
            }
            
            el[me.prop] = me.getLoadUrl();
        },

        onLoadElementReady: function() {
            Boot.getHead().appendChild(this.getElement());
            this.evaluated = true;
        },

        inject: function (content, asset) {
            
            _debug("injecting content for " + this.url);
            
            var me = this,
                head = Boot.getHead(),
                url = me.url,
                key = me.key,
                base, el, ieMode, basePath;

            if (me.isCss()) {
                me.preserve = true;
                basePath = key.substring(0, key.lastIndexOf("/") + 1);
                base = doc.createElement('base');
                base.href = basePath;
                if(head.firstChild) {
                    head.insertBefore(base, head.firstChild);
                } else {
                    head.appendChild(base);
                }
                
                base.href = base.href;

                if (url) {
                    content += "\n/*# sourceURL=" + key + " */";
                }

                
                el = me.getElement("style");

                ieMode = ('styleSheet' in el);

                head.appendChild(base);
                if(ieMode) {
                    head.appendChild(el);
                    el.styleSheet.cssText = content;
                } else {
                    el.textContent = content;
                    head.appendChild(el);
                }
                head.removeChild(base);

            } else {
                
                
                
                if (url) {
                    content += "\n//# sourceURL=" + key;
                }
                Ext.globalEval(content);
            }
            return me;
        },

        loadCrossDomain: function() {
            var me = this,
                complete = function(){
                    me.loaded = me.evaluated = me.done = true;
                    me.notifyRequests();
                };
            if(me.isCss()) {
                me.createLoadElement();
                me.evaluateLoadElement();
                complete();
            } else {
                me.createLoadElement(function(){
                    complete();
                });
                me.evaluateLoadElement();
                
                
                
                return false;
            }
            return true;
        },

        loadElement: function() {
            var me = this,
                complete = function(){
                    me.loaded = me.evaluated = me.done = true;
                    me.notifyRequests();
                };
            if(me.isCss()) {
                return me.loadCrossDomain();
            } else {
                me.createLoadElement(function(){
                    complete();
                });
                me.evaluateLoadElement();
            }
            return true;
        },

        loadSync: function() {
            var me = this;
            me.fetch({
                async: false,
                complete: function (response) {
                    me.onContentLoaded(response);
                }
            });
            me.evaluate();
            me.notifyRequests();
        },

        load: function (sync) {
            var me = this;
            if (!me.loaded) {
                if(me.loading) {
                    
                    
                    
                    
                    
                    
                    
                    return false;
                }
                me.loading = true;

                
                if (!sync) {
                    
                    
                    if(me.isCrossDomain()) {
                        return me.loadCrossDomain();
                    }
                    
                    
                    
                    else if(!me.isCss() && Boot.hasReadyState) {
                        me.createLoadElement(function () {
                            me.loaded = true;
                            me.notifyRequests();
                        });
                    }

                    else if(Boot.useElements) {
                        return me.loadElement();
                    }
                    
                    
                    else {
                        me.fetch({
                            async: !sync,
                            complete: function (response) {
                                me.onContentLoaded(response);
                                me.notifyRequests();
                            }
                        });
                    }
                }

                
                
                
                else {
                    me.loadSync();
                }
            }
            
            return true;
        },

        evaluateContent: function () {
            this.inject(this.content);
            this.content = null;
        },

        evaluateLoadElement: function() {
            Boot.getHead().appendChild(this.getElement());
        },

        evaluate: function () {
            var me = this;
            if(!me.evaluated) {
                if(me.evaluating) {
                    return;
                }
                me.evaluating = true;
                if(me.content !== undefined) {
                    me.evaluateContent();
                } else if(!me.error) {
                    me.evaluateLoadElement();
                }
                me.evaluated = me.done = true;
                me.cleanup();
            }
        },

        
        cleanup: function () {
            var me = this,
                el = me.el,
                prop;

            if (!el) {
                return;
            }

            if (!me.preserve) {
                me.el = null;

                el.parentNode.removeChild(el); 

                for (prop in el) {
                    try {
                        if (prop !== me.prop) {
                            
                            
                            el[prop] = null;
                        }
                        delete el[prop];      
                    } catch (cleanEx) {
                        
                    }
                }
            }

            
            
            
            el.onload = el.onerror = el.onreadystatechange = emptyFn;
        },

        notifyRequests: function () {
            var requests = this.requests,
                len = requests.length,
                i, request;
            for (i = 0; i < len; i++) {
                request = requests[i];
                request.processLoadedEntries();
            }
            if(this.done) {
                this.fireListeners();
            }
        },

        onDone: function(listener) {
            var me = this,
                listeners = me.listeners || (me.listeners = []);
            if(me.done) {
                listener(me);
            } else {
                listeners.push(listener);
            }
        },

        fireListeners: function() {
            var listeners = this.listeners,
                listener;
            if(listeners && listeners.length > 0) {
                
                _debug("firing event listeners for url " + this.url);
                
                while((listener = listeners.shift())) {
                    listener(this);
                }
            }
        }
    };

    
    Ext.disableCacheBuster = function (disable, path) {
        var date = new Date();
        date.setTime(date.getTime() + (disable ? 10 * 365 : -1) * 24 * 60 * 60 * 1000);
        date = date.toGMTString();
        doc.cookie = 'ext-cache=1; expires=' + date + '; path=' + (path || '/');
    };


    if (_environment.node) {
        Boot.prototype.load = Boot.prototype.loadSync = function (request) {
            
            require(filePath);
            onLoad.call(scope);
        };
        Boot.prototype.init = emptyFn;
    }


    Boot.init();
    return Boot;



}(function () {
}));


Ext.globalEval = Ext.globalEval || (this.execScript
    ? function (code) { execScript(code); }
    : function ($$code) { eval.call(window, $$code); });



if (!Function.prototype.bind) {
    (function () {
        var slice = Array.prototype.slice,
        
        
            bind = function (me) {
                var args = slice.call(arguments, 1),
                    method = this;

                if (args.length) {
                    return function () {
                        var t = arguments;
                        
                        return method.apply(me, t.length ? args.concat(slice.call(t)) : args);
                    };
                }
                

                args = null;
                return function () {
                    return method.apply(me, arguments);
                };
            };
        Function.prototype.bind = bind;
        bind.$extjs = true; 
    }());
}





var Ext = Ext || {};

Ext._startTime = Date.now ? Date.now() : (+new Date());
(function() {
    var global = this,
        objectPrototype = Object.prototype,
        toString = objectPrototype.toString,
        enumerables = [
            
            'valueOf',
            'toLocaleString',
            'toString',
            'constructor'
        ],
        emptyFn = function() {},
        privateFn = function() {},
        identityFn = function(o) {
            return o;
        },
        
        
        callOverrideParent = function() {
            var method = callOverrideParent.caller.caller;
            
            return method.$owner.prototype[method.$name].apply(this, arguments);
        },
        manifest = Ext.manifest || {},
        i,
        iterableRe = /\[object\s*(?:Array|Arguments|\w*Collection|\w*List|HTML\s+document\.all\s+class)\]/,
        MSDateRe = /^\\?\/Date\(([-+])?(\d+)(?:[+-]\d{4})?\)\\?\/$/;
    Ext.global = global;
    
    emptyFn.$nullFn = identityFn.$nullFn = emptyFn.$emptyFn = identityFn.$identityFn = privateFn.$nullFn = true;
    privateFn.$privacy = 'framework';
    
    
    Ext['suspendLayouts'] = Ext['resumeLayouts'] = emptyFn;
    for (i in {
        toString: 1
    }) {
        enumerables = null;
    }
    
    Ext.enumerables = enumerables;
    
    Ext.apply = function(object, config, defaults) {
        if (defaults) {
            Ext.apply(object, defaults);
        }
        if (object && config && typeof config === 'object') {
            var i, j, k;
            for (i in config) {
                object[i] = config[i];
            }
            if (enumerables) {
                for (j = enumerables.length; j--; ) {
                    k = enumerables[j];
                    if (config.hasOwnProperty(k)) {
                        object[k] = config[k];
                    }
                }
            }
        }
        return object;
    };
    Ext.buildSettings = Ext.apply({
        baseCSSPrefix: 'x-'
    }, Ext.buildSettings || {});
    Ext.apply(Ext, {
        
        idSeed: 0,
        
        idPrefix: 'ext-',
        
        isSecure: /^https/i.test(window.location.protocol),
        
        enableGarbageCollector: false,
        
        enableListenerCollection: true,
        
        name: Ext.sandboxName || 'Ext',
        
        privateFn: privateFn,
        
        emptyFn: emptyFn,
        
        identityFn: identityFn,
        
        frameStartTime: +new Date(),
        
        manifest: manifest,
        
        debugConfig: Ext.debugConfig || manifest.debug || {
            hooks: {
                '*': true
            }
        },
        
        validIdRe: /^[a-z_][a-z0-9\-_]*$/i,
        
        BLANK_IMAGE_URL: 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
        
        makeIdSelector: function(id) {
            if (!Ext.validIdRe.test(id)) {
                Ext.Error.raise('Invalid id selector: "' + id + '"');
            }
            return '#' + id;
        },
        
        id: function(o, prefix) {
            if (o && o.id) {
                return o.id;
            }
            var id = (prefix || Ext.idPrefix) + (++Ext.idSeed);
            if (o) {
                o.id = id;
            }
            return id;
        },
        
        returnId: function(o) {
            return o.getId();
        },
        
        returnTrue: function() {
            return true;
        },
        
        emptyString: new String(),
        
        baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
        
        $eventNameMap: {},
        $vendorEventRe: /^(Moz.+|MS.+)/,
        
        
        
        canonicalEventName: function(name) {
            return Ext.$eventNameMap[name] || (Ext.$eventNameMap[name] = (Ext.$vendorEventRe.test(name) ? name : name.toLowerCase()));
        },
        
        applyIf: function(object, config) {
            var property;
            if (object) {
                for (property in config) {
                    if (object[property] === undefined) {
                        object[property] = config[property];
                    }
                }
            }
            return object;
        },
        
        now: (global.performance && global.performance.now) ? function() {
            return performance.now();
        } : (Date.now || (Date.now = function() {
            return +new Date();
        })),
        
        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();
                    }
                }
            }
            return null;
        },
        
        destroyMembers: function(object) {
            for (var ref, name,
                i = 1,
                a = arguments,
                len = a.length; i < len; i++) {
                ref = object[name = a[i]];
                
                if (ref != null) {
                    object[name] = Ext.destroy(ref);
                }
            }
        },
        
        override: function(target, overrides) {
            if (target.$isClass) {
                target.override(overrides);
            } else if (typeof target == 'function') {
                Ext.apply(target.prototype, overrides);
            } else {
                var owner = target.self,
                    name, value;
                if (owner && owner.$isClass) {
                    
                    for (name in overrides) {
                        if (overrides.hasOwnProperty(name)) {
                            value = overrides[name];
                            if (typeof value === 'function') {
                                if (owner.$className) {
                                    value.name = owner.$className + '#' + name;
                                }
                                value.$name = name;
                                value.$owner = owner;
                                value.$previous = target.hasOwnProperty(name) ? target[name] : 
                                callOverrideParent;
                            }
                            
                            target[name] = value;
                        }
                    }
                } else {
                    Ext.apply(target, overrides);
                }
            }
            return target;
        },
        
        valueFrom: function(value, defaultValue, allowBlank) {
            return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
        },
        
        isEmpty: function(value, allowEmptyString) {
            return (value == null) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
        },
        
        isArray: ('isArray' in Array) ? Array.isArray : function(value) {
            return toString.call(value) === '[object Array]';
        },
        
        isDate: function(value) {
            return toString.call(value) === '[object Date]';
        },
        
        isMSDate: function(value) {
            if (!Ext.isString(value)) {
                return false;
            }
            return MSDateRe.test(value);
        },
        
        isObject: (toString.call(null) === '[object Object]') ? function(value) {
            
            return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
        } : function(value) {
            return toString.call(value) === '[object Object]';
        },
        
        isSimpleObject: function(value) {
            return value instanceof Object && value.constructor === Object;
        },
        
        isPrimitive: function(value) {
            var type = typeof value;
            return type === 'string' || type === 'number' || type === 'boolean';
        },
        
        isFunction: 
        
        (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
            return !!value && toString.call(value) === '[object Function]';
        } : function(value) {
            return !!value && typeof value === 'function';
        },
        
        isNumber: function(value) {
            return typeof value === 'number' && isFinite(value);
        },
        
        isNumeric: function(value) {
            return !isNaN(parseFloat(value)) && isFinite(value);
        },
        
        isString: function(value) {
            return typeof value === 'string';
        },
        
        isBoolean: function(value) {
            return typeof value === 'boolean';
        },
        
        isElement: function(value) {
            return value ? value.nodeType === 1 : false;
        },
        
        isTextNode: function(value) {
            return value ? value.nodeName === "#text" : false;
        },
        
        isDefined: function(value) {
            return typeof value !== 'undefined';
        },
        
        isIterable: function(value) {
            
            if (!value || typeof value.length !== 'number' || typeof value === 'string' || Ext.isFunction(value)) {
                return false;
            }
            
            
            
            if (!value.propertyIsEnumerable) {
                return !!value.item;
            }
            
            
            if (value.hasOwnProperty('length') && !value.propertyIsEnumerable('length')) {
                return true;
            }
            
            return iterableRe.test(toString.call(value));
        },
        
        isDebugEnabled: function(className, defaultEnabled) {
            var debugConfig = Ext.debugConfig.hooks;
            if (debugConfig.hasOwnProperty(className)) {
                return debugConfig[className];
            }
            var enabled = debugConfig['*'],
                prefixLength = 0;
            if (defaultEnabled !== undefined) {
                enabled = defaultEnabled;
            }
            if (!className) {
                return enabled;
            }
            for (var prefix in debugConfig) {
                var value = debugConfig[prefix];
                
                if (className.charAt(prefix.length) === '.') {
                    if (className.substring(0, prefix.length) === prefix) {
                        if (prefixLength < prefix.length) {
                            prefixLength = prefix.length;
                            enabled = value;
                        }
                    }
                }
            }
            return enabled;
        } || emptyFn,
        
        clone: function(item) {
            if (item === null || item === undefined) {
                return item;
            }
            
            
            
            if (item.nodeType && item.cloneNode) {
                return item.cloneNode(true);
            }
            var type = toString.call(item),
                i, j, k, clone, key;
            
            if (type === '[object Date]') {
                return new Date(item.getTime());
            }
            
            if (type === '[object Array]') {
                i = item.length;
                clone = [];
                while (i--) {
                    clone[i] = Ext.clone(item[i]);
                }
            }
            
            else if (type === '[object Object]' && item.constructor === Object) {
                clone = {};
                for (key in item) {
                    clone[key] = Ext.clone(item[key]);
                }
                if (enumerables) {
                    for (j = enumerables.length; j--; ) {
                        k = enumerables[j];
                        if (item.hasOwnProperty(k)) {
                            clone[k] = item[k];
                        }
                    }
                }
            }
            return clone || item;
        },
        
        getUniqueGlobalNamespace: function() {
            var uniqueGlobalNamespace = this.uniqueGlobalNamespace,
                i;
            if (uniqueGlobalNamespace === undefined) {
                i = 0;
                do {
                    uniqueGlobalNamespace = 'ExtBox' + (++i);
                } while (global[uniqueGlobalNamespace] !== undefined);
                global[uniqueGlobalNamespace] = Ext;
                this.uniqueGlobalNamespace = uniqueGlobalNamespace;
            }
            return uniqueGlobalNamespace;
        },
        
        functionFactoryCache: {},
        cacheableFunctionFactory: function() {
            var me = this,
                args = Array.prototype.slice.call(arguments),
                cache = me.functionFactoryCache,
                idx, fn, ln;
            if (Ext.isSandboxed) {
                ln = args.length;
                if (ln > 0) {
                    ln--;
                    args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
                }
            }
            idx = args.join('');
            fn = cache[idx];
            if (!fn) {
                fn = Function.prototype.constructor.apply(Function.prototype, args);
                cache[idx] = fn;
            }
            return fn;
        },
        functionFactory: function() {
            var args = Array.prototype.slice.call(arguments),
                ln;
            if (Ext.isSandboxed) {
                ln = args.length;
                if (ln > 0) {
                    ln--;
                    args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
                }
            }
            return Function.prototype.constructor.apply(Function.prototype, args);
        },
        
        Logger: {
            log: function(message, priority) {
                if (message && global.console) {
                    if (!priority || !(priority in global.console)) {
                        priority = 'log';
                    }
                    message = '[' + priority.toUpperCase() + '] ' + message;
                    global.console[priority](message);
                }
            },
            verbose: function(message) {
                this.log(message, 'verbose');
            },
            info: function(message) {
                this.log(message, 'info');
            },
            warn: function(message) {
                this.log(message, 'warn');
            },
            error: function(message) {
                throw new Error(message);
            },
            deprecate: function(message) {
                this.log(message, 'warn');
            }
        } || {
            verbose: emptyFn,
            log: emptyFn,
            info: emptyFn,
            warn: emptyFn,
            error: function(message) {
                throw new Error(message);
            },
            deprecate: emptyFn
        },
        
        getElementById: function(id) {
            return document.getElementById(id);
        },
        
        splitAndUnescape: (function() {
            var cache = {};
            return function(origin, delimiter) {
                if (!origin) {
                    return [];
                } else if (!delimiter) {
                    return [
                        origin
                    ];
                }
                var replaceRe = cache[delimiter] || (cache[delimiter] = new RegExp('\\\\' + delimiter,'g')),
                    result = [],
                    parts, part;
                parts = origin.split(delimiter);
                while ((part = parts.shift()) !== undefined) {
                    
                    
                    while (part.charAt(part.length - 1) === '\\' && parts.length > 0) {
                        part = part + delimiter + parts.shift();
                    }
                    
                    part = part.replace(replaceRe, delimiter);
                    result.push(part);
                }
                return result;
            };
        })()
    });
    
    Ext.returnTrue.$nullFn = Ext.returnId.$nullFn = true;
}());


(function() {
    
    
    
    function toString() {
        var me = this,
            cls = me.sourceClass,
            method = me.sourceMethod,
            msg = me.msg;
        if (method) {
            if (msg) {
                method += '(): ';
                method += msg;
            } else {
                method += '()';
            }
        }
        if (cls) {
            method = method ? (cls + '.' + method) : cls;
        }
        return method || msg || '';
    }
    Ext.Error = function(config) {
        if (Ext.isString(config)) {
            config = {
                msg: config
            };
        }
        var error = new Error();
        Ext.apply(error, config);
        error.message = error.message || error.msg;
        
        
        error.toString = toString;
        return error;
    };
    Ext.apply(Ext.Error, {
        
        ignore: false,
        
        raise: function(err) {
            err = err || {};
            if (Ext.isString(err)) {
                err = {
                    msg: err
                };
            }
            var me = this,
                method = me.raise.caller,
                msg, name;
            if (method) {
                if (!err.sourceMethod && (name = method.$name)) {
                    err.sourceMethod = name;
                }
                if (!err.sourceClass && (name = method.$owner) && (name = name.$className)) {
                    err.sourceClass = name;
                }
            }
            if (me.handle(err) !== true) {
                msg = toString.call(err);
                Ext.log({
                    msg: msg,
                    level: 'error',
                    dump: err,
                    stack: true
                });
                throw new Ext.Error(err);
            }
        },
        
        handle: function() {
            return this.ignore;
        }
    });
})();

Ext.deprecated = function(suggestion) {
    if (!suggestion) {
        suggestion = '';
    }
    function fail() {
        Ext.Error.raise('The method "' + fail.$owner.$className + '.' + fail.$name + '" has been removed. ' + suggestion);
    }
    return fail;
    return Ext.emptyFn;
};

(function() {
    if (typeof window === 'undefined') {
        return;
    }
    
    var last = 0,
        
        notify = function() {
            var cnt = Ext.log && Ext.log.counters,
                n = cnt && (cnt.error + cnt.warn + cnt.info + cnt.log),
                msg;
            
            if (n && last !== n) {
                msg = [];
                if (cnt.error) {
                    msg.push('Errors: ' + cnt.error);
                }
                if (cnt.warn) {
                    msg.push('Warnings: ' + cnt.warn);
                }
                if (cnt.info) {
                    msg.push('Info: ' + cnt.info);
                }
                if (cnt.log) {
                    msg.push('Log: ' + cnt.log);
                }
                window.status = '*** ' + msg.join(' -- ');
                last = n;
            }
        };
    
    
    setInterval(notify, 1000);
}());


Ext.Array = new (function() {
    
    
    
    
    var arrayPrototype = Array.prototype,
        slice = arrayPrototype.slice,
        supportsSplice = (function() {
            var array = [],
                lengthBefore,
                j = 20;
            if (!array.splice) {
                return false;
            }
            
            
            while (j--) {
                array.push("A");
            }
            array.splice(15, 0, "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F");
            lengthBefore = array.length;
            
            array.splice(13, 0, "XXX");
            
            if (lengthBefore + 1 !== array.length) {
                return false;
            }
            
            return true;
        }()),
        supportsIndexOf = 'indexOf' in arrayPrototype,
        supportsSliceOnNodeList = true;
    
    
    function stableSort(array, userComparator) {
        var len = array.length,
            indices = new Array(len),
            i;
        
        for (i = 0; i < len; i++) {
            indices[i] = i;
        }
        
        indices.sort(function(index1, index2) {
            return userComparator(array[index1], array[index2]) || (index1 - index2);
        });
        
        for (i = 0; i < len; i++) {
            indices[i] = array[indices[i]];
        }
        
        for (i = 0; i < len; i++) {
            array[i] = indices[i];
        }
        return array;
    }
    try {
        
        if (typeof document !== 'undefined') {
            slice.call(document.getElementsByTagName('body'));
        }
    } catch (e) {
        supportsSliceOnNodeList = false;
    }
    var fixArrayIndex = function(array, index) {
            return (index < 0) ? Math.max(0, array.length + index) : Math.min(array.length, index);
        },
        
        replaceSim = function(array, index, removeCount, insert) {
            var add = insert ? insert.length : 0,
                length = array.length,
                pos = fixArrayIndex(array, index);
            
            if (pos === length) {
                if (add) {
                    array.push.apply(array, insert);
                }
            } else {
                var remove = Math.min(removeCount, length - pos),
                    tailOldPos = pos + remove,
                    tailNewPos = tailOldPos + add - remove,
                    tailCount = length - tailOldPos,
                    lengthAfterRemove = length - remove,
                    i;
                if (tailNewPos < tailOldPos) {
                    
                    for (i = 0; i < tailCount; ++i) {
                        array[tailNewPos + i] = array[tailOldPos + i];
                    }
                } else if (tailNewPos > tailOldPos) {
                    
                    for (i = tailCount; i--; ) {
                        array[tailNewPos + i] = array[tailOldPos + i];
                    }
                }
                
                if (add && pos === lengthAfterRemove) {
                    array.length = lengthAfterRemove;
                    
                    array.push.apply(array, insert);
                } else {
                    array.length = lengthAfterRemove + add;
                    
                    for (i = 0; i < add; ++i) {
                        array[pos + i] = insert[i];
                    }
                }
            }
            return array;
        },
        replaceNative = function(array, index, removeCount, insert) {
            if (insert && insert.length) {
                
                if (index === 0 && !removeCount) {
                    array.unshift.apply(array, insert);
                }
                
                else if (index < array.length) {
                    array.splice.apply(array, [
                        index,
                        removeCount
                    ].concat(insert));
                } else 
                {
                    array.push.apply(array, insert);
                }
            } else {
                array.splice(index, removeCount);
            }
            return array;
        },
        eraseSim = function(array, index, removeCount) {
            return replaceSim(array, index, removeCount);
        },
        eraseNative = function(array, index, removeCount) {
            array.splice(index, removeCount);
            return array;
        },
        spliceSim = function(array, index, removeCount) {
            var pos = fixArrayIndex(array, index),
                removed = array.slice(index, fixArrayIndex(array, pos + removeCount));
            if (arguments.length < 4) {
                replaceSim(array, pos, removeCount);
            } else {
                replaceSim(array, pos, removeCount, slice.call(arguments, 3));
            }
            return removed;
        },
        spliceNative = function(array) {
            return array.splice.apply(array, slice.call(arguments, 1));
        },
        erase = supportsSplice ? eraseNative : eraseSim,
        replace = supportsSplice ? replaceNative : replaceSim,
        splice = supportsSplice ? spliceNative : spliceSim,
        
        ExtArray = {
            
            binarySearch: function(array, item, begin, end, compareFn) {
                var length = array.length,
                    middle, comparison;
                if (begin instanceof Function) {
                    compareFn = begin;
                    begin = 0;
                    end = length;
                } else if (end instanceof Function) {
                    compareFn = end;
                    end = length;
                } else {
                    if (begin === undefined) {
                        begin = 0;
                    }
                    if (end === undefined) {
                        end = length;
                    }
                    compareFn = compareFn || ExtArray.lexicalCompare;
                }
                --end;
                while (begin <= end) {
                    middle = (begin + end) >> 1;
                    comparison = compareFn(item, array[middle]);
                    if (comparison >= 0) {
                        begin = middle + 1;
                    } else if (comparison < 0) {
                        end = middle - 1;
                    }
                }
                return begin;
            },
            defaultCompare: function(lhs, rhs) {
                return (lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0);
            },
            
            
            lexicalCompare: function(lhs, rhs) {
                lhs = String(lhs);
                rhs = String(rhs);
                return (lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0);
            },
            
            each: function(array, fn, scope, reverse) {
                array = ExtArray.from(array);
                var i,
                    ln = array.length;
                if (reverse !== true) {
                    for (i = 0; i < ln; i++) {
                        if (fn.call(scope || array[i], array[i], i, array) === false) {
                            return i;
                        }
                    }
                } else {
                    for (i = ln - 1; i > -1; i--) {
                        if (fn.call(scope || array[i], array[i], i, array) === false) {
                            return i;
                        }
                    }
                }
                return true;
            },
            
            forEach: ('forEach' in arrayPrototype) ? function(array, fn, scope) {
                return array.forEach(fn, scope);
            } : function(array, fn, scope) {
                for (var i = 0,
                    ln = array.length; i < ln; i++) {
                    fn.call(scope, array[i], i, array);
                }
            },
            
            indexOf: supportsIndexOf ? function(array, item, from) {
                return arrayPrototype.indexOf.call(array, item, from);
            } : function(array, item, from) {
                var i,
                    length = array.length;
                for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
                    if (array[i] === item) {
                        return i;
                    }
                }
                return -1;
            },
            
            contains: supportsIndexOf ? function(array, item) {
                return arrayPrototype.indexOf.call(array, item) !== -1;
            } : function(array, item) {
                var i, ln;
                for (i = 0 , ln = array.length; i < ln; i++) {
                    if (array[i] === item) {
                        return true;
                    }
                }
                return false;
            },
            
            toArray: function(iterable, start, end) {
                if (!iterable || !iterable.length) {
                    return [];
                }
                if (typeof iterable === 'string') {
                    iterable = iterable.split('');
                }
                if (supportsSliceOnNodeList) {
                    return slice.call(iterable, start || 0, end || iterable.length);
                }
                var array = [],
                    i;
                start = start || 0;
                end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
                for (i = start; i < end; i++) {
                    array.push(iterable[i]);
                }
                return array;
            },
            
            pluck: function(array, propertyName) {
                var ret = [],
                    i, ln, item;
                for (i = 0 , ln = array.length; i < ln; i++) {
                    item = array[i];
                    ret.push(item[propertyName]);
                }
                return ret;
            },
            
            map: ('map' in arrayPrototype) ? function(array, fn, scope) {
                Ext.Assert.isFunction(fn, 'Ext.Array.map must have a callback function passed as second argument.');
                return array.map(fn, scope);
            } : function(array, fn, scope) {
                Ext.Assert.isFunction(fn, 'Ext.Array.map must have a callback function passed as second argument.');
                var results = [],
                    i = 0,
                    len = array.length;
                for (; i < len; i++) {
                    results[i] = fn.call(scope, array[i], i, array);
                }
                return results;
            },
            
            every: ('every' in arrayPrototype) ? function(array, fn, scope) {
                Ext.Assert.isFunction(fn, 'Ext.Array.every must have a callback function passed as second argument.');
                return array.every(fn, scope);
            } : function(array, fn, scope) {
                Ext.Assert.isFunction(fn, 'Ext.Array.every must have a callback function passed as second argument.');
                var i = 0,
                    ln = array.length;
                for (; i < ln; ++i) {
                    if (!fn.call(scope, array[i], i, array)) {
                        return false;
                    }
                }
                return true;
            },
            
            some: ('some' in arrayPrototype) ? function(array, fn, scope) {
                Ext.Assert.isFunction(fn, 'Ext.Array.some must have a callback function passed as second argument.');
                return array.some(fn, scope);
            } : function(array, fn, scope) {
                Ext.Assert.isFunction(fn, 'Ext.Array.some must have a callback function passed as second argument.');
                var i = 0,
                    ln = array.length;
                for (; i < ln; ++i) {
                    if (fn.call(scope, array[i], i, array)) {
                        return true;
                    }
                }
                return false;
            },
            
            equals: function(array1, array2) {
                var len1 = array1.length,
                    len2 = array2.length,
                    i;
                
                if (array1 === array2) {
                    return true;
                }
                if (len1 !== len2) {
                    return false;
                }
                for (i = 0; i < len1; ++i) {
                    if (array1[i] !== array2[i]) {
                        return false;
                    }
                }
                return true;
            },
            
            clean: function(array) {
                var results = [],
                    i = 0,
                    ln = array.length,
                    item;
                for (; i < ln; i++) {
                    item = array[i];
                    if (!Ext.isEmpty(item)) {
                        results.push(item);
                    }
                }
                return results;
            },
            
            unique: function(array) {
                var clone = [],
                    i = 0,
                    ln = array.length,
                    item;
                for (; i < ln; i++) {
                    item = array[i];
                    if (ExtArray.indexOf(clone, item) === -1) {
                        clone.push(item);
                    }
                }
                return clone;
            },
            
            filter: ('filter' in arrayPrototype) ? function(array, fn, scope) {
                Ext.Assert.isFunction(fn, 'Ext.Array.filter must have a filter function passed as second argument.');
                return array.filter(fn, scope);
            } : function(array, fn, scope) {
                Ext.Assert.isFunction(fn, 'Ext.Array.filter must have a filter function passed as second argument.');
                var results = [],
                    i = 0,
                    ln = array.length;
                for (; i < ln; i++) {
                    if (fn.call(scope, array[i], i, array)) {
                        results.push(array[i]);
                    }
                }
                return results;
            },
            
            findBy: function(array, fn, scope) {
                var i = 0,
                    len = array.length;
                for (; i < len; i++) {
                    if (fn.call(scope || array, array[i], i)) {
                        return array[i];
                    }
                }
                return null;
            },
            
            from: function(value, newReference) {
                if (value === undefined || value === null) {
                    return [];
                }
                if (Ext.isArray(value)) {
                    return (newReference) ? slice.call(value) : value;
                }
                var type = typeof value;
                
                
                if (value && value.length !== undefined && type !== 'string' && (type !== 'function' || !value.apply)) {
                    return ExtArray.toArray(value);
                }
                return [
                    value
                ];
            },
            
            remove: function(array, item) {
                var index = ExtArray.indexOf(array, item);
                if (index !== -1) {
                    erase(array, index, 1);
                }
                return array;
            },
            
            removeAt: function(array, index, count) {
                var len = array.length;
                if (index >= 0 && index < len) {
                    count = count || 1;
                    count = Math.min(count, len - index);
                    erase(array, index, count);
                }
                return array;
            },
            
            include: function(array, item) {
                if (!ExtArray.contains(array, item)) {
                    array.push(item);
                }
            },
            
            clone: function(array) {
                return slice.call(array);
            },
            
            merge: function() {
                var args = slice.call(arguments),
                    array = [],
                    i, ln;
                for (i = 0 , ln = args.length; i < ln; i++) {
                    array = array.concat(args[i]);
                }
                return ExtArray.unique(array);
            },
            
            intersect: function() {
                var intersection = [],
                    arrays = slice.call(arguments),
                    arraysLength, array, arrayLength, minArray, minArrayIndex, minArrayCandidate, minArrayLength, element, elementCandidate, elementCount, i, j, k;
                if (!arrays.length) {
                    return intersection;
                }
                
                arraysLength = arrays.length;
                for (i = minArrayIndex = 0; i < arraysLength; i++) {
                    minArrayCandidate = arrays[i];
                    if (!minArray || minArrayCandidate.length < minArray.length) {
                        minArray = minArrayCandidate;
                        minArrayIndex = i;
                    }
                }
                minArray = ExtArray.unique(minArray);
                erase(arrays, minArrayIndex, 1);
                
                
                
                minArrayLength = minArray.length;
                arraysLength = arrays.length;
                for (i = 0; i < minArrayLength; i++) {
                    element = minArray[i];
                    elementCount = 0;
                    for (j = 0; j < arraysLength; j++) {
                        array = arrays[j];
                        arrayLength = array.length;
                        for (k = 0; k < arrayLength; k++) {
                            elementCandidate = array[k];
                            if (element === elementCandidate) {
                                elementCount++;
                                break;
                            }
                        }
                    }
                    if (elementCount === arraysLength) {
                        intersection.push(element);
                    }
                }
                return intersection;
            },
            
            difference: function(arrayA, arrayB) {
                var clone = slice.call(arrayA),
                    ln = clone.length,
                    i, j, lnB;
                for (i = 0 , lnB = arrayB.length; i < lnB; i++) {
                    for (j = 0; j < ln; j++) {
                        if (clone[j] === arrayB[i]) {
                            erase(clone, j, 1);
                            j--;
                            ln--;
                        }
                    }
                }
                return clone;
            },
            
            
            slice: ([
                1,
                2
            ].slice(1, undefined).length ? function(array, begin, end) {
                return slice.call(array, begin, end);
            } : function(array, begin, end) {
                
                if (typeof begin === 'undefined') {
                    return slice.call(array);
                }
                if (typeof end === 'undefined') {
                    return slice.call(array, begin);
                }
                return slice.call(array, begin, end);
            }),
            
            sort: function(array, sortFn) {
                return stableSort(array, sortFn || ExtArray.lexicalCompare);
            },
            
            flatten: function(array) {
                var worker = [];
                function rFlatten(a) {
                    var i, ln, v;
                    for (i = 0 , ln = a.length; i < ln; i++) {
                        v = a[i];
                        if (Ext.isArray(v)) {
                            rFlatten(v);
                        } else {
                            worker.push(v);
                        }
                    }
                    return worker;
                }
                return rFlatten(array);
            },
            
            min: function(array, comparisonFn) {
                var min = array[0],
                    i, ln, item;
                for (i = 0 , ln = array.length; i < ln; i++) {
                    item = array[i];
                    if (comparisonFn) {
                        if (comparisonFn(min, item) === 1) {
                            min = item;
                        }
                    } else {
                        if (item < min) {
                            min = item;
                        }
                    }
                }
                return min;
            },
            
            max: function(array, comparisonFn) {
                var max = array[0],
                    i, ln, item;
                for (i = 0 , ln = array.length; i < ln; i++) {
                    item = array[i];
                    if (comparisonFn) {
                        if (comparisonFn(max, item) === -1) {
                            max = item;
                        }
                    } else {
                        if (item > max) {
                            max = item;
                        }
                    }
                }
                return max;
            },
            
            mean: function(array) {
                return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
            },
            
            sum: function(array) {
                var sum = 0,
                    i, ln, item;
                for (i = 0 , ln = array.length; i < ln; i++) {
                    item = array[i];
                    sum += item;
                }
                return sum;
            },
            
            toMap: function(array, getKey, scope) {
                var map = {},
                    i = array.length;
                if (!getKey) {
                    while (i--) {
                        map[array[i]] = i + 1;
                    }
                } else if (typeof getKey === 'string') {
                    while (i--) {
                        map[array[i][getKey]] = i + 1;
                    }
                } else {
                    while (i--) {
                        map[getKey.call(scope, array[i])] = i + 1;
                    }
                }
                return map;
            },
            
            toValueMap: function(array, getKey, scope, arrayify) {
                var map = {},
                    i = array.length,
                    autoArray, alwaysArray, entry, fn, key, value;
                if (!getKey) {
                    while (i--) {
                        value = array[i];
                        map[value] = value;
                    }
                } else {
                    if (!(fn = (typeof getKey !== 'string'))) {
                        arrayify = scope;
                    }
                    alwaysArray = arrayify === 1;
                    autoArray = arrayify === 2;
                    while (i--) {
                        value = array[i];
                        key = fn ? getKey.call(scope, value) : value[getKey];
                        if (alwaysArray) {
                            if (key in map) {
                                map[key].push(value);
                            } else {
                                map[key] = [
                                    value
                                ];
                            }
                        } else if (autoArray && (key in map)) {
                            if ((entry = map[key]) instanceof Array) {
                                entry.push(value);
                            } else {
                                map[key] = [
                                    entry,
                                    value
                                ];
                            }
                        } else {
                            map[key] = value;
                        }
                    }
                }
                return map;
            },
            _replaceSim: replaceSim,
            
            _spliceSim: spliceSim,
            
            erase: erase,
            
            insert: function(array, index, items) {
                return replace(array, index, 0, items);
            },
            
            replace: replace,
            
            splice: splice,
            
            push: function(target) {
                var len = arguments.length,
                    i = 1,
                    newItem;
                if (target === undefined) {
                    target = [];
                } else if (!Ext.isArray(target)) {
                    target = [
                        target
                    ];
                }
                for (; i < len; i++) {
                    newItem = arguments[i];
                    Array.prototype.push[Ext.isIterable(newItem) ? 'apply' : 'call'](target, newItem);
                }
                return target;
            },
            
            numericSortFn: function(a, b) {
                return a - b;
            }
        };
    
    Ext.each = ExtArray.each;
    
    ExtArray.union = ExtArray.merge;
    
    Ext.min = ExtArray.min;
    
    Ext.max = ExtArray.max;
    
    Ext.sum = ExtArray.sum;
    
    Ext.mean = ExtArray.mean;
    
    Ext.flatten = ExtArray.flatten;
    
    Ext.clean = ExtArray.clean;
    
    Ext.unique = ExtArray.unique;
    
    Ext.pluck = ExtArray.pluck;
    
    Ext.toArray = function() {
        return ExtArray.toArray.apply(ExtArray, arguments);
    };
    return ExtArray;
})();





Ext.Assert = {
    
    falsey: function(b, msg) {
        if (b) {
            Ext.Error.raise(msg || ('Expected a falsey value but was ' + b));
        }
    },
    
    falseyProp: function(object, property) {
        Ext.Assert.truthy(object);
        var b = object[property];
        if (b) {
            if (object.$className) {
                property = object.$className + '#' + property;
            }
            Ext.Error.raise('Expected a falsey value for ' + property + ' but was ' + b);
        }
    },
    
    truthy: function(b, msg) {
        if (!b) {
            Ext.Error.raise(msg || ('Expected a truthy value but was ' + typeof b));
        }
    },
    
    truthyProp: function(object, property) {
        Ext.Assert.truthy(object);
        var b = object[property];
        if (!b) {
            if (object.$className) {
                property = object.$className + '#' + property;
            }
            Ext.Error.raise('Expected a truthy value for ' + property + ' but was ' + typeof b);
        }
    }
};
(function() {
    function makeAssert(name, kind) {
        var testFn = Ext[name],
            def;
        return function(value, msg) {
            if (!testFn(value)) {
                Ext.Error.raise(msg || def || (def = 'Expected value to be ' + kind));
            }
        };
    }
    function makeAssertProp(name, kind) {
        var testFn = Ext[name],
            def;
        return function(object, prop) {
            Ext.Assert.truthy(object);
            if (!testFn(object[prop])) {
                Ext.Error.raise(def || (def = 'Expected ' + (object.$className ? object.$className + '#' : '') + prop + ' to be ' + kind));
            }
        };
    }
    function makeNotAssert(name, kind) {
        var testFn = Ext[name],
            def;
        return function(value, msg) {
            if (testFn(value)) {
                Ext.Error.raise(msg || def || (def = 'Expected value to NOT be ' + kind));
            }
        };
    }
    function makeNotAssertProp(name, kind) {
        var testFn = Ext[name],
            def;
        return function(object, prop) {
            Ext.Assert.truthy(object);
            if (testFn(object[prop])) {
                Ext.Error.raise(def || (def = 'Expected ' + (object.$className ? object.$className + '#' : '') + prop + ' to NOT be ' + kind));
            }
        };
    }
    for (var name in Ext) {
        if (name.substring(0, 2) == "is" && Ext.isFunction(Ext[name])) {
            var kind = name.substring(2);
            Ext.Assert[name] = makeAssert(name, kind);
            Ext.Assert[name + 'Prop'] = makeAssertProp(name, kind);
            Ext.Assert['isNot' + kind] = makeNotAssert(name, kind);
            Ext.Assert['isNot' + kind + 'Prop'] = makeNotAssertProp(name, kind);
        }
    }
}());


Ext.String = (function() {
    
    
    
    
    var trimRegex = /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
        escapeRe = /('|\\)/g,
        escapeRegexRe = /([-.*+?\^${}()|\[\]\/\\])/g,
        basicTrimRe = /^\s+|\s+$/g,
        whitespaceRe = /\s+/,
        varReplace = /(^[^a-z]*|[^\w])/gi,
        charToEntity, entityToChar, charToEntityRegex, entityToCharRegex,
        htmlEncodeReplaceFn = function(match, capture) {
            return charToEntity[capture];
        },
        htmlDecodeReplaceFn = function(match, capture) {
            return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
        },
        boundsCheck = function(s, other) {
            if (s === null || s === undefined || other === null || other === undefined) {
                return false;
            }
            return other.length <= s.length;
        },
        ExtString;
    return ExtString = {
        
        insert: function(s, value, index) {
            if (!s) {
                return value;
            }
            if (!value) {
                return s;
            }
            var len = s.length;
            if (!index && index !== 0) {
                index = len;
            }
            if (index < 0) {
                index *= -1;
                if (index >= len) {
                    
                    index = 0;
                } else {
                    index = len - index;
                }
            }
            if (index === 0) {
                s = value + s;
            } else if (index >= s.length) {
                s += value;
            } else {
                s = s.substr(0, index) + value + s.substr(index);
            }
            return s;
        },
        
        startsWith: function(s, start, ignoreCase) {
            var result = boundsCheck(s, start);
            if (result) {
                if (ignoreCase) {
                    s = s.toLowerCase();
                    start = start.toLowerCase();
                }
                result = s.lastIndexOf(start, 0) === 0;
            }
            return result;
        },
        
        endsWith: function(s, end, ignoreCase) {
            var result = boundsCheck(s, end);
            if (result) {
                if (ignoreCase) {
                    s = s.toLowerCase();
                    end = end.toLowerCase();
                }
                result = s.indexOf(end, s.length - end.length) !== -1;
            }
            return result;
        },
        
        createVarName: function(s) {
            return s.replace(varReplace, '');
        },
        
        htmlEncode: function(value) {
            return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
        },
        
        htmlDecode: function(value) {
            return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
        },
        
        hasHtmlCharacters: function(s) {
            return charToEntityRegex.test(s);
        },
        
        addCharacterEntities: function(newEntities) {
            var charKeys = [],
                entityKeys = [],
                key, echar;
            for (key in newEntities) {
                echar = newEntities[key];
                entityToChar[key] = echar;
                charToEntity[echar] = key;
                charKeys.push(echar);
                entityKeys.push(key);
            }
            charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')','g');
            entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')','g');
        },
        
        resetCharacterEntities: function() {
            charToEntity = {};
            entityToChar = {};
            
            this.addCharacterEntities({
                '&amp;': '&',
                '&gt;': '>',
                '&lt;': '<',
                '&quot;': '"',
                '&#39;': "'"
            });
        },
        
        urlAppend: function(url, string) {
            if (!Ext.isEmpty(string)) {
                return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
            }
            return url;
        },
        
        trim: function(string) {
            if (string) {
                string = string.replace(trimRegex, "");
            }
            return string || '';
        },
        
        capitalize: function(string) {
            if (string) {
                string = string.charAt(0).toUpperCase() + string.substr(1);
            }
            return string || '';
        },
        
        uncapitalize: function(string) {
            if (string) {
                string = string.charAt(0).toLowerCase() + string.substr(1);
            }
            return string || '';
        },
        
        ellipsis: function(value, length, word) {
            if (value && value.length > length) {
                if (word) {
                    var vs = value.substr(0, length - 2),
                        index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
                    if (index !== -1 && index >= (length - 15)) {
                        return vs.substr(0, index) + "...";
                    }
                }
                return value.substr(0, length - 3) + "...";
            }
            return value;
        },
        
        escapeRegex: function(string) {
            return string.replace(escapeRegexRe, "\\$1");
        },
        
        createRegex: function(value, startsWith, endsWith, ignoreCase) {
            var ret = value;
            if (value != null && !value.exec) {
                
                ret = ExtString.escapeRegex(String(value));
                if (startsWith !== false) {
                    ret = '^' + ret;
                }
                if (endsWith !== false) {
                    ret += '$';
                }
                ret = new RegExp(ret,(ignoreCase !== false) ? 'i' : '');
            }
            return ret;
        },
        
        escape: function(string) {
            return string.replace(escapeRe, "\\$1");
        },
        
        toggle: function(string, value, other) {
            return string === value ? other : value;
        },
        
        leftPad: function(string, size, character) {
            var result = String(string);
            character = character || " ";
            while (result.length < size) {
                result = character + result;
            }
            return result;
        },
        
        repeat: function(pattern, count, sep) {
            if (count < 1) {
                count = 0;
            }
            for (var buf = [],
                i = count; i--; ) {
                buf.push(pattern);
            }
            return buf.join(sep || '');
        },
        
        splitWords: function(words) {
            if (words && typeof words == 'string') {
                return words.replace(basicTrimRe, '').split(whitespaceRe);
            }
            return words || [];
        }
    };
}());

Ext.String.resetCharacterEntities();

Ext.htmlEncode = Ext.String.htmlEncode;

Ext.htmlDecode = Ext.String.htmlDecode;

Ext.urlAppend = Ext.String.urlAppend;


Ext.Date = (function() {
    
    
    
    
    var utilDate,
        nativeDate = Date,
        stripEscapeRe = /(\\.)/g,
        hourInfoRe = /([gGhHisucUOPZ]|MS)/,
        dateInfoRe = /([djzmnYycU]|MS)/,
        slashRe = /\\/gi,
        numberTokenRe = /\{(\d+)\}/g,
        MSFormatRe = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/'),
        pad = Ext.String.leftPad,
        
        
        
        
        code = [
            
            "var me = this, dt, y, m, d, h, i, s, ms, o, O, z, zz, u, v, W, year, jan4, week1monday, daysInMonth, dayMatched,",
            "def = me.defaults,",
            "from = Ext.Number.from,",
            "results = String(input).match(me.parseRegexes[{0}]);",
            
            "if(results){",
            "{1}",
            "if(u != null){",
            
            "v = new Date(u * 1000);",
            
            "}else{",
            
            
            
            "dt = me.clearTime(new Date);",
            "y = from(y, from(def.y, dt.getFullYear()));",
            "m = from(m, from(def.m - 1, dt.getMonth()));",
            "dayMatched = d !== undefined;",
            "d = from(d, from(def.d, dt.getDate()));",
            
            
            
            
            
            
            "if (!dayMatched) {",
            "dt.setDate(1);",
            "dt.setMonth(m);",
            "dt.setFullYear(y);",
            "daysInMonth = me.getDaysInMonth(dt);",
            "if (d > daysInMonth) {",
            "d = daysInMonth;",
            "}",
            "}",
            "h  = from(h, from(def.h, dt.getHours()));",
            "i  = from(i, from(def.i, dt.getMinutes()));",
            "s  = from(s, from(def.s, dt.getSeconds()));",
            "ms = from(ms, from(def.ms, dt.getMilliseconds()));",
            "if(z >= 0 && y >= 0){",
            
            
            
            
            "v = me.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);",
            
            "v = !strict? v : (strict === true && (z <= 364 || (me.isLeapYear(v) && z <= 365))? me.add(v, me.DAY, z) : null);",
            "}else if(strict === true && !me.isValid(y, m + 1, d, h, i, s, ms)){",
            
            "v = null;",
            
            "}else{",
            "if (W) {",
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            "year = y || (new Date()).getFullYear();",
            "jan4 = new Date(year, 0, 4, 0, 0, 0);",
            "d = jan4.getDay();",
            
            
            "week1monday = new Date(jan4.getTime() - ((d === 0 ? 6 : d - 1) * 86400000));",
            
            
            
            
            "v = Ext.Date.clearTime(new Date(week1monday.getTime() + ((W - 1) * 604800000 + 43200000)));",
            "} else {",
            
            
            "v = me.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);",
            "}",
            "}",
            "}",
            "}",
            "if(v){",
            
            "if(zz != null){",
            
            "v = me.add(v, me.SECOND, -v.getTimezoneOffset() * 60 - zz);",
            "}else if(o){",
            
            "v = me.add(v, me.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
            "}",
            "}",
            "return v;"
        ].join('\n');
    
    
    
    if (!Date.prototype.toISOString) {
        Date.prototype.toISOString = function() {
            var me = this;
            return pad(me.getUTCFullYear(), 4, '0') + '-' + pad(me.getUTCMonth() + 1, 2, '0') + '-' + pad(me.getUTCDate(), 2, '0') + 'T' + pad(me.getUTCHours(), 2, '0') + ':' + pad(me.getUTCMinutes(), 2, '0') + ':' + pad(me.getUTCSeconds(), 2, '0') + '.' + pad(me.getUTCMilliseconds(), 3, '0') + 'Z';
        };
    }
    
    
    
    function xf(format) {
        var args = Array.prototype.slice.call(arguments, 1);
        return format.replace(numberTokenRe, function(m, i) {
            return args[i];
        });
    }
    return utilDate = {
        
        now: nativeDate.now,
        
        
        toString: function(date) {
            if (!date) {
                date = new nativeDate();
            }
            return date.getFullYear() + "-" + pad(date.getMonth() + 1, 2, '0') + "-" + pad(date.getDate(), 2, '0') + "T" + pad(date.getHours(), 2, '0') + ":" + pad(date.getMinutes(), 2, '0') + ":" + pad(date.getSeconds(), 2, '0');
        },
        
        getElapsed: function(dateA, dateB) {
            return Math.abs(dateA - (dateB || utilDate.now()));
        },
        
        useStrict: false,
        
        formatCodeToRegex: function(character, currentGroup) {
            
            var p = utilDate.parseCodes[character];
            if (p) {
                p = typeof p === 'function' ? p() : p;
                utilDate.parseCodes[character] = p;
            }
            
            return p ? Ext.applyIf({
                c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
            }, p) : {
                g: 0,
                c: null,
                s: Ext.String.escapeRegex(character)
            };
        },
        
        
        parseFunctions: {
            "MS": function(input, strict) {
                
                
                var r = (input || '').match(MSFormatRe);
                return r ? new nativeDate(((r[1] || '') + r[2]) * 1) : null;
            },
            "time": function(input, strict) {
                var num = parseInt(input, 10);
                if (num || num === 0) {
                    return new nativeDate(num);
                }
                return null;
            },
            "timestamp": function(input, strict) {
                var num = parseInt(input, 10);
                if (num || num === 0) {
                    return new nativeDate(num * 1000);
                }
                return null;
            }
        },
        parseRegexes: [],
        
        formatFunctions: {
            "MS": function() {
                
                return '\\/Date(' + this.getTime() + ')\\/';
            },
            "time": function() {
                return this.getTime().toString();
            },
            "timestamp": function() {
                return utilDate.format(this, 'U');
            }
        },
        y2kYear: 50,
        
        MILLI: "ms",
        
        SECOND: "s",
        
        MINUTE: "mi",
        
        HOUR: "h",
        
        DAY: "d",
        
        MONTH: "mo",
        
        YEAR: "y",
        
        defaults: {},
        
        
        dayNames: [
            "Sunday",
            "Monday",
            "Tuesday",
            "Wednesday",
            "Thursday",
            "Friday",
            "Saturday"
        ],
        
        
        
        monthNames: [
            "January",
            "February",
            "March",
            "April",
            "May",
            "June",
            "July",
            "August",
            "September",
            "October",
            "November",
            "December"
        ],
        
        
        
        monthNumbers: {
            January: 0,
            Jan: 0,
            February: 1,
            Feb: 1,
            March: 2,
            Mar: 2,
            April: 3,
            Apr: 3,
            May: 4,
            June: 5,
            Jun: 5,
            July: 6,
            Jul: 6,
            August: 7,
            Aug: 7,
            September: 8,
            Sep: 8,
            October: 9,
            Oct: 9,
            November: 10,
            Nov: 10,
            December: 11,
            Dec: 11
        },
        
        
        
        defaultFormat: "m/d/Y",
        
        
        
        getShortMonthName: function(month) {
            return utilDate.monthNames[month].substring(0, 3);
        },
        
        
        
        getShortDayName: function(day) {
            return utilDate.dayNames[day].substring(0, 3);
        },
        
        
        
        getMonthNumber: function(name) {
            
            return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
        },
        
        
        formatContainsHourInfo: function(format) {
            return hourInfoRe.test(format.replace(stripEscapeRe, ''));
        },
        
        formatContainsDateInfo: function(format) {
            return dateInfoRe.test(format.replace(stripEscapeRe, ''));
        },
        
        unescapeFormat: function(format) {
            
            
            
            return format.replace(slashRe, '');
        },
        
        formatCodes: {
            d: "Ext.String.leftPad(m.getDate(), 2, '0')",
            D: "Ext.Date.getShortDayName(m.getDay())",
            
            j: "m.getDate()",
            l: "Ext.Date.dayNames[m.getDay()]",
            N: "(m.getDay() ? m.getDay() : 7)",
            S: "Ext.Date.getSuffix(m)",
            w: "m.getDay()",
            z: "Ext.Date.getDayOfYear(m)",
            W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(m), 2, '0')",
            F: "Ext.Date.monthNames[m.getMonth()]",
            m: "Ext.String.leftPad(m.getMonth() + 1, 2, '0')",
            M: "Ext.Date.getShortMonthName(m.getMonth())",
            
            n: "(m.getMonth() + 1)",
            t: "Ext.Date.getDaysInMonth(m)",
            L: "(Ext.Date.isLeapYear(m) ? 1 : 0)",
            o: "(m.getFullYear() + (Ext.Date.getWeekOfYear(m) == 1 && m.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(m) >= 52 && m.getMonth() < 11 ? -1 : 0)))",
            Y: "Ext.String.leftPad(m.getFullYear(), 4, '0')",
            y: "('' + m.getFullYear()).substring(2, 4)",
            a: "(m.getHours() < 12 ? 'am' : 'pm')",
            A: "(m.getHours() < 12 ? 'AM' : 'PM')",
            g: "((m.getHours() % 12) ? m.getHours() % 12 : 12)",
            G: "m.getHours()",
            h: "Ext.String.leftPad((m.getHours() % 12) ? m.getHours() % 12 : 12, 2, '0')",
            H: "Ext.String.leftPad(m.getHours(), 2, '0')",
            i: "Ext.String.leftPad(m.getMinutes(), 2, '0')",
            s: "Ext.String.leftPad(m.getSeconds(), 2, '0')",
            u: "Ext.String.leftPad(m.getMilliseconds(), 3, '0')",
            O: "Ext.Date.getGMTOffset(m)",
            P: "Ext.Date.getGMTOffset(m, true)",
            T: "Ext.Date.getTimezone(m)",
            Z: "(m.getTimezoneOffset() * -60)",
            c: function() {
                
                var c = "Y-m-dTH:i:sP",
                    code = [],
                    i,
                    l = c.length,
                    e;
                for (i = 0; i < l; ++i) {
                    e = c.charAt(i);
                    code.push(e === "T" ? "'T'" : utilDate.getFormatCode(e));
                }
                
                return code.join(" + ");
            },
            C: function() {
                
                return 'm.toISOString()';
            },
            U: "Math.round(m.getTime() / 1000)"
        },
        
        isValid: function(y, m, d, h, i, s, ms) {
            
            h = h || 0;
            i = i || 0;
            s = s || 0;
            ms = ms || 0;
            
            var dt = utilDate.add(new nativeDate(y < 100 ? 100 : y,m - 1,d,h,i,s,ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
            return y === dt.getFullYear() && m === dt.getMonth() + 1 && d === dt.getDate() && h === dt.getHours() && i === dt.getMinutes() && s === dt.getSeconds() && ms === dt.getMilliseconds();
        },
        
        parse: function(input, format, strict) {
            var p = utilDate.parseFunctions;
            if (p[format] == null) {
                utilDate.createParser(format);
            }
            return p[format].call(utilDate, input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
        },
        
        parseDate: function(input, format, strict) {
            return utilDate.parse(input, format, strict);
        },
        
        getFormatCode: function(character) {
            var f = utilDate.formatCodes[character];
            if (f) {
                f = typeof f === 'function' ? f() : f;
                utilDate.formatCodes[character] = f;
            }
            
            
            return f || ("'" + Ext.String.escape(character) + "'");
        },
        
        createFormat: function(format) {
            var code = [],
                special = false,
                ch = '',
                i;
            for (i = 0; i < format.length; ++i) {
                ch = format.charAt(i);
                if (!special && ch === "\\") {
                    special = true;
                } else if (special) {
                    special = false;
                    code.push("'" + Ext.String.escape(ch) + "'");
                } else {
                    if (ch === '\n') {
                        code.push("'\\n'");
                    } else {
                        code.push(utilDate.getFormatCode(ch));
                    }
                }
            }
            utilDate.formatFunctions[format] = Ext.functionFactory("var m=this;return " + code.join('+'));
        },
        
        createParser: function(format) {
            var regexNum = utilDate.parseRegexes.length,
                currentGroup = 1,
                calc = [],
                regex = [],
                special = false,
                ch = "",
                i = 0,
                len = format.length,
                atEnd = [],
                obj;
            for (; i < len; ++i) {
                ch = format.charAt(i);
                if (!special && ch === "\\") {
                    special = true;
                } else if (special) {
                    special = false;
                    regex.push(Ext.String.escape(ch));
                } else {
                    obj = utilDate.formatCodeToRegex(ch, currentGroup);
                    currentGroup += obj.g;
                    regex.push(obj.s);
                    if (obj.g && obj.c) {
                        if (obj.calcAtEnd) {
                            atEnd.push(obj.c);
                        } else {
                            calc.push(obj.c);
                        }
                    }
                }
            }
            calc = calc.concat(atEnd);
            utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$",'i');
            utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
        },
        
        parseCodes: {
            
            d: {
                g: 1,
                c: "d = parseInt(results[{0}], 10);\n",
                s: "(3[0-1]|[1-2][0-9]|0[1-9])"
            },
            
            j: {
                g: 1,
                c: "d = parseInt(results[{0}], 10);\n",
                s: "(3[0-1]|[1-2][0-9]|[1-9])"
            },
            
            D: function() {
                for (var a = [],
                    i = 0; i < 7; a.push(utilDate.getShortDayName(i)) , ++i){}
                
                return {
                    g: 0,
                    c: null,
                    s: "(?:" + a.join("|") + ")"
                };
            },
            l: function() {
                return {
                    g: 0,
                    c: null,
                    s: "(?:" + utilDate.dayNames.join("|") + ")"
                };
            },
            N: {
                g: 0,
                c: null,
                s: "[1-7]"
            },
            
            
            S: {
                g: 0,
                c: null,
                s: "(?:st|nd|rd|th)"
            },
            
            w: {
                g: 0,
                c: null,
                s: "[0-6]"
            },
            
            z: {
                g: 1,
                c: "z = parseInt(results[{0}], 10);\n",
                s: "(\\d{1,3})"
            },
            
            W: {
                g: 1,
                c: "W = parseInt(results[{0}], 10);\n",
                s: "(\\d{2})"
            },
            
            F: function() {
                return {
                    g: 1,
                    c: "m = parseInt(me.getMonthNumber(results[{0}]), 10);\n",
                    
                    s: "(" + utilDate.monthNames.join("|") + ")"
                };
            },
            M: function() {
                for (var a = [],
                    i = 0; i < 12; a.push(utilDate.getShortMonthName(i)) , ++i){}
                
                return Ext.applyIf({
                    s: "(" + a.join("|") + ")"
                }, utilDate.formatCodeToRegex("F"));
            },
            m: {
                g: 1,
                c: "m = parseInt(results[{0}], 10) - 1;\n",
                s: "(1[0-2]|0[1-9])"
            },
            
            n: {
                g: 1,
                c: "m = parseInt(results[{0}], 10) - 1;\n",
                s: "(1[0-2]|[1-9])"
            },
            
            t: {
                g: 0,
                c: null,
                s: "(?:\\d{2})"
            },
            
            L: {
                g: 0,
                c: null,
                s: "(?:1|0)"
            },
            o: {
                g: 1,
                c: "y = parseInt(results[{0}], 10);\n",
                s: "(\\d{4})"
            },
            
            Y: {
                g: 1,
                c: "y = parseInt(results[{0}], 10);\n",
                s: "(\\d{4})"
            },
            
            y: {
                g: 1,
                c: "var ty = parseInt(results[{0}], 10);\n" + "y = ty > me.y2kYear ? 1900 + ty : 2000 + ty;\n",
                
                s: "(\\d{1,2})"
            },
            
            
            a: {
                g: 1,
                c: "if (/(am)/i.test(results[{0}])) {\n" + "if (!h || h == 12) { h = 0; }\n" + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
                s: "(am|pm|AM|PM)",
                calcAtEnd: true
            },
            
            
            A: {
                g: 1,
                c: "if (/(am)/i.test(results[{0}])) {\n" + "if (!h || h == 12) { h = 0; }\n" + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
                s: "(AM|PM|am|pm)",
                calcAtEnd: true
            },
            
            g: {
                g: 1,
                c: "h = parseInt(results[{0}], 10);\n",
                s: "(1[0-2]|[0-9])"
            },
            
            G: {
                g: 1,
                c: "h = parseInt(results[{0}], 10);\n",
                s: "(2[0-3]|1[0-9]|[0-9])"
            },
            
            h: {
                g: 1,
                c: "h = parseInt(results[{0}], 10);\n",
                s: "(1[0-2]|0[1-9])"
            },
            
            H: {
                g: 1,
                c: "h = parseInt(results[{0}], 10);\n",
                s: "(2[0-3]|[0-1][0-9])"
            },
            
            i: {
                g: 1,
                c: "i = parseInt(results[{0}], 10);\n",
                s: "([0-5][0-9])"
            },
            
            s: {
                g: 1,
                c: "s = parseInt(results[{0}], 10);\n",
                s: "([0-5][0-9])"
            },
            
            u: {
                g: 1,
                c: "ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
                s: "(\\d+)"
            },
            
            O: {
                g: 1,
                c: [
                    "o = results[{0}];",
                    "var sn = o.substring(0,1),",
                    
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),",
                    
                    "mn = o.substring(3,5) % 60;",
                    
                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n"
                ].
                join("\n"),
                s: "([+-]\\d{4})"
            },
            
            P: {
                g: 1,
                c: [
                    "o = results[{0}];",
                    "var sn = o.substring(0,1),",
                    
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),",
                    
                    "mn = o.substring(4,6) % 60;",
                    
                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n"
                ].
                join("\n"),
                s: "([+-]\\d{2}:\\d{2})"
            },
            
            T: {
                g: 0,
                c: null,
                s: "[A-Z]{1,5}"
            },
            
            Z: {
                g: 1,
                c: "zz = results[{0}] * 1;\n" + 
                "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
                s: "([+-]?\\d{1,5})"
            },
            
            c: function() {
                var calc = [],
                    arr = [
                        utilDate.formatCodeToRegex("Y", 1),
                        
                        utilDate.formatCodeToRegex("m", 2),
                        
                        utilDate.formatCodeToRegex("d", 3),
                        
                        utilDate.formatCodeToRegex("H", 4),
                        
                        utilDate.formatCodeToRegex("i", 5),
                        
                        utilDate.formatCodeToRegex("s", 6),
                        
                        {
                            c: "ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"
                        },
                        
                        {
                            c: [
                                
                                "if(results[8]) {",
                                
                                "if(results[8] == 'Z'){",
                                "zz = 0;",
                                
                                "}else if (results[8].indexOf(':') > -1){",
                                utilDate.formatCodeToRegex("P", 8).c,
                                
                                "}else{",
                                utilDate.formatCodeToRegex("O", 8).c,
                                
                                "}",
                                "}"
                            ].join('\n')
                        }
                    ],
                    i, l;
                for (i = 0 , l = arr.length; i < l; ++i) {
                    calc.push(arr[i].c);
                }
                return {
                    g: 1,
                    c: calc.join(""),
                    s: [
                        arr[0].s,
                        
                        "(?:",
                        "-",
                        arr[1].s,
                        
                        "(?:",
                        "-",
                        arr[2].s,
                        
                        "(?:",
                        "(?:T| )?",
                        
                        arr[3].s,
                        ":",
                        arr[4].s,
                        
                        "(?::",
                        arr[5].s,
                        ")?",
                        
                        "(?:(?:\\.|,)(\\d+))?",
                        
                        "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?",
                        
                        ")?",
                        ")?",
                        ")?"
                    ].join("")
                };
            },
            U: {
                g: 1,
                c: "u = parseInt(results[{0}], 10);\n",
                s: "(-?\\d+)"
            }
        },
        
        
        
        dateFormat: function(date, format) {
            return utilDate.format(date, format);
        },
        
        isEqual: function(date1, date2) {
            
            if (date1 && date2) {
                return (date1.getTime() === date2.getTime());
            }
            
            return !(date1 || date2);
        },
        
        format: function(date, format) {
            var formatFunctions = utilDate.formatFunctions;
            if (!Ext.isDate(date)) {
                return '';
            }
            if (formatFunctions[format] == null) {
                utilDate.createFormat(format);
            }
            return formatFunctions[format].call(date) + '';
        },
        
        getTimezone: function(date) {
            
            
            
            
            
            
            
            
            
            
            
            
            return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,5})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
        },
        
        getGMTOffset: function(date, colon) {
            var offset = date.getTimezoneOffset();
            return (offset > 0 ? "-" : "+") + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0") + (colon ? ":" : "") + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
        },
        
        getDayOfYear: function(date) {
            var num = 0,
                d = utilDate.clone(date),
                m = date.getMonth(),
                i;
            for (i = 0 , d.setDate(1) , d.setMonth(0); i < m; d.setMonth(++i)) {
                num += utilDate.getDaysInMonth(d);
            }
            return num + date.getDate() - 1;
        },
        
        getWeekOfYear: (function() {
            
            var ms1d = 86400000,
                
                ms7d = 7 * ms1d;
            
            return function(date) {
                
                var DC3 = nativeDate.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d,
                    
                    AWN = Math.floor(DC3 / 7),
                    
                    Wyr = new nativeDate(AWN * ms7d).getUTCFullYear();
                return AWN - Math.floor(nativeDate.UTC(Wyr, 0, 7) / ms7d) + 1;
            };
        }()),
        
        isLeapYear: function(date) {
            var year = date.getFullYear();
            return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
        },
        
        getFirstDayOfMonth: function(date) {
            var day = (date.getDay() - (date.getDate() - 1)) % 7;
            return (day < 0) ? (day + 7) : day;
        },
        
        getLastDayOfMonth: function(date) {
            return utilDate.getLastDateOfMonth(date).getDay();
        },
        
        getFirstDateOfMonth: function(date) {
            return new nativeDate(date.getFullYear(),date.getMonth(),1);
        },
        
        getLastDateOfMonth: function(date) {
            return new nativeDate(date.getFullYear(),date.getMonth(),utilDate.getDaysInMonth(date));
        },
        
        getDaysInMonth: (function() {
            var daysInMonth = [
                    31,
                    28,
                    31,
                    30,
                    31,
                    30,
                    31,
                    31,
                    30,
                    31,
                    30,
                    31
                ];
            return function(date) {
                
                var m = date.getMonth();
                return m === 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
            };
        }()),
        
        
        getSuffix: function(date) {
            switch (date.getDate()) {
                case 1:
                case 21:
                case 31:
                    return "st";
                case 2:
                case 22:
                    return "nd";
                case 3:
                case 23:
                    return "rd";
                default:
                    return "th";
            }
        },
        
        
        clone: function(date) {
            return new nativeDate(date.getTime());
        },
        
        isDST: function(date) {
            
            
            return new nativeDate(date.getFullYear(),0,1).getTimezoneOffset() !== date.getTimezoneOffset();
        },
        
        clearTime: function(date, clone) {
            if (clone) {
                return utilDate.clearTime(utilDate.clone(date));
            }
            
            var d = date.getDate(),
                hr, c;
            
            date.setHours(0);
            date.setMinutes(0);
            date.setSeconds(0);
            date.setMilliseconds(0);
            if (date.getDate() !== d) {
                
                
                
                
                for (hr = 1 , c = utilDate.add(date, utilDate.HOUR, hr); c.getDate() !== d; hr++ , c = utilDate.add(date, utilDate.HOUR, hr)){}
                date.setDate(d);
                date.setHours(c.getHours());
            }
            return date;
        },
        
        add: function(date, interval, value) {
            var d = utilDate.clone(date),
                day, decimalValue,
                base = 0;
            if (!interval || value === 0) {
                return d;
            }
            decimalValue = value - parseInt(value, 10);
            value = parseInt(value, 10);
            if (value) {
                switch (interval.toLowerCase()) {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    case utilDate.MILLI:
                        d.setTime(d.getTime() + value);
                        break;
                    case utilDate.SECOND:
                        d.setTime(d.getTime() + value * 1000);
                        break;
                    case utilDate.MINUTE:
                        d.setTime(d.getTime() + value * 60 * 1000);
                        break;
                    case utilDate.HOUR:
                        d.setTime(d.getTime() + value * 60 * 60 * 1000);
                        break;
                    case utilDate.DAY:
                        d.setDate(d.getDate() + value);
                        break;
                    case utilDate.MONTH:
                        day = date.getDate();
                        if (day > 28) {
                            day = Math.min(day, utilDate.getLastDateOfMonth(utilDate.add(utilDate.getFirstDateOfMonth(date), utilDate.MONTH, value)).getDate());
                        };
                        d.setDate(day);
                        d.setMonth(date.getMonth() + value);
                        break;
                    case utilDate.YEAR:
                        day = date.getDate();
                        if (day > 28) {
                            day = Math.min(day, utilDate.getLastDateOfMonth(utilDate.add(utilDate.getFirstDateOfMonth(date), utilDate.YEAR, value)).getDate());
                        };
                        d.setDate(day);
                        d.setFullYear(date.getFullYear() + value);
                        break;
                }
            }
            if (decimalValue) {
                switch (interval.toLowerCase()) {
                    case utilDate.MILLI:
                        base = 1;
                        break;
                    case utilDate.SECOND:
                        base = 1000;
                        break;
                    case utilDate.MINUTE:
                        base = 1000 * 60;
                        break;
                    case utilDate.HOUR:
                        base = 1000 * 60 * 60;
                        break;
                    case utilDate.DAY:
                        base = 1000 * 60 * 60 * 24;
                        break;
                    case utilDate.MONTH:
                        day = utilDate.getDaysInMonth(d);
                        base = 1000 * 60 * 60 * 24 * day;
                        break;
                    case utilDate.YEAR:
                        day = (utilDate.isLeapYear(d) ? 366 : 365);
                        base = 1000 * 60 * 60 * 24 * day;
                        break;
                }
                if (base) {
                    d.setTime(d.getTime() + base * decimalValue);
                }
            }
            return d;
        },
        
        subtract: function(date, interval, value) {
            return utilDate.add(date, interval, -value);
        },
        
        between: function(date, start, end) {
            var t = date.getTime();
            return start.getTime() <= t && t <= end.getTime();
        },
        
        compat: function() {
            var p,
                statics = [
                    'useStrict',
                    'formatCodeToRegex',
                    'parseFunctions',
                    'parseRegexes',
                    'formatFunctions',
                    'y2kYear',
                    'MILLI',
                    'SECOND',
                    'MINUTE',
                    'HOUR',
                    'DAY',
                    'MONTH',
                    'YEAR',
                    'defaults',
                    'dayNames',
                    'monthNames',
                    'monthNumbers',
                    'getShortMonthName',
                    'getShortDayName',
                    'getMonthNumber',
                    'formatCodes',
                    'isValid',
                    'parseDate',
                    'getFormatCode',
                    'createFormat',
                    'createParser',
                    'parseCodes'
                ],
                proto = [
                    'dateFormat',
                    'format',
                    'getTimezone',
                    'getGMTOffset',
                    'getDayOfYear',
                    'getWeekOfYear',
                    'isLeapYear',
                    'getFirstDayOfMonth',
                    'getLastDayOfMonth',
                    'getDaysInMonth',
                    'getSuffix',
                    'clone',
                    'isDST',
                    'clearTime',
                    'add',
                    'between'
                ],
                sLen = statics.length,
                pLen = proto.length,
                stat, prot, s;
            
            for (s = 0; s < sLen; s++) {
                stat = statics[s];
                nativeDate[stat] = utilDate[stat];
            }
            
            for (p = 0; p < pLen; p++) {
                prot = proto[p];
                nativeDate.prototype[prot] = function() {
                    var args = Array.prototype.slice.call(arguments);
                    args.unshift(this);
                    return utilDate[prot].apply(utilDate, args);
                };
            }
        },
        
        diff: function(min, max, unit) {
            var est,
                diff = +max - min;
            switch (unit) {
                case utilDate.MILLI:
                    return diff;
                case utilDate.SECOND:
                    return Math.floor(diff / 1000);
                case utilDate.MINUTE:
                    return Math.floor(diff / 60000);
                case utilDate.HOUR:
                    return Math.floor(diff / 3600000);
                case utilDate.DAY:
                    return Math.floor(diff / 86400000);
                case 'w':
                    return Math.floor(diff / 604800000);
                case utilDate.MONTH:
                    est = (max.getFullYear() * 12 + max.getMonth()) - (min.getFullYear() * 12 + min.getMonth());
                    if (utilDate.add(min, unit, est) > max) {
                        return est - 1;
                    } else {
                        return est;
                    };
                case utilDate.YEAR:
                    est = max.getFullYear() - min.getFullYear();
                    if (utilDate.add(min, unit, est) > max) {
                        return est - 1;
                    } else {
                        return est;
                    };
            }
        },
        
        align: function(date, unit, step) {
            var num = new nativeDate(+date);
            switch (unit.toLowerCase()) {
                case utilDate.MILLI:
                    return num;
                    break;
                case utilDate.SECOND:
                    num.setUTCSeconds(num.getUTCSeconds() - num.getUTCSeconds() % step);
                    num.setUTCMilliseconds(0);
                    return num;
                    break;
                case utilDate.MINUTE:
                    num.setUTCMinutes(num.getUTCMinutes() - num.getUTCMinutes() % step);
                    num.setUTCSeconds(0);
                    num.setUTCMilliseconds(0);
                    return num;
                    break;
                case utilDate.HOUR:
                    num.setUTCHours(num.getUTCHours() - num.getUTCHours() % step);
                    num.setUTCMinutes(0);
                    num.setUTCSeconds(0);
                    num.setUTCMilliseconds(0);
                    return num;
                    break;
                case utilDate.DAY:
                    if (step === 7 || step === 14) {
                        num.setUTCDate(num.getUTCDate() - num.getUTCDay() + 1);
                    };
                    num.setUTCHours(0);
                    num.setUTCMinutes(0);
                    num.setUTCSeconds(0);
                    num.setUTCMilliseconds(0);
                    return num;
                    break;
                case utilDate.MONTH:
                    num.setUTCMonth(num.getUTCMonth() - (num.getUTCMonth() - 1) % step, 1);
                    num.setUTCHours(0);
                    num.setUTCMinutes(0);
                    num.setUTCSeconds(0);
                    num.setUTCMilliseconds(0);
                    return num;
                    break;
                case utilDate.YEAR:
                    num.setUTCFullYear(num.getUTCFullYear() - num.getUTCFullYear() % step, 1, 1);
                    num.setUTCHours(0);
                    num.setUTCMinutes(0);
                    num.setUTCSeconds(0);
                    num.setUTCMilliseconds(0);
                    return date;
                    break;
            }
        }
    };
}());


Ext.Function = (function() {
    
    
    
    
    var lastTime = 0,
        animFrameId,
        animFrameHandlers = [],
        animFrameNoArgs = [],
        idSource = 0,
        animFrameMap = {},
        win = window,
        requestAnimFrame = win.requestAnimationFrame || win.webkitRequestAnimationFrame || win.mozRequestAnimationFrame || win.oRequestAnimationFrame || function(callback) {
            var currTime = Ext.now(),
                timeToCall = Math.max(0, 16 - (currTime - lastTime)),
                id = win.setTimeout(function() {
                    callback(currTime + timeToCall);
                }, timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        },
        fireHandlers = function() {
            var len = animFrameHandlers.length,
                id, i, handler;
            animFrameId = null;
            
            for (i = 0; i < len; i++) {
                handler = animFrameHandlers[i];
                id = handler[3];
                
                if (animFrameMap[id]) {
                    handler[0].apply(handler[1] || Ext.global, handler[2] || animFrameNoArgs);
                    delete animFrameMap[id];
                }
            }
            
            
            animFrameHandlers = animFrameHandlers.slice(len);
        },
        fireElevatedHandlers = function() {
            Ext.elevateFunction(fireHandlers);
        },
        ExtFunction = {
            
            flexSetter: function(setter) {
                return function(name, value) {
                    var k, i;
                    if (name !== null) {
                        if (typeof name !== 'string') {
                            for (k in name) {
                                if (name.hasOwnProperty(k)) {
                                    setter.call(this, k, name[k]);
                                }
                            }
                            if (Ext.enumerables) {
                                for (i = Ext.enumerables.length; i--; ) {
                                    k = Ext.enumerables[i];
                                    if (name.hasOwnProperty(k)) {
                                        setter.call(this, k, name[k]);
                                    }
                                }
                            }
                        } else {
                            setter.call(this, name, value);
                        }
                    }
                    return this;
                };
            },
            
            bind: function(fn, scope, args, appendArgs) {
                if (arguments.length === 2) {
                    return function() {
                        return fn.apply(scope, arguments);
                    };
                }
                var method = fn,
                    slice = Array.prototype.slice;
                return function() {
                    var callArgs = args || arguments;
                    if (appendArgs === true) {
                        callArgs = slice.call(arguments, 0);
                        callArgs = callArgs.concat(args);
                    } else if (typeof appendArgs == 'number') {
                        callArgs = slice.call(arguments, 0);
                        
                        Ext.Array.insert(callArgs, appendArgs, args);
                    }
                    return method.apply(scope || Ext.global, callArgs);
                };
            },
            
            bindCallback: function(callback, scope, args, delay, caller) {
                return function() {
                    var a = Ext.Array.slice(arguments);
                    return Ext.callback(callback, scope, args ? args.concat(a) : a, delay, caller);
                };
            },
            
            pass: function(fn, args, scope) {
                if (!Ext.isArray(args)) {
                    if (Ext.isIterable(args)) {
                        args = Ext.Array.clone(args);
                    } else {
                        args = args !== undefined ? [
                            args
                        ] : [];
                    }
                }
                return function() {
                    var fnArgs = args.slice();
                    fnArgs.push.apply(fnArgs, arguments);
                    return fn.apply(scope || this, fnArgs);
                };
            },
            
            alias: function(object, methodName) {
                return function() {
                    return object[methodName].apply(object, arguments);
                };
            },
            
            clone: function(method) {
                return function() {
                    return method.apply(this, arguments);
                };
            },
            
            createInterceptor: function(origFn, newFn, scope, returnValue) {
                if (!Ext.isFunction(newFn)) {
                    return origFn;
                } else {
                    returnValue = Ext.isDefined(returnValue) ? returnValue : null;
                    return function() {
                        var me = this,
                            args = arguments;
                        newFn.target = me;
                        newFn.method = origFn;
                        return (newFn.apply(scope || me || Ext.global, args) !== false) ? origFn.apply(me || Ext.global, args) : returnValue;
                    };
                }
            },
            
            createDelayed: function(fn, delay, scope, args, appendArgs) {
                if (scope || args) {
                    fn = Ext.Function.bind(fn, scope, args, appendArgs);
                }
                return function() {
                    var me = this,
                        args = Array.prototype.slice.call(arguments);
                    setTimeout(function() {
                        if (Ext.elevateFunction) {
                            Ext.elevateFunction(fn, me, args);
                        } else {
                            fn.apply(me, args);
                        }
                    }, delay);
                };
            },
            
            defer: function(fn, millis, scope, args, appendArgs) {
                fn = Ext.Function.bind(fn, scope, args, appendArgs);
                if (millis > 0) {
                    return setTimeout(function() {
                        if (Ext.elevateFunction) {
                            Ext.elevateFunction(fn);
                        } else {
                            fn();
                        }
                    }, millis);
                }
                fn();
                return 0;
            },
            
            interval: function(fn, millis, scope, args, appendArgs) {
                fn = Ext.Function.bind(fn, scope, args, appendArgs);
                return setInterval(function() {
                    if (Ext.elevateFunction) {
                        Ext.elevateFunction(fn);
                    } else {
                        fn();
                    }
                }, millis);
            },
            
            createSequence: function(originalFn, newFn, scope) {
                if (!newFn) {
                    return originalFn;
                } else {
                    return function() {
                        var result = originalFn.apply(this, arguments);
                        newFn.apply(scope || this, arguments);
                        return result;
                    };
                }
            },
            
            createBuffered: function(fn, buffer, scope, args) {
                var timerId;
                return function() {
                    var callArgs = args || Array.prototype.slice.call(arguments, 0),
                        me = scope || this;
                    if (timerId) {
                        clearTimeout(timerId);
                    }
                    timerId = setTimeout(function() {
                        if (Ext.elevateFunction) {
                            Ext.elevateFunction(fn, me, callArgs);
                        } else {
                            fn.apply(me, callArgs);
                        }
                    }, buffer);
                };
            },
            
            createAnimationFrame: function(fn, scope, args, queueStrategy) {
                var timerId;
                queueStrategy = queueStrategy || 3;
                return function() {
                    var callArgs = args || Array.prototype.slice.call(arguments, 0);
                    scope = scope || this;
                    if (queueStrategy === 3 && timerId) {
                        ExtFunction.cancelAnimationFrame(timerId);
                    }
                    if ((queueStrategy & 1) || !timerId) {
                        timerId = ExtFunction.requestAnimationFrame(function() {
                            timerId = null;
                            fn.apply(scope, callArgs);
                        });
                    }
                };
            },
            
            requestAnimationFrame: function(fn, scope, args) {
                var id = ++idSource,
                    
                    handler = Array.prototype.slice.call(arguments, 0);
                handler[3] = id;
                animFrameMap[id] = 1;
                
                
                
                animFrameHandlers.push(handler);
                if (!animFrameId) {
                    animFrameId = requestAnimFrame(Ext.elevateFunction ? fireElevatedHandlers : fireHandlers);
                }
                return id;
            },
            cancelAnimationFrame: function(id) {
                
                
                
                delete animFrameMap[id];
            },
            
            createThrottled: function(fn, interval, scope) {
                var lastCallTime = 0,
                    elapsed, lastArgs, timer,
                    execute = function() {
                        if (Ext.elevateFunction) {
                            Ext.elevateFunction(fn, scope, lastArgs);
                        } else {
                            fn.apply(scope, lastArgs);
                        }
                        lastCallTime = Ext.now();
                        timer = null;
                    };
                return function() {
                    
                    if (!scope) {
                        scope = this;
                    }
                    elapsed = Ext.now() - lastCallTime;
                    lastArgs = arguments;
                    
                    
                    if (elapsed >= interval) {
                        clearTimeout(timer);
                        execute();
                    }
                    
                    else if (!timer) {
                        timer = Ext.defer(execute, interval - elapsed);
                    }
                };
            },
            
            createBarrier: function(count, fn, scope) {
                return function() {
                    if (!--count) {
                        fn.apply(scope, arguments);
                    }
                };
            },
            
            interceptBefore: function(object, methodName, fn, scope) {
                var method = object[methodName] || Ext.emptyFn;
                return (object[methodName] = function() {
                    var ret = fn.apply(scope || this, arguments);
                    method.apply(this, arguments);
                    return ret;
                });
            },
            
            interceptAfter: function(object, methodName, fn, scope) {
                var method = object[methodName] || Ext.emptyFn;
                return (object[methodName] = function() {
                    method.apply(this, arguments);
                    return fn.apply(scope || this, arguments);
                });
            },
            makeCallback: function(callback, scope) {
                if (!scope[callback]) {
                    if (scope.$className) {
                        Ext.Error.raise('No method "' + callback + '" on ' + scope.$className);
                    }
                    Ext.Error.raise('No method "' + callback + '"');
                }
                return function() {
                    return scope[callback].apply(scope, arguments);
                };
            }
        };
    
    Ext.defer = ExtFunction.defer;
    
    Ext.interval = ExtFunction.interval;
    
    Ext.pass = ExtFunction.pass;
    
    Ext.bind = ExtFunction.bind;
    Ext.deferCallback = ExtFunction.requestAnimationFrame;
    return ExtFunction;
})();


Ext.Number = new function() {
    
    
    
    var ExtNumber = this,
        isToFixedBroken = (0.9).toFixed() !== '1',
        math = Math,
        ClipDefault = {
            count: false,
            inclusive: false,
            wrap: true
        };
    Ext.apply(ExtNumber, {
        Clip: {
            DEFAULT: ClipDefault,
            COUNT: Ext.applyIf({
                count: true
            }, ClipDefault),
            INCLUSIVE: Ext.applyIf({
                inclusive: true
            }, ClipDefault),
            NOWRAP: Ext.applyIf({
                wrap: false
            }, ClipDefault)
        },
        
        clipIndices: function(length, indices, options) {
            options = options || ClipDefault;
            var defaultValue = 0,
                
                wrap = options.wrap,
                begin, end, i;
            indices = indices || [];
            for (i = 0; i < 2; ++i) {
                
                
                begin = end;
                
                end = indices[i];
                if (end == null) {
                    end = defaultValue;
                } else if (i && options.count) {
                    end += begin;
                    
                    end = (end > length) ? length : end;
                } else {
                    if (wrap) {
                        end = (end < 0) ? (length + end) : end;
                    }
                    if (i && options.inclusive) {
                        ++end;
                    }
                    end = (end < 0) ? 0 : ((end > length) ? length : end);
                }
                defaultValue = length;
            }
            
            
            
            
            indices[0] = begin;
            indices[1] = (end < begin) ? begin : end;
            return indices;
        },
        
        constrain: function(number, min, max) {
            var x = parseFloat(number);
            
            
            
            if (min === null) {
                min = number;
            }
            if (max === null) {
                max = number;
            }
            
            
            
            return (x < min) ? min : ((x > max) ? max : x);
        },
        
        snap: function(value, increment, minValue, maxValue) {
            var m;
            
            
            if (value === undefined || value < minValue) {
                return minValue || 0;
            }
            if (increment) {
                m = value % increment;
                if (m !== 0) {
                    value -= m;
                    if (m * 2 >= increment) {
                        value += increment;
                    } else if (m * 2 < -increment) {
                        value -= increment;
                    }
                }
            }
            return ExtNumber.constrain(value, minValue, maxValue);
        },
        
        snapInRange: function(value, increment, minValue, maxValue) {
            var tween;
            
            minValue = (minValue || 0);
            
            if (value === undefined || value < minValue) {
                return minValue;
            }
            
            if (increment && (tween = ((value - minValue) % increment))) {
                value -= tween;
                tween *= 2;
                if (tween >= increment) {
                    value += increment;
                }
            }
            
            if (maxValue !== undefined) {
                if (value > (maxValue = ExtNumber.snapInRange(maxValue, increment, minValue))) {
                    value = maxValue;
                }
            }
            return value;
        },
        
        sign: function(x) {
            x = +x;
            
            if (x === 0 || isNaN(x)) {
                return x;
            }
            return (x > 0) ? 1 : -1;
        },
        
        toFixed: isToFixedBroken ? function(value, precision) {
            precision = precision || 0;
            var pow = math.pow(10, precision);
            return (math.round(value * pow) / pow).toFixed(precision);
        } : function(value, precision) {
            return value.toFixed(precision);
        },
        
        from: function(value, defaultValue) {
            if (isFinite(value)) {
                value = parseFloat(value);
            }
            return !isNaN(value) ? value : defaultValue;
        },
        
        randomInt: function(from, to) {
            return math.floor(math.random() * (to - from + 1) + from);
        },
        
        correctFloat: function(n) {
            
            
            
            return parseFloat(n.toPrecision(14));
        }
    });
    
    Ext.num = function() {
        return ExtNumber.from.apply(this, arguments);
    };
}();


(function() {
    
    var TemplateClass = function() {},
        ExtObject = Ext.Object = {
            
            
            
            
            
            chain: Object.create || function(object) {
                TemplateClass.prototype = object;
                var result = new TemplateClass();
                TemplateClass.prototype = null;
                return result;
            },
            
            clear: function(object) {
                
                for (var key in object) {
                    delete object[key];
                }
                return object;
            },
            
            freeze: Object.freeze ? function(obj, deep) {
                if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {
                    Object.freeze(obj);
                    if (deep) {
                        for (var name in obj) {
                            ExtObject.freeze(obj[name], deep);
                        }
                    }
                }
                return obj;
            } : Ext.identityFn,
            
            toQueryObjects: function(name, value, recursive) {
                var self = ExtObject.toQueryObjects,
                    objects = [],
                    i, ln;
                if (Ext.isArray(value)) {
                    for (i = 0 , ln = value.length; i < ln; i++) {
                        if (recursive) {
                            objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                        } else {
                            objects.push({
                                name: name,
                                value: value[i]
                            });
                        }
                    }
                } else if (Ext.isObject(value)) {
                    for (i in value) {
                        if (value.hasOwnProperty(i)) {
                            if (recursive) {
                                objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                            } else {
                                objects.push({
                                    name: name,
                                    value: value[i]
                                });
                            }
                        }
                    }
                } else {
                    objects.push({
                        name: name,
                        value: value
                    });
                }
                return objects;
            },
            
            toQueryString: function(object, recursive) {
                var paramObjects = [],
                    params = [],
                    i, j, ln, paramObject, value;
                for (i in object) {
                    if (object.hasOwnProperty(i)) {
                        paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
                    }
                }
                for (j = 0 , ln = paramObjects.length; j < ln; j++) {
                    paramObject = paramObjects[j];
                    value = paramObject.value;
                    if (Ext.isEmpty(value)) {
                        value = '';
                    } else if (Ext.isDate(value)) {
                        value = Ext.Date.toString(value);
                    }
                    params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
                }
                return params.join('&');
            },
            
            fromQueryString: function(queryString, recursive) {
                var parts = queryString.replace(/^\?/, '').split('&'),
                    object = {},
                    temp, components, name, value, i, ln, part, j, subLn, matchedKeys, matchedName, keys, key, nextKey;
                for (i = 0 , ln = parts.length; i < ln; i++) {
                    part = parts[i];
                    if (part.length > 0) {
                        components = part.split('=');
                        name = decodeURIComponent(components[0]);
                        value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
                        if (!recursive) {
                            if (object.hasOwnProperty(name)) {
                                if (!Ext.isArray(object[name])) {
                                    object[name] = [
                                        object[name]
                                    ];
                                }
                                object[name].push(value);
                            } else {
                                object[name] = value;
                            }
                        } else {
                            matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
                            matchedName = name.match(/^([^\[]+)/);
                            if (!matchedName) {
                                throw new Error('[Ext.Object.fromQueryString] Malformed query string given, failed parsing name from "' + part + '"');
                            }
                            name = matchedName[0];
                            keys = [];
                            if (matchedKeys === null) {
                                object[name] = value;
                                
                                continue;
                            }
                            for (j = 0 , subLn = matchedKeys.length; j < subLn; j++) {
                                key = matchedKeys[j];
                                key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
                                keys.push(key);
                            }
                            keys.unshift(name);
                            temp = object;
                            for (j = 0 , subLn = keys.length; j < subLn; j++) {
                                key = keys[j];
                                if (j === subLn - 1) {
                                    if (Ext.isArray(temp) && key === '') {
                                        temp.push(value);
                                    } else {
                                        temp[key] = value;
                                    }
                                } else {
                                    if (temp[key] === undefined || typeof temp[key] === 'string') {
                                        nextKey = keys[j + 1];
                                        temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
                                    }
                                    temp = temp[key];
                                }
                            }
                        }
                    }
                }
                return object;
            },
            
            each: function(object, fn, scope) {
                var enumerables = Ext.enumerables,
                    i, property;
                scope = scope || object;
                for (property in object) {
                    if (object.hasOwnProperty(property)) {
                        if (fn.call(scope, property, object[property], object) === false) {
                            return;
                        }
                    }
                }
                if (enumerables) {
                    for (i = enumerables.length; i--; ) {
                        if (object.hasOwnProperty(property = enumerables[i])) {
                            if (fn.call(scope, property, object[property], object) === false) {
                                return;
                            }
                        }
                    }
                }
            },
            
            eachValue: function(object, fn, scope) {
                var enumerables = Ext.enumerables,
                    i, property;
                scope = scope || object;
                for (property in object) {
                    if (object.hasOwnProperty(property)) {
                        if (fn.call(scope, object[property]) === false) {
                            return;
                        }
                    }
                }
                if (enumerables) {
                    for (i = enumerables.length; i--; ) {
                        if (object.hasOwnProperty(property = enumerables[i])) {
                            if (fn.call(scope, object[property]) === false) {
                                return;
                            }
                        }
                    }
                }
            },
            
            merge: function(destination) {
                var i = 1,
                    ln = arguments.length,
                    mergeFn = ExtObject.merge,
                    cloneFn = Ext.clone,
                    object, key, value, sourceKey;
                for (; i < ln; i++) {
                    object = arguments[i];
                    for (key in object) {
                        value = object[key];
                        if (value && value.constructor === Object) {
                            sourceKey = destination[key];
                            if (sourceKey && sourceKey.constructor === Object) {
                                mergeFn(sourceKey, value);
                            } else {
                                destination[key] = cloneFn(value);
                            }
                        } else {
                            destination[key] = value;
                        }
                    }
                }
                return destination;
            },
            
            mergeIf: function(destination) {
                var i = 1,
                    ln = arguments.length,
                    cloneFn = Ext.clone,
                    object, key, value;
                for (; i < ln; i++) {
                    object = arguments[i];
                    for (key in object) {
                        if (!(key in destination)) {
                            value = object[key];
                            if (value && value.constructor === Object) {
                                destination[key] = cloneFn(value);
                            } else {
                                destination[key] = value;
                            }
                        }
                    }
                }
                return destination;
            },
            
            getAllKeys: function(object) {
                var keys = [],
                    property;
                for (property in object) {
                    keys.push(property);
                }
                return keys;
            },
            
            getKey: function(object, value) {
                for (var property in object) {
                    if (object.hasOwnProperty(property) && object[property] === value) {
                        return property;
                    }
                }
                return null;
            },
            
            getValues: function(object) {
                var values = [],
                    property;
                for (property in object) {
                    if (object.hasOwnProperty(property)) {
                        values.push(object[property]);
                    }
                }
                return values;
            },
            
            getKeys: (typeof Object.keys == 'function') ? function(object) {
                if (!object) {
                    return [];
                }
                return Object.keys(object);
            } : function(object) {
                var keys = [],
                    property;
                for (property in object) {
                    if (object.hasOwnProperty(property)) {
                        keys.push(property);
                    }
                }
                return keys;
            },
            
            getSize: function(object) {
                var size = 0,
                    property;
                for (property in object) {
                    if (object.hasOwnProperty(property)) {
                        size++;
                    }
                }
                return size;
            },
            
            isEmpty: function(object) {
                for (var key in object) {
                    if (object.hasOwnProperty(key)) {
                        return false;
                    }
                }
                return true;
            },
            
            equals: (function() {
                var check = function(o1, o2) {
                        var key;
                        for (key in o1) {
                            if (o1.hasOwnProperty(key)) {
                                if (o1[key] !== o2[key]) {
                                    return false;
                                }
                            }
                        }
                        return true;
                    };
                return function(object1, object2) {
                    
                    if (object1 === object2) {
                        return true;
                    }
                    if (object1 && object2) {
                        
                        
                        return check(object1, object2) && check(object2, object1);
                    } else if (!object1 && !object2) {
                        return object1 === object2;
                    } else {
                        return false;
                    }
                };
            })(),
            
            fork: function(obj) {
                var ExtArray = Ext.Array,
                    ret, key, value;
                if (obj && obj.constructor === Object) {
                    ret = ExtObject.chain(obj);
                    for (key in obj) {
                        value = obj[key];
                        if (value) {
                            if (value.constructor === Object) {
                                ret[key] = ExtObject.fork(value);
                            } else if (value instanceof Array) {
                                ret[key] = Ext.Array.clone(value);
                            }
                        }
                    }
                } else {
                    ret = obj;
                }
                return ret;
            },
            defineProperty: ('defineProperty' in Object) ? Object.defineProperty : function(object, name, descriptor) {
                if (!Object.prototype.__defineGetter__) {
                    return;
                }
                if (descriptor.get) {
                    object.__defineGetter__(name, descriptor.get);
                }
                if (descriptor.set) {
                    object.__defineSetter__(name, descriptor.set);
                }
            },
            
            classify: function(object) {
                var prototype = object,
                    objectProperties = [],
                    propertyClassesMap = {},
                    objectClass = function() {
                        var i = 0,
                            ln = objectProperties.length,
                            property;
                        for (; i < ln; i++) {
                            property = objectProperties[i];
                            this[property] = new propertyClassesMap[property]();
                        }
                    },
                    key, value;
                for (key in object) {
                    if (object.hasOwnProperty(key)) {
                        value = object[key];
                        if (value && value.constructor === Object) {
                            objectProperties.push(key);
                            propertyClassesMap[key] = ExtObject.classify(value);
                        }
                    }
                }
                objectClass.prototype = prototype;
                return objectClass;
            }
        };
    
    Ext.merge = Ext.Object.merge;
    
    Ext.mergeIf = Ext.Object.mergeIf;
}());


Ext.apply(Ext, {
    
    
    
    
    _namedScopes: {
        'this': {
            isThis: 1
        },
        controller: {
            isController: 1
        },
        
        
        self: {
            isSelf: 1
        },
        'self.controller': {
            isSelf: 1,
            isController: 1
        }
    },
    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 : 
            
            id.replace(escapeRx, escapeFn).replace(leadingNumRx, numEscapeFn);
        };
    }()),
    
    callback: function(callback, scope, args, delay, caller, defaultScope) {
        if (!callback) {
            return;
        }
        var namedScope = (scope in Ext._namedScopes);
        if (callback.charAt) {
            
            if ((!scope || namedScope) && caller) {
                scope = caller.resolveListenerScope(namedScope ? scope : defaultScope);
            }
            if (!scope || !Ext.isObject(scope)) {
                Ext.Error.raise('Named method "' + callback + '" requires a scope object');
            }
            if (!Ext.isFunction(scope[callback])) {
                Ext.Error.raise('No method named "' + callback + '" on ' + (scope.$className || 'scope object'));
            }
            callback = scope[callback];
        } else if (namedScope) {
            scope = defaultScope || caller;
        } else if (!scope) {
            scope = caller;
        }
        var ret;
        if (callback && Ext.isFunction(callback)) {
            scope = scope || Ext.global;
            if (delay) {
                Ext.defer(callback, delay, scope, args);
            } else if (Ext.elevateFunction) {
                ret = Ext.elevateFunction(callback, scope, args);
            } else if (args) {
                ret = callback.apply(scope, args);
            } else {
                ret = callback.call(scope);
            }
        }
        return ret;
    },
    
    coerce: function(from, to) {
        var fromType = Ext.typeOf(from),
            toType = Ext.typeOf(to),
            isString = typeof from === 'string';
        if (fromType !== toType) {
            switch (toType) {
                case 'string':
                    return String(from);
                case 'number':
                    return Number(from);
                case 'boolean':
                    return isString && (!from || from === 'false') ? false : Boolean(from);
                case 'null':
                    return isString && (!from || from === 'null') ? null : from;
                case 'undefined':
                    return isString && (!from || from === 'undefined') ? undefined : from;
                case 'date':
                    return isString && isNaN(from) ? Ext.Date.parse(from, Ext.Date.defaultFormat) : Date(Number(from));
            }
        }
        return from;
    },
    
    copyTo: function(dest, source, names, usePrototypeKeys) {
        if (typeof names === 'string') {
            names = names.split(Ext.propertyNameSplitRe);
        }
        for (var name,
            i = 0,
            n = names ? names.length : 0; i < n; i++) {
            name = names[i];
            if (usePrototypeKeys || source.hasOwnProperty(name)) {
                dest[name] = source[name];
            }
        }
        return dest;
    },
    propertyNameSplitRe: /[,;\s]+/,
    
    copyToIf: function(destination, source, names) {
        if (typeof names === 'string') {
            names = names.split(Ext.propertyNameSplitRe);
        }
        for (var name,
            i = 0,
            n = names ? names.length : 0; i < n; i++) {
            name = names[i];
            if (destination[name] === undefined) {
                destination[name] = source[name];
            }
        }
        return destination;
    },
    
    extend: (function() {
        
        var objectConstructor = Object.prototype.constructor,
            inlineOverrides = function(o) {
                for (var m in o) {
                    if (!o.hasOwnProperty(m)) {
                        
                        continue;
                    }
                    this[m] = o[m];
                }
            };
        return function(subclass, superclass, overrides) {
            
            if (Ext.isObject(superclass)) {
                overrides = superclass;
                superclass = subclass;
                subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
                    superclass.apply(this, arguments);
                };
            }
            if (!superclass) {
                Ext.Error.raise({
                    sourceClass: 'Ext',
                    sourceMethod: 'extend',
                    msg: 'Attempting to extend from a class which has not been loaded on the page.'
                });
            }
            
            var F = function() {},
                subclassProto,
                superclassProto = superclass.prototype;
            F.prototype = superclassProto;
            subclassProto = subclass.prototype = new F();
            subclassProto.constructor = subclass;
            subclass.superclass = superclassProto;
            if (superclassProto.constructor === objectConstructor) {
                superclassProto.constructor = superclass;
            }
            subclass.override = function(overrides) {
                Ext.override(subclass, overrides);
            };
            subclassProto.override = inlineOverrides;
            subclassProto.proto = subclassProto;
            subclass.override(overrides);
            subclass.extend = function(o) {
                return Ext.extend(subclass, o);
            };
            return subclass;
        };
    }()),
    
    iterate: function(object, fn, scope) {
        if (Ext.isEmpty(object)) {
            return;
        }
        if (scope === undefined) {
            scope = object;
        }
        if (Ext.isIterable(object)) {
            Ext.Array.each.call(Ext.Array, object, fn, scope);
        } else {
            Ext.Object.each.call(Ext.Object, object, fn, scope);
        }
    },
    
    urlEncode: function() {
        var args = Ext.Array.from(arguments),
            prefix = '';
        
        if (Ext.isString(args[1])) {
            prefix = args[1] + '&';
            args[1] = false;
        }
        return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
    },
    
    urlDecode: function() {
        return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
    },
    
    getScrollbarSize: function(force) {
        if (!Ext.isDomReady) {
            Ext.Error.raise("getScrollbarSize called before DomReady");
        }
        var scrollbarSize = Ext._scrollbarSize;
        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);
            
            
            Ext._scrollbarSize = scrollbarSize = {
                width: div.offsetWidth - div.clientWidth,
                height: div.offsetHeight - div.clientHeight
            };
            db.removeChild(div);
        }
        return scrollbarSize;
    },
    
    typeOf: (function() {
        var nonWhitespaceRe = /\S/,
            toString = Object.prototype.toString,
            typeofTypes = {
                number: 1,
                string: 1,
                'boolean': 1,
                'undefined': 1
            },
            toStringTypes = {
                '[object Array]': 'array',
                '[object Date]': 'date',
                '[object Boolean]': 'boolean',
                '[object Number]': 'number',
                '[object RegExp]': 'regexp'
            };
        return function(value) {
            if (value === null) {
                return 'null';
            }
            var type = typeof value,
                ret, typeToString;
            if (typeofTypes[type]) {
                return type;
            }
            ret = toStringTypes[typeToString = toString.call(value)];
            if (ret) {
                return ret;
            }
            if (type === 'function') {
                return 'function';
            }
            if (type === 'object') {
                if (value.nodeType !== undefined) {
                    if (value.nodeType === 3) {
                        return nonWhitespaceRe.test(value.nodeValue) ? 'textnode' : 'whitespace';
                    } else {
                        return 'element';
                    }
                }
                return 'object';
            }
            Ext.Error.raise({
                sourceClass: 'Ext',
                sourceMethod: 'typeOf',
                msg: 'Failed to determine the type of "' + value + '".'
            });
            return typeToString;
        };
    }()),
    
    factory: function(config, classReference, instance, aliasNamespace) {
        var manager = Ext.ClassManager,
            newInstance;
        
        
        if (!config || config.isInstance) {
            if (instance && instance !== config) {
                instance.destroy();
            }
            return config;
        }
        if (aliasNamespace) {
            
            if (typeof config == 'string') {
                return manager.instantiateByAlias(aliasNamespace + '.' + config);
            }
            
            else if (Ext.isObject(config) && 'type' in config) {
                return manager.instantiateByAlias(aliasNamespace + '.' + config.type, config);
            }
        }
        if (config === true) {
            return instance || Ext.create(classReference);
        }
        if (!Ext.isObject(config)) {
            Ext.Logger.error("Invalid config, must be a valid config object");
        }
        if ('xtype' in config) {
            newInstance = manager.instantiateByAlias('widget.' + config.xtype, config);
        } else if ('xclass' in config) {
            newInstance = Ext.create(config.xclass, config);
        }
        if (newInstance) {
            if (instance) {
                instance.destroy();
            }
            return newInstance;
        }
        if (instance) {
            return instance.setConfig(config);
        }
        return Ext.create(classReference, config);
    },
    
    log: (function() {
        
        var primitiveRe = /string|number|boolean/;
        function dumpObject(object, level, maxLevel, withFunctions) {
            var member, type, value, name, prefix, suffix,
                members = [];
            if (Ext.isArray(object)) {
                prefix = '[';
                suffix = ']';
            } else if (Ext.isObject(object)) {
                prefix = '{';
                suffix = '}';
            }
            if (!maxLevel) {
                maxLevel = 3;
            }
            if (level > maxLevel) {
                return prefix + '...' + suffix;
            }
            level = level || 1;
            var spacer = (new Array(level)).join('    ');
            
            for (name in object) {
                if (object.hasOwnProperty(name)) {
                    value = object[name];
                    type = typeof value;
                    if (type == 'function') {
                        if (!withFunctions) {
                            
                            continue;
                        }
                        member = type;
                    } else 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 = this.dumpObject(value, level + 1, maxLevel, withFunctions);
                    } else if (Ext.isObject(value)) {
                        member = this.dumpObject(value, level + 1, maxLevel, withFunctions);
                    } else {
                        member = type;
                    }
                    members.push(spacer + name + ': ' + member);
                }
            }
            
            if (members.length) {
                return prefix + '\n    ' + members.join(',\n    ') + '\n' + spacer + suffix;
            }
            return prefix + suffix;
        }
        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;
            
            if (level != 'log') {
                message = '[' + level.charAt(0).toUpperCase() + '] ' + message;
            }
            
            
            
            if (con) {
                
                if (con[level]) {
                    con[level](message);
                } else {
                    con.log(message);
                }
                if (dump) {
                    con.dir(dump);
                }
                if (stack && con.trace) {
                    
                    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) {
                    
                    
                    Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4));
                }
                
                out.push(message);
            }
            
            ++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;
        return log;
    }()) || (function() {
        var nullLog = function() {};
        nullLog.info = nullLog.warn = nullLog.error = Ext.emptyFn;
        return nullLog;
    }())
});


(function() {
    
    
    var 
        checkVerTemp = [
            ''
        ],
        endOfVersionRe = /([^\d\.])/,
        notDigitsRe = /[^\d]/g,
        plusMinusRe = /[\-+]/g,
        stripRe = /\s/g,
        underscoreRe = /_/g,
        Version;
    Ext.Version = Version = function(version, defaultMode) {
        var me = this,
            padModes = me.padModes,
            ch, i, pad, parts, release, releaseStartIndex, ver;
        if (version.isVersion) {
            version = version.version;
        }
        me.version = ver = String(version).toLowerCase().replace(underscoreRe, '.').replace(plusMinusRe, '');
        ch = ver.charAt(0);
        if (ch in padModes) {
            ver = ver.substring(1);
            pad = padModes[ch];
        } else {
            pad = defaultMode ? padModes[defaultMode] : 0;
        }
        
        me.pad = pad;
        releaseStartIndex = ver.search(endOfVersionRe);
        me.shortVersion = ver;
        if (releaseStartIndex !== -1) {
            me.release = release = ver.substr(releaseStartIndex, version.length);
            me.shortVersion = ver.substr(0, releaseStartIndex);
            release = Version.releaseValueMap[release] || release;
        }
        me.releaseValue = release || pad;
        me.shortVersion = me.shortVersion.replace(notDigitsRe, '');
        
        me.parts = parts = ver.split('.');
        for (i = parts.length; i--; ) {
            parts[i] = parseInt(parts[i], 10);
        }
        if (pad === Infinity) {
            
            parts.push(pad);
        }
        
        me.major = parts[0] || pad;
        
        me.minor = parts[1] || pad;
        
        me.patch = parts[2] || pad;
        
        me.build = parts[3] || pad;
        return me;
    };
    Version.prototype = {
        isVersion: true,
        padModes: {
            '~': NaN,
            '^': Infinity
        },
        
        release: '',
        
        compareTo: function(other) {
            
            
            var me = this,
                lhsPad = me.pad,
                lhsParts = me.parts,
                lhsLength = lhsParts.length,
                rhsVersion = other.isVersion ? other : new Version(other),
                rhsPad = rhsVersion.pad,
                rhsParts = rhsVersion.parts,
                rhsLength = rhsParts.length,
                length = Math.max(lhsLength, rhsLength),
                i, lhs, rhs;
            for (i = 0; i < length; i++) {
                lhs = (i < lhsLength) ? lhsParts[i] : lhsPad;
                rhs = (i < rhsLength) ? rhsParts[i] : rhsPad;
                
                
                if (lhs < rhs) {
                    return -1;
                }
                if (lhs > rhs) {
                    return 1;
                }
            }
            
            lhs = me.releaseValue;
            rhs = rhsVersion.releaseValue;
            if (lhs < rhs) {
                return -1;
            }
            if (lhs > rhs) {
                return 1;
            }
            return 0;
        },
        
        toString: function() {
            return this.version;
        },
        
        valueOf: function() {
            return this.version;
        },
        
        getMajor: function() {
            return this.major;
        },
        
        getMinor: function() {
            return this.minor;
        },
        
        getPatch: function() {
            return this.patch;
        },
        
        getBuild: function() {
            return this.build;
        },
        
        getRelease: function() {
            return this.release;
        },
        
        getReleaseValue: function() {
            return this.releaseValue;
        },
        
        isGreaterThan: function(target) {
            return this.compareTo(target) > 0;
        },
        
        isGreaterThanOrEqual: function(target) {
            return this.compareTo(target) >= 0;
        },
        
        isLessThan: function(target) {
            return this.compareTo(target) < 0;
        },
        
        isLessThanOrEqual: function(target) {
            return this.compareTo(target) <= 0;
        },
        
        equals: function(target) {
            return this.compareTo(target) === 0;
        },
        
        match: function(target) {
            target = String(target);
            return this.version.substr(0, target.length) === target;
        },
        
        toArray: function() {
            var me = this;
            return [
                me.getMajor(),
                me.getMinor(),
                me.getPatch(),
                me.getBuild(),
                me.getRelease()
            ];
        },
        
        getShortVersion: function() {
            return this.shortVersion;
        },
        
        gt: function(target) {
            return this.compareTo(target) > 0;
        },
        
        lt: function(target) {
            return this.compareTo(target) < 0;
        },
        
        gtEq: function(target) {
            return this.compareTo(target) >= 0;
        },
        
        ltEq: function(target) {
            return this.compareTo(target) <= 0;
        }
    };
    Ext.apply(Version, {
        aliases: {
            from: {
                extjs: 'ext',
                core: 'sencha-core'
            },
            to: {
                ext: [
                    'extjs'
                ],
                'sencha-core': [
                    'core'
                ]
            }
        },
        
        releaseValueMap: {
            dev: -6,
            alpha: -5,
            a: -5,
            beta: -4,
            b: -4,
            rc: -3,
            '#': -2,
            p: -1,
            pl: -1
        },
        
        getComponentValue: function(value) {
            return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
        },
        
        compare: function(current, target) {
            var ver = current.isVersion ? current : new Version(current);
            return ver.compareTo(target);
        },
        set: function(collection, packageName, version) {
            var aliases = Version.aliases.to[packageName],
                ver = version.isVersion ? version : new Version(version),
                i;
            collection[packageName] = ver;
            if (aliases) {
                for (i = aliases.length; i-- > 0; ) {
                    collection[aliases[i]] = ver;
                }
            }
            return ver;
        }
    });
    
    Ext.apply(Ext, {
        
        compatVersions: {},
        
        versions: {},
        
        lastRegisteredVersion: null,
        
        getCompatVersion: function(packageName) {
            var versions = Ext.compatVersions,
                compat;
            if (!packageName) {
                compat = versions.ext || versions.touch || versions.core;
            } else {
                compat = versions[Version.aliases.from[packageName] || packageName];
            }
            return compat || Ext.getVersion(packageName);
        },
        
        setCompatVersion: function(packageName, version) {
            Version.set(Ext.compatVersions, packageName, version);
        },
        
        setVersion: function(packageName, version) {
            Ext.lastRegisteredVersion = Version.set(Ext.versions, packageName, version);
            return this;
        },
        
        getVersion: function(packageName) {
            var versions = Ext.versions;
            if (!packageName) {
                return versions.ext || versions.touch || versions.core;
            }
            return versions[Version.aliases.from[packageName] || packageName];
        },
        
        checkVersion: function(specs, matchAll) {
            var isArray = Ext.isArray(specs),
                aliases = Version.aliases.from,
                compat = isArray ? specs : checkVerTemp,
                length = compat.length,
                versions = Ext.versions,
                frameworkVer = versions.ext || versions.touch,
                i, index, matches, minVer, maxVer, packageName, spec, range, ver;
            if (!isArray) {
                checkVerTemp[0] = specs;
            }
            for (i = 0; i < length; ++i) {
                if (!Ext.isString(spec = compat[i])) {
                    matches = Ext.checkVersion(spec.and || spec.or, !spec.or);
                    if (spec.not) {
                        matches = !matches;
                    }
                } else {
                    if (spec.indexOf(' ') >= 0) {
                        spec = spec.replace(stripRe, '');
                    }
                    
                    
                    index = spec.indexOf('@');
                    if (index < 0) {
                        range = spec;
                        ver = frameworkVer;
                    } else {
                        packageName = spec.substring(0, index);
                        if (!(ver = versions[aliases[packageName] || packageName])) {
                            
                            
                            if (matchAll) {
                                return false;
                            }
                            
                            
                            
                            continue;
                        }
                        range = spec.substring(index + 1);
                    }
                    
                    index = range.indexOf('-');
                    if (index < 0) {
                        
                        if (range.charAt(index = range.length - 1) === '+') {
                            minVer = range.substring(0, index);
                            maxVer = null;
                        } else {
                            minVer = maxVer = range;
                        }
                    } else if (index > 0) {
                        
                        minVer = range.substring(0, index);
                        maxVer = range.substring(index + 1);
                    } else 
                    {
                        
                        minVer = null;
                        maxVer = range.substring(index + 1);
                    }
                    matches = true;
                    if (minVer) {
                        minVer = new Version(minVer,'~');
                        
                        matches = minVer.ltEq(ver);
                    }
                    if (matches && maxVer) {
                        maxVer = new Version(maxVer,'~');
                        
                        matches = maxVer.gtEq(ver);
                    }
                }
                
                if (matches) {
                    
                    if (!matchAll) {
                        return true;
                    }
                } else if (matchAll) {
                    
                    return false;
                }
            }
            
            
            
            
            return !!matchAll;
        },
        
        deprecate: function(packageName, since, closure, scope) {
            if (Version.compare(Ext.getVersion(packageName), since) < 1) {
                closure.call(scope);
            }
        }
    });
}());


(function(manifest) {
    var packages = (manifest && manifest.packages) || {},
        compat = manifest && manifest.compatibility,
        name, pkg;
    for (name in packages) {
        pkg = packages[name];
        Ext.setVersion(name, pkg.version);
    }
    if (compat) {
        if (Ext.isString(compat)) {
            Ext.setCompatVersion('core', compat);
        } else {
            for (name in compat) {
                Ext.setCompatVersion(name, compat[name]);
            }
        }
    }
    if (!packages.ext && !packages.touch) {
        Ext.setVersion('ext','5.1.0.107');Ext.setVersion('sencha-core','5.1.0.107');
    }
})(Ext.manifest);


Ext.Config = function(name) {
    
    
    var me = this,
        capitalizedName = name.charAt(0).toUpperCase() + name.substr(1);
    
    me.name = name;
    
    me.names = {
        internal: '_' + name,
        initializing: 'is' + capitalizedName + 'Initializing',
        apply: 'apply' + capitalizedName,
        update: 'update' + capitalizedName,
        get: 'get' + capitalizedName,
        set: 'set' + capitalizedName,
        initGet: 'initGet' + capitalizedName,
        doSet: 'doSet' + capitalizedName,
        changeEvent: name.toLowerCase() + 'change'
    };
    
    
    me.root = me;
};
Ext.Config.map = {};
Ext.Config.get = function(name) {
    var map = Ext.Config.map,
        ret = map[name] || (map[name] = new Ext.Config(name));
    return ret;
};
Ext.Config.prototype = {
    self: Ext.Config,
    isConfig: true,
    
    
    
    getGetter: function() {
        return this.getter || (this.root.getter = this.makeGetter());
    },
    getInitGetter: function() {
        return this.initGetter || (this.root.initGetter = this.makeInitGetter());
    },
    getSetter: function() {
        return this.setter || (this.root.setter = this.makeSetter());
    },
    
    getInternalName: function(target) {
        return target.$configPrefixed ? this.names.internal : this.name;
    },
    mergeNew: function(newValue, oldValue, target, mixinClass) {
        var ret, key;
        if (!oldValue) {
            ret = newValue;
        } else if (!newValue) {
            ret = oldValue;
        } else {
            ret = Ext.Object.chain(oldValue);
            for (key in newValue) {
                if (!mixinClass || !(key in ret)) {
                    ret[key] = newValue[key];
                }
            }
        }
        return ret;
    },
    
    mergeSets: function(newValue, oldValue, preserveExisting) {
        var ret = oldValue ? Ext.Object.chain(oldValue) : {},
            i, val;
        if (newValue instanceof Array) {
            for (i = newValue.length; i--; ) {
                val = newValue[i];
                if (!preserveExisting || !(val in ret)) {
                    ret[val] = true;
                }
            }
        } else if (newValue) {
            if (newValue.constructor === Object) {
                for (i in newValue) {
                    val = newValue[i];
                    if (!preserveExisting || !(i in ret)) {
                        ret[i] = val;
                    }
                }
            } else if (!preserveExisting || !(newValue in ret)) {
                ret[newValue] = true;
            }
        }
        return ret;
    },
    
    
    makeGetter: function() {
        var name = this.name,
            prefixedName = this.names.internal;
        return function() {
            var internalName = this.$configPrefixed ? prefixedName : name;
            return this[internalName];
        };
    },
    makeInitGetter: function() {
        var name = this.name,
            names = this.names,
            setName = names.set,
            getName = names.get,
            initializingName = names.initializing;
        return function() {
            var me = this;
            me[initializingName] = true;
            
            delete me[getName];
            me[setName](me.config[name]);
            delete me[initializingName];
            return me[getName].apply(me, arguments);
        };
    },
    makeSetter: function() {
        var name = this.name,
            names = this.names,
            prefixedName = names.internal,
            getName = names.get,
            applyName = names.apply,
            updateName = names.update,
            setter;
        
        
        setter = function(value) {
            var me = this,
                internalName = me.$configPrefixed ? prefixedName : name,
                oldValue = me[internalName];
            
            delete me[getName];
            if (!me[applyName] || (value = me[applyName](value, oldValue)) !== undefined) {
                
                
                if (value !== (oldValue = me[internalName])) {
                    me[internalName] = value;
                    if (me[updateName]) {
                        me[updateName](value, oldValue);
                    }
                }
            }
            return me;
        };
        setter.$isDefault = true;
        return setter;
    }
};


(function() {
    
    var ExtConfig = Ext.Config,
        configPropMap = ExtConfig.map,
        ExtObject = Ext.Object;
    Ext.Configurator = function(cls) {
        
        
        
        var me = this,
            prototype = cls.prototype,
            zuper = cls.superclass ? cls.superclass.self.$config : null;
        
        me.cls = cls;
        if (zuper) {
            
            me.configs = ExtObject.chain(zuper.configs);
            
            me.cachedConfigs = ExtObject.chain(zuper.cachedConfigs);
            
            me.initMap = ExtObject.chain(zuper.initMap);
            
            me.values = ExtObject.chain(zuper.values);
        } else {
            me.configs = {};
            me.cachedConfigs = {};
            me.initMap = {};
            me.values = {};
        }
        prototype.config = prototype.defaultConfig = me.values;
        cls.$config = me;
    };
    Ext.Configurator.prototype = {
        self: Ext.Configurator,
        
        initList: null,
        
        add: function(config, mixinClass) {
            var me = this,
                Cls = me.cls,
                configs = me.configs,
                cachedConfigs = me.cachedConfigs,
                initMap = me.initMap,
                prototype = Cls.prototype,
                mixinConfigs = mixinClass && mixinClass.$config.configs,
                values = me.values,
                isObject, meta, isCached, merge, cfg, currentValue, name, names, s, value;
            for (name in config) {
                value = config[name];
                isObject = value && value.constructor === Object;
                meta = isObject && '$value' in value ? value : null;
                if (meta) {
                    isCached = !!meta.cached;
                    value = meta.$value;
                }
                merge = meta && meta.merge;
                cfg = configs[name];
                if (cfg) {
                    
                    if (mixinClass) {
                        merge = cfg.merge;
                        if (!merge) {
                            
                            continue;
                        }
                        
                        meta = null;
                    } else {
                        merge = merge || cfg.merge;
                    }
                    
                    
                    if (!mixinClass && isCached && !cachedConfigs[name]) {
                        Ext.Error.raise('Redefining config as cached: ' + name + ' in class: ' + Cls.$className);
                    }
                    
                    
                    
                    currentValue = values[name];
                    if (merge) {
                        value = merge.call(cfg, value, currentValue, Cls, mixinClass);
                    } else if (isObject) {
                        if (currentValue && currentValue.constructor === Object) {
                            
                            
                            
                            
                            
                            value = ExtObject.merge({}, currentValue, value);
                        }
                    }
                } else 
                
                {
                    
                    
                    
                    if (mixinConfigs) {
                        
                        
                        
                        cfg = mixinConfigs[name];
                        meta = null;
                    } else {
                        cfg = ExtConfig.get(name);
                    }
                    configs[name] = cfg;
                    if (cfg.cached || isCached) {
                        cachedConfigs[name] = true;
                    }
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    names = cfg.names;
                    if (!prototype[s = names.get]) {
                        prototype[s] = cfg.getGetter();
                    }
                    if (!prototype[s = names.set]) {
                        prototype[s] = cfg.getSetter();
                    }
                }
                if (meta) {
                    if (cfg.owner !== Cls) {
                        configs[name] = cfg = Ext.Object.chain(cfg);
                        cfg.owner = Cls;
                    }
                    Ext.apply(cfg, meta);
                    delete cfg.$value;
                }
                
                if (value !== null) {
                    initMap[name] = true;
                } else {
                    if (prototype.$configPrefixed) {
                        prototype[configs[name].names.internal] = null;
                    } else {
                        prototype[configs[name].name] = null;
                    }
                    if (name in initMap) {
                        
                        initMap[name] = false;
                    }
                }
                values[name] = value;
            }
        },
        
        configure: function(instance, instanceConfig) {
            var me = this,
                configs = me.configs,
                initMap = me.initMap,
                initListMap = me.initListMap,
                initList = me.initList,
                prototype = me.cls.prototype,
                
                
                values = ExtObject.fork(me.values),
                remaining = 0,
                firstInstance = !initList,
                cachedInitList, cfg, getter, needsInit, i, internalName, ln, names, name, value, isCached, valuesKey;
            if (firstInstance) {
                
                
                me.initList = initList = [];
                me.initListMap = initListMap = {};
                instance.isFirstInstance = true;
                for (name in initMap) {
                    needsInit = initMap[name];
                    cfg = configs[name];
                    isCached = cfg.cached;
                    if (needsInit) {
                        names = cfg.names;
                        value = values[name];
                        if (!prototype[names.set].$isDefault || prototype[names.apply] || prototype[names.update] || typeof value === 'object') {
                            if (isCached) {
                                
                                
                                
                                
                                
                                (cachedInitList || (cachedInitList = [])).push(cfg);
                            } else {
                                
                                
                                initList.push(cfg);
                                initListMap[name] = true;
                            }
                            
                            
                            
                            instance[names.get] = cfg.initGetter || cfg.getInitGetter();
                        } else {
                            
                            
                            prototype[cfg.getInternalName(prototype)] = value;
                        }
                    } else if (isCached) {
                        prototype[cfg.getInternalName(prototype)] = undefined;
                    }
                }
            }
            ln = cachedInitList && cachedInitList.length;
            if (ln) {
                
                
                
                
                for (i = 0; i < ln; ++i) {
                    internalName = cachedInitList[i].getInternalName(prototype);
                    
                    
                    
                    instance[internalName] = null;
                }
                for (i = 0; i < ln; ++i) {
                    names = (cfg = cachedInitList[i]).names;
                    getter = names.get;
                    if (instance.hasOwnProperty(getter)) {
                        instance[names.set](values[cfg.name]);
                        delete instance[getter];
                    }
                }
                for (i = 0; i < ln; ++i) {
                    internalName = cachedInitList[i].getInternalName(prototype);
                    prototype[internalName] = instance[internalName];
                    delete instance[internalName];
                }
            }
            
            
            
            
            if (instanceConfig && instanceConfig.platformConfig) {
                instanceConfig = me.resolvePlatformConfig(instance, instanceConfig);
            }
            if (firstInstance) {
                
                
                
                if (instance.afterCachedConfig && !instance.afterCachedConfig.$nullFn) {
                    instance.afterCachedConfig(instanceConfig);
                }
            }
            
            instance.isConfiguring = true;
            
            
            
            instance.config = values;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            for (i = 0 , ln = initList.length; i < ln; ++i) {
                cfg = initList[i];
                instance[cfg.names.get] = cfg.initGetter || cfg.getInitGetter();
            }
            
            if (instance.transformInstanceConfig) {
                instanceConfig = instance.transformInstanceConfig(instanceConfig);
            }
            
            
            
            
            
            
            
            
            
            
            
            
            if (instanceConfig) {
                for (name in instanceConfig) {
                    value = instanceConfig[name];
                    cfg = configs[name];
                    if (!cfg) {
                        if (instance.$configStrict && typeof instance.self.prototype[name] === 'function') {
                            
                            Ext.Error.raise("Cannot override method " + name + " on " + instance.$className + " instance.");
                        }
                        
                        
                        instance[name] = value;
                    } else {
                        
                        
                        if (!cfg.lazy) {
                            ++remaining;
                        }
                        if (!initListMap[name]) {
                            instance[cfg.names.get] = cfg.initGetter || cfg.getInitGetter();
                        }
                        if (cfg.merge) {
                            value = cfg.merge(value, values[name], instance);
                        } else if (value && value.constructor === Object) {
                            valuesKey = values[name];
                            if (valuesKey && valuesKey.constructor === Object) {
                                value = ExtObject.merge(values[name], value);
                            } else {
                                value = Ext.clone(value);
                            }
                        }
                    }
                    values[name] = value;
                }
            }
            
            if (instance.beforeInitConfig && !instance.beforeInitConfig.$nullFn) {
                if (instance.beforeInitConfig(instanceConfig) === false) {
                    return;
                }
            }
            if (instanceConfig) {
                for (name in instanceConfig) {
                    if (!remaining) {
                        
                        
                        break;
                    }
                    cfg = configs[name];
                    if (cfg && !cfg.lazy) {
                        --remaining;
                        
                        names = cfg.names;
                        getter = names.get;
                        
                        
                        
                        
                        if (instance.hasOwnProperty(getter)) {
                            instance[names.set](values[name]);
                            
                            
                            
                            delete instance[names.get];
                        }
                    }
                }
            }
            
            for (i = 0 , ln = initList.length; i < ln; ++i) {
                cfg = initList[i];
                names = cfg.names;
                getter = names.get;
                if (!cfg.lazy && instance.hasOwnProperty(getter)) {
                    
                    
                    
                    
                    instance[names.set](values[cfg.name]);
                    delete instance[getter];
                }
            }
            
            delete instance.isConfiguring;
        },
        getCurrentConfig: function(instance) {
            var defaultConfig = instance.defaultConfig,
                config = {},
                name;
            for (name in defaultConfig) {
                config[name] = instance[configPropMap[name].names.get]();
            }
            return config;
        },
        
        merge: function(instance, baseConfig, config) {
            
            
            var configs = this.configs,
                name, value, baseValue, cfg;
            for (name in config) {
                value = config[name];
                cfg = configs[name];
                if (cfg) {
                    if (cfg.merge) {
                        value = cfg.merge(value, baseConfig[name], instance);
                    } else if (value && value.constructor === Object) {
                        baseValue = baseConfig[name];
                        if (baseValue && baseValue.constructor === Object) {
                            value = Ext.Object.merge(baseValue, value);
                        } else {
                            value = Ext.clone(value);
                        }
                    }
                }
                baseConfig[name] = value;
            }
            return baseConfig;
        },
        
        reconfigure: function(instance, instanceConfig, options) {
            var currentConfig = instance.config,
                configList = [],
                strict = instance.$configStrict,
                configs = this.configs,
                defaults = options && options.defaults,
                applyProps = options && options.strict === false,
                cfg, getter, i, len, name, names, setter;
            for (name in instanceConfig) {
                if (defaults && instance.hasOwnProperty(name)) {
                    
                    continue;
                }
                currentConfig[name] = instanceConfig[name];
                cfg = configs[name];
                if (cfg) {
                    
                    
                    instance[cfg.names.get] = cfg.initGetter || cfg.getInitGetter();
                } else if (strict) {
                    if (name !== 'type') {
                        Ext.log.error('No such config "' + name + '" for class ' + instance.$className);
                    }
                    
                    continue;
                }
                configList.push(name);
            }
            for (i = 0 , len = configList.length; i < len; i++) {
                name = configList[i];
                cfg = configs[name];
                if (cfg) {
                    names = cfg.names;
                    getter = names.get;
                    if (instance.hasOwnProperty(getter)) {
                        
                        
                        
                        
                        instance[names.set](instanceConfig[name]);
                        delete instance[getter];
                    }
                } else {
                    cfg = configPropMap[name] || Ext.Config.get(name);
                    names = cfg.names;
                    if (instance[names.set]) {
                        instance[names.set](instanceConfig[name]);
                    } else if (applyProps) {
                        if (instance.$configStrict && typeof instance.self.prototype[name] === 'function') {
                            
                            Ext.Error.raise("Cannot override method " + name + " on " + instance.$className + " instance.");
                        }
                        
                        instance[name] = instanceConfig[name];
                    } else if (name !== 'type') {
                        Ext.Error.raise('Config "' + name + '" has no setter on class ' + instance.$className);
                    }
                }
            }
        },
        
        resolvePlatformConfig: function(instance, instanceConfig) {
            var platformConfig = instanceConfig && instanceConfig.platformConfig,
                ret = instanceConfig,
                i, keys, n;
            if (platformConfig) {
                keys = Ext.getPlatformConfigKeys(platformConfig);
                n = keys.length;
                if (n) {
                    ret = Ext.merge({}, ret);
                    
                    for (i = 0 , n = keys.length; i < n; ++i) {
                        this.merge(instance, ret, platformConfig[keys[i]]);
                    }
                }
            }
            return ret;
        }
    };
}());



Ext.Base = (function(flexSetter) {
    
    
    
    
    
    var noArgs = [],
        baseStaticMember,
        baseStaticMembers = [],
        getConfig = function(name, peek) {
            var me = this,
                ret, cfg, getterName;
            if (name) {
                cfg = Ext.Config.map[name];
                if (!cfg) {
                    Ext.Logger.error("Invalid property name for getter: '" + name + "' for '" + me.$className + "'.");
                }
                getterName = cfg.names.get;
                if (peek && me.hasOwnProperty(getterName)) {
                    ret = me.config[name];
                } else {
                    ret = me[getterName]();
                }
            } else {
                ret = me.getCurrentConfig();
            }
            return ret;
        },
        makeDeprecatedMethod = function(oldName, newName, msg) {
            var message = '"' + oldName + '" is deprecated.';
            if (msg) {
                message += ' ' + msg;
            } else if (newName) {
                message += ' Please use "' + newName + '" instead.';
            }
            return function() {
                Ext.Error.raise(message);
            };
        },
        addDeprecatedProperty = function(object, oldName, newName, message) {
            if (!message) {
                message = '"' + oldName + '" is deprecated.';
            }
            if (newName) {
                message += ' Please use "' + newName + '" instead.';
            }
            if (message) {
                Ext.Object.defineProperty(object, oldName, {
                    get: function() {
                        Ext.Error.raise(message);
                    },
                    set: function(value) {
                        Ext.Error.raise(message);
                    },
                    configurable: true
                });
            }
        },
        makeAliasFn = function(name) {
            return function() {
                return this[name].apply(this, arguments);
            };
        },
        Version = Ext.Version,
        leadingDigitRe = /^\d/,
        oneMember = {},
        aliasOneMember = {},
        Base = function() {},
        BasePrototype = Base.prototype;
    
    Ext.apply(Base, {
        $className: 'Ext.Base',
        $isClass: true,
        
        create: function() {
            return Ext.create.apply(Ext, [
                this
            ].concat(Array.prototype.slice.call(arguments, 0)));
        },
        
        addDeprecations: function(deprecations) {
            var me = this,
                all = [],
                compatVersion = Ext.getCompatVersion(deprecations.name),
                displayName = (me.$className || '') + '#',
                deprecate, versionSpec, index, message, target, enabled, existing, fn, names, oldName, newName, member, statics, version;
            for (versionSpec in deprecations) {
                if (leadingDigitRe.test(versionSpec)) {
                    version = new Ext.Version(versionSpec);
                    version.deprecations = deprecations[versionSpec];
                    all.push(version);
                }
            }
            all.sort(Version.compare);
            for (index = all.length; index--; ) {
                deprecate = (version = all[index]).deprecations;
                target = me.prototype;
                statics = deprecate.statics;
                
                
                
                
                
                
                enabled = compatVersion && compatVersion.lt(version);
                if (!enabled) {} else if (!enabled) {
                    
                    break;
                }
                while (deprecate) {
                    names = deprecate.methods;
                    if (names) {
                        for (oldName in names) {
                            member = names[oldName];
                            fn = null;
                            if (!member) {
                                
                                
                                Ext.Assert.isNotDefinedProp(target, oldName);
                                fn = makeDeprecatedMethod(displayName + oldName);
                            } else if (Ext.isString(member)) {
                                
                                
                                Ext.Assert.isNotDefinedProp(target, oldName);
                                Ext.Assert.isDefinedProp(target, member);
                                if (enabled) {
                                    
                                    
                                    fn = makeAliasFn(member);
                                } else {
                                    fn = makeDeprecatedMethod(displayName + oldName, member);
                                }
                            } else {
                                
                                message = '';
                                if (member.message || member.fn) {
                                    message = member.message;
                                    member = member.fn;
                                }
                                existing = target.hasOwnProperty(oldName) && target[oldName];
                                if (enabled && member) {
                                    member.$owner = me;
                                    member.$name = oldName;
                                    member.name = displayName + oldName;
                                    if (existing) {
                                        member.$previous = existing;
                                    }
                                    fn = member;
                                } else if (!existing) {
                                    fn = makeDeprecatedMethod(displayName + oldName, null, message);
                                }
                            }
                            if (fn) {
                                target[oldName] = fn;
                            }
                        }
                    }
                    
                    names = deprecate.properties;
                    if (names && !enabled) {
                        
                        
                        
                        for (oldName in names) {
                            newName = names[oldName];
                            if (Ext.isString(newName)) {
                                addDeprecatedProperty(target, displayName + oldName, newName);
                            } else if (newName && newName.message) {
                                addDeprecatedProperty(target, displayName + oldName, null, newName.message);
                            } else {
                                addDeprecatedProperty(target, displayName + oldName);
                            }
                        }
                    }
                    
                    deprecate = statics;
                    statics = null;
                    target = me;
                }
            }
        },
        
        extend: function(parent) {
            var me = this,
                parentPrototype = parent.prototype,
                prototype, i, ln, name, statics;
            prototype = me.prototype = Ext.Object.chain(parentPrototype);
            prototype.self = me;
            me.superclass = prototype.superclass = parentPrototype;
            if (!parent.$isClass) {
                for (i in BasePrototype) {
                    if (i in prototype) {
                        prototype[i] = BasePrototype[i];
                    }
                }
            }
            
            statics = parentPrototype.$inheritableStatics;
            if (statics) {
                for (i = 0 , ln = statics.length; i < ln; i++) {
                    name = statics[i];
                    if (!me.hasOwnProperty(name)) {
                        me[name] = parent[name];
                    }
                }
            }
            if (parent.$onExtended) {
                me.$onExtended = parent.$onExtended.slice();
            }
            me.getConfigurator();
        },
        
        $onExtended: [],
        
        triggerExtended: function() {
            Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#triggerExtended', arguments);
            var callbacks = this.$onExtended,
                ln = callbacks.length,
                i, callback;
            if (ln > 0) {
                for (i = 0; i < ln; i++) {
                    callback = callbacks[i];
                    callback.fn.apply(callback.scope || this, arguments);
                }
            }
        },
        
        onExtended: function(fn, scope) {
            this.$onExtended.push({
                fn: fn,
                scope: scope
            });
            return this;
        },
        
        addStatics: function(members) {
            this.addMembers(members, true);
            return this;
        },
        
        addInheritableStatics: function(members) {
            var inheritableStatics, hasInheritableStatics,
                prototype = this.prototype,
                name, member;
            inheritableStatics = prototype.$inheritableStatics;
            hasInheritableStatics = prototype.$hasInheritableStatics;
            if (!inheritableStatics) {
                inheritableStatics = prototype.$inheritableStatics = [];
                hasInheritableStatics = prototype.$hasInheritableStatics = {};
            }
            var className = Ext.getClassName(this) + '.';
            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    if (typeof member == 'function') {
                        member.name = className + name;
                    }
                    this[name] = member;
                    if (!hasInheritableStatics[name]) {
                        hasInheritableStatics[name] = true;
                        inheritableStatics.push(name);
                    }
                }
            }
            return this;
        },
        
        addMembers: function(members, isStatic, privacy) {
            var me = this,
                
                cloneFunction = Ext.Function.clone,
                target = isStatic ? me : me.prototype,
                defaultConfig = !isStatic && target.defaultConfig,
                enumerables = Ext.enumerables,
                privates = members.privates,
                configs, i, ln, member, name, subPrivacy, privateStatics;
            var displayName = (me.$className || '') + '#';
            if (privates) {
                
                
                delete members.privates;
                if (!isStatic) {
                    privateStatics = privates.statics;
                    delete privates.statics;
                }
                subPrivacy = privates.privacy || privacy || 'framework';
                me.addMembers(privates, isStatic, subPrivacy);
                if (privateStatics) {
                    me.addMembers(privateStatics, true, subPrivacy);
                }
            }
            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    if (privacy === true) {
                        privacy = 'framework';
                    }
                    if (member && member.$nullFn && privacy !== member.$privacy) {
                        Ext.Error.raise('Cannot use stock function for private method ' + (me.$className ? me.$className + '#' : '') + name);
                    }
                    if (typeof member === 'function' && !member.$isClass && !member.$nullFn) {
                        if (member.$owner) {
                            member = cloneFunction(member);
                        }
                        if (target.hasOwnProperty(name)) {
                            member.$previous = target[name];
                        }
                        
                        
                        member.$owner = me;
                        member.$name = name;
                        member.name = displayName + name;
                        var existing = target[name];
                        if (privacy) {
                            member.$privacy = privacy;
                            
                            
                            
                            
                            
                            
                            if (existing && existing.$privacy && existing.$privacy !== privacy) {
                                Ext.privacyViolation(me, existing, member, isStatic);
                            }
                        } else if (existing && existing.$privacy) {
                            Ext.privacyViolation(me, existing, member, isStatic);
                        }
                    }
                    
                    
                    else if (defaultConfig && (name in defaultConfig) && !target.config.hasOwnProperty(name)) {
                        
                        
                        (configs || (configs = {}))[name] = member;
                        
                        continue;
                    }
                    target[name] = member;
                }
            }
            if (configs) {
                
                me.addConfig(configs);
            }
            if (enumerables) {
                for (i = 0 , ln = enumerables.length; i < ln; ++i) {
                    if (members.hasOwnProperty(name = enumerables[i])) {
                        member = members[name];
                        
                        if (member && !member.$nullFn) {
                            if (member.$owner) {
                                member = cloneFunction(member);
                            }
                            member.$owner = me;
                            member.$name = name;
                            member.name = displayName + name;
                            if (target.hasOwnProperty(name)) {
                                member.$previous = target[name];
                            }
                        }
                        target[name] = member;
                    }
                }
            }
            return this;
        },
        
        addMember: function(name, member) {
            oneMember[name] = member;
            this.addMembers(oneMember);
            delete oneMember[name];
            return this;
        },
        
        borrow: function(fromClass, members) {
            Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#borrow', arguments);
            var prototype = fromClass.prototype,
                membersObj = {},
                i, ln, name;
            members = Ext.Array.from(members);
            for (i = 0 , ln = members.length; i < ln; i++) {
                name = members[i];
                membersObj[name] = prototype[name];
            }
            return this.addMembers(membersObj);
        },
        
        override: function(members) {
            var me = this,
                statics = members.statics,
                inheritableStatics = members.inheritableStatics,
                config = members.config,
                mixins = members.mixins,
                cachedConfig = members.cachedConfig;
            if (statics || inheritableStatics || config) {
                members = Ext.apply({}, members);
            }
            if (statics) {
                me.addMembers(statics, true);
                delete members.statics;
            }
            if (inheritableStatics) {
                me.addInheritableStatics(inheritableStatics);
                delete members.inheritableStatics;
            }
            if (config) {
                me.addConfig(config);
                delete members.config;
            }
            if (cachedConfig) {
                me.addCachedConfig(cachedConfig);
                delete members.cachedConfig;
            }
            delete members.mixins;
            me.addMembers(members);
            if (mixins) {
                me.mixin(mixins);
            }
            return me;
        },
        
        callParent: function(args) {
            var method;
            
            return (method = this.callParent.caller) && (method.$previous || ((method = method.$owner ? method : method.caller) && method.$owner.superclass.self[method.$name])).apply(this, args || noArgs);
        },
        
        callSuper: function(args) {
            var method;
            
            return (method = this.callSuper.caller) && ((method = method.$owner ? method : method.caller) && method.$owner.superclass.self[method.$name]).apply(this, args || noArgs);
        },
        
        mixin: function(name, mixinClass) {
            var me = this,
                mixin, prototype, key, statics, i, ln, staticName, mixinValue, mixins;
            if (typeof name !== 'string') {
                mixins = name;
                if (mixins instanceof Array) {
                    for (i = 0 , ln = mixins.length; i < ln; i++) {
                        mixin = mixins[i];
                        me.mixin(mixin.prototype.mixinId || mixin.$className, mixin);
                    }
                } else {
                    
                    
                    
                    
                    for (var mixinName in mixins) {
                        me.mixin(mixinName, mixins[mixinName]);
                    }
                }
                return;
            }
            mixin = mixinClass.prototype;
            prototype = me.prototype;
            if (mixin.onClassMixedIn) {
                mixin.onClassMixedIn.call(mixinClass, me);
            }
            if (!prototype.hasOwnProperty('mixins')) {
                if ('mixins' in prototype) {
                    prototype.mixins = Ext.Object.chain(prototype.mixins);
                } else {
                    prototype.mixins = {};
                }
            }
            for (key in mixin) {
                mixinValue = mixin[key];
                if (key === 'mixins') {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    Ext.applyIf(prototype.mixins, mixinValue);
                } else if (!(key === 'mixinId' || key === 'config') && (prototype[key] === undefined)) {
                    prototype[key] = mixinValue;
                }
            }
            
            statics = mixin.$inheritableStatics;
            if (statics) {
                for (i = 0 , ln = statics.length; i < ln; i++) {
                    staticName = statics[i];
                    if (!me.hasOwnProperty(staticName)) {
                        me[staticName] = mixinClass[staticName];
                    }
                }
            }
            if ('config' in mixin) {
                me.addConfig(mixin.config, mixinClass);
            }
            prototype.mixins[name] = mixin;
            if (mixin.afterClassMixedIn) {
                mixin.afterClassMixedIn.call(mixinClass, me);
            }
            return me;
        },
        
        addConfig: function(config, mixinClass) {
            var cfg = this.$config || this.getConfigurator();
            cfg.add(config, mixinClass);
        },
        addCachedConfig: function(config, isMixin) {
            var cached = {},
                key;
            for (key in config) {
                cached[key] = {
                    cached: true,
                    $value: config[key]
                };
            }
            this.addConfig(cached, isMixin);
        },
        
        getConfigurator: function() {
            
            return this.$config || new Ext.Configurator(this);
        },
        
        getName: function() {
            return Ext.getClassName(this);
        },
        
        createAlias: flexSetter(function(alias, origin) {
            aliasOneMember[alias] = function() {
                return this[origin].apply(this, arguments);
            };
            this.override(aliasOneMember);
            delete aliasOneMember[alias];
        })
    });
    
    
    for (baseStaticMember in Base) {
        if (Base.hasOwnProperty(baseStaticMember)) {
            baseStaticMembers.push(baseStaticMember);
        }
    }
    Base.$staticMembers = baseStaticMembers;
    Base.getConfigurator();
    
    Base.addMembers({
        
        $className: 'Ext.Base',
        
        isInstance: true,
        
        $configPrefixed: true,
        
        $configStrict: true,
        
        isConfiguring: false,
        
        isFirstInstance: false,
        
        statics: function() {
            var method = this.statics.caller,
                self = this.self;
            if (!method) {
                return self;
            }
            return method.$owner;
        },
        
        callParent: function(args) {
            
            
            
            
            var method,
                superMethod = (method = this.callParent.caller) && (method.$previous || ((method = method.$owner ? method : method.caller) && method.$owner.superclass[method.$name]));
            if (!superMethod) {
                method = this.callParent.caller;
                var parentClass, methodName;
                if (!method.$owner) {
                    if (!method.caller) {
                        throw new Error("Attempting to call a protected method from the public scope, which is not allowed");
                    }
                    method = method.caller;
                }
                parentClass = method.$owner.superclass;
                methodName = method.$name;
                if (!(methodName in parentClass)) {
                    throw new Error("this.callParent() was called but there's no such method (" + methodName + ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");
                }
            }
            return superMethod.apply(this, args || noArgs);
        },
        
        callSuper: function(args) {
            
            
            
            
            var method,
                superMethod = (method = this.callSuper.caller) && ((method = method.$owner ? method : method.caller) && method.$owner.superclass[method.$name]);
            if (!superMethod) {
                method = this.callSuper.caller;
                var parentClass, methodName;
                if (!method.$owner) {
                    if (!method.caller) {
                        throw new Error("Attempting to call a protected method from the public scope, which is not allowed");
                    }
                    method = method.caller;
                }
                parentClass = method.$owner.superclass;
                methodName = method.$name;
                if (!(methodName in parentClass)) {
                    throw new Error("this.callSuper() was called but there's no such method (" + methodName + ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");
                }
            }
            return superMethod.apply(this, args || noArgs);
        },
        
        self: Base,
        
        constructor: function() {
            return this;
        },
        getConfigurator: function() {
            return this.$config || this.self.getConfigurator();
        },
        
        initConfig: function(instanceConfig) {
            var me = this,
                cfg = me.getConfigurator();
            me.initConfig = Ext.emptyFn;
            
            me.initialConfig = instanceConfig || {};
            cfg.configure(me, instanceConfig);
            return me;
        },
        beforeInitConfig: Ext.emptyFn,
        
        getConfig: getConfig,
        
        setConfig: function(name, value, 
        options) {
            
            
            
            
            
            var me = this,
                config;
            if (name) {
                if (typeof name === 'string') {
                    config = {};
                    config[name] = value;
                } else {
                    config = name;
                }
                me.getConfigurator().reconfigure(me, config, options);
            }
            return me;
        },
        
        getCurrentConfig: function() {
            var cfg = this.getConfigurator();
            return cfg.getCurrentConfig(this);
        },
        
        hasConfig: function(name) {
            return name in this.defaultConfig;
        },
        
        getInitialConfig: function(name) {
            var config = this.config;
            if (!name) {
                return config;
            }
            return config[name];
        },
        $links: null,
        
        link: function(name, value) {
            var me = this,
                links = me.$links || (me.$links = {});
            links[name] = true;
            me[name] = value;
            return value;
        },
        
        unlink: function(names) {
            var me = this,
                i, ln, link, value;
            if (!Ext.isArray(names)) {
                Ext.Error.raise('Invalid argument - expected array of strings');
            }
            for (i = 0 , ln = names.length; i < ln; i++) {
                link = names[i];
                value = me[link];
                if (value) {
                    if (value.isInstance && !value.isDestroyed) {
                        value.destroy();
                    } else if (value.parentNode && 'nodeType' in value) {
                        value.parentNode.removeChild(value);
                    }
                }
                me[link] = null;
            }
            return me;
        },
        
        destroy: function() {
            var me = this,
                links = me.$links;
            me.destroy = Ext.emptyFn;
            me.isDestroyed = true;
            if (links) {
                me.$links = null;
                me.unlink(Ext.Object.getKeys(links));
            }
        }
    });
    
    BasePrototype.callOverridden = BasePrototype.callParent;
    Ext.privacyViolation = function(cls, existing, member, isStatic) {
        var name = member.$name,
            conflictCls = existing.$owner && existing.$owner.$className,
            s = isStatic ? 'static ' : '',
            msg = member.$privacy ? 'Private ' + s + member.$privacy + ' method "' + name + '"' : 'Public ' + s + 'method "' + name + '"';
        if (cls.$className) {
            msg = cls.$className + ': ' + msg;
        }
        if (!existing.$privacy) {
            msg += conflictCls ? ' hides public method inherited from ' + conflictCls : ' hides inherited public method.';
        } else {
            msg += conflictCls ? ' conflicts with private ' + existing.$privacy + ' method declared by ' + conflictCls : ' conflicts with inherited private ' + existing.$privacy + ' method.';
        }
        var compat = Ext.getCompatVersion();
        var ver = Ext.getVersion();
        
        if (ver && compat && compat.lt(ver)) {
            Ext.log.error(msg);
        } else {
            Ext.Error.raise(msg);
        }
    };
    return Base;
}(Ext.Function.flexSetter));


(function(Cache, prototype) {
    
    
    
    (Ext.util || (Ext.util = {})).Cache = Cache = function(config) {
        var me = this,
            head;
        if (config) {
            Ext.apply(me, config);
        }
        
        me.head = head = {
            id: (me.seed = 0),
            key: null,
            value: null
        };
        me.map = {};
        head.next = head.prev = head;
    };
    Cache.prototype = prototype = {
        
        maxSize: 100,
        
        count: 0,
        
        
        clear: function() {
            var me = this,
                head = me.head,
                entry = head.next;
            head.next = head.prev = head;
            if (!me.evict.$nullFn) {
                for (; entry !== head; entry = entry.next) {
                    me.evict(entry.key, entry.value);
                }
            }
            me.count = 0;
        },
        
        each: function(fn, scope) {
            scope = scope || this;
            for (var head = this.head,
                ent = head.next; ent !== head; ent = ent.next) {
                if (fn.call(scope, ent.key, ent.value)) {
                    break;
                }
            }
        },
        
        get: function(key) {
            var me = this,
                head = me.head,
                map = me.map,
                entry = map[key];
            if (entry) {
                if (entry.prev !== head) {
                    
                    
                    me.unlinkEntry(entry);
                    me.linkEntry(entry);
                }
            } else {
                map[key] = entry = {
                    id: ++me.seed,
                    key: key,
                    value: me.miss.apply(me, arguments)
                };
                me.linkEntry(entry);
                ++me.count;
                while (me.count > me.maxSize) {
                    me.unlinkEntry(head.prev, true);
                    --me.count;
                }
            }
            return entry.value;
        },
        
        
        
        evict: Ext.emptyFn,
        
        linkEntry: function(entry) {
            var head = this.head,
                first = head.next;
            entry.next = first;
            entry.prev = head;
            head.next = entry;
            first.prev = entry;
        },
        
        unlinkEntry: function(entry, evicted) {
            var next = entry.next,
                prev = entry.prev;
            prev.next = next;
            next.prev = prev;
            if (evicted) {
                this.evict(entry.key, entry.value);
            }
        }
    };
    prototype.destroy = prototype.clear;
}());


(function() {
    
    
    
    
    
    var ExtClass,
        Base = Ext.Base,
        baseStaticMembers = Base.$staticMembers,
        ruleKeySortFn = function(a, b) {
            
            return (a.length - b.length) || ((a < b) ? -1 : ((a > b) ? 1 : 0));
        };
    
    function makeCtor(className) {
        function constructor() {
            
            
            return this.constructor.apply(this, arguments) || null;
        }
        if (className) {
            constructor.name = className;
        }
        return constructor;
    }
    
    Ext.Class = ExtClass = function(Class, data, onCreated) {
        if (typeof Class != 'function') {
            onCreated = data;
            data = Class;
            Class = null;
        }
        if (!data) {
            data = {};
        }
        Class = ExtClass.create(Class, data);
        ExtClass.process(Class, data, onCreated);
        return Class;
    };
    Ext.apply(ExtClass, {
        makeCtor: makeCtor,
        
        onBeforeCreated: function(Class, data, hooks) {
            Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '>> Ext.Class#onBeforeCreated', arguments);
            Class.addMembers(data);
            hooks.onCreated.call(Class, Class);
            Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '<< Ext.Class#onBeforeCreated', arguments);
        },
        
        create: function(Class, data) {
            var i = baseStaticMembers.length,
                name;
            if (!Class) {
                Class = makeCtor(data.$className);
            }
            while (i--) {
                name = baseStaticMembers[i];
                Class[name] = Base[name];
            }
            return Class;
        },
        
        process: function(Class, data, onCreated) {
            var preprocessorStack = data.preprocessors || ExtClass.defaultPreprocessors,
                registeredPreprocessors = this.preprocessors,
                hooks = {
                    onBeforeCreated: this.onBeforeCreated
                },
                preprocessors = [],
                preprocessor, preprocessorsProperties, i, ln, j, subLn, preprocessorProperty;
            delete data.preprocessors;
            Class._classHooks = hooks;
            for (i = 0 , ln = preprocessorStack.length; i < ln; i++) {
                preprocessor = preprocessorStack[i];
                if (typeof preprocessor == 'string') {
                    preprocessor = registeredPreprocessors[preprocessor];
                    preprocessorsProperties = preprocessor.properties;
                    if (preprocessorsProperties === true) {
                        preprocessors.push(preprocessor.fn);
                    } else if (preprocessorsProperties) {
                        for (j = 0 , subLn = preprocessorsProperties.length; j < subLn; j++) {
                            preprocessorProperty = preprocessorsProperties[j];
                            if (data.hasOwnProperty(preprocessorProperty)) {
                                preprocessors.push(preprocessor.fn);
                                break;
                            }
                        }
                    }
                } else {
                    preprocessors.push(preprocessor);
                }
            }
            hooks.onCreated = onCreated ? onCreated : Ext.emptyFn;
            hooks.preprocessors = preprocessors;
            this.doProcess(Class, data, hooks);
        },
        doProcess: function(Class, data, hooks) {
            var me = this,
                preprocessors = hooks.preprocessors,
                preprocessor = preprocessors.shift(),
                doProcess = me.doProcess;
            for (; preprocessor; preprocessor = preprocessors.shift()) {
                
                if (preprocessor.call(me, Class, data, hooks, doProcess) === false) {
                    return;
                }
            }
            hooks.onBeforeCreated.apply(me, arguments);
        },
        
        preprocessors: {},
        
        registerPreprocessor: function(name, fn, properties, position, relativeTo) {
            if (!position) {
                position = 'last';
            }
            if (!properties) {
                properties = [
                    name
                ];
            }
            this.preprocessors[name] = {
                name: name,
                properties: properties || false,
                fn: fn
            };
            this.setDefaultPreprocessorPosition(name, position, relativeTo);
            return this;
        },
        
        getPreprocessor: function(name) {
            return this.preprocessors[name];
        },
        
        getPreprocessors: function() {
            return this.preprocessors;
        },
        
        defaultPreprocessors: [],
        
        getDefaultPreprocessors: function() {
            return this.defaultPreprocessors;
        },
        
        setDefaultPreprocessors: function(preprocessors) {
            this.defaultPreprocessors = Ext.Array.from(preprocessors);
            return this;
        },
        
        setDefaultPreprocessorPosition: function(name, offset, relativeName) {
            var defaultPreprocessors = this.defaultPreprocessors,
                index;
            if (typeof offset == 'string') {
                if (offset === 'first') {
                    defaultPreprocessors.unshift(name);
                    return this;
                } else if (offset === 'last') {
                    defaultPreprocessors.push(name);
                    return this;
                }
                offset = (offset === 'after') ? 1 : -1;
            }
            index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
            if (index !== -1) {
                Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
            }
            return this;
        }
    });
    
    ExtClass.registerPreprocessor('extend', function(Class, data, hooks) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extendPreProcessor', arguments);
        var Base = Ext.Base,
            basePrototype = Base.prototype,
            extend = data.extend,
            Parent, parentPrototype, i;
        delete data.extend;
        if (extend && extend !== Object) {
            Parent = extend;
        } else {
            Parent = Base;
        }
        parentPrototype = Parent.prototype;
        if (!Parent.$isClass) {
            for (i in basePrototype) {
                if (!parentPrototype[i]) {
                    parentPrototype[i] = basePrototype[i];
                }
            }
        }
        Class.extend(Parent);
        Class.triggerExtended.apply(Class, arguments);
        if (data.onClassExtended) {
            Class.onExtended(data.onClassExtended, Class);
            delete data.onClassExtended;
        }
    }, true);
    
    ExtClass.registerPreprocessor('privates', function(Class, data) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#privatePreprocessor', arguments);
        var privates = data.privates,
            statics = privates.statics,
            privacy = privates.privacy || true;
        delete data.privates;
        delete privates.statics;
        
        
        
        Class.addMembers(privates, false, privacy);
        if (statics) {
            Class.addMembers(statics, true, privacy);
        }
    });
    
    ExtClass.registerPreprocessor('statics', function(Class, data) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#staticsPreprocessor', arguments);
        Class.addStatics(data.statics);
        delete data.statics;
    });
    
    ExtClass.registerPreprocessor('inheritableStatics', function(Class, data) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#inheritableStaticsPreprocessor', arguments);
        Class.addInheritableStatics(data.inheritableStatics);
        delete data.inheritableStatics;
    });
    Ext.createRuleFn = function(code) {
        return new Function('$c','with($c) { return (' + code + '); }');
    };
    Ext.expressionCache = new Ext.util.Cache({
        miss: Ext.createRuleFn
    });
    Ext.ruleKeySortFn = ruleKeySortFn;
    Ext.getPlatformConfigKeys = function(platformConfig) {
        var ret = [],
            platform, rule;
        for (platform in platformConfig) {
            rule = Ext.expressionCache.get(platform);
            if (rule(Ext.platformTags)) {
                ret.push(platform);
            }
        }
        ret.sort(ruleKeySortFn);
        return ret;
    };
    
    ExtClass.registerPreprocessor('platformConfig', function(Class, data, hooks) {
        var platformConfigs = data.platformConfig,
            config = data.config,
            added, classConfigs, configs, configurator, hoisted, keys, name, value, platform, theme, platformConfig, i, ln, j, ln2, themeName;
        delete data.platformConfig;
        if (platformConfigs instanceof Array) {
            
            config = config || {};
            themeName = (Ext.theme || (Ext.theme = {
                name: 'Default'
            })).name;
            for (i = 0 , ln = platformConfigs.length; i < ln; i++) {
                platformConfig = platformConfigs[i];
                platform = platformConfig.platform;
                delete platformConfig.platform;
                theme = [].concat(platformConfig.theme);
                ln2 = theme.length;
                delete platformConfig.theme;
                if (platform && Ext.filterPlatform(platform)) {
                    Ext.merge(config, platformConfig);
                }
                if (ln2) {
                    for (j = 0; j < ln2; j++) {
                        if (themeName === theme[j]) {
                            Ext.merge(config, platformConfig);
                        }
                    }
                }
            }
        } else {
            configurator = Class.getConfigurator();
            classConfigs = configurator.configs;
            
            keys = Ext.getPlatformConfigKeys(platformConfigs);
            
            
            
            
            
            
            
            
            
            
            for (i = 0 , ln = keys.length; i < ln; ++i) {
                configs = platformConfigs[keys[i]];
                hoisted = added = null;
                for (name in configs) {
                    value = configs[name];
                    
                    if (config && name in config) {
                        
                        (added || (added = {}))[name] = value;
                        (hoisted || (hoisted = {}))[name] = config[name];
                        delete config[name];
                    } else if (name in classConfigs) {
                        
                        (added || (added = {}))[name] = value;
                    } else {
                        
                        data[name] = value;
                    }
                }
                if (hoisted) {
                    configurator.add(hoisted);
                }
                if (added) {
                    configurator.add(added);
                }
            }
        }
    });
    
    ExtClass.registerPreprocessor('config', function(Class, data) {
        
        if (data.hasOwnProperty('$configPrefixed')) {
            Class.prototype.$configPrefixed = data.$configPrefixed;
        }
        Class.addConfig(data.config);
        
        
        
        delete data.config;
    });
    
    ExtClass.registerPreprocessor('cachedConfig', function(Class, data) {
        
        if (data.hasOwnProperty('$configPrefixed')) {
            Class.prototype.$configPrefixed = data.$configPrefixed;
        }
        Class.addCachedConfig(data.cachedConfig);
        
        delete data.cachedConfig;
    });
    
    ExtClass.registerPreprocessor('mixins', function(Class, data, hooks) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor', arguments);
        var mixins = data.mixins,
            onCreated = hooks.onCreated;
        delete data.mixins;
        hooks.onCreated = function() {
            Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor#beforeCreated', arguments);
            
            
            hooks.onCreated = onCreated;
            Class.mixin(mixins);
            
            
            return hooks.onCreated.apply(this, arguments);
        };
    });
    
    Ext.extend = function(Class, Parent, members) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extend-backwards-compatible', arguments);
        if (arguments.length === 2 && Ext.isObject(Parent)) {
            members = Parent;
            Parent = Class;
            Class = null;
        }
        var cls;
        if (!Parent) {
            throw new Error("[Ext.extend] Attempting to extend from a class which has not been loaded on the page.");
        }
        members.extend = Parent;
        members.preprocessors = [
            'extend',
            'statics',
            'inheritableStatics',
            'mixins',
            'platformConfig',
            'config'
        ];
        if (Class) {
            cls = new ExtClass(Class,members);
            
            cls.prototype.constructor = Class;
        } else {
            cls = new ExtClass(members);
        }
        cls.prototype.override = function(o) {
            for (var m in o) {
                if (o.hasOwnProperty(m)) {
                    this[m] = o[m];
                }
            }
        };
        return cls;
    };
}());



Ext.Inventory = function() {
    
    
    
    var me = this;
    me.names = [];
    me.paths = {};
    me.alternateToName = {};
    me.aliasToName = {};
    me.nameToAliases = {};
    me.nameToAlternates = {};
};
Ext.Inventory.prototype = {
    _array1: [
        0
    ],
    prefixes: null,
    dotRe: /\./g,
    wildcardRe: /\*/g,
    addAlias: function(className, alias) {
        return this.addMapping(className, alias, this.aliasToName, this.nameToAliases);
    },
    addAlternate: function(className, alternate) {
        return this.addMapping(className, alternate, this.alternateToName, this.nameToAlternates);
    },
    addMapping: function(className, alternate, toName, nameTo) {
        var name = className.$className || className,
            mappings = name,
            array = this._array1,
            a, aliases, cls, i, length, nameMapping;
        if (Ext.isString(name)) {
            mappings = {};
            mappings[name] = alternate;
        }
        for (cls in mappings) {
            aliases = mappings[cls];
            if (Ext.isString(aliases)) {
                array[0] = aliases;
                aliases = array;
            }
            length = aliases.length;
            nameMapping = nameTo[cls] || (nameTo[cls] = []);
            for (i = 0; i < length; ++i) {
                if (!(a = aliases[i])) {
                    
                    continue;
                }
                if (toName[a] !== cls) {
                    if (toName[a]) {
                        Ext.log.warn("Overriding existing mapping: '" + a + "' From '" + toName[a] + "' to '" + cls + "'. Is this intentional?");
                    }
                    toName[a] = cls;
                    nameMapping.push(a);
                }
            }
        }
    },
    
    getAliasesByName: function(name) {
        return this.nameToAliases[name] || null;
    },
    getAlternatesByName: function(name) {
        return this.nameToAlternates[name] || null;
    },
    
    getNameByAlias: function(alias) {
        return this.aliasToName[alias] || '';
    },
    
    getNameByAlternate: function(alternate) {
        return this.alternateToName[alternate] || '';
    },
    
    getNamesByExpression: function(expression, exclude, accumulate) {
        var me = this,
            aliasToName = me.aliasToName,
            alternateToName = me.alternateToName,
            nameToAliases = me.nameToAliases,
            nameToAlternates = me.nameToAlternates,
            map = accumulate ? exclude : {},
            names = [],
            expressions = Ext.isString(expression) ? [
                expression
            ] : expression,
            length = expressions.length,
            wildcardRe = me.wildcardRe,
            expr, i, list, match, n, name, regex;
        for (i = 0; i < length; ++i) {
            if ((expr = expressions[i]).indexOf('*') < 0) {
                
                if (!(name = aliasToName[expr])) {
                    if (!(name = alternateToName[expr])) {
                        name = expr;
                    }
                }
                if (!(name in map) && !(exclude && (name in exclude))) {
                    map[name] = 1;
                    names.push(name);
                }
            } else {
                regex = new RegExp('^' + expr.replace(wildcardRe, '(.*?)') + '$');
                for (name in nameToAliases) {
                    if (!(name in map) && !(exclude && (name in exclude))) {
                        if (!(match = regex.test(name))) {
                            n = (list = nameToAliases[name]).length;
                            while (!match && n-- > 0) {
                                match = regex.test(list[n]);
                            }
                            list = nameToAlternates[name];
                            if (list && !match) {
                                n = list.length;
                                while (!match && n-- > 0) {
                                    match = regex.test(list[n]);
                                }
                            }
                        }
                        if (match) {
                            map[name] = 1;
                            names.push(name);
                        }
                    }
                }
            }
        }
        return names;
    },
    getPath: function(className) {
        var me = this,
            paths = me.paths,
            ret = '',
            prefix;
        if (className in paths) {
            ret = paths[className];
        } else {
            prefix = me.getPrefix(className);
            if (prefix) {
                className = className.substring(prefix.length + 1);
                ret = paths[prefix];
                if (ret) {
                    ret += '/';
                }
            }
            ret += className.replace(me.dotRe, '/') + '.js';
        }
        return ret;
    },
    getPrefix: function(className) {
        if (className in this.paths) {
            return className;
        }
        var prefixes = this.getPrefixes(),
            i = prefixes.length,
            length, prefix;
        
        while (i-- > 0) {
            length = (prefix = prefixes[i]).length;
            if (length < className.length && className.charAt(length) === '.' && prefix === className.substring(0, length)) {
                return prefix;
            }
        }
        return '';
    },
    getPrefixes: function() {
        var me = this,
            prefixes = me.prefixes;
        if (!prefixes) {
            me.prefixes = prefixes = me.names.slice(0);
            prefixes.sort(me._compareNames);
        }
        return prefixes;
    },
    removeName: function(name) {
        var me = this,
            aliasToName = me.aliasToName,
            alternateToName = me.alternateToName,
            nameToAliases = me.nameToAliases,
            nameToAlternates = me.nameToAlternates,
            aliases = nameToAliases[name],
            alternates = nameToAlternates[name],
            i, a;
        delete nameToAliases[name];
        delete nameToAlternates[name];
        if (aliases) {
            for (i = aliases.length; i--; ) {
                
                
                
                if (name === (a = aliases[i])) {
                    delete aliasToName[a];
                }
            }
        }
        if (alternates) {
            for (i = alternates.length; i--; ) {
                
                if (name === (a = alternates[i])) {
                    delete alternateToName[a];
                }
            }
        }
    },
    resolveName: function(name) {
        var me = this,
            trueName;
        
        
        if (!(name in me.nameToAliases)) {
            
            if (!(trueName = me.aliasToName[name])) {
                
                
                trueName = me.alternateToName[name];
            }
        }
        return trueName || name;
    },
    
    select: function(receiver, scope) {
        var me = this,
            excludes = {},
            ret = {
                excludes: excludes,
                exclude: function() {
                    me.getNamesByExpression(arguments, excludes, true);
                    return this;
                }
            },
            name;
        for (name in receiver) {
            ret[name] = me.selectMethod(excludes, receiver[name], scope || receiver);
        }
        return ret;
    },
    selectMethod: function(excludes, fn, scope) {
        var me = this;
        return function(include) {
            var args = Ext.Array.slice(arguments, 1);
            args.unshift(me.getNamesByExpression(include, excludes));
            return fn.apply(scope, args);
        };
    },
    
    setPath: Ext.Function.flexSetter(function(name, path) {
        var me = this;
        me.paths[name] = path;
        me.names.push(name);
        me.prefixes = null;
        return me;
    }),
    _compareNames: function(lhs, rhs) {
        var cmp = lhs.length - rhs.length;
        if (!cmp) {
            cmp = (lhs < rhs) ? -1 : 1;
        }
        return cmp;
    }
};



Ext.ClassManager = (function(Class, alias, arraySlice, arrayFrom, global) {
    
    
    
    
    
    var makeCtor = Ext.Class.makeCtor,
        Manager = Ext.apply(new Ext.Inventory(), {
            
            classes: {},
            classState: {},
            
            
            existCache: {},
            
            namespaceRewrites: [
                {
                    from: 'Ext.',
                    to: Ext
                }
            ],
            
            enableNamespaceParseCache: true,
            
            namespaceParseCache: {},
            
            instantiators: [],
            
            isCreated: function(className) {
                var i, ln, part, root, parts;
                if (typeof className !== 'string' || className.length < 1) {
                    throw new Error("[Ext.ClassManager] Invalid classname, must be a string and must not be empty");
                }
                if (Manager.classes[className] || Manager.existCache[className]) {
                    return true;
                }
                root = global;
                parts = Manager.parseNamespace(className);
                for (i = 0 , ln = parts.length; i < ln; i++) {
                    part = parts[i];
                    if (typeof part !== 'string') {
                        root = part;
                    } else {
                        if (!root || !root[part]) {
                            return false;
                        }
                        root = root[part];
                    }
                }
                Manager.triggerCreated(className);
                return true;
            },
            
            createdListeners: [],
            
            nameCreatedListeners: {},
            
            existsListeners: [],
            
            nameExistsListeners: {},
            
            overrideMap: {},
            
            triggerCreated: function(className, state) {
                Manager.existCache[className] = state || 1;
                Manager.classState[className] += 40;
                Manager.notify(className, Manager.createdListeners, Manager.nameCreatedListeners);
            },
            
            onCreated: function(fn, scope, className) {
                Manager.addListener(fn, scope, className, Manager.createdListeners, Manager.nameCreatedListeners);
            },
            
            notify: function(className, listeners, nameListeners) {
                var alternateNames = Manager.getAlternatesByName(className),
                    names = [
                        className
                    ],
                    i, ln, j, subLn, listener, name;
                for (i = 0 , ln = listeners.length; i < ln; i++) {
                    listener = listeners[i];
                    listener.fn.call(listener.scope, className);
                }
                while (names) {
                    for (i = 0 , ln = names.length; i < ln; i++) {
                        name = names[i];
                        listeners = nameListeners[name];
                        if (listeners) {
                            for (j = 0 , subLn = listeners.length; j < subLn; j++) {
                                listener = listeners[j];
                                listener.fn.call(listener.scope, name);
                            }
                            delete nameListeners[name];
                        }
                    }
                    names = alternateNames;
                    
                    alternateNames = null;
                }
            },
            
            
            addListener: function(fn, scope, className, listeners, nameListeners) {
                if (Ext.isArray(className)) {
                    fn = Ext.Function.createBarrier(className.length, fn, scope);
                    for (i = 0; i < className.length; i++) {
                        this.addListener(fn, null, className[i], listeners, nameListeners);
                    }
                    return;
                }
                var i,
                    listener = {
                        fn: fn,
                        scope: scope
                    };
                if (className) {
                    if (this.isCreated(className)) {
                        fn.call(scope, className);
                        return;
                    }
                    if (!nameListeners[className]) {
                        nameListeners[className] = [];
                    }
                    nameListeners[className].push(listener);
                } else {
                    listeners.push(listener);
                }
            },
            
            parseNamespace: function(namespace) {
                if (typeof namespace !== 'string') {
                    throw new Error("[Ext.ClassManager] Invalid namespace, must be a string");
                }
                var cache = this.namespaceParseCache,
                    parts, rewrites, root, name, rewrite, from, to, i, ln;
                if (this.enableNamespaceParseCache) {
                    if (cache.hasOwnProperty(namespace)) {
                        return cache[namespace];
                    }
                }
                parts = [];
                rewrites = this.namespaceRewrites;
                root = global;
                name = namespace;
                for (i = 0 , ln = rewrites.length; i < ln; i++) {
                    rewrite = rewrites[i];
                    from = rewrite.from;
                    to = rewrite.to;
                    if (name === from || name.substring(0, from.length) === from) {
                        name = name.substring(from.length);
                        if (typeof to !== 'string') {
                            root = to;
                        } else {
                            parts = parts.concat(to.split('.'));
                        }
                        break;
                    }
                }
                parts.push(root);
                parts = parts.concat(name.split('.'));
                if (this.enableNamespaceParseCache) {
                    cache[namespace] = parts;
                }
                return parts;
            },
            
            setNamespace: function(name, value) {
                var root = global,
                    parts = this.parseNamespace(name),
                    ln = parts.length - 1,
                    leaf = parts[ln],
                    i, part;
                for (i = 0; i < ln; i++) {
                    part = parts[i];
                    if (typeof part !== 'string') {
                        root = part;
                    } else {
                        if (!root[part]) {
                            root[part] = {};
                        }
                        root = root[part];
                    }
                }
                root[leaf] = value;
                return root[leaf];
            },
            
            createNamespaces: function() {
                var root = global,
                    parts, part, i, j, ln, subLn;
                for (i = 0 , ln = arguments.length; i < ln; i++) {
                    parts = this.parseNamespace(arguments[i]);
                    for (j = 0 , subLn = parts.length; j < subLn; j++) {
                        part = parts[j];
                        if (typeof part !== 'string') {
                            root = part;
                        } else {
                            if (!root[part]) {
                                root[part] = {};
                            }
                            root = root[part];
                        }
                    }
                }
                return root;
            },
            
            set: function(name, value) {
                var me = this,
                    targetName = me.getName(value);
                me.classes[name] = me.setNamespace(name, value);
                if (targetName && targetName !== name) {
                    me.addAlternate(targetName, name);
                }
                return this;
            },
            
            get: function(name) {
                var classes = this.classes,
                    root, parts, part, i, ln;
                if (classes[name]) {
                    return classes[name];
                }
                root = global;
                parts = this.parseNamespace(name);
                for (i = 0 , ln = parts.length; i < ln; i++) {
                    part = parts[i];
                    if (typeof part !== 'string') {
                        root = part;
                    } else {
                        if (!root || !root[part]) {
                            return null;
                        }
                        root = root[part];
                    }
                }
                return root;
            },
            
            addNameAliasMappings: function(aliases) {
                this.addAlias(aliases);
            },
            
            addNameAlternateMappings: function(alternates) {
                this.addAlternate(alternates);
            },
            
            getByAlias: function(alias) {
                return this.get(this.getNameByAlias(alias));
            },
            
            getName: function(object) {
                return object && object.$className || '';
            },
            
            getClass: function(object) {
                return object && object.self || null;
            },
            
            create: function(className, data, createdFn) {
                if (className != null && typeof className !== 'string') {
                    throw new Error("[Ext.define] Invalid class name '" + className + "' specified, must be a non-empty string");
                }
                var ctor = makeCtor(className);
                if (typeof data === 'function') {
                    data = data(ctor);
                }
                if (className) {
                    if (Manager.classes[className]) {
                        Ext.log.warn("[Ext.define] Duplicate class name '" + className + "' specified, must be a non-empty string");
                    }
                    ctor.name = className;
                }
                data.$className = className;
                return new Class(ctor,data,function() {
                    var postprocessorStack = data.postprocessors || Manager.defaultPostprocessors,
                        registeredPostprocessors = Manager.postprocessors,
                        postprocessors = [],
                        postprocessor, i, ln, j, subLn, postprocessorProperties, postprocessorProperty;
                    delete data.postprocessors;
                    for (i = 0 , ln = postprocessorStack.length; i < ln; i++) {
                        postprocessor = postprocessorStack[i];
                        if (typeof postprocessor === 'string') {
                            postprocessor = registeredPostprocessors[postprocessor];
                            postprocessorProperties = postprocessor.properties;
                            if (postprocessorProperties === true) {
                                postprocessors.push(postprocessor.fn);
                            } else if (postprocessorProperties) {
                                for (j = 0 , subLn = postprocessorProperties.length; j < subLn; j++) {
                                    postprocessorProperty = postprocessorProperties[j];
                                    if (data.hasOwnProperty(postprocessorProperty)) {
                                        postprocessors.push(postprocessor.fn);
                                        break;
                                    }
                                }
                            }
                        } else {
                            postprocessors.push(postprocessor);
                        }
                    }
                    data.postprocessors = postprocessors;
                    data.createdFn = createdFn;
                    Manager.processCreate(className, this, data);
                });
            },
            processCreate: function(className, cls, clsData) {
                var me = this,
                    postprocessor = clsData.postprocessors.shift(),
                    createdFn = clsData.createdFn;
                if (!postprocessor) {
                    Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#classCreated', arguments);
                    if (className) {
                        me.set(className, cls);
                    }
                    delete cls._classHooks;
                    if (createdFn) {
                        createdFn.call(cls, cls);
                    }
                    if (className) {
                        me.triggerCreated(className);
                    }
                    return;
                }
                if (postprocessor.call(me, className, cls, clsData, me.processCreate) !== false) {
                    me.processCreate(className, cls, clsData);
                }
            },
            createOverride: function(className, data, createdFn) {
                var me = this,
                    overriddenClassName = data.override,
                    requires = data.requires,
                    uses = data.uses,
                    mixins = data.mixins,
                    mixinsIsArray,
                    compat = data.compatibility,
                    depedenciesLoaded,
                    classReady = function() {
                        var cls, dependencies, i, key, temp;
                        if (!depedenciesLoaded) {
                            dependencies = requires ? requires.slice(0) : [];
                            if (mixins) {
                                if (!(mixinsIsArray = mixins instanceof Array)) {
                                    for (key in mixins) {
                                        if (Ext.isString(cls = mixins[key])) {
                                            dependencies.push(cls);
                                        }
                                    }
                                } else {
                                    for (i = 0 , temp = mixins.length; i < temp; ++i) {
                                        if (Ext.isString(cls = mixins[i])) {
                                            dependencies.push(cls);
                                        }
                                    }
                                }
                            }
                            depedenciesLoaded = true;
                            if (dependencies.length) {
                                
                                
                                
                                Ext.require(dependencies, classReady);
                                return;
                            }
                        }
                        
                        
                        
                        
                        if (mixinsIsArray) {
                            for (i = 0 , temp = mixins.length; i < temp; ++i) {
                                if (Ext.isString(cls = mixins[i])) {
                                    mixins[i] = Ext.ClassManager.get(cls);
                                }
                            }
                        } else if (mixins) {
                            for (key in mixins) {
                                if (Ext.isString(cls = mixins[key])) {
                                    mixins[key] = Ext.ClassManager.get(cls);
                                }
                            }
                        }
                        
                        
                        cls = me.get(overriddenClassName);
                        
                        delete data.override;
                        delete data.compatibility;
                        delete data.requires;
                        delete data.uses;
                        Ext.override(cls, data);
                        
                        
                        
                        Ext.Loader.history.push(className);
                        if (uses) {
                            
                            
                            Ext['Loader'].addUsedClasses(uses);
                        }
                        
                        if (createdFn) {
                            createdFn.call(cls, cls);
                        }
                    };
                
                Manager.overrideMap[className] = true;
                if (!compat || Ext.checkVersion(compat)) {
                    
                    me.onCreated(classReady, me, overriddenClassName);
                }
                me.triggerCreated(className, 2);
                return me;
            },
            
            instantiateByAlias: function() {
                var alias = arguments[0],
                    args = arraySlice.call(arguments),
                    className = this.getNameByAlias(alias);
                if (!className) {
                    throw new Error("[Ext.createByAlias] Unrecognized alias: " + alias);
                }
                args[0] = className;
                return Ext.create.apply(Ext, args);
            },
            
            instantiate: function() {
                Ext.log.warn('Ext.ClassManager.instantiate() is deprecated.  Use Ext.create() instead.');
                return Ext.create.apply(Ext, arguments);
            },
            
            dynInstantiate: function(name, args) {
                args = arrayFrom(args, true);
                args.unshift(name);
                return Ext.create.apply(Ext, args);
            },
            
            getInstantiator: function(length) {
                var instantiators = this.instantiators,
                    instantiator, i, args;
                instantiator = instantiators[length];
                if (!instantiator) {
                    i = length;
                    args = [];
                    for (i = 0; i < length; i++) {
                        args.push('a[' + i + ']');
                    }
                    instantiator = instantiators[length] = new Function('c','a','return new c(' + args.join(',') + ')');
                    instantiator.name = "Ext.create" + length;
                }
                return instantiator;
            },
            
            postprocessors: {},
            
            defaultPostprocessors: [],
            
            registerPostprocessor: function(name, fn, properties, position, relativeTo) {
                if (!position) {
                    position = 'last';
                }
                if (!properties) {
                    properties = [
                        name
                    ];
                }
                this.postprocessors[name] = {
                    name: name,
                    properties: properties || false,
                    fn: fn
                };
                this.setDefaultPostprocessorPosition(name, position, relativeTo);
                return this;
            },
            
            setDefaultPostprocessors: function(postprocessors) {
                this.defaultPostprocessors = arrayFrom(postprocessors);
                return this;
            },
            
            setDefaultPostprocessorPosition: function(name, offset, relativeName) {
                var defaultPostprocessors = this.defaultPostprocessors,
                    index;
                if (typeof offset === 'string') {
                    if (offset === 'first') {
                        defaultPostprocessors.unshift(name);
                        return this;
                    } else if (offset === 'last') {
                        defaultPostprocessors.push(name);
                        return this;
                    }
                    offset = (offset === 'after') ? 1 : -1;
                }
                index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
                if (index !== -1) {
                    Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
                }
                return this;
            }
        });
    
    Manager.registerPostprocessor('alias', function(name, cls, data) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#aliasPostProcessor', arguments);
        var aliases = Ext.Array.from(data.alias),
            i, ln;
        for (i = 0 , ln = aliases.length; i < ln; i++) {
            alias = aliases[i];
            this.addAlias(cls, alias);
        }
    }, [
        'xtype',
        'alias'
    ]);
    
    Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#singletonPostProcessor', arguments);
        if (data.singleton) {
            fn.call(this, name, new cls(), data);
        } else {
            return true;
        }
        return false;
    });
    
    Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#alternateClassNamePostprocessor', arguments);
        var alternates = data.alternateClassName,
            i, ln, alternate;
        if (!(alternates instanceof Array)) {
            alternates = [
                alternates
            ];
        }
        for (i = 0 , ln = alternates.length; i < ln; i++) {
            alternate = alternates[i];
            if (typeof alternate !== 'string') {
                throw new Error("[Ext.define] Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string");
            }
            this.set(alternate, cls);
        }
    });
    
    Manager.registerPostprocessor('debugHooks', function(name, Class, data) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#debugHooks', arguments);
        if (Ext.isDebugEnabled(Class.$className, data.debugHooks.$enabled)) {
            delete data.debugHooks.$enabled;
            Ext.override(Class, data.debugHooks);
        }
        
        var target = Class.isInstance ? Class.self : Class;
        delete target.prototype.debugHooks;
    });
    
    Manager.registerPostprocessor('deprecated', function(name, Class, data) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#deprecated', arguments);
        
        var target = Class.isInstance ? Class.self : Class;
        target.addDeprecations(data.deprecated);
        delete target.prototype.deprecated;
    });
    Ext.apply(Ext, {
        
        create: function() {
            var name = arguments[0],
                nameType = typeof name,
                args = arraySlice.call(arguments, 1),
                cls;
            if (nameType === 'function') {
                cls = name;
            } else {
                if (nameType !== 'string' && args.length === 0) {
                    args = [
                        name
                    ];
                    if (!(name = name.xclass)) {
                        name = args[0].xtype;
                        if (name) {
                            name = 'widget.' + name;
                        }
                    }
                }
                if (typeof name !== 'string' || name.length < 1) {
                    throw new Error("[Ext.create] Invalid class name or alias '" + name + "' specified, must be a non-empty string");
                }
                name = Manager.resolveName(name);
                cls = Manager.get(name);
            }
            
            if (!cls) {
                Ext.log.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " + "Ext.require('" + name + "') above Ext.onReady");
                Ext.syncRequire(name);
                cls = Manager.get(name);
            }
            if (!cls) {
                throw new Error("[Ext.create] Unrecognized class name / alias: " + name);
            }
            if (typeof cls !== 'function') {
                throw new Error("[Ext.create] Singleton '" + name + "' cannot be instantiated.");
            }
            return Manager.getInstantiator(args.length)(cls, args);
        },
        
        widget: function(name, config) {
            
            
            
            
            
            
            
            var xtype = name,
                alias, className, T;
            if (typeof xtype !== 'string') {
                
                
                config = name;
                
                xtype = config.xtype;
                className = config.xclass;
            } else {
                config = config || {};
            }
            if (config.isComponent) {
                return config;
            }
            if (!className) {
                alias = 'widget.' + xtype;
                className = Manager.getNameByAlias(alias);
            }
            
            if (className) {
                T = Manager.get(className);
            }
            if (!T) {
                return Ext.create(className || alias, config);
            }
            return new T(config);
        },
        
        createByAlias: alias(Manager, 'instantiateByAlias'),
        
        define: function(className, data, createdFn) {
            Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'ClassManager#define', arguments);
            if (data.override) {
                Manager.classState[className] = 20;
                return Manager.createOverride.apply(Manager, arguments);
            }
            Manager.classState[className] = 10;
            return Manager.create.apply(Manager, arguments);
        },
        
        undefine: function(className) {
            Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#undefine', arguments);
            var classes = Manager.classes,
                parts, partCount, namespace, i;
            delete Manager.namespaceParseCache[className];
            delete classes[className];
            delete Manager.existCache[className];
            delete Manager.classState[className];
            Manager.removeName(className);
            parts = Manager.parseNamespace(className);
            partCount = parts.length - 1;
            namespace = parts[0];
            for (i = 1; i < partCount; i++) {
                namespace = namespace[parts[i]];
                if (!namespace) {
                    return;
                }
            }
            
            try {
                delete namespace[parts[partCount]];
            } catch (e) {
                namespace[parts[partCount]] = undefined;
            }
        },
        
        getClassName: alias(Manager, 'getName'),
        
        getDisplayName: function(object) {
            if (object) {
                if (object.displayName) {
                    return object.displayName;
                }
                if (object.$name && object.$class) {
                    return Ext.getClassName(object.$class) + '#' + object.$name;
                }
                if (object.$className) {
                    return object.$className;
                }
            }
            return 'Anonymous';
        },
        
        getClass: alias(Manager, 'getClass'),
        
        namespace: alias(Manager, 'createNamespaces')
    });
    
    Ext.createWidget = Ext.widget;
    
    Ext.ns = Ext.namespace;
    Class.registerPreprocessor('className', function(cls, data) {
        if ('$className' in data) {
            cls.$className = data.$className;
            cls.displayName = cls.$className;
        }
        Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#classNamePreprocessor', arguments);
    }, true, 'first');
    Class.registerPreprocessor('alias', function(cls, data) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor', arguments);
        var prototype = cls.prototype,
            xtypes = arrayFrom(data.xtype),
            aliases = arrayFrom(data.alias),
            widgetPrefix = 'widget.',
            widgetPrefixLength = widgetPrefix.length,
            xtypesChain = Array.prototype.slice.call(prototype.xtypesChain || []),
            xtypesMap = Ext.merge({}, prototype.xtypesMap || {}),
            i, ln, alias, xtype;
        for (i = 0 , ln = aliases.length; i < ln; i++) {
            alias = aliases[i];
            if (typeof alias !== 'string' || alias.length < 1) {
                throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string");
            }
            if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
                xtype = alias.substring(widgetPrefixLength);
                Ext.Array.include(xtypes, xtype);
            }
        }
        cls.xtype = data.xtype = xtypes[0];
        data.xtypes = xtypes;
        for (i = 0 , ln = xtypes.length; i < ln; i++) {
            xtype = xtypes[i];
            if (!xtypesMap[xtype]) {
                xtypesMap[xtype] = true;
                xtypesChain.push(xtype);
            }
        }
        data.xtypesChain = xtypesChain;
        data.xtypesMap = xtypesMap;
        Ext.Function.interceptAfter(data, 'onClassCreated', function() {
            Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor#afterClassCreated', arguments);
            var mixins = prototype.mixins,
                key, mixin;
            for (key in mixins) {
                if (mixins.hasOwnProperty(key)) {
                    mixin = mixins[key];
                    xtypes = mixin.xtypes;
                    if (xtypes) {
                        for (i = 0 , ln = xtypes.length; i < ln; i++) {
                            xtype = xtypes[i];
                            if (!xtypesMap[xtype]) {
                                xtypesMap[xtype] = true;
                                xtypesChain.push(xtype);
                            }
                        }
                    }
                }
            }
        });
        for (i = 0 , ln = xtypes.length; i < ln; i++) {
            xtype = xtypes[i];
            if (typeof xtype !== 'string' || xtype.length < 1) {
                throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string");
            }
            Ext.Array.include(aliases, widgetPrefix + xtype);
        }
        data.alias = aliases;
    }, [
        'xtype',
        'alias'
    ]);
    
    if (Ext.manifest) {
        var manifest = Ext.manifest,
            classes = manifest.classes,
            paths = manifest.paths,
            aliases = {},
            alternates = {},
            className, obj, name, path, baseUrl;
        if (paths) {
            
            
            
            if (manifest.bootRelative) {
                baseUrl = Ext.Boot.baseUrl;
                for (path in paths) {
                    if (paths.hasOwnProperty(path)) {
                        paths[path] = baseUrl + paths[path];
                    }
                }
            }
            Manager.setPath(paths);
        }
        if (classes) {
            for (className in classes) {
                alternates[className] = [];
                aliases[className] = [];
                obj = classes[className];
                if (obj.alias) {
                    aliases[className] = obj.alias;
                }
                if (obj.alternates) {
                    alternates[className] = obj.alternates;
                }
            }
        }
        Manager.addAlias(aliases);
        Manager.addAlternate(alternates);
    }
    return Manager;
}(Ext.Class, Ext.Function.alias, Array.prototype.slice, Ext.Array.from, Ext.global));


Ext.env || (Ext.env = {});
Ext.env.Browser = function(userAgent, publish) {
    
    
    
    
    var me = this,
        browserPrefixes = me.browserPrefixes,
        enginePrefixes = me.enginePrefixes,
        browserMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(browserPrefixes).join(')|(?:') + '))([\\w\\._]+)')),
        engineMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(enginePrefixes).join(')|(?:') + '))([\\w\\._]+)')),
        browserNames = me.browserNames,
        browserName = browserNames.other,
        engineNames = me.engineNames,
        engineName = engineNames.other,
        browserVersion = '',
        engineVersion = '',
        majorVer = '',
        isWebView = false,
        i, prefix, mode, name, maxIEVersion;
    
    me.userAgent = userAgent;
    if (browserMatch) {
        browserName = browserNames[Ext.Object.getKey(browserPrefixes, browserMatch[1])];
        if (browserName === 'Safari' && /^Opera/.test(userAgent)) {
            
            browserName = 'Opera';
        }
        browserVersion = new Ext.Version(browserMatch[2]);
    }
    if (engineMatch) {
        engineName = engineNames[Ext.Object.getKey(enginePrefixes, engineMatch[1])];
        engineVersion = new Ext.Version(engineMatch[2]);
    }
    if (engineName == 'Trident' && browserName != 'IE') {
        browserName = 'IE';
        var version = userAgent.match(/.*rv:(\d+.\d+)/);
        if (version && version.length) {
            version = version[1];
            browserVersion = new Ext.Version(version);
        }
    }
    
    
    if (userAgent.match(/FB/) && browserName == "Other") {
        browserName = browserNames.safari;
        engineName = engineNames.webkit;
    }
    if (userAgent.match(/Android.*Chrome/g)) {
        browserName = 'ChromeMobile';
    }
    if (userAgent.match(/OPR/)) {
        browserName = 'Opera';
        browserMatch = userAgent.match(/OPR\/(\d+.\d+)/);
        browserVersion = new Ext.Version(browserMatch[1]);
    }
    Ext.apply(this, {
        engineName: engineName,
        engineVersion: engineVersion,
        name: browserName,
        version: browserVersion
    });
    this.setFlag(browserName, true, publish);
    
    if (browserVersion) {
        majorVer = browserVersion.getMajor() || '';
        if (me.is.IE) {
            majorVer = parseInt(majorVer, 10);
            mode = document.documentMode;
            
            
            
            
            
            if (mode == 7 || (majorVer == 7 && mode != 8 && mode != 9 && mode != 10)) {
                majorVer = 7;
            } else if (mode == 8 || (majorVer == 8 && mode != 8 && mode != 9 && mode != 10)) {
                majorVer = 8;
            } else if (mode == 9 || (majorVer == 9 && mode != 7 && mode != 8 && mode != 10)) {
                majorVer = 9;
            } else if (mode == 10 || (majorVer == 10 && mode != 7 && mode != 8 && mode != 9)) {
                majorVer = 10;
            } else if (mode == 11 || (majorVer == 11 && mode != 7 && mode != 8 && mode != 9 && mode != 10)) {
                majorVer = 11;
            }
            maxIEVersion = Math.max(majorVer, 11);
            for (i = 7; i <= maxIEVersion; ++i) {
                prefix = 'isIE' + i;
                if (majorVer <= i) {
                    Ext[prefix + 'm'] = true;
                }
                if (majorVer === i) {
                    Ext[prefix] = true;
                }
                if (majorVer >= i) {
                    Ext[prefix + 'p'] = true;
                }
            }
        }
        if (me.is.Opera && parseInt(majorVer, 10) <= 12) {
            Ext.isOpera12m = true;
        }
        Ext.chromeVersion = Ext.isChrome ? majorVer : 0;
        Ext.firefoxVersion = Ext.isFirefox ? majorVer : 0;
        Ext.ieVersion = Ext.isIE ? majorVer : 0;
        Ext.operaVersion = Ext.isOpera ? majorVer : 0;
        Ext.safariVersion = Ext.isSafari ? majorVer : 0;
        Ext.webKitVersion = Ext.isWebKit ? majorVer : 0;
        this.setFlag(browserName + majorVer, true, publish);
        
        this.setFlag(browserName + browserVersion.getShortVersion());
    }
    for (i in browserNames) {
        if (browserNames.hasOwnProperty(i)) {
            name = browserNames[i];
            this.setFlag(name, browserName === name);
        }
    }
    this.setFlag(name);
    if (engineVersion) {
        this.setFlag(engineName + (engineVersion.getMajor() || ''));
        this.setFlag(engineName + engineVersion.getShortVersion());
    }
    for (i in engineNames) {
        if (engineNames.hasOwnProperty(i)) {
            name = engineNames[i];
            this.setFlag(name, engineName === name, publish);
        }
    }
    this.setFlag('Standalone', !!navigator.standalone);
    this.setFlag('Ripple', !!document.getElementById("tinyhippos-injected") && !Ext.isEmpty(window.top.ripple));
    this.setFlag('WebWorks', !!window.blackberry);
    if (typeof window.PhoneGap != 'undefined' || typeof window.Cordova != 'undefined' || typeof window.cordova != 'undefined') {
        isWebView = true;
        this.setFlag('PhoneGap');
        this.setFlag('Cordova');
    } else if (!!window.isNK) {
        isWebView = true;
        this.setFlag('Sencha');
    }
    if (/(Glass)/i.test(userAgent)) {
        this.setFlag('GoogleGlass');
    }
    
    if (/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i.test(userAgent)) {
        isWebView = true;
    }
    
    this.setFlag('WebView', isWebView);
    
    this.isStrict = Ext.isStrict = document.compatMode == "CSS1Compat";
    
    this.isSecure = Ext.isSecure;
    
    this.identity = browserName + majorVer + (this.isStrict ? 'Strict' : 'Quirks');
};
Ext.env.Browser.prototype = {
    constructor: Ext.env.Browser,
    browserNames: {
        ie: 'IE',
        firefox: 'Firefox',
        safari: 'Safari',
        chrome: 'Chrome',
        opera: 'Opera',
        dolfin: 'Dolfin',
        webosbrowser: 'webOSBrowser',
        chromeMobile: 'ChromeMobile',
        chromeiOS: 'ChromeiOS',
        silk: 'Silk',
        other: 'Other'
    },
    engineNames: {
        webkit: 'WebKit',
        gecko: 'Gecko',
        presto: 'Presto',
        trident: 'Trident',
        other: 'Other'
    },
    enginePrefixes: {
        webkit: 'AppleWebKit/',
        gecko: 'Gecko/',
        presto: 'Presto/',
        trident: 'Trident/'
    },
    browserPrefixes: {
        ie: 'MSIE ',
        firefox: 'Firefox/',
        chrome: 'Chrome/',
        safari: 'Version/',
        opera: 'OPR/',
        dolfin: 'Dolfin/',
        webosbrowser: 'wOSBrowser/',
        chromeMobile: 'CrMo/',
        chromeiOS: 'CriOS/',
        silk: 'Silk/'
    },
    styleDashPrefixes: {
        WebKit: '-webkit-',
        Gecko: '-moz-',
        Trident: '-ms-',
        Presto: '-o-',
        Other: ''
    },
    stylePrefixes: {
        WebKit: 'Webkit',
        Gecko: 'Moz',
        Trident: 'ms',
        Presto: 'O',
        Other: ''
    },
    propertyPrefixes: {
        WebKit: 'webkit',
        Gecko: 'moz',
        Trident: 'ms',
        Presto: 'o',
        Other: ''
    },
    
    
    is: function(name) {
        return !!this.is[name];
    },
    
    name: null,
    
    version: null,
    
    engineName: null,
    
    engineVersion: null,
    setFlag: function(name, value, publish) {
        if (typeof value == 'undefined') {
            value = true;
        }
        this.is[name] = value;
        this.is[name.toLowerCase()] = value;
        if (publish) {
            Ext['is' + name] = value;
        }
        return this;
    },
    getStyleDashPrefix: function() {
        return this.styleDashPrefixes[this.engineName];
    },
    getStylePrefix: function() {
        return this.stylePrefixes[this.engineName];
    },
    getVendorProperyName: function(name) {
        var prefix = this.propertyPrefixes[this.engineName];
        if (prefix.length > 0) {
            return prefix + Ext.String.capitalize(name);
        }
        return name;
    },
    getPreferredTranslationMethod: function(config) {
        if (typeof config == 'object' && 'translationMethod' in config && config.translationMethod !== 'auto') {
            return config.translationMethod;
        } else {
            return 'csstransform';
        }
    }
};

(function(userAgent) {
    Ext.browser = new Ext.env.Browser(userAgent,true);
    Ext.userAgent = userAgent.toLowerCase();
    
    Ext.SSL_SECURE_URL = Ext.isSecure && Ext.isIE ? 'javascript:\'\'' : 'about:blank';
}(Ext.global.navigator.userAgent));


Ext.env.OS = function(userAgent, platform, browserScope) {
    
    
    
    
    var me = this,
        names = me.names,
        prefixes = me.prefixes,
        name,
        version = '',
        is = me.is,
        i, prefix, match, item, match1;
    browserScope = browserScope || Ext.browser;
    for (i in prefixes) {
        if (prefixes.hasOwnProperty(i)) {
            prefix = prefixes[i];
            match = userAgent.match(new RegExp('(?:' + prefix + ')([^\\s;]+)'));
            if (match) {
                name = names[i];
                match1 = match[1];
                
                
                if (match1 && match1 == "HTC_") {
                    version = new Ext.Version("2.3");
                } else if (match1 && match1 == "Silk/") {
                    version = new Ext.Version("2.3");
                } else {
                    version = new Ext.Version(match[match.length - 1]);
                }
                break;
            }
        }
    }
    if (!name) {
        name = names[(userAgent.toLowerCase().match(/mac|win|linux/) || [
            'other'
        ])[0]];
        version = new Ext.Version('');
    }
    this.name = name;
    this.version = version;
    if (platform) {
        this.setFlag(platform.replace(/ simulator$/i, ''));
    }
    this.setFlag(name);
    if (version) {
        this.setFlag(name + (version.getMajor() || ''));
        this.setFlag(name + version.getShortVersion());
    }
    for (i in names) {
        if (names.hasOwnProperty(i)) {
            item = names[i];
            if (!is.hasOwnProperty(name)) {
                this.setFlag(item, (name === item));
            }
        }
    }
    
    if (this.name == "iOS" && window.screen.height == 568) {
        this.setFlag('iPhone5');
    }
    if (browserScope.is.Safari || browserScope.is.Silk) {
        
        if (this.is.Android2 || this.is.Android3 || browserScope.version.shortVersion == 501) {
            browserScope.setFlag("AndroidStock");
            browserScope.setFlag("AndroidStock2");
        }
        if (this.is.Android4) {
            browserScope.setFlag("AndroidStock");
            browserScope.setFlag("AndroidStock4");
        }
    }
};
Ext.env.OS.prototype = {
    constructor: Ext.env.OS,
    names: {
        ios: 'iOS',
        android: 'Android',
        windowsPhone: 'WindowsPhone',
        webos: 'webOS',
        blackberry: 'BlackBerry',
        rimTablet: 'RIMTablet',
        mac: 'MacOS',
        win: 'Windows',
        tizen: 'Tizen',
        linux: 'Linux',
        bada: 'Bada',
        chrome: 'ChromeOS',
        other: 'Other'
    },
    prefixes: {
        tizen: '(Tizen )',
        ios: 'i(?:Pad|Phone|Pod)(?:.*)CPU(?: iPhone)? OS ',
        android: '(Android |HTC_|Silk/)',
        
        
        windowsPhone: 'Windows Phone ',
        blackberry: '(?:BlackBerry|BB)(?:.*)Version/',
        rimTablet: 'RIM Tablet OS ',
        webos: '(?:webOS|hpwOS)/',
        bada: 'Bada/',
        chrome: 'CrOS '
    },
    
    is: function(name) {
        return !!this[name];
    },
    
    name: null,
    
    version: null,
    setFlag: function(name, value) {
        if (typeof value == 'undefined') {
            value = true;
        }
        if (this.flags) {
            this.flags[name] = value;
        }
        this.is[name] = value;
        this.is[name.toLowerCase()] = value;
        return this;
    }
};
(function() {
    var navigation = Ext.global.navigator,
        userAgent = navigation.userAgent,
        OS = Ext.env.OS,
        is = (Ext.is || (Ext.is = {})),
        osEnv, osName, deviceType;
    OS.prototype.flags = is;
    
    Ext.os = osEnv = new OS(userAgent,navigation.platform);
    osName = osEnv.name;
    
    Ext['is' + osName] = true;
    
    Ext.isMac = is.Mac = is.MacOS;
    var search = window.location.search.match(/deviceType=(Tablet|Phone)/),
        nativeDeviceType = window.deviceType;
    
    
    if (search && search[1]) {
        deviceType = search[1];
    } else if (nativeDeviceType === 'iPhone') {
        deviceType = 'Phone';
    } else if (nativeDeviceType === 'iPad') {
        deviceType = 'Tablet';
    } else {
        if (!osEnv.is.Android && !osEnv.is.iOS && !osEnv.is.WindowsPhone && /Windows|Linux|MacOS/.test(osName)) {
            deviceType = 'Desktop';
            
            Ext.browser.is.WebView = !!Ext.browser.is.Ripple;
        } else if (osEnv.is.iPad || osEnv.is.RIMTablet || osEnv.is.Android3 || Ext.browser.is.Silk || (osEnv.is.Android4 && userAgent.search(/mobile/i) == -1)) {
            deviceType = 'Tablet';
        } else {
            deviceType = 'Phone';
        }
    }
    
    osEnv.setFlag(deviceType, true);
    osEnv.deviceType = deviceType;
    delete OS.prototype.flags;
}());


Ext.feature = {
    
    
    
    
    
    
    
    has: function(name) {
        return !!this.has[name];
    },
    testElements: {},
    getTestElement: function(tag, createNew) {
        if (tag === undefined) {
            tag = 'div';
        } else if (typeof tag !== 'string') {
            return tag;
        }
        if (createNew) {
            return document.createElement(tag);
        }
        if (!this.testElements[tag]) {
            this.testElements[tag] = document.createElement(tag);
        }
        return this.testElements[tag];
    },
    isStyleSupported: function(name, tag) {
        var elementStyle = this.getTestElement(tag).style,
            cName = Ext.String.capitalize(name);
        if (typeof elementStyle[name] !== 'undefined' || typeof elementStyle[Ext.browser.getStylePrefix(name) + cName] !== 'undefined') {
            return true;
        }
        return false;
    },
    isStyleSupportedWithoutPrefix: function(name, tag) {
        var elementStyle = this.getTestElement(tag).style;
        if (typeof elementStyle[name] !== 'undefined') {
            return true;
        }
        return false;
    },
    isEventSupported: function(name, tag) {
        if (tag === undefined) {
            tag = window;
        }
        var element = this.getTestElement(tag),
            eventName = 'on' + name.toLowerCase(),
            isSupported = (eventName in element);
        if (!isSupported) {
            if (element.setAttribute && element.removeAttribute) {
                element.setAttribute(eventName, '');
                isSupported = typeof element[eventName] === 'function';
                if (typeof element[eventName] !== 'undefined') {
                    element[eventName] = undefined;
                }
                element.removeAttribute(eventName);
            }
        }
        return isSupported;
    },
    
    
    
    getStyle: function(element, styleName) {
        var view = element.ownerDocument.defaultView,
            style = (view ? view.getComputedStyle(element, null) : element.currentStyle) || element.style;
        return style[styleName];
    },
    getSupportedPropertyName: function(object, name) {
        var vendorName = Ext.browser.getVendorProperyName(name);
        if (vendorName in object) {
            return vendorName;
        } else if (name in object) {
            return name;
        }
        return null;
    },
    
    detect: function(isReady) {
        var me = this,
            doc = document,
            toRun = me.toRun || me.tests,
            n = toRun.length,
            div = doc.createElement('div'),
            notRun = [],
            supports = Ext.supports,
            has = me.has,
            name, test, vector, value;
        
        if (!Ext.theme) {
            Ext.theme = {
                name: 'Default'
            };
        }
        Ext.theme.is = {};
        Ext.theme.is[Ext.theme.name] = true;
        
        
        div.innerHTML = '<div style="height:30px;width:50px;">' + '<div style="height:20px;width:20px;"></div>' + '</div>' + '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">' + '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>' + '</div>' + '<div style="position: absolute; left: 10%; top: 10%;"></div>' + '<div style="float:left; background-color:transparent;"></div>';
        if (isReady) {
            doc.body.appendChild(div);
        }
        vector = me.preDetected[Ext.browser.identity] || [];
        while (n--) {
            test = toRun[n];
            value = vector[n];
            name = test.name;
            if (value === undefined) {
                if (!isReady && test.ready) {
                    
                    notRun.push(test);
                    
                    continue;
                }
                value = test.fn.call(me, doc, div);
            }
            
            supports[name] = has[name] = value;
        }
        if (isReady) {
            doc.body.removeChild(div);
        }
        me.toRun = notRun;
    },
    
    report: function() {
        var values = [],
            len = this.tests.length,
            i;
        for (i = 0; i < len; ++i) {
            values.push(this.has[this.tests[i].name] ? 1 : 0);
        }
        Ext.log(Ext.browser.identity + ': [' + values.join(',') + ']');
    },
    
    preDetected: {},
    
    
    tests: [
        {
            
            name: 'CSSPointerEvents',
            fn: function(doc) {
                return 'pointerEvents' in doc.documentElement.style;
            }
        },
        {
            
            name: 'CSS3BoxShadow',
            fn: function(doc) {
                return 'boxShadow' in doc.documentElement.style || 'WebkitBoxShadow' in doc.documentElement.style || 'MozBoxShadow' in doc.documentElement.style;
            }
        },
        {
            
            name: 'ClassList',
            fn: function(doc) {
                return !!doc.documentElement.classList;
            }
        },
        {
            
            name: 'Canvas',
            fn: function() {
                var element = this.getTestElement('canvas');
                return !!(element && element.getContext && element.getContext('2d'));
            }
        },
        {
            
            name: 'Svg',
            fn: function(doc) {
                return !!(doc.createElementNS && !!doc.createElementNS("http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect);
            }
        },
        {
            
            name: 'Vml',
            fn: function() {
                var element = this.getTestElement(),
                    ret = false;
                element.innerHTML = "<!--[if vml]><br><![endif]-->";
                ret = (element.childNodes.length === 1);
                element.innerHTML = "";
                return ret;
            }
        },
        {
            
            name: 'touchScroll',
            fn: function() {
                var supports = Ext.supports,
                    touchScroll = 0;
                if (navigator.msMaxTouchPoints || (Ext.isWebKit && supports.TouchEvents && Ext.os.is.Desktop)) {
                    touchScroll = 1;
                } else if (supports.Touch) {
                    touchScroll = 2;
                }
                return touchScroll;
            }
        },
        {
            
            name: 'Touch',
            fn: function() {
                
                var maxTouchPoints = navigator.msMaxTouchPoints || navigator.maxTouchPoints;
                
                
                
                
                
                
                
                return (Ext.supports.TouchEvents && maxTouchPoints !== 1) || maxTouchPoints > 1;
            }
        },
        {
            
            name: 'TouchEvents',
            fn: function() {
                return this.isEventSupported('touchend');
            }
        },
        {
            name: 'PointerEvents',
            fn: function() {
                return navigator.pointerEnabled;
            }
        },
        {
            name: 'MSPointerEvents',
            fn: function() {
                return navigator.msPointerEnabled;
            }
        },
        {
            
            name: 'Orientation',
            fn: function() {
                return ('orientation' in window) && this.isEventSupported('orientationchange');
            }
        },
        {
            
            name: 'OrientationChange',
            fn: function() {
                return this.isEventSupported('orientationchange');
            }
        },
        {
            
            name: 'DeviceMotion',
            fn: function() {
                return this.isEventSupported('devicemotion');
            }
        },
        {
            
            
            names: [
                'Geolocation',
                'GeoLocation'
            ],
            fn: function() {
                return 'geolocation' in window.navigator;
            }
        },
        {
            name: 'SqlDatabase',
            fn: function() {
                return 'openDatabase' in window;
            }
        },
        {
            name: 'WebSockets',
            fn: function() {
                return 'WebSocket' in window;
            }
        },
        {
            
            name: 'Range',
            fn: function() {
                return !!document.createRange;
            }
        },
        {
            
            name: 'CreateContextualFragment',
            fn: function() {
                var range = !!document.createRange ? document.createRange() : false;
                return range && !!range.createContextualFragment;
            }
        },
        {
            
            name: 'History',
            fn: function() {
                return ('history' in window && 'pushState' in window.history);
            }
        },
        {
            name: 'CssTransforms',
            fn: function() {
                return this.isStyleSupported('transform');
            }
        },
        {
            name: 'CssTransformNoPrefix',
            fn: function() {
                return this.isStyleSupportedWithoutPrefix('transform');
            }
        },
        {
            
            name: 'Css3dTransforms',
            fn: function() {
                
                return this.has('CssTransforms') && this.isStyleSupported('perspective') && !Ext.browser.is.AndroidStock2;
            }
        },
        
        
        {
            name: 'CssAnimations',
            fn: function() {
                return this.isStyleSupported('animationName');
            }
        },
        {
            
            names: [
                'CssTransitions',
                'Transitions'
            ],
            fn: function() {
                return this.isStyleSupported('transitionProperty');
            }
        },
        {
            
            
            names: [
                'Audio',
                'AudioTag'
            ],
            fn: function() {
                return !!this.getTestElement('audio').canPlayType;
            }
        },
        {
            
            name: 'Video',
            fn: function() {
                return !!this.getTestElement('video').canPlayType;
            }
        },
        {
            
            name: 'LocalStorage',
            fn: function() {
                try {
                    
                    
                    if ('localStorage' in window && window['localStorage'] !== null) {
                        
                        localStorage.setItem('sencha-localstorage-test', 'test success');
                        
                        localStorage.removeItem('sencha-localstorage-test');
                        return true;
                    }
                } catch (e) {}
                
                return false;
            }
        },
        {
            
            name: 'XHR2',
            fn: function() {
                return window.ProgressEvent && window.FormData && window.XMLHttpRequest && ('withCredentials' in new XMLHttpRequest());
            }
        },
        {
            
            name: 'XHRUploadProgress',
            fn: function() {
                if (window.XMLHttpRequest && !Ext.browser.is.AndroidStock) {
                    var xhr = new XMLHttpRequest();
                    return xhr && ('upload' in xhr) && ('onprogress' in xhr.upload);
                }
                return false;
            }
        },
        {
            
            name: 'NumericInputPlaceHolder',
            fn: function() {
                return !(Ext.browser.is.AndroidStock4 && Ext.os.version.getMinor() < 2);
            }
        },
        {
            name: 'ProperHBoxStretching',
            ready: true,
            fn: function() {
                
                var bodyElement = document.createElement('div'),
                    innerElement = bodyElement.appendChild(document.createElement('div')),
                    contentElement = innerElement.appendChild(document.createElement('div')),
                    innerWidth;
                bodyElement.setAttribute('style', 'width: 100px; height: 100px; position: relative;');
                innerElement.setAttribute('style', 'position: absolute; display: -ms-flexbox; display: -webkit-flex; display: -moz-flexbox; display: flex; -ms-flex-direction: row; -webkit-flex-direction: row; -moz-flex-direction: row; flex-direction: row; min-width: 100%;');
                contentElement.setAttribute('style', 'width: 200px; height: 50px;');
                document.body.appendChild(bodyElement);
                innerWidth = innerElement.offsetWidth;
                document.body.removeChild(bodyElement);
                return (innerWidth > 100);
            }
        },
        
        {
            name: 'matchesSelector',
            fn: function() {
                var el = document.documentElement,
                    w3 = 'matches',
                    wk = 'webkitMatchesSelector',
                    ms = 'msMatchesSelector',
                    mz = 'mozMatchesSelector';
                return el[w3] ? w3 : el[wk] ? wk : el[ms] ? ms : el[mz] ? mz : null;
            }
        },
        
        {
            name: 'RightMargin',
            ready: true,
            fn: function(doc, div) {
                var view = doc.defaultView;
                return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
            }
        },
        
        {
            name: 'DisplayChangeInputSelectionBug',
            fn: function() {
                var webKitVersion = Ext.webKitVersion;
                
                return 0 < webKitVersion && webKitVersion < 533;
            }
        },
        
        {
            name: 'DisplayChangeTextAreaSelectionBug',
            fn: function() {
                var webKitVersion = Ext.webKitVersion;
                
                return 0 < webKitVersion && webKitVersion < 534.24;
            }
        },
        
        {
            name: 'TransparentColor',
            ready: true,
            fn: function(doc, div, view) {
                view = doc.defaultView;
                return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
            }
        },
        
        {
            name: 'ComputedStyle',
            ready: true,
            fn: function(doc, div, view) {
                view = doc.defaultView;
                return view && view.getComputedStyle;
            }
        },
        
        {
            name: 'Float',
            fn: function(doc) {
                return 'cssFloat' in doc.documentElement.style;
            }
        },
        
        {
            name: 'CSS3BorderRadius',
            ready: true,
            fn: function(doc) {
                var domPrefixes = [
                        'borderRadius',
                        'BorderRadius',
                        'MozBorderRadius',
                        'WebkitBorderRadius',
                        'OBorderRadius',
                        'KhtmlBorderRadius'
                    ],
                    pass = false,
                    i;
                for (i = 0; i < domPrefixes.length; i++) {
                    if (doc.documentElement.style[domPrefixes[i]] !== undefined) {
                        pass = true;
                    }
                }
                return pass && !Ext.isIE9;
            }
        },
        
        {
            name: 'CSS3LinearGradient',
            fn: function(doc, div) {
                var property = 'background-image:',
                    webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
                    w3c = 'linear-gradient(left top, black, white)',
                    moz = '-moz-' + w3c,
                    ms = '-ms-' + w3c,
                    opera = '-o-' + w3c,
                    options = [
                        property + webkit,
                        property + w3c,
                        property + moz,
                        property + ms,
                        property + opera
                    ];
                div.style.cssText = options.join(';');
                return (("" + div.style.backgroundImage).indexOf('gradient') !== -1) && !Ext.isIE9;
            }
        },
        
        {
            name: 'MouseEnterLeave',
            fn: function(doc) {
                return ('onmouseenter' in doc.documentElement && 'onmouseleave' in doc.documentElement);
            }
        },
        
        {
            name: 'MouseWheel',
            fn: function(doc) {
                return ('onmousewheel' in doc.documentElement);
            }
        },
        
        {
            name: 'Opacity',
            fn: function(doc, div) {
                
                if (Ext.isIE8) {
                    return false;
                }
                div.firstChild.style.cssText = 'opacity:0.73';
                return div.firstChild.style.opacity == '0.73';
            }
        },
        
        {
            name: 'Placeholder',
            fn: function(doc) {
                return 'placeholder' in doc.createElement('input');
            }
        },
        
        {
            name: 'Direct2DBug',
            fn: function(doc) {
                return Ext.isString(doc.documentElement.style.msTransformOrigin) && Ext.isIE9m;
            }
        },
        
        {
            name: 'BoundingClientRect',
            fn: function(doc) {
                return 'getBoundingClientRect' in doc.documentElement;
            }
        },
        
        {
            name: 'RotatedBoundingClientRect',
            ready: true,
            fn: function(doc) {
                var body = doc.body,
                    supports = false,
                    el = this.getTestElement(),
                    style = el.style;
                if (el.getBoundingClientRect) {
                    style.WebkitTransform = style.MozTransform = style.msTransform = style.OTransform = style.transform = 'rotate(90deg)';
                    style.width = '100px';
                    style.height = '30px';
                    body.appendChild(el);
                    supports = el.getBoundingClientRect().height !== 100;
                    body.removeChild(el);
                }
                return supports;
            }
        },
        
        {
            name: 'ChildContentClearedWhenSettingInnerHTML',
            ready: true,
            fn: function() {
                var el = this.getTestElement(),
                    child;
                el.innerHTML = '<div>a</div>';
                child = el.firstChild;
                el.innerHTML = '<div>b</div>';
                return child.innerHTML !== 'a';
            }
        },
        {
            name: 'IncludePaddingInWidthCalculation',
            ready: true,
            fn: function(doc, div) {
                return div.childNodes[1].firstChild.offsetWidth == 210;
            }
        },
        {
            name: 'IncludePaddingInHeightCalculation',
            ready: true,
            fn: function(doc, div) {
                return div.childNodes[1].firstChild.offsetHeight == 210;
            }
        },
        
        {
            name: 'TextAreaMaxLength',
            fn: function(doc) {
                return ('maxlength' in doc.createElement('textarea'));
            }
        },
        
        
        {
            name: 'GetPositionPercentage',
            ready: true,
            fn: function(doc, div) {
                return Ext.feature.getStyle(div.childNodes[2], 'left') == '10%';
            }
        },
        
        {
            name: 'PercentageHeightOverflowBug',
            ready: true,
            fn: function(doc) {
                var hasBug = false,
                    style, el;
                if (Ext.getScrollbarSize().height) {
                    
                    el = this.getTestElement();
                    style = el.style;
                    style.height = '50px';
                    style.width = '50px';
                    style.overflow = 'auto';
                    style.position = 'absolute';
                    el.innerHTML = [
                        '<div style="display:table;height:100%;">',
                        
                        
                        
                        '<div style="width:51px;"></div>',
                        '</div>'
                    ].join('');
                    doc.body.appendChild(el);
                    if (el.firstChild.offsetHeight === 50) {
                        hasBug = true;
                    }
                    doc.body.removeChild(el);
                }
                return hasBug;
            }
        },
        
        {
            name: 'xOriginBug',
            ready: true,
            fn: function(doc, div) {
                div.innerHTML = '<div id="b1" style="height:100px;width:100px;direction:rtl;position:relative;overflow:scroll">' + '<div id="b2" style="position:relative;width:100%;height:20px;"></div>' + '<div id="b3" style="position:absolute;width:20px;height:20px;top:0px;right:0px"></div>' + '</div>';
                var outerBox = document.getElementById('b1').getBoundingClientRect(),
                    b2 = document.getElementById('b2').getBoundingClientRect(),
                    b3 = document.getElementById('b3').getBoundingClientRect();
                return (b2.left !== outerBox.left && b3.right !== outerBox.right);
            }
        },
        
        {
            name: 'ScrollWidthInlinePaddingBug',
            ready: true,
            fn: function(doc) {
                var hasBug = false,
                    style, el;
                el = doc.createElement('div');
                style = el.style;
                style.height = '50px';
                style.width = '50px';
                style.padding = '10px';
                style.overflow = 'hidden';
                style.position = 'absolute';
                el.innerHTML = '<span style="display:inline-block;zoom:1;height:60px;width:60px;"></span>';
                doc.body.appendChild(el);
                if (el.scrollWidth === 70) {
                    hasBug = true;
                }
                doc.body.removeChild(el);
                return hasBug;
            }
        },
        
        {
            name: 'rtlVertScrollbarOnRight',
            ready: true,
            fn: function(doc, div) {
                div.innerHTML = '<div style="height:100px;width:100px;direction:rtl;overflow:scroll">' + '<div style="width:20px;height:200px;"></div>' + '</div>';
                var outerBox = div.firstChild,
                    innerBox = outerBox.firstChild;
                return (innerBox.offsetLeft + innerBox.offsetWidth !== outerBox.offsetLeft + outerBox.offsetWidth);
            }
        },
        
        {
            name: 'rtlVertScrollbarOverflowBug',
            ready: true,
            fn: function(doc, div) {
                div.innerHTML = '<div style="height:100px;width:100px;direction:rtl;overflow:auto">' + '<div style="width:95px;height:200px;"></div>' + '</div>';
                
                
                
                var outerBox = div.firstChild;
                return outerBox.clientHeight === outerBox.offsetHeight;
            }
        },
        {
            identity: 'defineProperty',
            fn: function() {
                if (Ext.isIE8m) {
                    Ext.Object.defineProperty = Ext.emptyFn;
                    return false;
                }
                return true;
            }
        },
        {
            identify: 'nativeXhr',
            fn: function() {
                if (typeof XMLHttpRequest !== 'undefined') {
                    return true;
                }
                
                XMLHttpRequest = function() {
                    try {
                        return new ActiveXObject('MSXML2.XMLHTTP.3.0');
                    } catch (ex) {
                        return null;
                    }
                };
                return false;
            }
        },
        
        {
            name: 'SpecialKeyDownRepeat',
            fn: function() {
                return Ext.isWebKit ? parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 : !((Ext.isGecko && !Ext.isWindows) || (Ext.isOpera && Ext.operaVersion < 12));
            }
        },
        
        {
            name: 'EmulatedMouseOver',
            fn: function() {
                
                return Ext.os.is.iOS;
            }
        },
        
        {
            
            name: 'Hashchange',
            fn: function() {
                
                var docMode = document.documentMode;
                return 'onhashchange' in window && (docMode === undefined || docMode > 7);
            }
        },
        
        {
            name: 'FixedTableWidthBug',
            ready: true,
            fn: function() {
                if (Ext.isIE8) {
                    
                    return false;
                }
                var outer = document.createElement('div'),
                    inner = document.createElement('div'),
                    width;
                outer.setAttribute('style', 'display:table;table-layout:fixed;');
                inner.setAttribute('style', 'display:table-cell;min-width:50px;');
                outer.appendChild(inner);
                document.body.appendChild(outer);
                
                outer.offsetWidth;
                outer.style.width = '25px';
                width = outer.offsetWidth;
                document.body.removeChild(outer);
                return width === 50;
            }
        },
        
        {
            name: 'FocusinFocusoutEvents',
            
            
            
            
            fn: function() {
                return !Ext.isGecko;
            }
        }
    ]
};
Ext.supports = {};
Ext.feature.detect();


Ext.env.Ready = {
    
    
    
    
    
    blocks: (location.search || '').indexOf('ext-pauseReadyFire') > 0 ? 1 : 0,
    
    bound: 0,
    
    delay: 1,
    
    events: [],
    
    firing: false,
    
    generation: 0,
    
    listeners: [],
    
    nextId: 0,
    
    sortGeneration: 0,
    
    state: 0,
    
    timer: null,
    
    bind: function() {
        var me = Ext.env.Ready,
            doc = document;
        if (!me.bound) {
            
            if (doc.readyState == 'complete') {
                
                me.onReadyEvent({
                    type: doc.readyState || 'body'
                });
            } else {
                me.bound = 1;
                if (Ext.browser.is.PhoneGap && !Ext.os.is.Desktop) {
                    me.bound = 2;
                    doc.addEventListener('deviceready', me.onReadyEvent, false);
                }
                doc.addEventListener('DOMContentLoaded', me.onReadyEvent, false);
                window.addEventListener('load', me.onReadyEvent, false);
            }
        }
    },
    block: function() {
        ++this.blocks;
        Ext.isReady = false;
    },
    
    fireReady: function() {
        var me = Ext.env.Ready;
        if (!me.state) {
            Ext._readyTime = Ext.now();
            Ext.isDomReady = true;
            me.state = 1;
            
            Ext.feature.detect(true);
            if (!me.delay) {
                me.handleReady();
            } else if (navigator.standalone) {
                
                
                
                
                me.timer = Ext.defer(function() {
                    me.timer = null;
                    me.handleReadySoon();
                }, 1);
            } else {
                me.handleReadySoon();
            }
        }
    },
    
    handleReady: function() {
        var me = this;
        if (me.state === 1) {
            me.state = 2;
            Ext._beforeReadyTime = Ext.now();
            me.invokeAll();
            Ext._afterReadytime = Ext.now();
        }
    },
    
    handleReadySoon: function(delay) {
        var me = this;
        if (!me.timer) {
            me.timer = Ext.defer(function() {
                me.timer = null;
                me.handleReady();
            }, delay || me.delay);
        }
    },
    
    invoke: function(listener) {
        var delay = listener.delay;
        if (delay) {
            Ext.defer(listener.fn, delay, listener.scope);
        } else {
            if (Ext.elevateFunction) {
                Ext.elevateFunction(listener.fn, listener.scope);
            } else {
                listener.fn.call(listener.scope);
            }
        }
    },
    
    invokeAll: function() {
        if (Ext.elevateFunction) {
            Ext.elevateFunction(this.doInvokeAll, this);
        } else {
            this.doInvokeAll();
        }
    },
    doInvokeAll: function() {
        var me = this,
            listeners = me.listeners,
            listener;
        if (!me.blocks) {
            
            Ext.isReady = true;
        }
        me.firing = true;
        
        
        while (listeners.length) {
            if (me.sortGeneration !== me.generation) {
                me.sortGeneration = me.generation;
                
                
                
                
                listeners.sort(me.sortFn);
            }
            listener = listeners.pop();
            if (me.blocks && !listener.dom) {
                
                
                
                listeners.push(listener);
                break;
            }
            me.invoke(listener);
        }
        me.firing = false;
    },
    
    makeListener: function(fn, scope, options) {
        var ret = {
                fn: fn,
                id: ++this.nextId,
                
                scope: scope,
                dom: false,
                priority: 0
            };
        if (options) {
            Ext.apply(ret, options);
        }
        ret.phase = ret.dom ? 0 : 1;
        
        return ret;
    },
    
    on: function(fn, scope, options) {
        var me = Ext.env.Ready,
            listener = me.makeListener(fn, scope, options);
        if (me.state === 2 && !me.firing && (listener.dom || !me.blocks)) {
            
            
            
            
            
            
            
            me.invoke(listener);
        } else {
            me.listeners.push(listener);
            ++me.generation;
            if (!me.bound) {
                
                
                
                me.bind();
            }
        }
    },
    
    onReadyEvent: function(ev) {
        var me = Ext.env.Ready;
        if (Ext.elevateFunction) {
            Ext.elevateFunction(me.doReadyEvent, me, arguments);
        } else {
            me.doReadyEvent(ev);
        }
    },
    doReadyEvent: function(ev) {
        var me = this;
        if (ev && ev.type) {
            me.events.push(ev);
        }
        if (me.bound > 0) {
            me.unbind();
            me.bound = -1;
        }
        
        if (!me.state) {
            me.fireReady();
        }
    },
    
    sortFn: function(a, b) {
        return -((a.phase - b.phase) || (b.priority - a.priority) || (a.id - b.id));
    },
    unblock: function() {
        var me = this;
        if (me.blocks) {
            if (!--me.blocks) {
                if (me.state === 2 && !me.firing) {
                    
                    
                    me.invokeAll();
                }
            }
        }
    },
    
    
    
    
    
    
    unbind: function() {
        var me = this,
            doc = document;
        if (me.bound > 1) {
            doc.removeEventListener('deviceready', me.onReadyEvent, false);
        }
        doc.removeEventListener('DOMContentLoaded', me.onReadyEvent, false);
        window.removeEventListener('load', me.onReadyEvent, false);
    }
};
(function() {
    var Ready = Ext.env.Ready;
    
    if (Ext.isIE9m) {
        
        Ext.apply(Ready, {
            
            scrollTimer: null,
            
            readyStatesRe: /complete/i,
            
            pollScroll: function() {
                var scrollable = true;
                try {
                    document.documentElement.doScroll('left');
                } catch (e) {
                    scrollable = false;
                }
                
                
                if (scrollable && document.body) {
                    Ready.onReadyEvent({
                        type: 'doScroll'
                    });
                } else {
                    
                    
                    
                    Ready.scrollTimer = Ext.defer(Ready.pollScroll, 20);
                }
                return scrollable;
            },
            bind: function() {
                if (Ready.bound) {
                    return;
                }
                var doc = document,
                    topContext;
                
                try {
                    topContext = window.frameElement === undefined;
                } catch (e) {}
                
                
                if (!topContext || !doc.documentElement.doScroll) {
                    Ready.pollScroll = Ext.emptyFn;
                }
                
                else if (Ready.pollScroll()) {
                    
                    return;
                }
                if (doc.readyState == 'complete') {
                    
                    Ready.onReadyEvent({
                        type: 'already ' + (doc.readyState || 'body')
                    });
                } else {
                    doc.attachEvent('onreadystatechange', Ready.onReadyStateChange);
                    window.attachEvent('onload', Ready.onReadyEvent);
                    Ready.bound = 1;
                }
            },
            unbind: function() {
                document.detachEvent('onreadystatechange', Ready.onReadyStateChange);
                window.detachEvent('onload', Ready.onReadyEvent);
                if (Ext.isNumber(Ready.scrollTimer)) {
                    clearTimeout(Ready.scrollTimer);
                    Ready.scrollTimer = null;
                }
            },
            
            onReadyStateChange: function() {
                var state = document.readyState;
                if (Ready.readyStatesRe.test(state)) {
                    Ready.onReadyEvent({
                        type: state
                    });
                }
            }
        });
    }
    
    
    
    Ext.onDocumentReady = function(fn, scope, options) {
        var opt = {
                dom: true
            };
        if (options) {
            Ext.apply(opt, options);
        }
        Ready.on(fn, scope, opt);
    };
    
    Ext.onReady = function(fn, scope, options) {
        Ready.on(fn, scope, options);
    };
    Ready.bind();
}());



Ext.Loader = new function() {
    
    
    
    
    
    
    
    var Loader = this,
        Manager = Ext.ClassManager,
        
        Boot = Ext.Boot,
        Class = Ext.Class,
        Ready = Ext.env.Ready,
        alias = Ext.Function.alias,
        dependencyProperties = [
            'extend',
            'mixins',
            'requires'
        ],
        isInHistory = {},
        history = [],
        readyListeners = [],
        usedClasses = [],
        _requiresMap = {},
        _missingQueue = {},
        _config = {
            
            enabled: true,
            
            scriptChainDelay: false,
            
            disableCaching: true,
            
            disableCachingParam: '_dc',
            
            paths: Manager.paths,
            
            preserveScripts: true,
            
            scriptCharset: undefined
        },
        
        delegatedConfigs = {
            disableCaching: true,
            disableCachingParam: true,
            preserveScripts: true,
            scriptChainDelay: 'loadDelay'
        };
    Ext.apply(Loader, {
        
        isInHistory: isInHistory,
        
        isLoading: false,
        
        history: history,
        
        config: _config,
        
        readyListeners: readyListeners,
        
        optionalRequires: usedClasses,
        
        requiresMap: _requiresMap,
        
        hasFileLoadError: false,
        
        scriptsLoading: 0,
        
        classesLoading: [],
        
        syncModeEnabled: false,
        
        missingQueue: _missingQueue,
        init: function() {
            
            var scripts = document.getElementsByTagName('script'),
                src = scripts[scripts.length - 1].src,
                path = src.substring(0, src.lastIndexOf('/') + 1),
                meta = Ext._classPathMetadata,
                microloader = Ext.Microloader,
                manifest = Ext.manifest,
                loadOrder, baseUrl, loadlen, l, loadItem;
            if (src.indexOf("packages/sencha-core/src/") !== -1) {
                path = path + "../../";
            } else if (src.indexOf("/core/src/class/") !== -1) {
                path = path + "../../../";
            }
            if (!Manager.getPath("Ext")) {
                Manager.setPath('Ext', path + 'src');
            }
            
            if (meta) {
                Ext._classPathMetadata = null;
                Loader.addClassPathMappings(meta);
            }
            if (manifest) {
                loadOrder = manifest.loadOrder;
                
                
                
                baseUrl = Ext.Boot.baseUrl;
                if (loadOrder && manifest.bootRelative) {
                    for (loadlen = loadOrder.length , l = 0; l < loadlen; l++) {
                        loadItem = loadOrder[l];
                        loadItem.path = baseUrl + loadItem.path;
                    }
                }
            }
            if (microloader) {
                Ready.block();
                microloader.onMicroloaderReady(function() {
                    Ready.unblock();
                });
            }
        },
        
        setConfig: Ext.Function.flexSetter(function(name, value) {
            if (name === 'paths') {
                Loader.setPath(value);
            } else {
                _config[name] = value;
                var delegated = delegatedConfigs[name];
                if (delegated) {
                    Boot.setConfig((delegated === true) ? name : delegated, value);
                }
            }
            return Loader;
        }),
        
        getConfig: function(name) {
            return name ? _config[name] : _config;
        },
        
        setPath: function() {
            
            Manager.setPath.apply(Manager, arguments);
            return Loader;
        },
        
        addClassPathMappings: function(paths) {
            
            Manager.setPath(paths);
            return Loader;
        },
        
        addBaseUrlClassPathMappings: function(pathConfig) {
            for (var name in pathConfig) {
                pathConfig[name] = Boot.baseUrl + pathConfig[name];
            }
            Ext.Loader.addClassPathMappings(pathConfig);
        },
        
        getPath: function(className) {
            
            return Manager.getPath(className);
        },
        require: function(expressions, fn, scope, excludes) {
            if (excludes) {
                return Loader.exclude(excludes).require(expressions, fn, scope);
            }
            var classNames = Manager.getNamesByExpression(expressions);
            return Loader.load(classNames, fn, scope);
        },
        syncRequire: function() {
            var wasEnabled = Loader.syncModeEnabled;
            Loader.syncModeEnabled = true;
            var ret = Loader.require.apply(Loader, arguments);
            Loader.syncModeEnabled = wasEnabled;
            return ret;
        },
        exclude: function(excludes) {
            var selector = Manager.select({
                    require: function(classNames, fn, scope) {
                        return Loader.load(classNames, fn, scope);
                    },
                    syncRequire: function(classNames, fn, scope) {
                        var wasEnabled = Loader.syncModeEnabled;
                        Loader.syncModeEnabled = true;
                        var ret = Loader.load(classNames, fn, scope);
                        Loader.syncModeEnabled = wasEnabled;
                        return ret;
                    }
                });
            selector.exclude(excludes);
            return selector;
        },
        load: function(classNames, callback, scope) {
            if (callback) {
                if (callback.length) {
                    
                    
                    callback = Loader.makeLoadCallback(classNames, callback);
                }
                callback = callback.bind(scope || Ext.global);
            }
            var missingClassNames = [],
                numClasses = classNames.length,
                className, i, numMissing,
                urls = [],
                state = Manager.classState;
            for (i = 0; i < numClasses; ++i) {
                className = Manager.resolveName(classNames[i]);
                if (!Manager.isCreated(className)) {
                    missingClassNames.push(className);
                    _missingQueue[className] = Loader.getPath(className);
                    if (!state[className]) {
                        urls.push(_missingQueue[className]);
                    }
                }
            }
            
            
            numMissing = missingClassNames.length;
            if (numMissing) {
                Loader.missingCount += numMissing;
                Ext.Array.push(Loader.classesLoading, missingClassNames);
                Manager.onCreated(function() {
                    Ext.Array.remove(Loader.classesLoading, missingClassNames);
                    Ext.each(missingClassNames, function(name) {
                        Ext.Array.remove(Loader.classesLoading, name);
                    });
                    if (callback) {
                        Ext.callback(callback, scope, arguments);
                    }
                    Loader.checkReady();
                }, Loader, missingClassNames);
                if (!_config.enabled) {
                    Ext.Error.raise("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " + "Missing required class" + ((missingClassNames.length > 1) ? "es" : "") + ": " + missingClassNames.join(', '));
                }
                if (urls.length) {
                    Loader.loadScripts({
                        url: urls,
                        
                        _classNames: missingClassNames
                    });
                } else {
                    
                    
                    
                    Loader.checkReady();
                }
            } else {
                if (callback) {
                    callback.call(scope);
                }
                
                
                
                Loader.checkReady();
            }
            if (Loader.syncModeEnabled) {
                
                if (numClasses === 1) {
                    return Manager.get(classNames[0]);
                }
            }
            return Loader;
        },
        makeLoadCallback: function(classNames, callback) {
            return function() {
                var classes = [],
                    i = classNames.length;
                while (i-- > 0) {
                    classes[i] = Manager.get(classNames[i]);
                }
                return callback.apply(this, classes);
            };
        },
        onLoadFailure: function() {
            var options = this,
                onError = options.onError;
            Loader.hasFileLoadError = true;
            --Loader.scriptsLoading;
            if (onError) {
                
                onError.call(options.userScope, options);
            } else {
                Ext.log.error("[Ext.Loader] Some requested files failed to load.");
            }
            Loader.checkReady();
        },
        onLoadSuccess: function() {
            var options = this,
                onLoad = options.onLoad;
            --Loader.scriptsLoading;
            if (onLoad) {
                
                onLoad.call(options.userScope, options);
            }
            
            Loader.checkReady();
        },
        
        reportMissingClasses: function() {
            if (!Loader.syncModeEnabled && !Loader.scriptsLoading && Loader.isLoading && !Loader.hasFileLoadError) {
                var missingClasses = [],
                    missingPaths = [];
                for (var missingClassName in _missingQueue) {
                    missingClasses.push(missingClassName);
                    missingPaths.push(_missingQueue[missingClassName]);
                }
                if (missingClasses.length) {
                    throw new Error("The following classes are not declared even if their files have been " + "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " + "corresponding files for possible typos: '" + missingPaths.join("', '"));
                }
            }
        },
        
        onReady: function(fn, scope, withDomReady, options) {
            if (withDomReady) {
                Ready.on(fn, scope, options);
            } else {
                var listener = Ready.makeListener(fn, scope, options);
                if (Loader.isLoading) {
                    readyListeners.push(listener);
                } else {
                    Ready.invoke(listener);
                }
            }
        },
        
        addUsedClasses: function(classes) {
            var cls, i, ln;
            if (classes) {
                classes = (typeof classes === 'string') ? [
                    classes
                ] : classes;
                for (i = 0 , ln = classes.length; i < ln; i++) {
                    cls = classes[i];
                    if (typeof cls === 'string' && !Ext.Array.contains(usedClasses, cls)) {
                        usedClasses.push(cls);
                    }
                }
            }
            return Loader;
        },
        
        triggerReady: function() {
            var listener,
                refClasses = usedClasses;
            if (Loader.isLoading && refClasses.length) {
                
                usedClasses = [];
                
                
                Loader.require(refClasses);
            } else {
                
                
                Loader.isLoading = false;
                
                
                readyListeners.sort(Ready.sortFn);
                
                
                
                while (readyListeners.length && !Loader.isLoading) {
                    
                    
                    listener = readyListeners.pop();
                    Ready.invoke(listener);
                }
                
                
                
                
                
                
                
                
                Ready.unblock();
            }
        },
        
        historyPush: function(className) {
            if (className && !isInHistory[className] && !Manager.overrideMap[className]) {
                isInHistory[className] = true;
                history.push(className);
            }
            return Loader;
        },
        
        loadScripts: function(params) {
            var manifest = Ext.manifest,
                loadOrder = manifest && manifest.loadOrder,
                loadOrderMap = manifest && manifest.loadOrderMap,
                options;
            ++Loader.scriptsLoading;
            
            
            if (loadOrder && !loadOrderMap) {
                manifest.loadOrderMap = loadOrderMap = Boot.createLoadOrderMap(loadOrder);
            }
            
            
            Loader.checkReady();
            options = Ext.apply({
                loadOrder: loadOrder,
                loadOrderMap: loadOrderMap,
                charset: _config.scriptCharset,
                success: Loader.onLoadSuccess,
                failure: Loader.onLoadFailure,
                sync: Loader.syncModeEnabled,
                _classNames: []
            }, params);
            options.userScope = options.scope;
            options.scope = options;
            Boot.load(options);
        },
        
        loadScriptsSync: function(urls) {
            var syncwas = Loader.syncModeEnabled;
            Loader.syncModeEnabled = true;
            Loader.loadScripts({
                url: urls
            });
            Loader.syncModeEnabled = syncwas;
        },
        
        loadScriptsSyncBasePrefix: function(urls) {
            var syncwas = Loader.syncModeEnabled;
            Loader.syncModeEnabled = true;
            Loader.loadScripts({
                url: urls,
                prependBaseUrl: true
            });
            Loader.syncModeEnabled = syncwas;
        },
        
        loadScript: function(options) {
            var isString = typeof options === 'string',
                isArray = options instanceof Array,
                isObject = !isArray && !isString,
                url = isObject ? options.url : options,
                onError = isObject && options.onError,
                onLoad = isObject && options.onLoad,
                scope = isObject && options.scope,
                request = {
                    url: url,
                    scope: scope,
                    onLoad: onLoad,
                    onError: onError,
                    _classNames: []
                };
            Loader.loadScripts(request);
        },
        
        flushMissingQueue: function() {
            var name, val,
                missingwas = 0,
                missing = 0;
            for (name in _missingQueue) {
                missingwas++;
                val = _missingQueue[name];
                if (Manager.isCreated(name)) {
                    delete _missingQueue[name];
                } else if (Manager.existCache[name] === 2) {
                    delete _missingQueue[name];
                } else {
                    ++missing;
                }
            }
            this.missingCount = missing;
        },
        
        checkReady: function() {
            var wasLoading = Loader.isLoading,
                isLoading;
            Loader.flushMissingQueue();
            isLoading = Loader.missingCount + Loader.scriptsLoading;
            if (isLoading && !wasLoading) {
                Ready.block();
                Loader.isLoading = !!isLoading;
            } else if (!isLoading && wasLoading) {
                Loader.triggerReady();
            }
        }
    });
    
    Ext.require = alias(Loader, 'require');
    
    Ext.syncRequire = alias(Loader, 'syncRequire');
    
    Ext.exclude = alias(Loader, 'exclude');
    
    Class.registerPreprocessor('loader', function(cls, data, hooks, continueFn) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.Loader#loaderPreprocessor', arguments);
        var me = this,
            dependencies = [],
            dependency,
            className = Manager.getName(cls),
            i, j, ln, subLn, value, propertyName, propertyValue, requiredMap;
        
        for (i = 0 , ln = dependencyProperties.length; i < ln; i++) {
            propertyName = dependencyProperties[i];
            if (data.hasOwnProperty(propertyName)) {
                propertyValue = data[propertyName];
                if (typeof propertyValue == 'string') {
                    dependencies.push(propertyValue);
                } else if (propertyValue instanceof Array) {
                    for (j = 0 , subLn = propertyValue.length; j < subLn; j++) {
                        value = propertyValue[j];
                        if (typeof value == 'string') {
                            dependencies.push(value);
                        }
                    }
                } else if (typeof propertyValue != 'function') {
                    for (j in propertyValue) {
                        if (propertyValue.hasOwnProperty(j)) {
                            value = propertyValue[j];
                            if (typeof value == 'string') {
                                dependencies.push(value);
                            }
                        }
                    }
                }
            }
        }
        if (dependencies.length === 0) {
            return;
        }
        if (className) {
            _requiresMap[className] = dependencies;
        }
        var deadlockPath = [],
            detectDeadlock;
        
        if (className) {
            requiredMap = Loader.requiredByMap || (Loader.requiredByMap = {});
            for (i = 0 , ln = dependencies.length; i < ln; i++) {
                dependency = dependencies[i];
                (requiredMap[dependency] || (requiredMap[dependency] = [])).push(className);
            }
            detectDeadlock = function(cls) {
                deadlockPath.push(cls);
                if (_requiresMap[cls]) {
                    if (Ext.Array.contains(_requiresMap[cls], className)) {
                        Ext.Error.raise("Circular requirement detected! '" + className + "' and '" + deadlockPath[1] + "' mutually require each other. Path: " + deadlockPath.join(' -> ') + " -> " + deadlockPath[0]);
                    }
                    for (i = 0 , ln = _requiresMap[cls].length; i < ln; i++) {
                        detectDeadlock(_requiresMap[cls][i]);
                    }
                }
            };
            detectDeadlock(className);
        }
        (className ? Loader.exclude(className) : Loader).require(dependencies, function() {
            for (i = 0 , ln = dependencyProperties.length; i < ln; i++) {
                propertyName = dependencyProperties[i];
                if (data.hasOwnProperty(propertyName)) {
                    propertyValue = data[propertyName];
                    if (typeof propertyValue == 'string') {
                        data[propertyName] = Manager.get(propertyValue);
                    } else if (propertyValue instanceof Array) {
                        for (j = 0 , subLn = propertyValue.length; j < subLn; j++) {
                            value = propertyValue[j];
                            if (typeof value == 'string') {
                                data[propertyName][j] = Manager.get(value);
                            }
                        }
                    } else if (typeof propertyValue != 'function') {
                        for (var k in propertyValue) {
                            if (propertyValue.hasOwnProperty(k)) {
                                value = propertyValue[k];
                                if (typeof value == 'string') {
                                    data[propertyName][k] = Manager.get(value);
                                }
                            }
                        }
                    }
                }
            }
            continueFn.call(me, cls, data, hooks);
        });
        return false;
    }, true, 'after', 'className');
    
    Manager.registerPostprocessor('uses', function(name, cls, data) {
        Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.Loader#usesPostprocessor', arguments);
        var manifest = Ext.manifest,
            loadOrder = manifest && manifest.loadOrder,
            classes = manifest && manifest.classes,
            uses, clazz, item, len, i, indexMap;
        if (loadOrder) {
            clazz = classes[name];
            if (clazz && !isNaN(i = clazz.idx)) {
                item = loadOrder[i];
                uses = item.uses;
                indexMap = {};
                for (len = uses.length , i = 0; i < len; i++) {
                    indexMap[uses[i]] = true;
                }
                uses = Ext.Boot.getPathsFromIndexes(indexMap, loadOrder, true);
                if (uses.length > 0) {
                    Loader.loadScripts({
                        url: uses,
                        sequential: true
                    });
                }
            }
        }
        if (data.uses) {
            uses = data.uses;
            Loader.addUsedClasses(uses);
        }
    });
    Manager.onCreated(Loader.historyPush);
    Loader.init();
}();

Ext._endTime = new Date().getTime();



if (Ext._beforereadyhandler) {
    Ext._beforereadyhandler();
}


Ext.define('Ext.Mixin', function(Mixin) {
    return {
        statics: {
            addHook: function(hookFn, targetClass, methodName, mixinClassPrototype) {
                var isFunc = Ext.isFunction(hookFn),
                    hook = function() {
                        var a = arguments,
                            fn = isFunc ? hookFn : mixinClassPrototype[hookFn],
                            result = this.callParent(a);
                        fn.apply(this, a);
                        return result;
                    },
                    existingFn = targetClass.hasOwnProperty(methodName) && targetClass[methodName];
                if (isFunc) {
                    hookFn.$previous = Ext.emptyFn;
                }
                
                hook.$name = methodName;
                hook.$owner = targetClass.self;
                if (existingFn) {
                    hook.$previous = existingFn.$previous;
                    existingFn.$previous = hook;
                } else {
                    targetClass[methodName] = hook;
                }
            }
        },
        onClassExtended: function(cls, data) {
            var mixinConfig = data.mixinConfig,
                hooks = data.xhooks,
                superclass = cls.superclass,
                onClassMixedIn = data.onClassMixedIn,
                parentMixinConfig, befores, afters, extended;
            if (hooks) {
                
                delete data.xhooks;
                (mixinConfig || (data.mixinConfig = mixinConfig = {})).on = hooks;
            }
            if (mixinConfig) {
                parentMixinConfig = superclass.mixinConfig;
                if (parentMixinConfig) {
                    data.mixinConfig = mixinConfig = Ext.merge({}, parentMixinConfig, mixinConfig);
                }
                data.mixinId = mixinConfig.id;
                if (mixinConfig.beforeHooks) {
                    Ext.Error.raise('Use of "beforeHooks" is deprecated - use "before" instead');
                }
                if (mixinConfig.hooks) {
                    Ext.Error.raise('Use of "hooks" is deprecated - use "after" instead');
                }
                if (mixinConfig.afterHooks) {
                    Ext.Error.raise('Use of "afterHooks" is deprecated - use "after" instead');
                }
                befores = mixinConfig.before;
                afters = mixinConfig.after;
                hooks = mixinConfig.on;
                extended = mixinConfig.extended;
            }
            if (befores || afters || hooks || extended) {
                
                data.onClassMixedIn = function(targetClass) {
                    var mixin = this.prototype,
                        targetProto = targetClass.prototype,
                        key;
                    if (befores) {
                        Ext.Object.each(befores, function(key, value) {
                            targetClass.addMember(key, function() {
                                if (mixin[value].apply(this, arguments) !== false) {
                                    return this.callParent(arguments);
                                }
                            });
                        });
                    }
                    if (afters) {
                        Ext.Object.each(afters, function(key, value) {
                            targetClass.addMember(key, function() {
                                var ret = this.callParent(arguments);
                                mixin[value].apply(this, arguments);
                                return ret;
                            });
                        });
                    }
                    if (hooks) {
                        for (key in hooks) {
                            Mixin.addHook(hooks[key], targetProto, key, mixin);
                        }
                    }
                    if (extended) {
                        targetClass.onExtended(function() {
                            var args = Ext.Array.slice(arguments, 0);
                            args.unshift(targetClass);
                            return extended.apply(this, args);
                        }, this);
                    }
                    if (onClassMixedIn) {
                        onClassMixedIn.apply(this, arguments);
                    }
                };
            }
        }
    };
});



Ext.util = Ext.util || {};
Ext.util.DelayedTask = function(fn, scope, args, cancelOnDelay, fireIdleEvent) {
    
    
    var me = this,
        delay,
        call = function() {
            var globalEvents = Ext.GlobalEvents;
            clearInterval(me.id);
            me.id = null;
            fn.apply(scope, args || []);
            if (fireIdleEvent !== false && globalEvents.hasListeners.idle) {
                globalEvents.fireEvent('idle');
            }
        };
    cancelOnDelay = typeof cancelOnDelay === 'boolean' ? cancelOnDelay : true;
    
    me.id = null;
    
    me.delay = function(newDelay, newFn, newScope, newArgs) {
        if (cancelOnDelay) {
            me.cancel();
        }
        if (typeof newDelay === 'number') {
            delay = newDelay;
        }
        fn = newFn || fn;
        scope = newScope || scope;
        args = newArgs || args;
        if (!me.id) {
            me.id = Ext.interval(call, delay);
        }
    };
    
    me.cancel = function() {
        if (me.id) {
            clearInterval(me.id);
            me.id = null;
        }
    };
};



Ext.define('Ext.util.Event', function() {
    var arraySlice = Array.prototype.slice,
        arrayInsert = Ext.Array.insert,
        toArray = Ext.Array.toArray,
        fireArgs = {};
    return {
        requires: 'Ext.util.DelayedTask',
        
        isEvent: true,
        
        suspended: 0,
        noOptions: {},
        constructor: function(observable, name) {
            this.name = name;
            this.observable = observable;
            this.listeners = [];
        },
        addListener: function(fn, scope, options, caller, manager) {
            var me = this,
                added = false,
                observable = me.observable,
                eventName = me.name,
                listeners, listener, priority, isNegativePriority, highestNegativePriorityIndex, hasNegativePriorityIndex, length, index, i, listenerPriority;
            if (scope && !Ext._namedScopes[scope] && (typeof fn === 'string') && (typeof scope[fn] !== 'function')) {
                Ext.Error.raise("No method named '" + fn + "' found on scope object");
            }
            if (!me.isListening(fn, scope)) {
                listener = me.createListener(fn, scope, options, caller, manager);
                if (me.firing) {
                    
                    me.listeners = me.listeners.slice(0);
                }
                listeners = me.listeners;
                index = length = listeners.length;
                priority = options && options.priority;
                highestNegativePriorityIndex = me._highestNegativePriorityIndex;
                hasNegativePriorityIndex = (highestNegativePriorityIndex !== undefined);
                if (priority) {
                    
                    
                    isNegativePriority = (priority < 0);
                    if (!isNegativePriority || hasNegativePriorityIndex) {
                        
                        
                        
                        
                        
                        
                        for (i = (isNegativePriority ? highestNegativePriorityIndex : 0); i < length; i++) {
                            
                            listenerPriority = listeners[i].o ? listeners[i].o.priority || 0 : 0;
                            if (listenerPriority < priority) {
                                index = i;
                                break;
                            }
                        }
                    } else {
                        
                        
                        
                        me._highestNegativePriorityIndex = index;
                    }
                } else if (hasNegativePriorityIndex) {
                    
                    
                    
                    
                    index = highestNegativePriorityIndex;
                }
                if (!isNegativePriority && index <= highestNegativePriorityIndex) {
                    me._highestNegativePriorityIndex++;
                }
                if (index === length) {
                    me.listeners[length] = listener;
                } else {
                    arrayInsert(me.listeners, index, [
                        listener
                    ]);
                }
                if (observable.isElement) {
                    
                    
                    
                    
                    
                    
                    observable._getPublisher(eventName).subscribe(observable, eventName, options.delegated !== false, options.capture);
                }
                added = true;
            }
            return added;
        },
        createListener: function(fn, scope, o, caller, manager) {
            var me = this,
                namedScope = (Ext._namedScopes[scope]),
                listener = {
                    fn: fn,
                    scope: scope,
                    ev: me,
                    caller: caller,
                    manager: manager,
                    namedScope: namedScope,
                    defaultScope: namedScope ? (scope || me.observable) : undefined,
                    lateBound: typeof fn === 'string'
                },
                handler = fn,
                wrapped = false,
                type;
            
            
            if (o) {
                listener.o = o;
                if (o.single) {
                    handler = me.createSingle(handler, listener, o, scope);
                    wrapped = true;
                }
                if (o.target) {
                    handler = me.createTargeted(handler, listener, o, scope, wrapped);
                    wrapped = true;
                }
                if (o.delay) {
                    handler = me.createDelayed(handler, listener, o, scope, wrapped);
                    wrapped = true;
                }
                if (o.buffer) {
                    handler = me.createBuffered(handler, listener, o, scope, wrapped);
                    wrapped = true;
                }
                if (me.observable.isElement) {
                    
                    
                    
                    type = o.type;
                    if (type) {
                        listener.type = type;
                    }
                }
            }
            listener.fireFn = handler;
            listener.wrapped = wrapped;
            return listener;
        },
        findListener: function(fn, scope) {
            var listeners = this.listeners,
                i = listeners.length,
                listener;
            while (i--) {
                listener = listeners[i];
                if (listener) {
                    
                    if (listener.fn === fn && listener.scope == scope) {
                        return i;
                    }
                }
            }
            return -1;
        },
        isListening: function(fn, scope) {
            return this.findListener(fn, scope) !== -1;
        },
        removeListener: function(fn, scope, index) {
            var me = this,
                removed = false,
                observable = me.observable,
                eventName = me.name,
                listener, highestNegativePriorityIndex, options, k, manager, managedListeners, managedListener, i;
            index = index || me.findListener(fn, scope);
            if (index != -1) {
                listener = me.listeners[index];
                options = listener.o;
                highestNegativePriorityIndex = me._highestNegativePriorityIndex;
                if (me.firing) {
                    me.listeners = me.listeners.slice(0);
                }
                
                if (listener.task) {
                    listener.task.cancel();
                    delete listener.task;
                }
                
                k = listener.tasks && listener.tasks.length;
                if (k) {
                    while (k--) {
                        listener.tasks[k].cancel();
                    }
                    delete listener.tasks;
                }
                
                
                
                me.listeners.splice(index, 1);
                manager = listener.manager;
                if (manager) {
                    
                    
                    
                    
                    
                    
                    
                    
                    managedListeners = manager.managedListeners;
                    if (managedListeners) {
                        for (i = managedListeners.length; i--; ) {
                            managedListener = managedListeners[i];
                            if (managedListener.item === me.observable && managedListener.ename === eventName && managedListener.fn === fn && managedListener.scope === scope) {
                                managedListeners.splice(i, 1);
                            }
                        }
                    }
                }
                
                
                if (highestNegativePriorityIndex) {
                    if (index < highestNegativePriorityIndex) {
                        me._highestNegativePriorityIndex--;
                    } else if (index === highestNegativePriorityIndex && index === me.listeners.length) {
                        delete me._highestNegativePriorityIndex;
                    }
                }
                if (observable.isElement) {
                    observable._getPublisher(eventName).unsubscribe(observable, eventName, options.delegated !== false, options.capture);
                }
                removed = true;
            }
            return removed;
        },
        
        clearListeners: function() {
            var listeners = this.listeners,
                i = listeners.length,
                listener;
            while (i--) {
                listener = listeners[i];
                this.removeListener(listener.fn, listener.scope);
            }
        },
        suspend: function() {
            ++this.suspended;
        },
        resume: function() {
            if (this.suspended) {
                --this.suspended;
            }
        },
        isSuspended: function() {
            return this.suspended > 0;
        },
        fire: function() {
            var me = this,
                listeners = me.listeners,
                count = listeners.length,
                isElement = me.observable.isElement,
                options, delegate, fireInfo, i, args, listener, len, delegateEl, currentTarget, type, chained, firingArgs, e;
            if (!me.suspended && count > 0) {
                me.firing = true;
                args = arguments.length ? arraySlice.call(arguments, 0) : [];
                len = args.length;
                if (isElement) {
                    e = args[0];
                }
                for (i = 0; i < count; i++) {
                    listener = listeners[i];
                    options = listener.o;
                    if (isElement) {
                        if (currentTarget) {
                            
                            
                            e.setCurrentTarget(currentTarget);
                        }
                        
                        
                        
                        
                        type = listener.type;
                        if (type) {
                            
                            
                            
                            
                            chained = e;
                            e = args[0] = chained.chain({
                                type: type
                            });
                        }
                        
                        
                        Ext.EventObject = e;
                    }
                    firingArgs = args;
                    if (options) {
                        if (isElement) {
                            delegate = options.delegate;
                            if (delegate) {
                                
                                
                                delegateEl = e.getTarget('#' + e.currentTarget.id + ' ' + delegate);
                                if (delegateEl) {
                                    args[1] = delegateEl;
                                    
                                    
                                    currentTarget = e.currentTarget;
                                    e.setCurrentTarget(delegateEl);
                                } else {
                                    
                                    continue;
                                }
                            }
                        }
                        args[len] = options;
                        if (options.args) {
                            firingArgs = options.args.concat(args);
                        }
                    }
                    fireInfo = me.getFireInfo(listener);
                    if (fireInfo.fn.apply(fireInfo.scope, firingArgs) === false) {
                        return (me.firing = false);
                    }
                    if (chained) {
                        
                        
                        
                        e = args[0] = chained;
                        chained = null;
                    }
                }
            }
            me.firing = false;
            return true;
        },
        getFireInfo: function(listener, fromWrapped) {
            var observable = this.observable,
                fireFn = listener.fireFn,
                scope = listener.scope,
                namedScope = listener.namedScope,
                fn;
            
            
            if (!fromWrapped && listener.wrapped) {
                fireArgs.fn = fireFn;
                return fireArgs;
            }
            fn = fromWrapped ? listener.fn : fireFn;
            var name = fn;
            if (listener.lateBound) {
                
                if (!scope || namedScope) {
                    
                    
                    
                    
                    scope = (listener.caller || observable).resolveListenerScope(listener.defaultScope);
                }
                if (!scope) {
                    Ext.Error.raise('Unable to dynamically resolve scope for "' + listener.ev.name + '" listener on ' + this.observable.id);
                }
                if (!Ext.isFunction(scope[fn])) {
                    Ext.Error.raise('No method named "' + fn + '" on ' + (scope.$className || 'scope object.'));
                }
                fn = scope[fn];
            } else if (namedScope && namedScope.isController) {
                
                
                scope = (listener.caller || observable).resolveListenerScope(listener.defaultScope);
                if (!scope) {
                    Ext.Error.raise('Unable to dynamically resolve scope for "' + listener.ev.name + '" listener on ' + this.observable.id);
                }
            } else if (!scope || namedScope) {
                
                
                scope = observable;
            }
            
            
            
            fireArgs.fn = fn;
            fireArgs.scope = scope;
            if (!fn) {
                Ext.Error.raise('Unable to dynamically resolve method "' + name + '" on ' + this.observable.$className);
            }
            return fireArgs;
        },
        createTargeted: function(handler, listener, o, scope, wrapped) {
            return function() {
                if (o.target === arguments[0]) {
                    var fireInfo;
                    if (!wrapped) {
                        fireInfo = listener.ev.getFireInfo(listener, true);
                        handler = fireInfo.fn;
                        scope = fireInfo.scope;
                    }
                    return handler.apply(scope, arguments);
                }
            };
        },
        createBuffered: function(handler, listener, o, scope, wrapped) {
            listener.task = new Ext.util.DelayedTask();
            return function() {
                var fireInfo;
                if (!wrapped) {
                    fireInfo = listener.ev.getFireInfo(listener, true);
                    handler = fireInfo.fn;
                    scope = fireInfo.scope;
                }
                listener.task.delay(o.buffer, handler, scope, toArray(arguments));
            };
        },
        createDelayed: function(handler, listener, o, scope, wrapped) {
            return function() {
                var task = new Ext.util.DelayedTask(),
                    fireInfo;
                if (!wrapped) {
                    fireInfo = listener.ev.getFireInfo(listener, true);
                    handler = fireInfo.fn;
                    scope = fireInfo.scope;
                }
                if (!listener.tasks) {
                    listener.tasks = [];
                }
                listener.tasks.push(task);
                task.delay(o.delay || 10, handler, scope, toArray(arguments));
            };
        },
        createSingle: function(handler, listener, o, scope, wrapped) {
            return function() {
                var event = listener.ev,
                    fireInfo;
                if (event.removeListener(listener.fn, scope) && event.observable) {
                    
                    
                    event.observable.hasListeners[event.name]--;
                }
                if (!wrapped) {
                    fireInfo = event.getFireInfo(listener, true);
                    handler = fireInfo.fn;
                    scope = fireInfo.scope;
                }
                return handler.apply(scope, arguments);
            };
        }
    };
});

//@tag dom,core


Ext.define('Ext.mixin.Identifiable', {
    statics: {
        uniqueIds: {}
    },
    isIdentifiable: true,
    mixinId: 'identifiable',
    idCleanRegex: /\.|[^\w\-]/g,
    defaultIdPrefix: 'ext-',
    defaultIdSeparator: '-',
    getOptimizedId: function() {
        return this.id;
    },
    getUniqueId: function() {
        var id = this.id,
            prototype, separator, xtype, uniqueIds, prefix;
        
        if (!(id || id === 0)) {
            prototype = this.self.prototype;
            separator = this.defaultIdSeparator;
            uniqueIds = Ext.mixin.Identifiable.uniqueIds;
            if (!prototype.hasOwnProperty('identifiablePrefix')) {
                xtype = this.xtype;
                if (xtype) {
                    prefix = this.defaultIdPrefix + xtype.replace(this.idCleanRegex, separator) + separator;
                } else if (!(prefix = prototype.$className)) {
                    prefix = this.defaultIdPrefix + 'anonymous' + separator;
                } else {
                    prefix = prefix.replace(this.idCleanRegex, separator).toLowerCase() + separator;
                }
                prototype.identifiablePrefix = prefix;
            }
            prefix = this.identifiablePrefix;
            if (!uniqueIds.hasOwnProperty(prefix)) {
                uniqueIds[prefix] = 0;
            }
            id = this.id = prefix + (++uniqueIds[prefix]);
        }
        this.getUniqueId = this.getOptimizedId;
        return id;
    },
    setId: function(id) {
        this.id = id;
    },
    
    getId: function() {
        var id = this.id;
        if (!id) {
            id = this.getUniqueId();
        }
        this.getId = this.getOptimizedId;
        return id;
    }
});



Ext.define('Ext.mixin.Observable', function(Observable) {
    var emptyFn = Ext.emptyFn,
        emptyArray = [],
        arrayProto = Array.prototype,
        arraySlice = arrayProto.slice,
        
        ListenerRemover = function(observable) {
            
            if (observable instanceof ListenerRemover) {
                return observable;
            }
            this.observable = observable;
            
            
            if (arguments[1].isObservable) {
                this.managedListeners = true;
            }
            this.args = arraySlice.call(arguments, 1);
        };
    ListenerRemover.prototype.destroy = function() {
        this.destroy = Ext.emptyFn;
        var observable = this.observable;
        observable[this.managedListeners ? 'mun' : 'un'].apply(observable, this.args);
    };
    return {
        extend: 'Ext.Mixin',
        mixinConfig: {
            id: 'observable',
            after: {
                destroy: 'clearListeners'
            }
        },
        requires: [
            'Ext.util.Event'
        ],
        mixins: [
            'Ext.mixin.Identifiable'
        ],
        statics: {
            
            releaseCapture: function(o) {
                o.fireEventArgs = this.prototype.fireEventArgs;
            },
            
            capture: function(o, fn, scope) {
                
                
                
                
                var newFn = function(eventName, args) {
                        return fn.apply(scope, [
                            eventName
                        ].concat(args));
                    };
                this.captureArgs(o, newFn, scope);
            },
            
            captureArgs: function(o, fn, scope) {
                o.fireEventArgs = Ext.Function.createInterceptor(o.fireEventArgs, fn, scope);
            },
            
            observe: function(cls, listeners) {
                if (cls) {
                    if (!cls.isObservable) {
                        Ext.applyIf(cls, new this());
                        this.captureArgs(cls.prototype, cls.fireEventArgs, cls);
                    }
                    if (Ext.isObject(listeners)) {
                        cls.on(listeners);
                    }
                }
                return cls;
            },
            
            prepareClass: function(T, mixin, data) {
                
                
                
                
                var listeners = T.listeners = [],
                    
                    
                    
                    
                    target = data || T.prototype,
                    targetListeners = target.listeners,
                    superListeners = mixin ? mixin.listeners : T.superclass.self.listeners,
                    name, scope, namedScope;
                
                
                
                
                if (superListeners) {
                    listeners.push(superListeners);
                }
                if (targetListeners) {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    scope = targetListeners.scope;
                    if (!scope) {
                        targetListeners.scope = 'self';
                    } else {
                        namedScope = Ext._namedScopes[scope];
                        if (namedScope && namedScope.isController) {
                            targetListeners.scope = 'self.controller';
                        }
                    }
                    listeners.push(targetListeners);
                    
                    
                    
                    
                    
                    target.listeners = null;
                }
                if (!T.HasListeners) {
                    
                    
                    
                    var HasListeners = function() {},
                        SuperHL = T.superclass.HasListeners || (mixin && mixin.HasListeners) || Observable.HasListeners;
                    
                    T.prototype.HasListeners = T.HasListeners = HasListeners;
                    
                    
                    HasListeners.prototype = T.hasListeners = new SuperHL();
                }
            }
        },
        
        
        
        isObservable: true,
        
        eventsSuspended: 0,
        
        constructor: function(config) {
            var me = this,
                self = me.self,
                declaredListeners, listeners, bubbleEvents;
            me.hasListeners = new me.HasListeners();
            me.events = me.events || {};
            declaredListeners = self.listeners;
            if (declaredListeners && !me._addDeclaredListeners(declaredListeners)) {
                
                
                
                self.listeners = null;
            }
            listeners = (config && config.listeners) || me.listeners;
            if (listeners) {
                if (listeners instanceof Array) {
                    
                    
                    
                    
                    
                    
                    Ext.each(listeners, me.on, me);
                } else {
                    me.on(listeners);
                }
            }
            bubbleEvents = (config && config.bubbleEvents) || me.bubbleEvents;
            if (bubbleEvents) {
                me.enableBubble(bubbleEvents);
            }
            if (me.$applyConfigs) {
                
                Ext.apply(me, config);
            } else {
                
                me.initConfig(config);
            }
            if (listeners) {
                
                
                
                me.listeners = null;
            }
        },
        onClassExtended: function(T, data) {
            if (!T.HasListeners) {
                
                
                Observable.prepareClass(T, T.prototype.$observableMixedIn ? undefined : data);
            }
        },
        
        
        $eventOptions: {
            scope: 1,
            delay: 1,
            buffer: 1,
            onFrame: 1,
            single: 1,
            args: 1,
            destroyable: 1,
            priority: 1,
            order: 1
        },
        $orderToPriority: {
            before: 100,
            current: 0,
            after: -100
        },
        
        _addDeclaredListeners: function(listeners) {
            var me = this;
            if (listeners instanceof Array) {
                Ext.each(listeners, me._addDeclaredListeners, me);
            } else {
                me._addedDeclaredListeners = true;
                me.addListener(listeners);
            }
            return me._addedDeclaredListeners;
        },
        
        addManagedListener: function(item, ename, fn, scope, options, 
        noDestroy) {
            var me = this,
                managedListeners = me.managedListeners = me.managedListeners || [],
                config, passedOptions;
            if (typeof ename !== 'string') {
                
                
                
                
                passedOptions = arguments.length > 4 ? options : ename;
                options = ename;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!item.$eventOptions[ename]) {
                            
                            
                            me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope || scope, config.fn ? config : passedOptions, true);
                        }
                    }
                }
                if (options && options.destroyable) {
                    return new ListenerRemover(me,item,options);
                }
            } else {
                if (fn !== emptyFn) {
                    item.doAddListener(ename, fn, scope, options, null, me, me);
                    
                    if (!noDestroy && options && options.destroyable) {
                        return new ListenerRemover(me,item,ename,fn,scope);
                    }
                }
            }
        },
        
        removeManagedListener: function(item, ename, fn, scope) {
            var me = this,
                options, config, managedListeners, length, i;
            if (typeof ename !== 'string') {
                options = ename;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!item.$eventOptions[ename]) {
                            me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope || scope);
                        }
                    }
                }
            } else {
                managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
                ename = Ext.canonicalEventName(ename);
                for (i = 0 , length = managedListeners.length; i < length; i++) {
                    me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
                }
            }
        },
        
        fireEvent: function(eventName) {
            return this.fireEventArgs(eventName, arraySlice.call(arguments, 1));
        },
        
        resolveListenerScope: function(defaultScope) {
            var namedScope = Ext._namedScopes[defaultScope];
            if (namedScope) {
                if (namedScope.isController) {
                    Ext.Error.raise('scope: "controller" can only be specified on classes that derive from Ext.Component or Ext.Widget');
                }
                if (namedScope.isSelf || namedScope.isThis) {
                    defaultScope = null;
                }
            }
            return defaultScope || this;
        },
        
        fireEventArgs: function(eventName, args) {
            eventName = Ext.canonicalEventName(eventName);
            var me = this,
                
                events = me.events,
                event = events && events[eventName],
                ret = true;
            
            
            if (me.hasListeners[eventName]) {
                ret = me.doFireEvent(eventName, args || emptyArray, event ? event.bubble : false);
            }
            return ret;
        },
        
        fireAction: function(eventName, args, fn, scope, options, order) {
            
            options = options ? Ext.Object.chain(options) : {};
            options.single = true;
            options.priority = ((order === 'after') ? -99.5 : 99.5);
            this.doAddListener(eventName, fn, scope, options);
            this.fireEventArgs(eventName, args);
        },
        
        doFireEvent: function(eventName, args, bubbles) {
            var target = this,
                queue, event,
                ret = true;
            do {
                if (target.eventsSuspended) {
                    if ((queue = target.eventQueue)) {
                        queue.push([
                            eventName,
                            args
                        ]);
                    }
                    return ret;
                } else {
                    event = target.events && target.events[eventName];
                    
                    
                    if (event && event !== true) {
                        if ((ret = event.fire.apply(event, args)) === false) {
                            break;
                        }
                    }
                }
            } while (bubbles && (target = target.getBubbleParent()));
            return ret;
        },
        
        getBubbleParent: function() {
            var me = this,
                parent = me.getBubbleTarget && me.getBubbleTarget();
            if (parent && parent.isObservable) {
                return parent;
            }
            return null;
        },
        
        addListener: function(ename, fn, scope, options, order, 
        caller) {
            var me = this,
                namedScopes = Ext._namedScopes,
                config, namedScope, isClassListener, innerScope, eventOptions;
            
            if (typeof ename !== 'string') {
                options = ename;
                scope = options.scope;
                namedScope = scope && namedScopes[scope];
                isClassListener = namedScope && namedScope.isSelf;
                
                
                eventOptions = ((me.isComponent || me.isWidget) && options.element) ? me.$elementEventOptions : me.$eventOptions;
                for (ename in options) {
                    config = options[ename];
                    if (!eventOptions[ename]) {
                        
                        innerScope = config.scope;
                        
                        
                        
                        
                        
                        if (innerScope && isClassListener) {
                            namedScope = namedScopes[innerScope];
                            if (namedScope && namedScope.isController) {
                                innerScope = 'self.controller';
                            }
                        }
                        me.doAddListener(ename, config.fn || config, innerScope || scope, config.fn ? config : options, order, caller);
                    }
                }
                if (options && options.destroyable) {
                    return new ListenerRemover(me,options);
                }
            } else {
                me.doAddListener(ename, fn, scope, options, order, caller);
                if (options && options.destroyable) {
                    return new ListenerRemover(me,ename,fn,scope,options);
                }
            }
            return me;
        },
        
        removeListener: function(ename, fn, scope, 
        eventOptions) {
            var me = this,
                config, options;
            if (typeof ename !== 'string') {
                options = ename;
                
                
                eventOptions = eventOptions || me.$eventOptions;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!me.$eventOptions[ename]) {
                            me.doRemoveListener(ename, config.fn || config, config.scope || options.scope);
                        }
                    }
                }
            } else {
                me.doRemoveListener(ename, fn, scope);
            }
            return me;
        },
        
        clearListeners: function() {
            var me = this,
                events = me.events,
                hasListeners = me.hasListeners,
                event, key;
            if (events) {
                for (key in events) {
                    if (events.hasOwnProperty(key)) {
                        event = events[key];
                        if (event.isEvent) {
                            delete hasListeners[key];
                            event.clearListeners();
                        }
                    }
                }
            }
            me.clearManagedListeners();
        },
        purgeListeners: function() {
            if (Ext.global.console) {
                Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
            }
            return this.clearListeners.apply(this, arguments);
        },
        
        clearManagedListeners: function() {
            var me = this,
                managedListeners = me.managedListeners ? me.managedListeners.slice() : [],
                i = 0,
                len = managedListeners.length;
            for (; i < len; i++) {
                me.removeManagedListenerItem(true, managedListeners[i]);
            }
            me.managedListeners = [];
        },
        
        removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope) {
            if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
                
                managedListener.item.doRemoveListener(managedListener.ename, managedListener.fn, managedListener.scope, managedListener.options);
                if (!isClear) {
                    Ext.Array.remove(this.managedListeners, managedListener);
                }
            }
        },
        purgeManagedListeners: function() {
            if (Ext.global.console) {
                Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
            }
            return this.clearManagedListeners.apply(this, arguments);
        },
        
        hasListener: function(ename) {
            ename = Ext.canonicalEventName(ename);
            return !!this.hasListeners[ename];
        },
        
        isSuspended: function(event) {
            var suspended = this.eventsSuspended > 0,
                events = this.events;
            if (!suspended && event && events) {
                event = events[event];
                if (event && event.isEvent) {
                    return event.isSuspended();
                }
            }
            return suspended;
        },
        
        suspendEvents: function(queueSuspended) {
            ++this.eventsSuspended;
            if (queueSuspended && !this.eventQueue) {
                this.eventQueue = [];
            }
        },
        
        suspendEvent: function() {
            var me = this,
                events = me.events,
                len = arguments.length,
                i, event, ename;
            for (i = 0; i < len; i++) {
                ename = arguments[i];
                ename = Ext.canonicalEventName(ename);
                event = events[ename];
                
                if (!event || !event.isEvent) {
                    event = me._initEvent(ename);
                }
                event.suspend();
            }
        },
        
        resumeEvent: function() {
            var events = this.events || 0,
                len = events && arguments.length,
                i, event;
            for (i = 0; i < len; i++) {
                
                event = events[arguments[i]];
                if (event && event.resume) {
                    event.resume();
                }
            }
        },
        
        resumeEvents: function(discardQueue) {
            var me = this,
                queued = me.eventQueue,
                qLen, q;
            if (me.eventsSuspended && !--me.eventsSuspended) {
                delete me.eventQueue;
                if (!discardQueue && queued) {
                    qLen = queued.length;
                    for (q = 0; q < qLen; q++) {
                        
                        me.fireEventArgs.apply(me, queued[q]);
                    }
                }
            }
        },
        
        relayEvents: function(origin, events, prefix) {
            var me = this,
                len = events.length,
                i = 0,
                oldName,
                relayers = {};
            for (; i < len; i++) {
                oldName = events[i];
                
                relayers[oldName] = me.createRelayer(prefix ? prefix + oldName : oldName);
            }
            
            
            
            me.mon(origin, relayers, null, null, undefined);
            
            return new ListenerRemover(me,origin,relayers);
        },
        
        createRelayer: function(newName, beginEnd) {
            var me = this;
            return function() {
                return me.fireEventArgs.call(me, newName, beginEnd ? arraySlice.apply(arguments, beginEnd) : arguments);
            };
        },
        
        enableBubble: function(eventNames) {
            if (eventNames) {
                var me = this,
                    names = (typeof eventNames == 'string') ? arguments : eventNames,
                    
                    events = me.events,
                    length = events && names.length,
                    ename, event, i;
                for (i = 0; i < length; ++i) {
                    ename = names[i];
                    ename = Ext.canonicalEventName(ename);
                    event = events[ename];
                    if (!event || !event.isEvent) {
                        event = me._initEvent(ename);
                    }
                    
                    
                    me.hasListeners._incr_(ename);
                    event.bubble = true;
                }
            }
        },
        destroy: function() {
            this.clearListeners();
            this.callParent();
        },
        privates: {
            doAddListener: function(ename, fn, scope, options, order, caller, manager) {
                var me = this,
                    event, managedListeners, priority;
                order = order || (options && options.order);
                if (order) {
                    priority = (options && options.priority);
                    if (!priority) {
                        
                        
                        options = options ? Ext.Object.chain(options) : {};
                        options.priority = me.$orderToPriority[order];
                    }
                }
                ename = Ext.canonicalEventName(ename);
                if (!fn) {
                    Ext.Error.raise("Cannot add '" + ename + "' listener to " + me.$className + " instance.  No function specified.");
                }
                if (!manager && (scope && scope.isObservable && (scope !== me))) {
                    manager = scope;
                }
                if (manager) {
                    
                    
                    managedListeners = manager.managedListeners = manager.managedListeners || [];
                    managedListeners.push({
                        item: me,
                        ename: ename,
                        fn: fn,
                        scope: scope,
                        options: options
                    });
                }
                event = (me.events || (me.events = {}))[ename];
                if (!event || !event.isEvent) {
                    event = me._initEvent(ename);
                }
                if (fn !== emptyFn) {
                    if (event.addListener(fn, scope, options, caller, manager)) {
                        
                        
                        me.hasListeners._incr_(ename);
                    }
                }
            },
            doRemoveListener: function(ename, fn, scope) {
                var me = this,
                    events = me.events,
                    event;
                ename = Ext.canonicalEventName(ename);
                event = events && events[ename];
                if (!fn) {
                    Ext.Error.raise("Cannot remove '" + ename + "' listener to " + me.$className + " instance.  No function specified.");
                }
                if (event && event.isEvent) {
                    if (event.removeListener(fn, scope)) {
                        me.hasListeners._decr_(ename);
                    }
                }
            },
            _initEvent: function(eventName) {
                return (this.events[eventName] = new Ext.util.Event(this,eventName));
            }
        },
        deprecated: {
            '5.0': {
                methods: {
                    addEvents: null
                }
            },
            '5.1': {
                methods: {
                    
                    addBeforeListener: function(eventName, fn, scope, options) {
                        return this.addListener(eventName, fn, scope, options, 'before');
                    },
                    
                    addAfterListener: function(eventName, fn, scope, options) {
                        return this.addListener(eventName, fn, scope, options, 'after');
                    },
                    
                    removeBeforeListener: function(eventName, fn, scope, options) {
                        return this.removeListener(eventName, fn, scope, options, 'before');
                    },
                    
                    removeAfterListener: function(eventName, fn, scope, options) {
                        return this.removeListener(eventName, fn, scope, options, 'after');
                    },
                    
                    onBefore: 'addBeforeListener',
                    
                    onAfter: 'addAfterListener',
                    
                    unBefore: 'removeBeforeListener',
                    
                    unAfter: 'removeAfterListener'
                }
            }
        }
    };
}, function() {
    var Observable = this,
        proto = Observable.prototype,
        HasListeners = function() {},
        prepareMixin = function(T) {
            if (!T.HasListeners) {
                var proto = T.prototype;
                
                
                proto.$observableMixedIn = 1;
                
                Observable.prepareClass(T, this);
                
                
                T.onExtended(function(U, data) {
                    Ext.classSystemMonitor && Ext.classSystemMonitor('extend mixin', arguments);
                    Observable.prepareClass(U, null, data);
                });
                
                
                if (proto.onClassMixedIn) {
                    
                    Ext.override(T, {
                        onClassMixedIn: function(U) {
                            prepareMixin.call(this, U);
                            this.callParent(arguments);
                        }
                    });
                } else {
                    
                    proto.onClassMixedIn = function(U) {
                        prepareMixin.call(this, U);
                    };
                }
            }
            superOnClassMixedIn.call(this, T);
        },
        
        
        superOnClassMixedIn = proto.onClassMixedIn;
    HasListeners.prototype = {
        
        _decr_: function(ev) {
            if (!--this[ev]) {
                
                
                
                delete this[ev];
            }
        },
        _incr_: function(ev) {
            if (this.hasOwnProperty(ev)) {
                
                ++this[ev];
            } else {
                
                
                this[ev] = 1;
            }
        }
    };
    proto.HasListeners = Observable.HasListeners = HasListeners;
    Observable.createAlias({
        
        on: 'addListener',
        
        un: 'removeListener',
        
        mon: 'addManagedListener',
        
        mun: 'removeManagedListener',
        
        setListeners: 'addListener'
    });
    
    Observable.observeClass = Observable.observe;
    
    
    
    function getMethodEvent(method) {
        var e = (this.methodEvents = this.methodEvents || {})[method],
            returnValue, v, cancel,
            obj = this,
            makeCall;
        if (!e) {
            this.methodEvents[method] = e = {};
            e.originalFn = this[method];
            e.methodName = method;
            e.before = [];
            e.after = [];
            makeCall = function(fn, scope, args) {
                if ((v = fn.apply(scope || obj, args)) !== undefined) {
                    if (typeof v == 'object') {
                        if (v.returnValue !== undefined) {
                            returnValue = v.returnValue;
                        } else {
                            returnValue = v;
                        }
                        cancel = !!v.cancel;
                    } else if (v === false) {
                        cancel = true;
                    } else {
                        returnValue = v;
                    }
                }
            };
            this[method] = function() {
                var args = Array.prototype.slice.call(arguments, 0),
                    b, i, len;
                returnValue = v = undefined;
                cancel = false;
                for (i = 0 , len = e.before.length; i < len; i++) {
                    b = e.before[i];
                    makeCall(b.fn, b.scope, args);
                    if (cancel) {
                        return returnValue;
                    }
                }
                if ((v = e.originalFn.apply(obj, args)) !== undefined) {
                    returnValue = v;
                }
                for (i = 0 , len = e.after.length; i < len; i++) {
                    b = e.after[i];
                    makeCall(b.fn, b.scope, args);
                    if (cancel) {
                        return returnValue;
                    }
                }
                return returnValue;
            };
        }
        return e;
    }
    Ext.apply(proto, {
        onClassMixedIn: prepareMixin,
        
        
        
        beforeMethod: function(method, fn, scope) {
            getMethodEvent.call(this, method).before.push({
                fn: fn,
                scope: scope
            });
        },
        
        afterMethod: function(method, fn, scope) {
            getMethodEvent.call(this, method).after.push({
                fn: fn,
                scope: scope
            });
        },
        removeMethodListener: function(method, fn, scope) {
            var e = this.getMethodEvent(method),
                i, len;
            for (i = 0 , len = e.before.length; i < len; i++) {
                if (e.before[i].fn == fn && e.before[i].scope == scope) {
                    Ext.Array.erase(e.before, i, 1);
                    return;
                }
            }
            for (i = 0 , len = e.after.length; i < len; i++) {
                if (e.after[i].fn == fn && e.after[i].scope == scope) {
                    Ext.Array.erase(e.after, i, 1);
                    return;
                }
            }
        },
        toggleEventLogging: function(toggle) {
            Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
                if (Ext.isDefined(Ext.global.console)) {
                    Ext.global.console.log(en, arguments);
                }
            });
        }
    });
});


Ext.define('Ext.util.HashMap', {
    mixins: [
        'Ext.mixin.Observable'
    ],
    
    generation: 0,
    config: {
        
        keyFn: null
    },
    
    
    
    
    
    constructor: function(config) {
        var me = this,
            fn;
        
        me.mixins.observable.constructor.call(me, config);
        me.clear(true);
        fn = me.getKeyFn();
        if (fn) {
            me.getKey = fn;
        }
    },
    
    getCount: function() {
        return this.length;
    },
    
    getData: function(key, value) {
        
        if (value === undefined) {
            value = key;
            key = this.getKey(value);
        }
        return [
            key,
            value
        ];
    },
    
    getKey: function(o) {
        return o.id;
    },
    
    add: function(key, value) {
        var me = this;
        
        
        if (arguments.length === 1) {
            value = key;
            key = me.getKey(value);
        }
        if (me.containsKey(key)) {
            return me.replace(key, value);
        }
        me.map[key] = value;
        ++me.length;
        me.generation++;
        if (me.hasListeners.add) {
            me.fireEvent('add', me, key, value);
        }
        return value;
    },
    
    replace: function(key, value) {
        var me = this,
            map = me.map,
            old;
        
        
        if (arguments.length === 1) {
            value = key;
            key = me.getKey(value);
        }
        if (!me.containsKey(key)) {
            me.add(key, value);
        }
        old = map[key];
        map[key] = value;
        me.generation++;
        if (me.hasListeners.replace) {
            me.fireEvent('replace', me, key, value, old);
        }
        return value;
    },
    
    remove: function(o) {
        var key = this.findKey(o);
        if (key !== undefined) {
            return this.removeAtKey(key);
        }
        return false;
    },
    
    removeAtKey: function(key) {
        var me = this,
            value;
        if (me.containsKey(key)) {
            value = me.map[key];
            delete me.map[key];
            --me.length;
            me.generation++;
            if (me.hasListeners.remove) {
                me.fireEvent('remove', me, key, value);
            }
            return true;
        }
        return false;
    },
    
    get: function(key) {
        var map = this.map;
        return map.hasOwnProperty(key) ? map[key] : undefined;
    },
    
    
    
    clear: function(
    initial) {
        var me = this;
        
        if (initial || me.generation) {
            me.map = {};
            me.length = 0;
            me.generation = initial ? 0 : me.generation + 1;
        }
        if (initial !== true && me.hasListeners.clear) {
            me.fireEvent('clear', me);
        }
        return me;
    },
    
    containsKey: function(key) {
        var map = this.map;
        return map.hasOwnProperty(key) && map[key] !== undefined;
    },
    
    contains: function(value) {
        return this.containsKey(this.findKey(value));
    },
    
    getKeys: function() {
        return this.getArray(true);
    },
    
    getValues: function() {
        return this.getArray(false);
    },
    
    getArray: function(isKey) {
        var arr = [],
            key,
            map = this.map;
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                arr.push(isKey ? key : map[key]);
            }
        }
        return arr;
    },
    
    each: function(fn, scope) {
        
        var items = Ext.apply({}, this.map),
            key,
            length = this.length;
        scope = scope || this;
        for (key in items) {
            if (items.hasOwnProperty(key)) {
                if (fn.call(scope, key, items[key], length) === false) {
                    break;
                }
            }
        }
        return this;
    },
    
    clone: function() {
        var hash = new this.self(this.initialConfig),
            map = this.map,
            key;
        hash.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                hash.add(key, map[key]);
            }
        }
        hash.resumeEvents();
        return hash;
    },
    
    findKey: function(value) {
        var key,
            map = this.map;
        for (key in map) {
            if (map.hasOwnProperty(key) && map[key] === value) {
                return key;
            }
        }
        return undefined;
    }
});


Ext.define('Ext.AbstractManager', {
    
    requires: [
        'Ext.util.HashMap'
    ],
    
    typeName: 'type',
    constructor: function(config) {
        Ext.apply(this, config || {});
        
        this.all = new Ext.util.HashMap();
        this.types = {};
    },
    
    get: function(id) {
        return this.all.get(id);
    },
    
    register: function(item) {
        var key = this.all.getKey(item);
        if (key === undefined) {
            Ext.Error.raise('Key is undefined. Please ensure the item has a key before registering the item.');
        }
        if (this.all.containsKey(key)) {
            Ext.Error.raise('Registering duplicate id "' + key + '" with ' + this.$className);
        }
        this.all.add(item);
    },
    
    unregister: function(item) {
        this.all.remove(item);
    },
    
    registerType: function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
    },
    
    isRegistered: function(type) {
        return this.types[type] !== undefined;
    },
    
    create: function(config, defaultType) {
        var type = config[this.typeName] || config.type || defaultType,
            Constructor = this.types[type];
        if (Constructor === undefined) {
            Ext.Error.raise("The '" + type + "' type has not been registered with this manager");
        }
        return new Constructor(config);
    },
    
    onAvailable: function(id, fn, scope) {
        var all = this.all,
            item, callback;
        if (all.containsKey(id)) {
            item = all.get(id);
            fn.call(scope || item, item);
        } else {
            callback = function(map, key, item) {
                if (key == id) {
                    fn.call(scope || item, item);
                    all.un('add', callback);
                }
            };
            all.on('add', callback);
        }
    },
    
    each: function(fn, scope) {
        this.all.each(fn, scope || this);
    },
    
    getCount: function() {
        return this.all.getCount();
    }
});


Ext.define('Ext.data.flash.BinaryXhr', {
    statics: {
        
        flashPluginActivated: function() {
            Ext.data.flash.BinaryXhr.flashPluginActive = true;
            Ext.data.flash.BinaryXhr.flashPlugin = document.getElementById("ext-flash-polyfill");
            Ext.GlobalEvents.fireEvent("flashready");
        },
        
        
        flashPluginActive: false,
        
        flashPluginInjected: false,
        
        connectionIndex: 1,
        
        liveConnections: {},
        
        flashPlugin: null,
        
        onFlashStateChange: function(javascriptId, state, data) {
            var connection;
            
            connection = this.liveConnections[Number(javascriptId)];
            
            if (connection) {
                connection.onFlashStateChange(state, data);
            } else {
                Ext.warn.log("onFlashStateChange for unknown connection ID: " + javascriptId);
            }
        },
        
        registerConnection: function(conn) {
            var i = this.connectionIndex;
            this.conectionIndex = this.connectionIndex + 1;
            this.liveConnections[i] = conn;
            return i;
        },
        
        injectFlashPlugin: function() {
            var me = this,
                flashLoaderPath, flashObjectPath;
            
            
            
            
            
            me.flashPolyfillEl = Ext.getBody().appendChild({
                id: 'ext-flash-polyfill',
                cn: [
                    {
                        tag: 'p',
                        html: 'To view this page ensure that Adobe Flash Player version 11.1.0 or greater is installed.'
                    },
                    {
                        tag: 'a',
                        href: 'http://www.adobe.com/go/getflashplayer',
                        cn: [
                            {
                                tag: 'img',
                                src: window.location.protocol + '//www.adobe.com/images/shared/download_buttons/get_flash_player.gif',
                                alt: 'Get Adobe Flash player'
                            }
                        ]
                    }
                ]
            });
            
            flashLoaderPath = [
                Ext.Loader.getPath('Ext.data.Connection'),
                '../../../plugins/flash/swfobject.js'
            ].join('/');
            flashObjectPath = "/plugins/flash/FlashPlugin.swf";
            flashObjectPath = [
                Ext.Loader.getPath('Ext.data.Connection'),
                '../../plugins/flash/FlashPlugin.swf'
            ].join('/');
            if (Ext.flashPluginPath) {
                flashObjectPath = Ext.flashPluginPath;
            }
            
            Ext.Loader.loadScript({
                url: flashLoaderPath,
                onLoad: function() {
                    
                    var swfVersionStr = "11.4.0";
                    
                    var xiSwfUrlStr = "playerProductInstall.swf";
                    var flashvars = {};
                    var params = {};
                    params.quality = "high";
                    params.bgcolor = "#ffffff";
                    params.allowscriptaccess = "sameDomain";
                    params.allowfullscreen = "true";
                    var attributes = {};
                    attributes.id = "ext-flash-polyfill";
                    attributes.name = "polyfill";
                    attributes.align = "middle";
                    swfobject.embedSWF(flashObjectPath, "ext-flash-polyfill", "0", "0", 
                    swfVersionStr, xiSwfUrlStr, flashvars, params, attributes);
                },
                onError: function() {
                    Ext.Error.raise("Could not load flash-loader file swfobject.js from " + flashLoader);
                },
                scope: me
            });
            Ext.data.flash.BinaryXhr.flashPluginInjected = true;
        }
    },
    
    readyState: 0,
    
    status: 0,
    
    statusText: "",
    
    responseBytes: null,
    
    javascriptId: null,
    
    constructor: function(config) {
        
        if (!Ext.data.flash.BinaryXhr.flashPluginInjected) {
            Ext.data.flash.BinaryXhr.injectFlashPlugin();
        }
        var me = this;
        Ext.apply(me, config);
        me.requestHeaders = {};
    },
    
    abort: function() {
        var me = this;
        
        if (me.readyState == 4) {
            Ext.warn.log("Aborting a connection that's completed its transfer: " + this.url);
            return;
        }
        
        me.aborted = true;
        
        if (!Ext.data.flash.BinaryXhr.flashPluginActive) {
            Ext.GlobalEvents.removeListener("flashready", me.onFlashReady, me);
            return;
        }
        
        Ext.data.flash.BinaryXhr.flashPlugin.abortRequest(me.javascriptId);
        
        delete Ext.data.flash.BinaryXhr.liveConnections[me.javascriptId];
    },
    
    getAllResponseHeaders: function() {
        var headers = [];
        Ext.Object.each(this.responseHeaders, function(name, value) {
            headers.push(name + ': ' + value);
        });
        return headers.join('\r\n');
    },
    
    getResponseHeader: function(header) {
        var headers = this.responseHeaders;
        return (headers && headers[header]) || null;
    },
    
    open: function(method, url, async, user, password) {
        var me = this;
        me.method = method;
        me.url = url;
        me.async = async !== false;
        me.user = user;
        me.password = password;
        if (!me.async) {
            Ext.Error.raise("Binary posts are only supported in async mode: " + url);
        }
        if (me.method != "POST") {
            Ext.log.warn("Binary data can only be sent as a POST request: " + url);
        }
    },
    
    overrideMimeType: function(mimeType) {
        this.mimeType = mimeType;
    },
    
    send: function(body) {
        var me = this;
        me.body = body;
        if (!Ext.data.flash.BinaryXhr.flashPluginActive) {
            Ext.GlobalEvents.addListener("flashready", me.onFlashReady, me);
        } else {
            this.onFlashReady();
        }
    },
    
    onFlashReady: function() {
        var me = this,
            req, status;
        me.javascriptId = Ext.data.flash.BinaryXhr.registerConnection(me);
        
        req = {
            method: me.method,
            
            url: me.url,
            user: me.user,
            password: me.password,
            mimeType: me.mimeType,
            requestHeaders: me.requestHeaders,
            body: me.body,
            javascriptId: me.javascriptId
        };
        status = Ext.data.flash.BinaryXhr.flashPlugin.postBinary(req);
    },
    
    setReadyState: function(state) {
        var me = this;
        if (me.readyState != state) {
            me.readyState = state;
            me.onreadystatechange();
        }
    },
    
    setRequestHeader: function(header, value) {
        this.requestHeaders[header] = value;
    },
    
    onreadystatechange: Ext.emptyFn,
    
    parseData: function(data) {
        var me = this;
        
        this.status = data.status || 0;
        
        me.responseHeaders = {};
        if (me.mimeType) {
            me.responseHeaders["content-type"] = me.mimeType;
        }
        if (data.reason == "complete") {
            
            this.responseBytes = data.data;
            me.responseHeaders["content-length"] = data.data.length;
        } else if (data.reason == "error" || data.reason == "securityError") {
            this.statusText = data.text;
            me.responseHeaders["content-length"] = 0;
        } else 
        {
            Ext.Error.raise("Unkown reason code in data: " + data.reason);
        }
    },
    
    onFlashStateChange: function(state, data) {
        var me = this;
        if (state == 4) {
            
            me.parseData(data);
            
            delete Ext.data.flash.BinaryXhr.liveConnections[me.javascriptId];
        }
        me.setReadyState(state);
    }
});


Ext.define('Ext.data.Connection', {
    mixins: {
        observable: 'Ext.mixin.Observable'
    },
    requires: [
        'Ext.data.flash.BinaryXhr'
    ],
    statics: {
        requestId: 0
    },
    config: {
        
        url: null,
        
        async: true,
        
        username: '',
        
        password: '',
        
        disableCaching: true,
        
        withCredentials: false,
        
        binary: false,
        
        cors: false,
        isXdr: false,
        defaultXdrContentType: 'text/plain',
        
        disableCachingParam: '_dc',
        
        timeout: 30000,
        
        extraParams: null,
        
        autoAbort: false,
        
        method: null,
        
        defaultHeaders: null,
        
        defaultPostHeader: 'application/x-www-form-urlencoded; charset=UTF-8',
        
        useDefaultXhrHeader: true,
        
        defaultXhrHeader: 'XMLHttpRequest'
    },
    constructor: function(config) {
        
        this.mixins.observable.constructor.call(this, config);
        
        
        
        this.requests = {};
    },
    
    request: function(options) {
        options = options || {};
        var me = this,
            scope = options.scope || window,
            username = options.username || me.getUsername(),
            password = options.password || me.getPassword() || '',
            async, requestOptions, request, headers, xdr, xhr;
        if (me.fireEvent('beforerequest', me, options) !== false) {
            requestOptions = me.setOptions(options, scope);
            if (me.isFormUpload(options)) {
                me.upload(options.form, requestOptions.url, requestOptions.data, options);
                return null;
            }
            
            if (options.autoAbort || me.getAutoAbort()) {
                me.abort();
            }
            
            async = options.async !== false ? (options.async || me.getAsync()) : false;
            xhr = me.openRequest(options, requestOptions, async, username, password);
            
            xdr = me.getIsXdr();
            if (!xdr) {
                headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);
            }
            
            request = {
                id: ++Ext.data.Connection.requestId,
                xhr: xhr,
                headers: headers,
                options: options,
                async: async,
                binary: options.binary || me.getBinary(),
                timeout: Ext.defer(function() {
                    request.timedout = true;
                    me.abort(request);
                }, options.timeout || me.getTimeout())
            };
            me.requests[request.id] = request;
            me.latestId = request.id;
            
            if (async) {
                if (!xdr) {
                    xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [
                        request
                    ]);
                }
            }
            if (xdr) {
                me.processXdrRequest(request, xhr);
            }
            
            xhr.send(requestOptions.data);
            if (!async) {
                return me.onComplete(request);
            }
            return request;
        } else {
            Ext.callback(options.callback, options.scope, [
                options,
                undefined,
                undefined
            ]);
            return null;
        }
    },
    processXdrRequest: function(request, xhr) {
        var me = this;
        
        delete request.headers;
        request.contentType = request.options.contentType || me.getDefaultXdrContentType();
        xhr.onload = Ext.Function.bind(me.onStateChange, me, [
            request,
            true
        ]);
        xhr.onerror = xhr.ontimeout = Ext.Function.bind(me.onStateChange, me, [
            request,
            false
        ]);
    },
    processXdrResponse: function(response, xhr) {
        
        response.getAllResponseHeaders = function() {
            return [];
        };
        response.getResponseHeader = function() {
            return '';
        };
        response.contentType = xhr.contentType || this.getDefaultXdrContentType();
    },
    
    upload: function(form, url, params, options) {
        form = Ext.getDom(form);
        options = options || {};
        var id = Ext.id(),
            frame = document.createElement('iframe'),
            hiddens = [],
            encoding = 'multipart/form-data',
            buf = {
                target: form.target,
                method: form.method,
                encoding: form.encoding,
                enctype: form.enctype,
                action: form.action
            },
            addField = function(name, value) {
                hiddenItem = document.createElement('input');
                Ext.fly(hiddenItem).set({
                    type: 'hidden',
                    value: value,
                    name: name
                });
                form.appendChild(hiddenItem);
                hiddens.push(hiddenItem);
            },
            hiddenItem, obj, value, name, vLen, v, hLen, h;
        
        Ext.fly(frame).set({
            id: id,
            name: id,
            cls: Ext.baseCSSPrefix + 'hidden-display',
            src: Ext.SSL_SECURE_URL,
            tabIndex: -1
        });
        document.body.appendChild(frame);
        
        if (document.frames) {
            document.frames[id].name = id;
        }
        Ext.fly(form).set({
            target: id,
            method: 'POST',
            enctype: encoding,
            encoding: encoding,
            action: url || buf.action
        });
        
        if (params) {
            obj = Ext.Object.fromQueryString(params) || {};
            for (name in obj) {
                if (obj.hasOwnProperty(name)) {
                    value = obj[name];
                    if (Ext.isArray(value)) {
                        vLen = value.length;
                        for (v = 0; v < vLen; v++) {
                            addField(name, value[v]);
                        }
                    } else {
                        addField(name, value);
                    }
                }
            }
        }
        
        Ext.get(frame).on({
            load: Ext.Function.bind(this.onUploadComplete, this, [
                frame,
                options
            ]),
            single: !Ext.isOpera
        });
        
        form.submit();
        
        Ext.fly(form).set(buf);
        for (hLen = hiddens.length , h = 0; h < hLen; h++) {
            Ext.removeNode(hiddens[h]);
        }
    },
    
    onUploadComplete: function(frame, options) {
        var me = this,
            
            response = {
                responseText: '',
                responseXML: null
            },
            callback, success, doc, contentNode;
        try {
            doc = (frame && (frame.contentWindow.document || frame.contentDocument)) || (window.frames[frame.id] || {}).document;
            
            
            if (doc) {
                
                if (Ext.isOpera && doc.location == Ext.SSL_SECURE_URL) {
                    return;
                }
                if (doc.body) {
                    
                    
                    if ((contentNode = doc.body.firstChild) && /pre/i.test(contentNode.tagName)) {
                        response.responseText = contentNode.textContent || contentNode.innerText;
                    }
                    
                    
                    else if ((contentNode = doc.getElementsByTagName('textarea')[0])) {
                        response.responseText = contentNode.value;
                    } else 
                    {
                        response.responseText = doc.body.textContent || doc.body.innerText;
                    }
                }
                
                response.responseXML = doc.XMLDocument || doc;
                callback = options.success;
                success = true;
            } else {
                Ext.Error.raise("Could not acquire a suitable connection for the file upload service.");
            }
        } catch (e) {
            
            response.responseText = '{success:false,message:"' + Ext.String.trim(e.message || e.description) + '"}';
            callback = options.failure;
            success = false;
        }
        me.fireEvent(success ? 'requestcomplete' : 'requestexception', me, response, options);
        Ext.callback(callback, options.scope, [
            response,
            options
        ]);
        Ext.callback(options.callback, options.scope, [
            options,
            success,
            response
        ]);
        
        Ext.defer(Ext.removeNode, 100, Ext, [
            frame
        ]);
    },
    
    isFormUpload: function(options) {
        var form = this.getForm(options);
        if (form) {
            return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
        }
        return false;
    },
    
    getForm: function(options) {
        var form = options.form || null;
        if (form) {
            form = Ext.getDom(form);
        }
        return form;
    },
    
    setOptions: function(options, scope) {
        var me = this,
            params = options.params || {},
            extraParams = me.getExtraParams(),
            urlParams = options.urlParams,
            url = options.url || me.getUrl(),
            jsonData = options.jsonData,
            method, disableCache, data;
        
        if (Ext.isFunction(params)) {
            params = params.call(scope, options);
        }
        
        if (Ext.isFunction(url)) {
            url = url.call(scope, options);
        }
        url = this.setupUrl(options, url);
        if (!url) {
            Ext.Error.raise({
                options: options,
                msg: 'No URL specified'
            });
        }
        
        data = options.rawData || options.binaryData || options.xmlData || jsonData || null;
        if (jsonData && !Ext.isPrimitive(jsonData)) {
            data = Ext.encode(data);
        }
        
        if (options.binaryData) {
            if (!Ext.isArray(options.binaryData)) {
                Ext.log.warn("Binary submission data must be an array of byte values! Instead got " + typeof (options.binaryData));
            }
            if (me.nativeBinaryPostSupport()) {
                data = (new Uint8Array(options.binaryData));
                if ((Ext.isChrome && Ext.chromeVersion < 22) || Ext.isSafari || Ext.isGecko) {
                    data = data.buffer;
                }
            }
        }
        
        
        if (Ext.isObject(params)) {
            params = Ext.Object.toQueryString(params);
        }
        if (Ext.isObject(extraParams)) {
            extraParams = Ext.Object.toQueryString(extraParams);
        }
        params = params + ((extraParams) ? ((params) ? '&' : '') + extraParams : '');
        urlParams = Ext.isObject(urlParams) ? Ext.Object.toQueryString(urlParams) : urlParams;
        params = this.setupParams(options, params);
        
        method = (options.method || me.getMethod() || ((params || data) ? 'POST' : 'GET')).toUpperCase();
        this.setupMethod(options, method);
        disableCache = options.disableCaching !== false ? (options.disableCaching || me.getDisableCaching()) : false;
        
        if (method === 'GET' && disableCache) {
            url = Ext.urlAppend(url, (options.disableCachingParam || me.getDisableCachingParam()) + '=' + (new Date().getTime()));
        }
        
        if ((method == 'GET' || data) && params) {
            url = Ext.urlAppend(url, params);
            params = null;
        }
        
        if (urlParams) {
            url = Ext.urlAppend(url, urlParams);
        }
        return {
            url: url,
            method: method,
            data: data || params || null
        };
    },
    
    setupUrl: function(options, url) {
        var form = this.getForm(options);
        if (form) {
            url = url || form.action;
        }
        return url;
    },
    
    setupParams: function(options, params) {
        var form = this.getForm(options),
            serializedForm;
        if (form && !this.isFormUpload(options)) {
            serializedForm = Ext.Element.serializeForm(form);
            params = params ? (params + '&' + serializedForm) : serializedForm;
        }
        return params;
    },
    
    setupMethod: function(options, method) {
        if (this.isFormUpload(options)) {
            return 'POST';
        }
        return method;
    },
    
    setupHeaders: function(xhr, options, data, params) {
        var me = this,
            headers = Ext.apply({}, options.headers || {}, me.getDefaultHeaders() || {}),
            contentType = me.getDefaultPostHeader(),
            jsonData = options.jsonData,
            xmlData = options.xmlData,
            type = 'Content-Type',
            useHeader = Ext.isDefined(options.useDefaultXhrHeader) ? options.useDefaultXhrHeader : me.getUseDefaultXhrHeader(),
            key, header;
        if (!headers.hasOwnProperty(type) && (data || params)) {
            if (data) {
                if (options.rawData) {
                    contentType = 'text/plain';
                } else {
                    if (xmlData && Ext.isDefined(xmlData)) {
                        contentType = 'text/xml';
                    } else if (jsonData && Ext.isDefined(jsonData)) {
                        contentType = 'application/json';
                    }
                }
            }
            headers[type] = contentType;
        }
        if (useHeader && !headers['X-Requested-With']) {
            headers['X-Requested-With'] = me.getDefaultXhrHeader();
        }
        
        
        if (headers[type] === undefined || headers[type] === null) {
            delete headers[type];
        }
        
        try {
            for (key in headers) {
                if (headers.hasOwnProperty(key)) {
                    header = headers[key];
                    xhr.setRequestHeader(key, header);
                }
            }
        } catch (e) {
            me.fireEvent('exception', key, header);
        }
        return headers;
    },
    
    newRequest: function(options) {
        var me = this,
            xhr;
        if (options.binaryData) {
            
            if (me.nativeBinaryPostSupport()) {
                xhr = me.getXhrInstance();
            } else 
            {
                
                xhr = new Ext.data.flash.BinaryXhr();
            }
        } else if ((options.cors || me.getCors()) && Ext.isIE && Ext.ieVersion <= 9) {
            xhr = me.getXdrInstance();
            me.setIsXdr(true);
        } else {
            xhr = me.getXhrInstance();
        }
        return xhr;
    },
    
    openRequest: function(options, requestOptions, async, username, password) {
        var me = this,
            xhr = me.newRequest(options);
        if (username) {
            xhr.open(requestOptions.method, requestOptions.url, async, username, password);
        } else {
            if (me.getIsXdr()) {
                xhr.open(requestOptions.method, requestOptions.url);
            } else {
                xhr.open(requestOptions.method, requestOptions.url, async);
            }
        }
        if (options.binary || me.getBinary()) {
            if (window.Uint8Array) {
                xhr.responseType = 'arraybuffer';
            } else if (xhr.overrideMimeType) {
                
                
                
                
                xhr.overrideMimeType('text/plain; charset=x-user-defined');
            } else if (!Ext.isIE) {
                Ext.log.warn("Your browser does not support loading binary data using Ajax.");
            }
        }
        if (options.withCredentials || me.getWithCredentials()) {
            xhr.withCredentials = true;
        }
        return xhr;
    },
    
    getXdrInstance: function() {
        var xdr;
        if (Ext.ieVersion >= 8) {
            xdr = new XDomainRequest();
        } else {
            Ext.Error.raise({
                msg: 'Your browser does not support CORS'
            });
        }
        return xdr;
    },
    
    getXhrInstance: (function() {
        var options = [
                function() {
                    return new XMLHttpRequest();
                },
                function() {
                    return new ActiveXObject('MSXML2.XMLHTTP.3.0');
                },
                function() {
                    return new ActiveXObject('MSXML2.XMLHTTP');
                },
                function() {
                    return new ActiveXObject('Microsoft.XMLHTTP');
                }
            ],
            i = 0,
            len = options.length,
            xhr;
        for (; i < len; ++i) {
            try {
                xhr = options[i];
                xhr();
                break;
            } catch (e) {}
        }
        return xhr;
    }()),
    
    isLoading: function(request) {
        if (!request) {
            request = this.getLatest();
        }
        if (!(request && request.xhr)) {
            return false;
        }
        
        var state = request.xhr.readyState,
            Cls = Ext.data.flash && Ext.data.flash.BinaryXhr;
        return ((request.xhr instanceof Cls) && state != 4) || !(state === 0 || state == 4);
    },
    
    abort: function(request) {
        var me = this,
            xhr;
        if (!request) {
            request = me.getLatest();
        }
        if (request && me.isLoading(request)) {
            
            xhr = request.xhr;
            try {
                xhr.onreadystatechange = null;
            } catch (e) {
                
                
                xhr.onreadystatechange = Ext.emptyFn;
            }
            xhr.abort();
            me.clearTimeout(request);
            if (!request.timedout) {
                request.aborted = true;
            }
            me.onComplete(request);
            me.cleanup(request);
        }
    },
    
    abortAll: function() {
        var requests = this.requests,
            id;
        for (id in requests) {
            if (requests.hasOwnProperty(id)) {
                this.abort(requests[id]);
            }
        }
    },
    
    getLatest: function() {
        var id = this.latestId,
            request;
        if (id) {
            request = this.requests[id];
        }
        return request || null;
    },
    
    onStateChange: function(request, xdrResult) {
        var me = this,
            globalEvents = Ext.GlobalEvents;
        
        if ((request.xhr && request.xhr.readyState == 4) || me.isXdr) {
            me.clearTimeout(request);
            me.onComplete(request, xdrResult);
            me.cleanup(request);
            if (globalEvents.hasListeners.idle) {
                globalEvents.fireEvent('idle');
            }
        }
    },
    
    clearTimeout: function(request) {
        clearTimeout(request.timeout);
        delete request.timeout;
    },
    
    cleanup: function(request) {
        request.xhr = null;
        delete request.xhr;
    },
    
    onComplete: function(request, xdrResult) {
        var me = this,
            options = request.options,
            xhr, result, success, response;
        try {
            xhr = request.xhr;
            result = me.parseStatus(xhr.status);
            if (result.success) {
                
                
                
                result.success = xhr.readyState === 4;
            }
        } catch (e) {
            
            result = {
                success: false,
                isException: false
            };
        }
        success = me.isXdr ? xdrResult : result.success;
        if (success) {
            response = me.createResponse(request);
            me.fireEvent('requestcomplete', me, response, options);
            Ext.callback(options.success, options.scope, [
                response,
                options
            ]);
        } else {
            if (result.isException || request.aborted || request.timedout) {
                response = me.createException(request);
            } else {
                response = me.createResponse(request);
            }
            me.fireEvent('requestexception', me, response, options);
            Ext.callback(options.failure, options.scope, [
                response,
                options
            ]);
        }
        Ext.callback(options.callback, options.scope, [
            options,
            success,
            response
        ]);
        delete me.requests[request.id];
        return response;
    },
    
    parseStatus: function(status) {
        
        status = status == 1223 ? 204 : status;
        var success = (status >= 200 && status < 300) || status == 304,
            isException = false;
        if (!success) {
            switch (status) {
                case 12002:
                case 12029:
                case 12030:
                case 12031:
                case 12152:
                case 13030:
                    isException = true;
                    break;
            }
        }
        return {
            success: success,
            isException: isException
        };
    },
    
    createResponse: function(request) {
        var me = this,
            xhr = request.xhr,
            isXdr = me.getIsXdr(),
            headers = {},
            lines = isXdr ? [] : xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'),
            count = lines.length,
            line, index, key, response, byteArray;
        while (count--) {
            line = lines[count];
            index = line.indexOf(':');
            if (index >= 0) {
                key = line.substr(0, index).toLowerCase();
                if (line.charAt(index + 1) == ' ') {
                    ++index;
                }
                headers[key] = line.substr(index + 1);
            }
        }
        request.xhr = null;
        delete request.xhr;
        response = {
            request: request,
            requestId: request.id,
            status: xhr.status,
            statusText: xhr.statusText,
            getResponseHeader: function(header) {
                return headers[header.toLowerCase()];
            },
            getAllResponseHeaders: function() {
                return headers;
            }
        };
        if (isXdr) {
            me.processXdrResponse(response, xhr);
        }
        if (request.binary) {
            response.responseBytes = me.getByteArray(xhr);
        } else {
            
            
            
            
            response.responseText = xhr.responseText;
            response.responseXML = xhr.responseXML;
        }
        
        
        xhr = null;
        return response;
    },
    
    createException: function(request) {
        return {
            request: request,
            requestId: request.id,
            status: request.aborted ? -1 : 0,
            statusText: request.aborted ? 'transaction aborted' : 'communication failure',
            aborted: request.aborted,
            timedout: request.timedout
        };
    },
    
    getByteArray: function(xhr) {
        var response = xhr.response,
            responseBody = xhr.responseBody,
            Cls = Ext.data.flash && Ext.data.flash.BinaryXhr,
            byteArray, responseText, len, i;
        if (xhr instanceof Cls) {
            
            byteArray = xhr.responseBytes;
        } else if (window.Uint8Array) {
            
            
            
            byteArray = response ? new Uint8Array(response) : [];
        } else if (Ext.isIE9p) {
            
            
            
            
            try {
                byteArray = new VBArray(responseBody).toArray();
            } catch (e) {
                
                
                
                
                byteArray = [];
            }
        } else if (Ext.isIE) {
            
            
            
            
            
            if (!this.self.vbScriptInjected) {
                this.injectVBScript();
            }
            getIEByteArray(xhr.responseBody, byteArray = []);
        } else {
            
            
            byteArray = [];
            responseText = xhr.responseText;
            len = responseText.length;
            for (i = 0; i < len; i++) {
                
                
                
                byteArray.push(responseText.charCodeAt(i) & 255);
            }
        }
        return byteArray;
    },
    
    injectVBScript: function() {
        var scriptTag = document.createElement('script');
        scriptTag.type = 'text/vbscript';
        scriptTag.text = [
            'Function getIEByteArray(byteArray, out)',
            'Dim len, i',
            'len = LenB(byteArray)',
            'For i = 1 to len',
            'out.push(AscB(MidB(byteArray, i, 1)))',
            'Next',
            'End Function'
        ].join('\n');
        Ext.getHead().dom.appendChild(scriptTag);
        this.self.vbScriptInjected = true;
    },
    
    nativeBinaryPostSupport: function() {
        return Ext.isChrome || (Ext.isSafari && Ext.isDefined(window.Uint8Array)) || (Ext.isGecko && Ext.isDefined(window.Uint8Array));
    }
});


Ext.define('Ext.Ajax', {
    extend: 'Ext.data.Connection',
    singleton: true,
    
    
    
    
    
    
    
    
    
    
    
    
    
    autoAbort: false
});


Ext.define('Ext.AnimationQueue', {
    singleton: true,
    constructor: function() {
        this.queue = [];
        this.taskQueue = [];
        this.runningQueue = [];
        this.idleQueue = [];
        this.isRunning = false;
        this.isIdle = true;
        this.run = Ext.Function.bind(this.run, this);
        
        
        
        
        
        if (Ext.os.is.iOS) {
            Ext.interval(this.watch, 500, this);
        }
    },
    
    start: function(fn, scope, args) {
        this.queue.push(arguments);
        if (!this.isRunning) {
            if (this.hasOwnProperty('idleTimer')) {
                clearTimeout(this.idleTimer);
                delete this.idleTimer;
            }
            if (this.hasOwnProperty('idleQueueTimer')) {
                clearTimeout(this.idleQueueTimer);
                delete this.idleQueueTimer;
            }
            this.isIdle = false;
            this.isRunning = true;
            this.startCountTime = Ext.now();
            this.count = 0;
            this.doStart();
        }
    },
    watch: function() {
        if (this.isRunning && Ext.now() - this.lastRunTime >= 500) {
            this.run();
        }
    },
    run: function() {
        if (!this.isRunning) {
            return;
        }
        var queue = this.runningQueue,
            i, ln;
        this.lastRunTime = Ext.now();
        this.frameStartTime = Ext.now();
        queue.push.apply(queue, this.queue);
        for (i = 0 , ln = queue.length; i < ln; i++) {
            this.invoke(queue[i]);
        }
        queue.length = 0;
        var now = this.frameStartTime,
            startCountTime = this.startCountTime,
            elapse = now - startCountTime,
            count = ++this.count;
        if (elapse >= 200) {
            this.onFpsChanged(count * 1000 / elapse, count, elapse);
            this.startCountTime = now;
            this.count = 0;
        }
        this.doIterate();
    },
    onFpsChanged: Ext.emptyFn,
    onStop: Ext.emptyFn,
    doStart: function() {
        this.animationFrameId = Ext.Function.requestAnimationFrame(this.run);
        this.lastRunTime = Ext.now();
    },
    doIterate: function() {
        this.animationFrameId = Ext.Function.requestAnimationFrame(this.run);
    },
    doStop: function() {
        Ext.Function.cancelAnimationFrame(this.animationFrameId);
    },
    
    stop: function(fn, scope, args) {
        if (!this.isRunning) {
            return;
        }
        var queue = this.queue,
            ln = queue.length,
            i, item;
        for (i = 0; i < ln; i++) {
            item = queue[i];
            if (item[0] === fn && item[1] === scope && item[2] === args) {
                queue.splice(i, 1);
                i--;
                ln--;
            }
        }
        if (ln === 0) {
            this.doStop();
            this.onStop();
            this.isRunning = false;
            this.idleTimer = Ext.defer(this.whenIdle, 100, this);
        }
    },
    onIdle: function(fn, scope, args) {
        var listeners = this.idleQueue,
            i, ln, listener;
        for (i = 0 , ln = listeners.length; i < ln; i++) {
            listener = listeners[i];
            if (fn === listener[0] && scope === listener[1] && args === listener[2]) {
                return;
            }
        }
        listeners.push(arguments);
        if (this.isIdle) {
            this.processIdleQueue();
        }
    },
    unIdle: function(fn, scope, args) {
        var listeners = this.idleQueue,
            i, ln, listener;
        for (i = 0 , ln = listeners.length; i < ln; i++) {
            listener = listeners[i];
            if (fn === listener[0] && scope === listener[1] && args === listener[2]) {
                listeners.splice(i, 1);
                return true;
            }
        }
        return false;
    },
    queueTask: function(fn, scope, args) {
        this.taskQueue.push(arguments);
        this.processTaskQueue();
    },
    dequeueTask: function(fn, scope, args) {
        var listeners = this.taskQueue,
            i, ln, listener;
        for (i = 0 , ln = listeners.length; i < ln; i++) {
            listener = listeners[i];
            if (fn === listener[0] && scope === listener[1] && args === listener[2]) {
                listeners.splice(i, 1);
                i--;
                ln--;
            }
        }
    },
    invoke: function(listener) {
        var fn = listener[0],
            scope = listener[1],
            args = listener[2];
        fn = (typeof fn == 'string' ? scope[fn] : fn);
        if (Ext.isArray(args)) {
            fn.apply(scope, args);
        } else {
            fn.call(scope, args);
        }
    },
    whenIdle: function() {
        this.isIdle = true;
        this.processIdleQueue();
    },
    processIdleQueue: function() {
        if (!this.hasOwnProperty('idleQueueTimer')) {
            this.idleQueueTimer = Ext.defer(this.processIdleQueueItem, 1, this);
        }
    },
    processIdleQueueItem: function() {
        delete this.idleQueueTimer;
        if (!this.isIdle) {
            return;
        }
        var listeners = this.idleQueue,
            listener;
        if (listeners.length > 0) {
            listener = listeners.shift();
            this.invoke(listener);
            this.processIdleQueue();
        }
    },
    processTaskQueue: function() {
        if (!this.hasOwnProperty('taskQueueTimer')) {
            this.taskQueueTimer = Ext.defer(this.processTaskQueueItem, 15, this);
        }
    },
    processTaskQueueItem: function() {
        delete this.taskQueueTimer;
        var listeners = this.taskQueue,
            listener;
        if (listeners.length > 0) {
            listener = listeners.shift();
            this.invoke(listener);
            this.processTaskQueue();
        }
    },
    showFps: function() {
        Ext.onReady(function() {
            Ext.Viewport.add([
                {
                    xtype: 'component',
                    bottom: 50,
                    left: 0,
                    width: 50,
                    height: 20,
                    html: 'Average',
                    style: 'background-color: black; color: white; text-align: center; line-height: 20px; font-size: 8px;'
                },
                {
                    id: '__averageFps',
                    xtype: 'component',
                    bottom: 0,
                    left: 0,
                    width: 50,
                    height: 50,
                    html: '0',
                    style: 'background-color: red; color: white; text-align: center; line-height: 50px;'
                },
                {
                    xtype: 'component',
                    bottom: 50,
                    left: 50,
                    width: 50,
                    height: 20,
                    html: 'Min (Last 1k)',
                    style: 'background-color: black; color: white; text-align: center; line-height: 20px; font-size: 8px;'
                },
                {
                    id: '__minFps',
                    xtype: 'component',
                    bottom: 0,
                    left: 50,
                    width: 50,
                    height: 50,
                    html: '0',
                    style: 'background-color: orange; color: white; text-align: center; line-height: 50px;'
                },
                {
                    xtype: 'component',
                    bottom: 50,
                    left: 100,
                    width: 50,
                    height: 20,
                    html: 'Max (Last 1k)',
                    style: 'background-color: black; color: white; text-align: center; line-height: 20px; font-size: 8px;'
                },
                {
                    id: '__maxFps',
                    xtype: 'component',
                    bottom: 0,
                    left: 100,
                    width: 50,
                    height: 50,
                    html: '0',
                    style: 'background-color: yellow; color: black; text-align: center; line-height: 50px;'
                },
                {
                    xtype: 'component',
                    bottom: 50,
                    left: 150,
                    width: 50,
                    height: 20,
                    html: 'Current',
                    style: 'background-color: black; color: white; text-align: center; line-height: 20px; font-size: 8px;'
                },
                {
                    id: '__currentFps',
                    xtype: 'component',
                    bottom: 0,
                    left: 150,
                    width: 50,
                    height: 50,
                    html: '0',
                    style: 'background-color: green; color: white; text-align: center; line-height: 50px;'
                }
            ]);
            Ext.AnimationQueue.resetFps();
        });
    },
    resetFps: function() {
        var currentFps = Ext.getCmp('__currentFps'),
            averageFps = Ext.getCmp('__averageFps'),
            minFps = Ext.getCmp('__minFps'),
            maxFps = Ext.getCmp('__maxFps'),
            min = 1000,
            max = 0,
            count = 0,
            sum = 0;
        Ext.AnimationQueue.onFpsChanged = function(fps) {
            count++;
            if (!(count % 10)) {
                min = 1000;
                max = 0;
            }
            sum += fps;
            min = Math.min(min, fps);
            max = Math.max(max, fps);
            currentFps.setHtml(Math.round(fps));
            averageFps.setHtml(Math.round(sum / count));
            minFps.setHtml(Math.round(min));
            maxFps.setHtml(Math.round(max));
        };
    }
}, function() {
    
    var paramsString = window.location.search.substr(1),
        paramsArray = paramsString.split("&");
    if (Ext.Array.contains(paramsArray, "showfps")) {
        Ext.AnimationQueue.showFps();
    }
});


Ext.define('Ext.ComponentManager', {
    alternateClassName: 'Ext.ComponentMgr',
    singleton: true,
    count: 0,
    typeName: 'xtype',
    constructor: function(config) {
        Ext.apply(this, config || {});
        this.all = {};
        this.references = {};
        this.onAvailableCallbacks = {};
    },
    
    create: function(config, defaultType) {
        if (typeof config == 'string') {
            return Ext.widget(config);
        }
        if (config.isComponent) {
            return config;
        }
        if ('xclass' in config) {
            return Ext.create(config.xclass, config);
        }
        return Ext.widget(config.xtype || defaultType, config);
    },
    
    get: function(id) {
        return this.all[id];
    },
    register: function(component) {
        var me = this,
            all = me.all,
            key = component.getId(),
            onAvailableCallbacks = me.onAvailableCallbacks;
        if (key === undefined) {
            Ext.Error.raise('Component id is undefined. Please ensure the component has an id.');
        }
        if (key in all) {
            Ext.Error.raise('Registering duplicate component id "' + key + '"');
        }
        all[key] = component;
        if (component.reference) {
            me.references[key] = component;
        }
        ++me.count;
        if (me.count === 1) {
            me.initFocusListener();
        }
        onAvailableCallbacks = onAvailableCallbacks && onAvailableCallbacks[key];
        if (onAvailableCallbacks && onAvailableCallbacks.length) {
            me.notifyAvailable(component);
        }
    },
    unregister: function(component) {
        var id = component.getId();
        if (component.reference) {
            delete this.references[id];
        }
        delete this.all[id];
        this.count--;
    },
    markReferencesDirty: function() {
        this.referencesDirty = true;
    },
    fixReferences: function() {
        var me = this,
            references = me.references,
            key;
        if (me.referencesDirty) {
            for (key in references) {
                if (references.hasOwnProperty(key)) {
                    references[key].fixReference();
                }
            }
            me.referencesDirty = false;
        }
    },
    
    onAvailable: function(id, fn, scope) {
        var me = this,
            callbacks = me.onAvailableCallbacks,
            all = me.all,
            item;
        if (id in all) {
            
            item = all[id];
            fn.call(scope || item, item);
        } else if (id) {
            
            if (!Ext.isArray(callbacks[id])) {
                callbacks[id] = [];
            }
            callbacks[id].push(function(item) {
                fn.call(scope || item, item);
            });
        }
    },
    
    notifyAvailable: function(item) {
        var callbacks = this.onAvailableCallbacks[item && item.getId()] || [];
        while (callbacks.length) {
            (callbacks.shift())(item);
        }
    },
    
    each: function(fn, scope) {
        return Ext.Object.each(this.all, fn, scope);
    },
    
    getCount: function() {
        return this.count;
    },
    
    getAll: function() {
        return Ext.Object.getValues(this.all);
    },
    
    byElement: function(node) {
        var topmost = document.body,
            cmpIdAttr = Ext.Component.componentIdAttribute,
            cmpId,
            target = Ext.getDom(node),
            cmp;
        while (target && target.nodeType === 1 && target !== topmost) {
            cmpId = target.getAttribute(cmpIdAttr) || target.id;
            if (cmpId) {
                cmp = this.all[cmpId];
                if (cmp) {
                    return cmp;
                }
            }
            target = target.parentNode;
        }
        return null;
    },
    
    getActiveComponent: function() {
        return this.byElement(Ext.dom.Element.getActiveElement());
    },
    initFocusListener: function() {
        Ext.on('focus', this.onGlobalFocus, this);
    },
    
    onGlobalFocus: function(e) {
        var me = this,
            toElement = e.toElement,
            fromElement = e.fromElement,
            toComponent = me.byElement(toElement),
            fromComponent = me.byElement(fromElement),
            commonAncestor = me.getCommonAncestor(fromComponent, toComponent),
            event, targetComponent;
        if (fromComponent && !fromComponent.isDestroyed) {
            
            if (fromComponent.focusable && fromElement === fromComponent.getFocusEl().dom) {
                event = new Ext.event.Event(e.event);
                event.type = 'blur';
                event.target = fromElement;
                event.relatedTarget = toElement;
                fromComponent.onBlur(event);
            }
            
            for (targetComponent = fromComponent; targetComponent && targetComponent !== commonAncestor; targetComponent = targetComponent.getRefOwner()) {
                targetComponent.onFocusLeave({
                    event: e.event,
                    type: 'focusleave',
                    target: fromElement,
                    relatedTarget: toElement,
                    fromComponent: fromComponent,
                    toComponent: toComponent
                });
            }
        }
        if (toComponent && !toComponent.isDestroyed) {
            
            if (toComponent.focusable && toElement === toComponent.getFocusEl().dom) {
                event = new Ext.event.Event(e.event);
                event.type = 'focus';
                event.relatedTarget = fromElement;
                event.target = toElement;
                toComponent.onFocus(event);
            }
            
            for (targetComponent = toComponent; targetComponent && targetComponent !== commonAncestor; targetComponent = targetComponent.getRefOwner()) {
                targetComponent.onFocusEnter({
                    event: e.event,
                    type: 'focusenter',
                    relatedTarget: fromElement,
                    target: toElement,
                    fromComponent: fromComponent,
                    toComponent: toComponent
                });
            }
        }
    },
    getCommonAncestor: function(compA, compB) {
        if (compA === compB) {
            return compA;
        }
        while (compA && !(compA.isAncestor(compB) || compA === compB)) {
            compA = compA.getRefOwner();
        }
        return compA;
    },
    deprecated: {
        5: {
            methods: {
                
                isRegistered: null,
                
                registerType: null
            }
        }
    }
}, function() {
    
    Ext.getCmp = function(id) {
        return Ext.ComponentManager.get(id);
    };
});


Ext.ns('Ext.util').Operators = {
    
    "=": function(a, v) {
        return a == v;
    },
    "!=": function(a, v) {
        return a != v;
    },
    "^=": function(a, v) {
        return a && a.substr(0, v.length) == v;
    },
    "$=": function(a, v) {
        return a && a.substr(a.length - v.length) == v;
    },
    "*=": function(a, v) {
        return a && a.indexOf(v) !== -1;
    },
    "%=": function(a, v) {
        return (a % v) === 0;
    },
    "|=": function(a, v) {
        return a && (a == v || a.substr(0, v.length + 1) == v + '-');
    },
    "~=": function(a, v) {
        return a && (' ' + a + ' ').indexOf(' ' + v + ' ') != -1;
    }
};


Ext.define('Ext.util.LruCache', {
    extend: 'Ext.util.HashMap',
    config: {
        
        maxSize: null
    },
    
    add: function(key, newValue) {
        var me = this,
            entry, last;
        me.removeAtKey(key);
        last = me.last;
        entry = {
            prev: last,
            next: null,
            key: key,
            value: newValue
        };
        if (last) {
            
            last.next = entry;
        } else {
            
            me.first = entry;
        }
        me.last = entry;
        me.callParent([
            key,
            entry
        ]);
        me.prune();
        return newValue;
    },
    
    insertBefore: function(key, newValue, sibling) {
        var me = this,
            existingKey, entry;
        
        
        if (sibling = this.map[this.findKey(sibling)]) {
            existingKey = me.findKey(newValue);
            
            if (existingKey) {
                me.unlinkEntry(entry = me.map[existingKey]);
            } else 
            {
                entry = {
                    prev: sibling.prev,
                    next: sibling,
                    key: key,
                    value: newValue
                };
            }
            if (sibling.prev) {
                entry.prev.next = entry;
            } else {
                me.first = entry;
            }
            entry.next = sibling;
            sibling.prev = entry;
            me.prune();
            return newValue;
        } else 
        {
            return me.add(key, newValue);
        }
    },
    
    get: function(key) {
        var entry = this.map[key];
        if (entry) {
            
            if (entry.next) {
                this.moveToEnd(entry);
            }
            return entry.value;
        }
    },
    
    removeAtKey: function(key) {
        this.unlinkEntry(this.map[key]);
        return this.callParent(arguments);
    },
    
    clear: function(
    initial) {
        this.first = this.last = null;
        return this.callParent(arguments);
    },
    
    unlinkEntry: function(entry) {
        
        if (entry) {
            if (entry.next) {
                entry.next.prev = entry.prev;
            } else {
                this.last = entry.prev;
            }
            if (entry.prev) {
                entry.prev.next = entry.next;
            } else {
                this.first = entry.next;
            }
            entry.prev = entry.next = null;
        }
    },
    
    moveToEnd: function(entry) {
        this.unlinkEntry(entry);
        
        
        if (entry.prev = this.last) {
            this.last.next = entry;
        } else 
        {
            this.first = entry;
        }
        this.last = entry;
    },
    
    getArray: function(isKey) {
        var arr = [],
            entry = this.first;
        while (entry) {
            arr.push(isKey ? entry.key : entry.value);
            entry = entry.next;
        }
        return arr;
    },
    
    each: function(fn, scope, reverse) {
        var me = this,
            entry = reverse ? me.last : me.first,
            length = me.length;
        scope = scope || me;
        while (entry) {
            if (fn.call(scope, entry.key, entry.value, length) === false) {
                break;
            }
            entry = reverse ? entry.prev : entry.next;
        }
        return me;
    },
    
    findKey: function(value) {
        var key,
            map = this.map;
        for (key in map) {
            
            
            if (map.hasOwnProperty(key) && map[key].value === value) {
                return key;
            }
        }
        return undefined;
    },
    
    clone: function() {
        var newCache = new this.self(this.initialConfig),
            map = this.map,
            key;
        newCache.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                newCache.add(key, map[key].value);
            }
        }
        newCache.resumeEvents();
        return newCache;
    },
    
    prune: function() {
        var me = this,
            max = me.getMaxSize(),
            purgeCount = max ? (me.length - max) : 0;
        if (purgeCount > 0) {
            for (; me.first && purgeCount; purgeCount--) {
                me.removeAtKey(me.first.key);
            }
        }
    }
});


Ext.define('Ext.ComponentQuery', {
    singleton: true,
    requires: [
        'Ext.ComponentManager',
        'Ext.util.Operators',
        'Ext.util.LruCache'
    ]
}, function() {
    var cq = this,
        queryOperators = Ext.util.Operators,
        nthRe = /(\d*)n\+?(\d*)/,
        nthRe2 = /\D/,
        stripLeadingSpaceRe = /^(\s)+/,
        unescapeRe = /\\(.)/g,
        regexCache = new Ext.util.LruCache({
            maxSize: 100
        }),
        
        
        filterFnPattern = [
            'var r = [],',
            'i = 0,',
            'it = items,',
            'l = it.length,',
            'c;',
            'for (; i < l; i++) {',
            'c = it[i];',
            'if (c.{0}) {',
            'r.push(c);',
            '}',
            '}',
            'return r;'
        ].join(''),
        filterItems = function(items, operation) {
            
            
            
            return operation.method.apply(this, [
                items
            ].concat(operation.args));
        },
        getItems = function(items, mode) {
            var result = [],
                i = 0,
                length = items.length,
                candidate,
                deep = mode !== '>';
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getRefItems) {
                    result = result.concat(candidate.getRefItems(deep));
                }
            }
            return result;
        },
        getAncestors = function(items) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                while (!!(candidate = candidate.getRefOwner())) {
                    result.push(candidate);
                }
            }
            return result;
        },
        
        filterByXType = function(items, xtype, shallow) {
            if (xtype === '*') {
                return items.slice();
            } else {
                var result = [],
                    i = 0,
                    length = items.length,
                    candidate;
                for (; i < length; i++) {
                    candidate = items[i];
                    if (candidate.isXType(xtype, shallow)) {
                        result.push(candidate);
                    }
                }
                return result;
            }
        },
        
        filterByAttribute = function(items, property, operator, compareTo) {
            var result = [],
                i = 0,
                length = items.length,
                mustBeOwnProperty, presenceOnly, candidate, propValue, j, propLen, config;
            
            if (property.charAt(0) === '@') {
                mustBeOwnProperty = true;
                property = property.substr(1);
            }
            if (property.charAt(0) === '?') {
                mustBeOwnProperty = true;
                presenceOnly = true;
                property = property.substr(1);
            }
            for (; i < length; i++) {
                candidate = items[i];
                config = candidate.self.$config.configs[property];
                if (config) {
                    propValue = candidate[config.names.get]();
                } else if (mustBeOwnProperty && !candidate.hasOwnProperty(property)) {
                    
                    continue;
                } else {
                    propValue = candidate[property];
                }
                if (presenceOnly) {
                    result.push(candidate);
                }
                
                else if (operator === '~=') {
                    if (propValue) {
                        
                        if (!Ext.isArray(propValue)) {
                            propValue = propValue.split(' ');
                        }
                        for (j = 0 , propLen = propValue.length; j < propLen; j++) {
                            if (queryOperators[operator](Ext.coerce(propValue[j], compareTo), compareTo)) {
                                result.push(candidate);
                                break;
                            }
                        }
                    }
                } else if (operator === '/=') {
                    if (propValue != null && compareTo.test(propValue)) {
                        result.push(candidate);
                    }
                } else if (!compareTo ? !!candidate[property] : queryOperators[operator](Ext.coerce(propValue, compareTo), compareTo)) {
                    result.push(candidate);
                }
            }
            return result;
        },
        
        filterById = function(items, id) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getItemId() === id) {
                    result.push(candidate);
                }
            }
            return result;
        },
        
        filterByPseudo = function(items, name, value) {
            return cq.pseudos[name](items, value);
        },
        
        
        modeRe = /^(\s?([>\^])\s?|\s|$)/,
        
        tokenRe = /^(#)?((?:\\\.|[\w\-])+|\*)(?:\((true|false)\))?/,
        matchers = [
            {
                
                re: /^\.((?:\\\.|[\w\-])+)(?:\((true|false)\))?/,
                method: filterByXType,
                argTransform: function(args) {
                    var selector = args[0];
                    Ext.log.warn('"' + selector + '" ComponentQuery selector style is deprecated,' + ' use "' + selector.replace(/^\./, '') + '" without the leading dot instead');
                    if (args[1] !== undefined) {
                        args[1] = args[1].replace(unescapeRe, '$1');
                    }
                    return args.slice(1);
                }
            },
            {
                
                
                
                
                
                
                
                
                re: /^(?:\[((?:[@?$])?[\w\-]*)\s*(?:([\^$*~%!\/]?=)\s*(['"])?((?:\\\]|.)*?)\3)?(?!\\)\])/,
                method: filterByAttribute,
                argTransform: function(args) {
                    var selector = args[0],
                        property = args[1],
                        operator = args[2],
                        quote = args[3],
                        compareTo = args[4],
                        compareRe;
                    
                    if (compareTo !== undefined) {
                        compareTo = compareTo.replace(unescapeRe, '$1');
                        var format = Ext.String.format,
                            msg = "ComponentQuery selector '{0}' has an unescaped ({1}) character at the {2} " + "of the attribute value pattern. Usually that indicates an error " + "where the opening quote is not followed by the closing quote. " + "If you need to match a ({1}) character at the {2} of the attribute " + "value, escape the quote character in your pattern: (\\{1})",
                            match;
                        if (match = /^(['"]).*?[^'"]$/.exec(compareTo)) {
                            Ext.log.warn(format(msg, selector, match[1], 'beginning'));
                        } else if (match = /^[^'"].*?(['"])$/.exec(compareTo)) {
                            Ext.log.warn(format(msg, selector, match[1], 'end'));
                        }
                    }
                    if (operator === '/=') {
                        compareRe = regexCache.get(compareTo);
                        if (compareRe) {
                            compareTo = compareRe;
                        } else {
                            compareTo = regexCache.add(compareTo, new RegExp(compareTo));
                        }
                    }
                    return [
                        property,
                        operator,
                        compareTo
                    ];
                }
            },
            {
                
                re: /^#((?:\\\.|[\w\-])+)/,
                method: filterById
            },
            {
                
                re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
                method: filterByPseudo,
                argTransform: function(args) {
                    if (args[2] !== undefined) {
                        args[2] = args[2].replace(unescapeRe, '$1');
                    }
                    return args.slice(1);
                }
            },
            {
                
                re: /^(?:\{([^\}]+)\})/,
                method: filterFnPattern
            }
        ];
    
    cq.Query = Ext.extend(Object, {
        constructor: function(cfg) {
            cfg = cfg || {};
            Ext.apply(this, cfg);
        },
        
        
        
        
        
        
        
        
        execute: function(root) {
            var operations = this.operations,
                result = [],
                op, i, len;
            for (i = 0 , len = operations.length; i < len; i++) {
                op = operations[i];
                result = result.concat(this._execute(root, op));
            }
            return result;
        },
        _execute: function(root, operations) {
            var i = 0,
                length = operations.length,
                operation, workingItems;
            
            if (!root) {
                workingItems = Ext.ComponentManager.getAll();
            }
            
            else if (Ext.isIterable(root)) {
                workingItems = root;
            }
            
            else if (root.isMixedCollection) {
                workingItems = root.items;
            }
            
            
            for (; i < length; i++) {
                operation = operations[i];
                
                
                
                
                
                
                if (operation.mode === '^') {
                    workingItems = getAncestors(workingItems || [
                        root
                    ]);
                } else if (operation.mode) {
                    workingItems = getItems(workingItems || [
                        root
                    ], operation.mode);
                } else {
                    workingItems = filterItems(workingItems || getItems([
                        root
                    ]), operation);
                }
                
                
                if (i === length - 1) {
                    return workingItems;
                }
            }
            return [];
        },
        is: function(component) {
            var operations = this.operations,
                result = false,
                len = operations.length,
                op, i;
            if (len === 0) {
                return true;
            }
            for (i = 0; i < len; i++) {
                op = operations[i];
                result = this._is(component, op);
                if (result) {
                    return result;
                }
            }
            return false;
        },
        _is: function(component, operations) {
            var len = operations.length,
                active = [
                    component
                ],
                operation, i, j, mode, matches, items, item;
            
            for (i = len - 1; i >= 0; --i) {
                operation = operations[i];
                mode = operation.mode;
                
                if (mode) {
                    if (mode === '^') {
                        active = getItems(active, ' ');
                    } else if (mode === '>') {
                        items = [];
                        for (j = 0 , len = active.length; j < len; ++j) {
                            item = active[j].getRefOwner();
                            if (item) {
                                items.push(item);
                            }
                        }
                        active = items;
                    } else {
                        active = getAncestors(active);
                    }
                    
                    if (active.length === 0) {
                        return false;
                    }
                } else {
                    active = filterItems(active, operation);
                    if (active.length === 0) {
                        return false;
                    }
                }
            }
            return true;
        },
        getMatches: function(components, operations) {
            var len = operations.length,
                i;
            for (i = 0; i < len; ++i) {
                components = filterItems(components, operations[i]);
                
                
                if (components.length === 0) {
                    break;
                }
            }
            return components;
        },
        isMultiMatch: function() {
            return this.operations.length > 1;
        }
    });
    Ext.apply(this, {
        
        cache: new Ext.util.LruCache({
            maxSize: 100
        }),
        
        pseudos: {
            not: function(components, selector) {
                var CQ = Ext.ComponentQuery,
                    i = 0,
                    length = components.length,
                    results = [],
                    index = -1,
                    component;
                for (; i < length; ++i) {
                    component = components[i];
                    if (!CQ.is(component, selector)) {
                        results[++index] = component;
                    }
                }
                return results;
            },
            first: function(components) {
                var ret = [];
                if (components.length > 0) {
                    ret.push(components[0]);
                }
                return ret;
            },
            last: function(components) {
                var len = components.length,
                    ret = [];
                if (len > 0) {
                    ret.push(components[len - 1]);
                }
                return ret;
            },
            focusable: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;
                for (; i < len; i++) {
                    c = cmps[i];
                    
                    
                    if (c.isFocusable()) {
                        results.push(c);
                    }
                }
                return results;
            },
            "nth-child": function(c, a) {
                var result = [],
                    m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
                    f = (m[1] || 1) - 0,
                    l = m[2] - 0,
                    i, n, nodeIndex;
                for (i = 0; n = c[i]; i++) {
                    nodeIndex = i + 1;
                    if (f == 1) {
                        if (l == 0 || nodeIndex == l) {
                            result.push(n);
                        }
                    } else if ((nodeIndex + l) % f == 0) {
                        result.push(n);
                    }
                }
                return result;
            }
        },
        
        query: function(selector, root) {
            
            if (!selector) {
                return Ext.ComponentManager.all.getArray();
            }
            var results = [],
                noDupResults = [],
                dupMatcher = {},
                query = this.cache.get(selector),
                resultsLn, cmp, i;
            if (!query) {
                query = this.cache.add(selector, this.parse(selector));
            }
            results = query.execute(root);
            
            
            if (query.isMultiMatch()) {
                resultsLn = results.length;
                for (i = 0; i < resultsLn; i++) {
                    cmp = results[i];
                    if (!dupMatcher[cmp.id]) {
                        noDupResults.push(cmp);
                        dupMatcher[cmp.id] = true;
                    }
                }
                results = noDupResults;
            }
            return results;
        },
        
        visitPreOrder: function(selector, root, fn, scope, extraArgs) {
            this._visit(true, selector, root, fn, scope, extraArgs);
        },
        
        visitPostOrder: function(selector, root, fn, scope, extraArgs) {
            this._visit(false, selector, root, fn, scope, extraArgs);
        },
        
        
        _visit: function(preOrder, selector, root, fn, scope, extraArgs) {
            var me = this,
                query = me.cache.get(selector),
                callArgs = [
                    root
                ],
                children,
                len = 0,
                i, rootMatch;
            if (!query) {
                query = me.cache.add(selector, me.parse(selector));
            }
            rootMatch = query.is(root);
            if (root.getRefItems) {
                children = root.getRefItems();
                len = children.length;
            }
            
            if (extraArgs) {
                Ext.Array.push(callArgs, extraArgs);
            }
            if (preOrder) {
                if (rootMatch) {
                    if (fn.apply(scope || root, callArgs) === false) {
                        return false;
                    }
                }
            }
            for (i = 0; i < len; i++) {
                if (me._visit.call(me, preOrder, selector, children[i], fn, scope, extraArgs) === false) {
                    return false;
                }
            }
            if (!preOrder) {
                if (rootMatch) {
                    if (fn.apply(scope || root, callArgs) === false) {
                        return false;
                    }
                }
            }
        },
        
        is: function(component, selector) {
            if (!selector) {
                return true;
            }
            var query = this.cache.get(selector);
            if (!query) {
                query = this.cache.add(selector, this.parse(selector));
            }
            return query.is(component);
        },
        parse: function(selector) {
            var operations = [],
                selectors, sel, i, len;
            selectors = Ext.splitAndUnescape(selector, ',');
            for (i = 0 , len = selectors.length; i < len; i++) {
                
                sel = Ext.String.trim(selectors[i]);
                
                
                
                if (sel === '') {
                    Ext.Error.raise('Invalid ComponentQuery selector: ""');
                }
                operations.push(this._parse(sel));
            }
            
            
            return new cq.Query({
                operations: operations
            });
        },
        _parse: function(selector) {
            var operations = [],
                trim = Ext.String.trim,
                length = matchers.length,
                lastSelector, tokenMatch, token, matchedChar, modeMatch, selectorMatch, transform, i, matcher, method, args;
            
            
            
            while (selector && lastSelector !== selector) {
                lastSelector = selector;
                
                tokenMatch = selector.match(tokenRe);
                if (tokenMatch) {
                    matchedChar = tokenMatch[1];
                    token = trim(tokenMatch[2]).replace(unescapeRe, '$1');
                    
                    if (matchedChar === '#') {
                        operations.push({
                            method: filterById,
                            args: [
                                token
                            ]
                        });
                    } else 
                    
                    {
                        operations.push({
                            method: filterByXType,
                            args: [
                                token,
                                Boolean(tokenMatch[3])
                            ]
                        });
                    }
                    
                    selector = selector.replace(tokenMatch[0], '').replace(stripLeadingSpaceRe, '$1');
                }
                
                
                
                while (!(modeMatch = selector.match(modeRe))) {
                    
                    
                    for (i = 0; selector && i < length; i++) {
                        matcher = matchers[i];
                        selectorMatch = selector.match(matcher.re);
                        method = matcher.method;
                        transform = matcher.argTransform;
                        
                        
                        
                        if (selectorMatch) {
                            
                            if (transform) {
                                args = transform(selectorMatch);
                            } else {
                                args = selectorMatch.slice(1);
                            }
                            operations.push({
                                method: Ext.isString(matcher.method) ? 
                                
                                
                                Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [
                                    method
                                ].concat(selectorMatch.slice(1)))) : matcher.method,
                                args: args
                            });
                            selector = selector.replace(selectorMatch[0], '').replace(stripLeadingSpaceRe, '$1');
                            break;
                        }
                        
                        
                        if (i === (length - 1)) {
                            Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
                        }
                    }
                }
                
                
                
                
                if (modeMatch[1]) {
                    
                    operations.push({
                        mode: modeMatch[2] || modeMatch[1]
                    });
                    
                    selector = selector.replace(modeMatch[0], '').replace(stripLeadingSpaceRe, '');
                }
            }
            return operations;
        }
    });
});


Ext.define('Ext.Evented', {
    alternateClassName: 'Ext.EventedBase',
    mixins: [
        'Ext.mixin.Observable'
    ],
    statics: {
        generateSetter: function(cfg) {
            var names = cfg.names,
                name = cfg.name,
                prefixedName = names.internal,
                applyName = names.apply,
                changeEventName = names.changeEvent,
                doSetName = names.doSet;
            return function(value) {
                var me = this,
                    internalName = me.$configPrefixed ? prefixedName : name,
                    initialized = me.initialized,
                    oldValue,
                    applier = me[applyName];
                if (applier) {
                    value = applier.call(me, value, me[internalName]);
                    if (value === undefined) {
                        return me;
                    }
                }
                
                
                oldValue = me[internalName];
                if (value !== oldValue) {
                    if (initialized) {
                        me.fireAction(changeEventName, [
                            me,
                            value,
                            oldValue
                        ], me.doSet, me, {
                            nameMap: names
                        });
                    } else {
                        me[internalName] = value;
                        if (me[doSetName]) {
                            me[doSetName](value, oldValue);
                        }
                    }
                }
                return this;
            };
        }
    },
    initialized: false,
    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);
        this.initialized = true;
    },
    doSet: function(me, value, oldValue, options) {
        var nameMap = options.nameMap;
        me[nameMap.internal] = value;
        if (me[nameMap.doSet]) {
            me[nameMap.doSet].call(this, value, oldValue);
        }
    },
    onClassExtended: function(cls, data) {
        if (!data.hasOwnProperty('eventedConfig')) {
            return;
        }
        var config = data.config,
            eventedConfig = data.eventedConfig,
            cacheName = 'eventedSetter',
            name, cfg;
        if (config) {
            Ext.applyIf(config, eventedConfig);
        } else {
            cls.addConfig(eventedConfig);
        }
        
        for (name in eventedConfig) {
            if (eventedConfig.hasOwnProperty(name)) {
                cfg = Ext.Config.get(name);
                data[cfg.names.set] = cfg[cacheName] || (cfg[cacheName] = this.generateSetter(cfg));
            }
        }
    }
});


Ext.define('Ext.util.Positionable', {
    mixinId: 'positionable',
    _positionTopLeft: [
        'position',
        'top',
        'left'
    ],
    _alignRe: /^([a-z]+)-([a-z]+)(\?)?$/,
    
    
    afterSetPosition: Ext.emptyFn,
    
    
    
    
    getAnchorToXY: function() {
        Ext.Error.raise("getAnchorToXY is not implemented in " + this.$className);
    },
    
    getBorderPadding: function() {
        Ext.Error.raise("getBorderPadding is not implemented in " + this.$className);
    },
    
    getLocalX: function() {
        Ext.Error.raise("getLocalX is not implemented in " + this.$className);
    },
    
    getLocalXY: function() {
        Ext.Error.raise("getLocalXY is not implemented in " + this.$className);
    },
    
    getLocalY: function() {
        Ext.Error.raise("getLocalY is not implemented in " + this.$className);
    },
    
    getX: function() {
        Ext.Error.raise("getX is not implemented in " + this.$className);
    },
    
    getXY: function() {
        Ext.Error.raise("getXY is not implemented in " + this.$className);
    },
    
    getY: function() {
        Ext.Error.raise("getY is not implemented in " + this.$className);
    },
    
    setLocalX: function() {
        Ext.Error.raise("setLocalX is not implemented in " + this.$className);
    },
    
    setLocalXY: function() {
        Ext.Error.raise("setLocalXY is not implemented in " + this.$className);
    },
    
    setLocalY: function() {
        Ext.Error.raise("setLocalY is not implemented in " + this.$className);
    },
    
    setX: function() {
        Ext.Error.raise("setX is not implemented in " + this.$className);
    },
    
    setXY: function() {
        Ext.Error.raise("setXY is not implemented in " + this.$className);
    },
    
    setY: function() {
        Ext.Error.raise("setY is not implemented in " + this.$className);
    },
    
    
    
    
    
    adjustForConstraints: function(xy, parent) {
        var vector = this.getConstrainVector(parent, xy);
        if (vector) {
            xy[0] += vector[0];
            xy[1] += vector[1];
        }
        return xy;
    },
    
    alignTo: function(element, position, offsets, 
    animate) {
        var me = this,
            el = me.el;
        return me.setXY(me.getAlignToXY(element, position, offsets), el.anim && !!animate ? el.anim(animate) : false);
    },
    
    calculateAnchorXY: function(anchor, extraX, extraY, mySize) {
        
        
        var me = this,
            el = me.el,
            doc = document,
            isViewport = (el.dom === doc.body || el.dom === doc),
            round = Math.round,
            xy, myWidth, myHeight;
        anchor = (anchor || "tl").toLowerCase();
        mySize = mySize || {};
        myWidth = mySize.width || (isViewport ? Ext.Element.getViewportWidth() : me.getWidth());
        myHeight = mySize.height || (isViewport ? Ext.Element.getViewportHeight() : me.getHeight());
        
        
        switch (anchor) {
            case 'tl':
                xy = [
                    0,
                    0
                ];
                break;
            case 'bl':
                xy = [
                    0,
                    myHeight
                ];
                break;
            case 'tr':
                xy = [
                    myWidth,
                    0
                ];
                break;
            case 'c':
                xy = [
                    round(myWidth * 0.5),
                    round(myHeight * 0.5)
                ];
                break;
            case 't':
                xy = [
                    round(myWidth * 0.5),
                    0
                ];
                break;
            case 'l':
                xy = [
                    0,
                    round(myHeight * 0.5)
                ];
                break;
            case 'r':
                xy = [
                    myWidth,
                    round(myHeight * 0.5)
                ];
                break;
            case 'b':
                xy = [
                    round(myWidth * 0.5),
                    myHeight
                ];
                break;
            case 'tc':
                xy = [
                    round(myWidth * 0.5),
                    0
                ];
                break;
            case 'bc':
                xy = [
                    round(myWidth * 0.5),
                    myHeight
                ];
                break;
            case 'br':
                xy = [
                    myWidth,
                    myHeight
                ];
        }
        return [
            xy[0] + extraX,
            xy[1] + extraY
        ];
    },
    
    convertPositionSpec: Ext.identityFn,
    
    getAlignToXY: function(alignToEl, posSpec, offset) {
        var me = this,
            constrainToEl, constrainTo, alignMatch, myPosition, alignToElPosition, myWidth, myHeight, alignToElRegion, swapY, swapX, constrain, align1, align2, p1y, p1x, p2y, p2x, x, y;
        alignToEl = Ext.get(alignToEl.el || alignToEl);
        if (!alignToEl || !alignToEl.dom) {
            Ext.Error.raise({
                sourceClass: 'Ext.util.Positionable',
                sourceMethod: 'getAlignToXY',
                msg: 'Attempted to align an element that doesn\'t exist'
            });
        }
        offset = offset || [
            0,
            0
        ];
        posSpec = (!posSpec || posSpec === "?" ? "tl-bl?" : (!(/-/).test(posSpec) && posSpec !== "" ? "tl-" + posSpec : posSpec || "tl-bl")).toLowerCase();
        posSpec = me.convertPositionSpec(posSpec);
        alignMatch = posSpec.match(me._alignRe);
        if (!alignMatch) {
            Ext.Error.raise({
                sourceClass: 'Ext.util.Positionable',
                sourceMethod: 'getAlignToXY',
                el: alignToEl,
                position: posSpec,
                offset: offset,
                msg: 'Attemmpted to align an element with an invalid position: "' + posSpec + '"'
            });
        }
        align1 = alignMatch[1];
        align2 = alignMatch[2];
        constrain = !!alignMatch[3];
        
        
        myPosition = me.getAnchorXY(align1, true);
        alignToElPosition = me.getAnchorToXY(alignToEl, align2, false);
        x = alignToElPosition[0] - myPosition[0] + offset[0];
        y = alignToElPosition[1] - myPosition[1] + offset[1];
        
        if (constrain) {
            
            
            
            
            constrainToEl = me.constrainTo || me.container || me.el.parent();
            constrainToEl = Ext.get(constrainToEl.el || constrainToEl);
            constrainTo = constrainToEl.getViewRegion();
            constrainTo.right = constrainTo.left + constrainToEl.el.dom.clientWidth;
            myWidth = me.getWidth();
            myHeight = me.getHeight();
            alignToElRegion = alignToEl.getRegion();
            
            
            
            
            p1y = align1.charAt(0);
            p1x = align1.charAt(align1.length - 1);
            p2y = align2.charAt(0);
            p2x = align2.charAt(align2.length - 1);
            
            
            swapY = (x < alignToElRegion.right && x + myWidth >= alignToElRegion.left) && ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t"));
            
            
            swapX = (y < alignToElRegion.bottom && y + myHeight >= alignToElRegion.top) && ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r"));
            if (x + myWidth > constrainTo.right) {
                if (swapX) {
                    x = alignToElRegion.left - myWidth;
                    
                    swapX = false;
                } else {
                    x = constrainTo.right - myWidth;
                }
            }
            if (x < constrainTo.left) {
                x = swapX ? alignToElRegion.right : constrainTo.left;
            }
            if (y + myHeight > constrainTo.bottom) {
                if (swapY) {
                    y = alignToElRegion.top - myHeight;
                    
                    swapY = false;
                } else {
                    y = constrainTo.bottom - myHeight;
                }
            }
            if (y < constrainTo.top) {
                y = swapY ? alignToElRegion.bottom : constrainTo.top;
            }
        }
        return [
            x,
            y
        ];
    },
    
    getAnchorXY: function(anchor, local, mySize) {
        var me = this,
            myPos = me.getXY(),
            el = me.el,
            doc = document,
            isViewport = el.dom == doc.body || el.dom == doc,
            scroll = el.getScroll(),
            extraX = isViewport ? scroll.left : local ? 0 : myPos[0],
            extraY = isViewport ? scroll.top : local ? 0 : myPos[1];
        return me.calculateAnchorXY(anchor, extraX, extraY, mySize);
    },
    
    getBox: function(contentBox, local) {
        var me = this,
            xy = local ? me.getLocalXY() : me.getXY(),
            x = xy[0],
            y = xy[1],
            w = me.getWidth(),
            h = me.getHeight(),
            borderPadding, beforeX, beforeY;
        if (contentBox) {
            borderPadding = me.getBorderPadding();
            beforeX = borderPadding.beforeX;
            beforeY = borderPadding.beforeY;
            x += beforeX;
            y += beforeY;
            w -= (beforeX + borderPadding.afterX);
            h -= (beforeY + borderPadding.afterY);
        }
        return {
            x: x,
            left: x,
            0: x,
            y: y,
            top: y,
            1: y,
            width: w,
            height: h,
            right: x + w,
            bottom: y + h
        };
    },
    
    calculateConstrainedPosition: function(constrainTo, proposedPosition, local, proposedSize) {
        var me = this,
            vector,
            fp = me.floatParent,
            parentNode = fp ? fp.getTargetEl() : null,
            parentOffset, borderPadding, proposedConstrainPosition,
            xy = false;
        if (local && fp) {
            parentOffset = parentNode.getXY();
            borderPadding = parentNode.getBorderPadding();
            parentOffset[0] += borderPadding.beforeX;
            parentOffset[1] += borderPadding.beforeY;
            if (proposedPosition) {
                proposedConstrainPosition = [
                    proposedPosition[0] + parentOffset[0],
                    proposedPosition[1] + parentOffset[1]
                ];
            }
        } else {
            proposedConstrainPosition = proposedPosition;
        }
        
        
        
        constrainTo = constrainTo || me.constrainTo || parentNode || me.container || me.el.parent();
        vector = me.getConstrainVector(constrainTo, proposedConstrainPosition, proposedSize);
        
        xy = proposedPosition || me.getPosition(local);
        if (vector) {
            xy[0] += vector[0];
            xy[1] += vector[1];
        }
        return xy;
    },
    
    getConstrainVector: function(constrainTo, proposedPosition, proposedSize) {
        var thisRegion = this.getRegion(),
            vector = [
                0,
                0
            ],
            shadow = this.shadow,
            shadowSize = (shadow && this.constrainShadow && !shadow.disabled) ? shadow.getShadowSize() : undefined,
            overflowed = false,
            constrainSize,
            constraintInsets = this.constraintInsets;
        if (!(constrainTo instanceof Ext.util.Region)) {
            constrainTo = Ext.get(constrainTo.el || constrainTo);
            
            
            constrainSize = constrainTo.getViewSize();
            constrainTo = constrainTo.getViewRegion();
            constrainTo.right = constrainTo.left + constrainSize.width;
            constrainTo.bottom = constrainTo.top + constrainSize.height;
        }
        
        if (constraintInsets) {
            constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets);
            constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.length);
        }
        
        if (proposedPosition) {
            thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
        }
        
        if (proposedSize) {
            thisRegion.right = thisRegion.left + proposedSize[0];
            thisRegion.bottom = thisRegion.top + proposedSize[1];
        }
        
        if (shadowSize) {
            constrainTo.adjust(shadowSize[0], -shadowSize[1], -shadowSize[2], shadowSize[3]);
        }
        
        if (thisRegion.right > constrainTo.right) {
            overflowed = true;
            vector[0] = (constrainTo.right - thisRegion.right);
        }
        
        if (thisRegion.left + vector[0] < constrainTo.left) {
            overflowed = true;
            vector[0] = (constrainTo.left - thisRegion.left);
        }
        
        
        if (thisRegion.bottom > constrainTo.bottom) {
            overflowed = true;
            vector[1] = (constrainTo.bottom - thisRegion.bottom);
        }
        
        if (thisRegion.top + vector[1] < constrainTo.top) {
            overflowed = true;
            vector[1] = (constrainTo.top - thisRegion.top);
        }
        
        return overflowed ? vector : false;
    },
    
    getOffsetsTo: function(offsetsTo) {
        var o = this.getXY(),
            e = Ext.fly(offsetsTo.el || offsetsTo).getXY();
        return [
            o[0] - e[0],
            o[1] - e[1]
        ];
    },
    
    getRegion: function() {
        var box = this.getBox();
        return new Ext.util.Region(box.top,box.right,box.bottom,box.left);
    },
    
    getViewRegion: function() {
        var me = this,
            el = me.el,
            isBody = el.dom.nodeName === 'BODY',
            borderPadding, scroll, pos, top, left, width, height;
        
        if (isBody) {
            scroll = el.getScroll();
            left = scroll.left;
            top = scroll.top;
            width = Ext.Element.getViewportWidth();
            height = Ext.Element.getViewportHeight();
        } else {
            borderPadding = me.getBorderPadding();
            pos = me.getXY();
            left = pos[0] + borderPadding.beforeX;
            top = pos[1] + borderPadding.beforeY;
            width = me.getWidth(true);
            height = me.getHeight(true);
        }
        return new Ext.util.Region(top,left + width,top + height,left);
    },
    
    move: function(direction, distance, 
    animate) {
        var me = this,
            xy = me.getXY(),
            x = xy[0],
            y = xy[1],
            left = [
                x - distance,
                y
            ],
            right = [
                x + distance,
                y
            ],
            top = [
                x,
                y - distance
            ],
            bottom = [
                x,
                y + distance
            ],
            hash = {
                l: left,
                left: left,
                r: right,
                right: right,
                t: top,
                top: top,
                up: top,
                b: bottom,
                bottom: bottom,
                down: bottom
            };
        direction = direction.toLowerCase();
        me.setXY([
            hash[direction][0],
            hash[direction][1]
        ], animate);
    },
    
    setBox: function(box) {
        var me = this,
            x, y;
        if (box.isRegion) {
            box = {
                x: box.left,
                y: box.top,
                width: box.right - box.left,
                height: box.bottom - box.top
            };
        }
        me.constrainBox(box);
        x = box.x;
        y = box.y;
        
        
        me.setXY([
            x,
            y
        ]);
        me.setSize(box.width, box.height);
        me.afterSetPosition(x, y);
        return me;
    },
    
    constrainBox: function(box) {
        var me = this,
            constrainedPos, x, y;
        if (me.constrain || me.constrainHeader) {
            x = ('x' in box) ? box.x : box.left;
            y = ('y' in box) ? box.y : box.top;
            constrainedPos = me.calculateConstrainedPosition(null, [
                x,
                y
            ], false, [
                box.width,
                box.height
            ]);
            
            if (constrainedPos) {
                box.x = constrainedPos[0];
                box.y = constrainedPos[1];
            }
        }
    },
    
    translatePoints: function(x, y) {
        var pos = this.translateXY(x, y);
        return {
            left: pos.x,
            top: pos.y
        };
    },
    
    translateXY: function(x, y) {
        var me = this,
            el = me.el,
            styles = el.getStyle(me._positionTopLeft),
            relative = styles.position === 'relative',
            left = parseFloat(styles.left),
            top = parseFloat(styles.top),
            xy = me.getXY();
        if (Ext.isArray(x)) {
            y = x[1];
            x = x[0];
        }
        if (isNaN(left)) {
            left = relative ? 0 : el.dom.offsetLeft;
        }
        if (isNaN(top)) {
            top = relative ? 0 : el.dom.offsetTop;
        }
        left = (typeof x === 'number') ? x - xy[0] + left : undefined;
        top = (typeof y === 'number') ? y - xy[1] + top : undefined;
        return {
            x: left,
            y: top
        };
    }
});


Ext.define('Ext.overrides.util.Positionable', {
    override: 'Ext.util.Positionable',
    
    
    anchorTo: function(anchorToEl, alignment, offsets, animate, monitorScroll, callback) {
        var me = this,
            scroll = !Ext.isEmpty(monitorScroll),
            action = function() {
                me.alignTo(anchorToEl, alignment, offsets, animate);
                Ext.callback(callback, me);
            },
            anchor = me.getAnchor();
        
        me.removeAnchor();
        Ext.apply(anchor, {
            fn: action,
            scroll: scroll
        });
        Ext.on('resize', action, null);
        if (scroll) {
            Ext.getWin().on('scroll', action, null, {
                buffer: !isNaN(monitorScroll) ? monitorScroll : 50
            });
        }
        action();
        
        return me;
    },
    getAnchor: function() {
        var el = this.el,
            data, anchor;
        if (!el.dom) {
            return;
        }
        data = el.getData();
        anchor = data._anchor;
        if (!anchor) {
            anchor = data._anchor = {};
        }
        return anchor;
    },
    
    
    removeAnchor: function() {
        var anchor = this.getAnchor();
        if (anchor && anchor.fn) {
            Ext.un('resize', anchor.fn);
            if (anchor.scroll) {
                Ext.getWin().on('scroll', anchor.fn);
            }
            delete anchor.fn;
        }
        return this;
    },
    
    setBox: function(box, animate) {
        var me = this;
        if (box.isRegion) {
            box = {
                x: box.left,
                y: box.top,
                width: box.right - box.left,
                height: box.bottom - box.top
            };
        }
        if (animate) {
            me.constrainBox(box);
            me.animate(Ext.applyIf({
                to: box,
                listeners: {
                    afteranimate: Ext.Function.bind(me.afterSetPosition, me, [
                        box.x,
                        box.y
                    ])
                }
            }, animate));
        } else {
            me.callParent([
                box
            ]);
        }
        return me;
    }
});


Ext.define('Ext.dom.UnderlayPool', {
    
    constructor: function(elementConfig) {
        this.elementConfig = elementConfig;
        this.cache = [];
    },
    
    checkOut: function() {
        var el = this.cache.shift();
        if (!el) {
            el = Ext.Element.create(this.elementConfig);
            el.setVisibilityMode(2);
            
            el.dom.setAttribute('data-sticky', true);
        }
        return el;
    },
    
    checkIn: function(el) {
        this.cache.push(el);
    },
    
    reset: function() {
        var cache = this.cache,
            i = cache.length;
        while (i--) {
            cache[i].destroy();
        }
        this.cache = [];
    }
});


Ext.define('Ext.dom.Underlay', {
    requires: [
        'Ext.dom.UnderlayPool'
    ],
    
    
    constructor: function(config) {
        Ext.apply(this, config);
    },
    
    beforeShow: Ext.emptyFn,
    
    getInsertionTarget: function() {
        return this.target;
    },
    
    getPool: function() {
        return this.pool || (this.self.prototype.pool = new Ext.dom.UnderlayPool(this.elementConfig));
    },
    
    hide: function() {
        var me = this,
            el = me.el;
        if (el) {
            el.hide();
            me.getPool().checkIn(el);
            me.el = null;
            me.hidden = true;
        }
    },
    
    realign: function(x, y, width, height) {
        var me = this,
            el = me.el,
            target = me.target,
            offsets = me.offsets,
            max = Math.max;
        if (el) {
            if (x == null) {
                x = target.getX();
            }
            if (y == null) {
                y = target.getY();
            }
            if (width == null) {
                width = target.getWidth();
            }
            if (height == null) {
                height = target.getHeight();
            }
            if (offsets) {
                x = max(x + offsets.x, 0);
                y = max(y + offsets.y, 0);
                width = max(width + offsets.w, 0);
                height = max(height + offsets.h, 0);
            }
            el.setXY([
                x,
                y
            ]);
            el.setSize(width, height);
        }
    },
    
    setZIndex: function(zIndex) {
        this.zIndex = zIndex;
        if (this.el) {
            this.el.setStyle("z-index", zIndex);
        }
    },
    
    show: function() {
        var me = this,
            target = me.target,
            zIndex = me.zIndex,
            el = me.el,
            insertionTarget = me.getInsertionTarget().dom,
            dom;
        if (!el) {
            el = me.el = me.getPool().checkOut();
        }
        me.beforeShow();
        if (zIndex == null) {
            
            
            
            
            
            
            zIndex = (parseInt(target.getStyle("z-index"), 10));
        }
        if (zIndex) {
            el.setStyle("z-index", zIndex);
        }
        
        el.setStyle('position', me.fixed ? 'fixed' : '');
        dom = el.dom;
        if (dom.nextSibling !== insertionTarget) {
            
            
            
            target.dom.parentNode.insertBefore(dom, insertionTarget);
        }
        el.show();
        me.realign();
        me.hidden = false;
    }
});


Ext.define('Ext.dom.Shadow', {
    extend: 'Ext.dom.Underlay',
    alternateClassName: 'Ext.Shadow',
    
    mode: 'drop',
    
    offset: 4,
    cls: Ext.baseCSSPrefix + (!Ext.supports.CSS3BoxShadow ? 'ie' : 'css') + '-shadow',
    
    constructor: function(config) {
        var me = this,
            outerOffsets, offsets, offset, rad;
        me.callParent([
            config
        ]);
        me.elementConfig = {
            cls: me.cls,
            role: 'presentation'
        };
        offset = me.offset;
        rad = Math.floor(offset / 2);
        me.opacity = 50;
        switch (me.mode.toLowerCase()) {
            case "drop":
                outerOffsets = {
                    x: 0,
                    y: 0,
                    w: offset,
                    h: offset
                };
                if (Ext.supports.CSS3BoxShadow) {
                    offsets = {
                        x: offset,
                        y: offset,
                        h: -offset,
                        w: -offset
                    };
                } else {
                    offsets = {
                        x: -rad,
                        y: -rad,
                        h: -rad,
                        w: -rad
                    };
                };
                break;
            case "sides":
                outerOffsets = {
                    x: -offset,
                    y: 0,
                    w: offset * 2,
                    h: offset
                };
                if (Ext.supports.CSS3BoxShadow) {
                    offsets = {
                        x: 0,
                        y: offset,
                        h: -offset,
                        w: 0
                    };
                } else {
                    offsets = {
                        x: 1 + rad - 2 * offset,
                        y: -(1 + rad),
                        h: -1,
                        w: rad - 1
                    };
                };
                break;
            case "frame":
                outerOffsets = {
                    x: -offset,
                    y: -offset,
                    w: offset * 2,
                    h: offset * 2
                };
                if (Ext.supports.CSS3BoxShadow) {
                    offsets = {
                        x: 0,
                        y: 0,
                        h: 0,
                        w: 0
                    };
                } else {
                    offsets = {
                        x: 1 + rad - 2 * offset,
                        y: 1 + rad - 2 * offset,
                        h: offset - rad - 1,
                        w: offset - rad - 1
                    };
                };
                break;
            case "bottom":
                outerOffsets = {
                    x: -offset,
                    y: 0,
                    w: offset * 2,
                    h: offset
                };
                if (Ext.supports.CSS3BoxShadow) {
                    offsets = {
                        x: 0,
                        y: offset,
                        h: -offset,
                        w: 0
                    };
                } else {
                    offsets = {
                        x: 0,
                        y: offset,
                        h: 0,
                        w: 0
                    };
                };
                break;
        }
        
        me.offsets = offsets;
        
        me.outerOffsets = outerOffsets;
    },
    
    getShadowSize: function() {
        var me = this,
            offset = me.el ? me.offset : 0,
            result = [
                offset,
                offset,
                offset,
                offset
            ],
            mode = me.mode.toLowerCase();
        
        if (me.el && mode !== 'frame') {
            result[0] = 0;
            if (mode == 'drop') {
                result[3] = 0;
            }
        }
        return result;
    },
    
    boxShadowProperty: (function() {
        var property = 'boxShadow',
            style = document.documentElement.style;
        if (!('boxShadow' in style)) {
            if ('WebkitBoxShadow' in style) {
                
                property = 'WebkitBoxShadow';
            } else if ('MozBoxShadow' in style) {
                
                property = 'MozBoxShadow';
            }
        }
        return property;
    }()),
    beforeShow: function() {
        var me = this,
            style = me.el.dom.style,
            shim = me.shim;
        if (Ext.supports.CSS3BoxShadow) {
            style[me.boxShadowProperty] = '0 0 ' + (me.offset + 2) + 'px #888';
        } else {
            style.filter = "progid:DXImageTransform.Microsoft.alpha(opacity=" + me.opacity + ") progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (me.offset) + ")";
        }
        
        
        if (shim) {
            shim.realign();
        }
    },
    
    setOpacity: function(opacity) {
        var el = this.el;
        if (el) {
            if (Ext.isIE && !Ext.supports.CSS3BoxShadow) {
                opacity = Math.floor(opacity * 100 / 2) / 100;
            }
            this.opacity = opacity;
            el.setOpacity(opacity);
        }
    }
});


Ext.define('Ext.dom.Shim', {
    extend: 'Ext.dom.Underlay',
    cls: Ext.baseCSSPrefix + 'shim',
    constructor: function(config) {
        this.callParent([
            config
        ]);
        this.elementConfig = {
            tag: 'iframe',
            cls: this.cls,
            role: 'presentation',
            frameBorder: '0',
            src: Ext.SSL_SECURE_URL,
            
            tabindex: '-1'
        };
    },
    getInsertionTarget: function() {
        
        
        var shadow = this.shadow;
        return (shadow && shadow.el) || this.target;
    }
});


Ext.define('Ext.dom.ElementEvent', {
    extend: 'Ext.util.Event',
    addListener: function(fn, scope, options, caller, manager) {
        var me = this,
            added = false,
            name = me.name,
            captures, directs, directCaptures;
        options = options || {};
        if (options.delegated === false || Ext.event.publisher.Dom.instance.directEvents[name]) {
            if (options.capture) {
                directCaptures = me.directCaptures || (me.directCaptures = new Ext.util.Event(me.observable,name));
                added = directCaptures.addListener(fn, scope, options, caller, manager);
            } else {
                directs = me.directs || (me.directs = new Ext.util.Event(me.observable,name));
                added = directs.addListener(fn, scope, options, caller, manager);
            }
        } else if (options.capture) {
            captures = me.captures || (me.captures = new Ext.util.Event(me.observable,name));
            added = captures.addListener(fn, scope, options, caller, manager);
        } else {
            added = me.callParent([
                fn,
                scope,
                options,
                caller,
                manager
            ]);
        }
        return added;
    },
    removeListener: function(fn, scope) {
        var me = this,
            captures = me.captures,
            directs = me.directs,
            directCaptures = me.directCaptures,
            removed = false,
            index = me.findListener(fn, scope);
        if (index !== -1) {
            removed = me.callParent([
                fn,
                scope,
                index
            ]);
        } else {
            if (directs) {
                index = directs.findListener(fn, scope);
            }
            if (index !== -1) {
                removed = directs.removeListener(fn, scope, index);
            } else {
                if (captures) {
                    index = captures.findListener(fn, scope);
                }
                if (index !== -1) {
                    removed = captures.removeListener(fn, scope, index);
                } else if (directCaptures) {
                    index = directCaptures.findListener(fn, scope);
                    if (index !== -1) {
                        removed = directCaptures.removeListener(fn, scope, index);
                    }
                }
            }
        }
        return removed;
    },
    clearListeners: function() {
        var me = this,
            directCaptures = me.directCaptures,
            directs = me.directs,
            captures = me.captures;
        if (directCaptures) {
            directCaptures.clearListeners();
        }
        if (directs) {
            directs.clearListeners();
        }
        if (captures) {
            captures.clearListeners();
        }
        me.callParent();
    },
    suspend: function() {
        var me = this,
            directCaptures = me.directCaptures,
            directs = me.directs,
            captures = me.captures;
        if (directCaptures) {
            directCaptures.suspend();
        }
        if (directs) {
            directs.suspend();
        }
        if (captures) {
            captures.suspend();
        }
        me.callParent();
    },
    resume: function() {
        var me = this,
            directCaptures = me.directCaptures,
            directs = me.directs,
            captures = me.captures;
        if (directCaptures) {
            directCaptures.resume();
        }
        if (directs) {
            directs.resume();
        }
        if (captures) {
            captures.resume();
        }
        me.callParent();
    }
});


Ext.define('Ext.event.publisher.Publisher', {
    
    handledEvents: [],
    statics: {
        
        publishers: {},
        
        publishersByEvent: {}
    },
    constructor: function() {
        var me = this,
            type = me.type;
        
        me.handles = {};
        if (!type) {
            Ext.Error.raise("Event publisher '" + me.$className + "' defined without a 'type' property.");
        }
        if (me.self.instance) {
            Ext.Error.raise("Cannot create multiple instances of '" + me.$className + "'. " + "Use '" + me.$className + ".instance' to retrieve the singleton instance.");
        }
        me.registerEvents();
        Ext.event.publisher.Publisher.publishers[type] = me;
    },
    
    registerEvents: function(events) {
        var me = this,
            publishersByEvent = Ext.event.publisher.Publisher.publishersByEvent,
            handledEvents = events || me.handledEvents,
            ln = handledEvents.length,
            eventName, i;
        for (i = 0; i < ln; i++) {
            eventName = handledEvents[i];
            me.handles[eventName] = 1;
            publishersByEvent[eventName] = me;
        }
    },
    fire: function(element, eventName, args) {
        var event;
        if (element.hasListeners[eventName]) {
            event = element.events[eventName];
            if (event) {
                event.fire.apply(event, args);
            }
        }
    },
    subscribe: function() {
        Ext.Error.raise("Ext.event.publisher.Publisher subclass '" + this.$className + '" has no subscribe method.');
    },
    unsubscribe: function() {
        Ext.Error.raise("Ext.event.publisher.Publisher subclass '" + this.$className + '" has no unsubscribe method.');
    },
    destroy: Ext.emptyFn
});


Ext.define('Ext.util.Offset', {
    
    statics: {
        fromObject: function(obj) {
            return new this(obj.x,obj.y);
        }
    },
    
    constructor: function(x, y) {
        this.x = (x != null && !isNaN(x)) ? x : 0;
        this.y = (y != null && !isNaN(y)) ? y : 0;
        return this;
    },
    copy: function() {
        return new Ext.util.Offset(this.x,this.y);
    },
    copyFrom: function(p) {
        this.x = p.x;
        this.y = p.y;
    },
    toString: function() {
        return "Offset[" + this.x + "," + this.y + "]";
    },
    equals: function(offset) {
        if (!(offset instanceof this.statics())) {
            Ext.Error.raise('Offset must be an instance of Ext.util.Offset');
        }
        return (this.x == offset.x && this.y == offset.y);
    },
    round: function(to) {
        if (!isNaN(to)) {
            var factor = Math.pow(10, to);
            this.x = Math.round(this.x * factor) / factor;
            this.y = Math.round(this.y * factor) / factor;
        } else {
            this.x = Math.round(this.x);
            this.y = Math.round(this.y);
        }
    },
    isZero: function() {
        return this.x == 0 && this.y == 0;
    }
});


Ext.define('Ext.util.Region', {
    requires: [
        'Ext.util.Offset'
    ],
    isRegion: true,
    statics: {
        
        getRegion: function(el) {
            return Ext.fly(el).getRegion();
        },
        
        from: function(o) {
            return new this(o.top,o.right,o.bottom,o.left);
        }
    },
    
    
    constructor: function(t, r, b, l) {
        var me = this;
        me.y = me.top = me[1] = t;
        me.right = r;
        me.bottom = b;
        me.x = me.left = me[0] = l;
    },
    
    contains: function(region) {
        var me = this;
        return (region.x >= me.x && region.right <= me.right && region.y >= me.y && region.bottom <= me.bottom);
    },
    
    intersect: function(region) {
        var me = this,
            t = Math.max(me.y, region.y),
            r = Math.min(me.right, region.right),
            b = Math.min(me.bottom, region.bottom),
            l = Math.max(me.x, region.x);
        if (b > t && r > l) {
            return new this.self(t,r,b,l);
        } else {
            return false;
        }
    },
    
    union: function(region) {
        var me = this,
            t = Math.min(me.y, region.y),
            r = Math.max(me.right, region.right),
            b = Math.max(me.bottom, region.bottom),
            l = Math.min(me.x, region.x);
        return new this.self(t,r,b,l);
    },
    
    constrainTo: function(r) {
        var me = this,
            constrain = Ext.Number.constrain;
        me.top = me.y = constrain(me.top, r.y, r.bottom);
        me.bottom = constrain(me.bottom, r.y, r.bottom);
        me.left = me.x = constrain(me.left, r.x, r.right);
        me.right = constrain(me.right, r.x, r.right);
        return me;
    },
    
    adjust: function(t, r, b, l) {
        var me = this;
        me.top = me.y += t;
        me.left = me.x += l;
        me.right += r;
        me.bottom += b;
        return me;
    },
    
    getOutOfBoundOffset: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.getOutOfBoundOffsetX(p);
            } else {
                return this.getOutOfBoundOffsetY(p);
            }
        } else {
            p = axis;
            var d = new Ext.util.Offset();
            d.x = this.getOutOfBoundOffsetX(p.x);
            d.y = this.getOutOfBoundOffsetY(p.y);
            return d;
        }
    },
    
    getOutOfBoundOffsetX: function(p) {
        if (p <= this.x) {
            return this.x - p;
        } else if (p >= this.right) {
            return this.right - p;
        }
        return 0;
    },
    
    getOutOfBoundOffsetY: function(p) {
        if (p <= this.y) {
            return this.y - p;
        } else if (p >= this.bottom) {
            return this.bottom - p;
        }
        return 0;
    },
    
    isOutOfBound: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.isOutOfBoundX(p);
            } else {
                return this.isOutOfBoundY(p);
            }
        } else {
            p = axis;
            return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y));
        }
    },
    
    isOutOfBoundX: function(p) {
        return (p < this.x || p > this.right);
    },
    
    isOutOfBoundY: function(p) {
        return (p < this.y || p > this.bottom);
    },
    
    restrict: function(axis, p, factor) {
        if (Ext.isObject(axis)) {
            var newP;
            factor = p;
            p = axis;
            if (p.copy) {
                newP = p.copy();
            } else {
                newP = {
                    x: p.x,
                    y: p.y
                };
            }
            newP.x = this.restrictX(p.x, factor);
            newP.y = this.restrictY(p.y, factor);
            return newP;
        } else {
            if (axis == 'x') {
                return this.restrictX(p, factor);
            } else {
                return this.restrictY(p, factor);
            }
        }
    },
    
    restrictX: function(p, factor) {
        if (!factor) {
            factor = 1;
        }
        if (p <= this.x) {
            p -= (p - this.x) * factor;
        } else if (p >= this.right) {
            p -= (p - this.right) * factor;
        }
        return p;
    },
    
    restrictY: function(p, factor) {
        if (!factor) {
            factor = 1;
        }
        if (p <= this.y) {
            p -= (p - this.y) * factor;
        } else if (p >= this.bottom) {
            p -= (p - this.bottom) * factor;
        }
        return p;
    },
    
    getSize: function() {
        return {
            width: this.right - this.x,
            height: this.bottom - this.y
        };
    },
    
    copy: function() {
        return new this.self(this.y,this.right,this.bottom,this.x);
    },
    
    copyFrom: function(p) {
        var me = this;
        me.top = me.y = me[1] = p.y;
        me.right = p.right;
        me.bottom = p.bottom;
        me.left = me.x = me[0] = p.x;
        return this;
    },
    
    toString: function() {
        return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
    },
    
    translateBy: function(x, y) {
        if (arguments.length == 1) {
            y = x.y;
            x = x.x;
        }
        var me = this;
        me.top = me.y += y;
        me.right += x;
        me.bottom += y;
        me.left = me.x += x;
        return me;
    },
    
    round: function() {
        var me = this;
        me.top = me.y = Math.round(me.y);
        me.right = Math.round(me.right);
        me.bottom = Math.round(me.bottom);
        me.left = me.x = Math.round(me.x);
        return me;
    },
    
    equals: function(region) {
        return (this.top === region.top && this.right === region.right && this.bottom === region.bottom && this.left === region.left);
    }
});


Ext.define('Ext.util.Point', {
    extend: 'Ext.util.Region',
    radianToDegreeConstant: 180 / Math.PI,
    origin: {
        x: 0,
        y: 0
    },
    statics: {
        
        fromEvent: function(e) {
            var changedTouches = e.changedTouches,
                touch = (changedTouches && changedTouches.length > 0) ? changedTouches[0] : e;
            return this.fromTouch(touch);
        },
        
        fromTouch: function(touch) {
            return new this(touch.pageX,touch.pageY);
        },
        
        from: function(object) {
            if (!object) {
                return new this(0,0);
            }
            if (!(object instanceof this)) {
                return new this(object.x,object.y);
            }
            return object;
        }
    },
    
    constructor: function(x, y) {
        if (x == null) {
            x = 0;
        }
        if (y == null) {
            y = 0;
        }
        this.callParent([
            y,
            x,
            y,
            x
        ]);
    },
    
    clone: function() {
        return new this.self(this.x,this.y);
    },
    
    copy: function() {
        return this.clone.apply(this, arguments);
    },
    
    copyFrom: function(point) {
        this.x = point.x;
        this.y = point.y;
        return this;
    },
    
    toString: function() {
        return "Point[" + this.x + "," + this.y + "]";
    },
    
    isCloseTo: function(point, threshold) {
        if (typeof threshold == 'number') {
            return this.getDistanceTo(point) <= threshold;
        }
        var x = point.x,
            y = point.y,
            thresholdX = threshold.x,
            thresholdY = threshold.y;
        return (this.x <= x + thresholdX && this.x >= x - thresholdX && this.y <= y + thresholdY && this.y >= y - thresholdY);
    },
    
    isWithin: function() {
        return this.isCloseTo.apply(this, arguments);
    },
    
    isContainedBy: function(region) {
        if (!(region instanceof Ext.util.Region)) {
            region = Ext.get(region.el || region).getRegion();
        }
        return region.contains(this);
    },
    
    roundedEquals: function(point) {
        if (!point || typeof point !== 'object') {
            point = this.origin;
        }
        return (Math.round(this.x) === Math.round(point.x) && Math.round(this.y) === Math.round(point.y));
    },
    getDistanceTo: function(point) {
        if (!point || typeof point !== 'object') {
            point = this.origin;
        }
        var deltaX = this.x - point.x,
            deltaY = this.y - point.y;
        return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    },
    getAngleTo: function(point) {
        if (!point || typeof point !== 'object') {
            point = this.origin;
        }
        var deltaX = this.x - point.x,
            deltaY = this.y - point.y;
        return Math.atan2(deltaY, deltaX) * this.radianToDegreeConstant;
    }
}, function() {
    
    this.prototype.translate = this.prototype.translateBy;
});


Ext.define('Ext.event.Event', {
    alternateClassName: 'Ext.EventObjectImpl',
    requires: [
        'Ext.util.Point'
    ],
    
    
    
    
    
    
    
    
    
    
    
    isStopped: false,
    isEvent: true,
    statics: {
        resolveTextNode: function(node) {
            return (node && node.nodeType === 3) ? node.parentNode : node;
        },
        
        pointerEvents: {
            pointerdown: 1,
            pointermove: 1,
            pointerup: 1,
            pointercancel: 1,
            pointerover: 1,
            pointerout: 1,
            pointerenter: 1,
            pointerleave: 1,
            MSPointerDown: 1,
            MSPointerMove: 1,
            MSPointerUp: 1,
            MSPointerOver: 1,
            MSPointerOut: 1,
            MSPointerCancel: 1,
            MSPointerEnter: 1,
            MSPointerLeave: 1
        },
        
        mouseEvents: {
            mousedown: 1,
            mousemove: 1,
            mouseup: 1,
            mouseover: 1,
            mouseout: 1,
            mouseenter: 1,
            mouseleave: 1
        },
        
        
        
        
        
        clickEvents: {
            click: 1,
            dblclick: 1
        },
        
        touchEvents: {
            touchstart: 1,
            touchmove: 1,
            touchend: 1,
            touchcancel: 1
        },
        
        focusEvents: {
            focus: 1,
            blur: 1,
            focusin: 1,
            focusout: 1,
            focusenter: 1,
            focusleave: 1
        },
        
        
        
        
        pointerTypes: {
            2: 'touch',
            3: 'pen',
            4: 'mouse',
            touch: 'touch',
            pen: 'pen',
            mouse: 'mouse'
        }
    },
    constructor: function(event) {
        var me = this,
            self = me.self,
            resolveTextNode = me.self.resolveTextNode,
            changedTouches = event.changedTouches,
            
            
            
            coordinateOwner = changedTouches ? changedTouches[0] : event,
            type = event.type,
            pointerType, relatedTarget;
        me.pageX = coordinateOwner.pageX;
        me.pageY = coordinateOwner.pageY;
        me.target = me.delegatedTarget = resolveTextNode(event.target);
        relatedTarget = event.relatedTarget;
        if (relatedTarget) {
            me.relatedTarget = resolveTextNode(relatedTarget);
        }
        me.browserEvent = me.event = event;
        me.type = type;
        
        
        me.button = event.button || 0;
        me.shiftKey = event.shiftKey;
        
        me.ctrlKey = event.ctrlKey || event.metaKey || false;
        me.altKey = event.altKey;
        me.charCode = event.charCode;
        me.keyCode = event.keyCode;
        if (self.forwardTab !== undefined && self.focusEvents[type]) {
            me.forwardTab = self.forwardTab;
        }
        if (self.mouseEvents[type] || self.clickEvents[type]) {
            pointerType = 'mouse';
        } else if (self.pointerEvents[type]) {
            pointerType = self.pointerTypes[event.pointerType];
        } else if (self.touchEvents[type]) {
            pointerType = 'touch';
        }
        if (pointerType) {
            me.pointerType = pointerType;
        }
        me.timeStamp = me.time = +(event.timeStamp || new Date());
    },
    
    chain: function(props) {
        var e = Ext.Object.chain(this);
        e.parentEvent = this;
        
        return Ext.apply(e, props);
    },
    
    correctWheelDelta: function(delta) {
        var scale = this.WHEEL_SCALE,
            ret = Math.round(delta / scale);
        if (!ret && delta) {
            ret = (delta < 0) ? -1 : 1;
        }
        
        return ret;
    },
    
    getCharCode: function() {
        return this.charCode || this.keyCode;
    },
    
    getKey: function() {
        return this.keyCode || this.charCode;
    },
    
    getPoint: function() {
        var xy = this.getXY();
        return new Ext.util.Point(xy[0],xy[1]);
    },
    
    getRelatedTarget: function(selector, maxDepth, returnEl) {
        var relatedTarget = this.relatedTarget,
            target = null;
        if (relatedTarget) {
            if (selector) {
                target = Ext.fly(relatedTarget).findParent(selector, maxDepth, returnEl);
            } else {
                target = returnEl ? Ext.get(relatedTarget) : relatedTarget;
            }
        }
        return target;
    },
    
    getTarget: function(selector, maxDepth, returnEl) {
        return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
    },
    
    getTime: function() {
        return this.time;
    },
    
    getWheelDelta: function() {
        var deltas = this.getWheelDeltas();
        return deltas.y;
    },
    
    getWheelDeltas: function() {
        var me = this,
            event = me.browserEvent,
            dx = 0,
            dy = 0;
        
        if (Ext.isDefined(event.wheelDeltaX)) {
            
            dx = event.wheelDeltaX;
            dy = event.wheelDeltaY;
        } else if (event.wheelDelta) {
            
            dy = event.wheelDelta;
        } else if (event.detail) {
            
            dy = -event.detail;
            
            
            
            if (dy > 100) {
                dy = 3;
            } else if (dy < -100) {
                dy = -3;
            }
            
            
            if (Ext.isDefined(event.axis) && event.axis === event.HORIZONTAL_AXIS) {
                dx = dy;
                dy = 0;
            }
        }
        return {
            x: me.correctWheelDelta(dx),
            y: me.correctWheelDelta(dy)
        };
    },
    
    getX: function() {
        return this.getXY()[0];
    },
    
    getXY: function() {
        var me = this,
            xy = me.xy;
        if (!xy) {
            xy = me.xy = [
                me.pageX,
                me.pageY
            ];
            var x = xy[0],
                browserEvent, doc, docEl, body;
            
            if (!x && x !== 0) {
                browserEvent = me.browserEvent;
                doc = document;
                docEl = doc.documentElement;
                body = doc.body;
                xy[0] = browserEvent.clientX + (docEl && docEl.scrollLeft || body && body.scrollLeft || 0) - (docEl && docEl.clientLeft || body && body.clientLeft || 0);
                xy[1] = browserEvent.clientY + (docEl && docEl.scrollTop || body && body.scrollTop || 0) - (docEl && docEl.clientTop || body && body.clientTop || 0);
            }
        }
        return xy;
    },
    
    getY: function() {
        return this.getXY()[1];
    },
    
    hasModifier: function() {
        var me = this;
        return !!(me.ctrlKey || me.altKey || me.shiftKey || me.metaKey);
    },
    
    isNavKeyPress: function(scrollableOnly) {
        var me = this,
            k = me.keyCode;
        return (k >= 33 && k <= 40) || 
        (!scrollableOnly && (k === me.RETURN || k === me.TAB || k === me.ESC));
    },
    
    isSpecialKey: function() {
        var k = this.keyCode;
        return (this.type === 'keypress' && this.ctrlKey) || this.isNavKeyPress() || (k === this.BACKSPACE) || 
        (k >= 16 && k <= 20) || 
        (k >= 44 && k <= 46);
    },
    
    makeUnpreventable: function() {
        this.browserEvent.preventDefault = Ext.emptyFn;
    },
    
    preventDefault: function() {
        this.browserEvent.preventDefault();
        return this;
    },
    setCurrentTarget: function(target) {
        this.currentTarget = this.delegatedTarget = target;
    },
    
    stopEvent: function() {
        return this.preventDefault().stopPropagation();
    },
    
    stopPropagation: function() {
        var me = this,
            browserEvent = me.browserEvent,
            parentEvent = me.parentEvent;
        
        
        me.isStopped = true;
        
        
        
        
        
        
        
        
        
        
        if (parentEvent) {
            parentEvent.isStopped = true;
        }
        if (!browserEvent.stopPropagation) {
            
            browserEvent.cancelBubble = true;
            return me;
        }
        
        
        browserEvent.stopPropagation();
        return me;
    },
    
    within: function(el, related, allowEl) {
        var t;
        if (el) {
            t = related ? this.getRelatedTarget() : this.getTarget();
        }
        return t ? Ext.fly(el).contains(t) || !!(allowEl && t === Ext.getDom(el)) : false;
    },
    deprecated: {
        '4.0': {
            methods: {
                
                getPageX: 'getX',
                
                getPageY: 'getY'
            }
        }
    }
}, function(Event) {
    var prototype = Event.prototype,
        constants = {
            
            BACKSPACE: 8,
            
            TAB: 9,
            
            NUM_CENTER: 12,
            
            ENTER: 13,
            
            RETURN: 13,
            
            SHIFT: 16,
            
            CTRL: 17,
            
            ALT: 18,
            
            PAUSE: 19,
            
            CAPS_LOCK: 20,
            
            ESC: 27,
            
            SPACE: 32,
            
            PAGE_UP: 33,
            
            PAGE_DOWN: 34,
            
            END: 35,
            
            HOME: 36,
            
            LEFT: 37,
            
            UP: 38,
            
            RIGHT: 39,
            
            DOWN: 40,
            
            PRINT_SCREEN: 44,
            
            INSERT: 45,
            
            DELETE: 46,
            
            ZERO: 48,
            
            ONE: 49,
            
            TWO: 50,
            
            THREE: 51,
            
            FOUR: 52,
            
            FIVE: 53,
            
            SIX: 54,
            
            SEVEN: 55,
            
            EIGHT: 56,
            
            NINE: 57,
            
            A: 65,
            
            B: 66,
            
            C: 67,
            
            D: 68,
            
            E: 69,
            
            F: 70,
            
            G: 71,
            
            H: 72,
            
            I: 73,
            
            J: 74,
            
            K: 75,
            
            L: 76,
            
            M: 77,
            
            N: 78,
            
            O: 79,
            
            P: 80,
            
            Q: 81,
            
            R: 82,
            
            S: 83,
            
            T: 84,
            
            U: 85,
            
            V: 86,
            
            W: 87,
            
            X: 88,
            
            Y: 89,
            
            Z: 90,
            
            CONTEXT_MENU: 93,
            
            NUM_ZERO: 96,
            
            NUM_ONE: 97,
            
            NUM_TWO: 98,
            
            NUM_THREE: 99,
            
            NUM_FOUR: 100,
            
            NUM_FIVE: 101,
            
            NUM_SIX: 102,
            
            NUM_SEVEN: 103,
            
            NUM_EIGHT: 104,
            
            NUM_NINE: 105,
            
            NUM_MULTIPLY: 106,
            
            NUM_PLUS: 107,
            
            NUM_MINUS: 109,
            
            NUM_PERIOD: 110,
            
            NUM_DIVISION: 111,
            
            F1: 112,
            
            F2: 113,
            
            F3: 114,
            
            F4: 115,
            
            F5: 116,
            
            F6: 117,
            
            F7: 118,
            
            F8: 119,
            
            F9: 120,
            
            F10: 121,
            
            F11: 122,
            
            F12: 123,
            
            WHEEL_SCALE: (function() {
                var scale;
                if (Ext.isGecko) {
                    
                    scale = 3;
                } else if (Ext.isMac) {
                    
                    
                    
                    if (Ext.isSafari && Ext.webKitVersion >= 532) {
                        
                        
                        
                        
                        
                        
                        scale = 120;
                    } else {
                        
                        
                        scale = 12;
                    }
                    
                    
                    
                    
                    scale *= 3;
                } else {
                    
                    scale = 120;
                }
                return scale;
            }())
        };
    Ext.apply(Event, constants);
    Ext.apply(prototype, constants);
    
    prototype.getTrueXY = prototype.getXY;
});

Ext.define('Ext.overrides.event.Event', {
    override: 'Ext.event.Event',
    
    mousedownEvents: {
        mousedown: 1,
        pointerdown: 1,
        touchstart: 1
    },
    
    injectEvent: (function() {
        var API,
            dispatchers = {},
            
            crazyIEButtons;
        
        
        
        if (!Ext.isIE9m && document.createEvent) {
            
            API = {
                createHtmlEvent: function(doc, type, bubbles, cancelable) {
                    var event = doc.createEvent('HTMLEvents');
                    event.initEvent(type, bubbles, cancelable);
                    return event;
                },
                createMouseEvent: function(doc, type, bubbles, cancelable, detail, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget) {
                    var event = doc.createEvent('MouseEvents'),
                        view = doc.defaultView || window;
                    if (event.initMouseEvent) {
                        event.initMouseEvent(type, bubbles, cancelable, view, detail, clientX, clientY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
                    } else {
                        
                        event = doc.createEvent('UIEvents');
                        event.initEvent(type, bubbles, cancelable);
                        event.view = view;
                        event.detail = detail;
                        event.screenX = clientX;
                        event.screenY = clientY;
                        event.clientX = clientX;
                        event.clientY = clientY;
                        event.ctrlKey = ctrlKey;
                        event.altKey = altKey;
                        event.metaKey = metaKey;
                        event.shiftKey = shiftKey;
                        event.button = button;
                        event.relatedTarget = relatedTarget;
                    }
                    return event;
                },
                createUIEvent: function(doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEvent('UIEvents'),
                        view = doc.defaultView || window;
                    event.initUIEvent(type, bubbles, cancelable, view, detail);
                    return event;
                },
                fireEvent: function(target, type, event) {
                    target.dispatchEvent(event);
                }
            };
        } else if (document.createEventObject) {
            
            crazyIEButtons = {
                0: 1,
                1: 4,
                2: 2
            };
            API = {
                createHtmlEvent: function(doc, type, bubbles, cancelable) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },
                createMouseEvent: function(doc, type, bubbles, cancelable, detail, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    event.detail = detail;
                    event.screenX = clientX;
                    event.screenY = clientY;
                    event.clientX = clientX;
                    event.clientY = clientY;
                    event.ctrlKey = ctrlKey;
                    event.altKey = altKey;
                    event.shiftKey = shiftKey;
                    event.metaKey = metaKey;
                    event.button = crazyIEButtons[button] || button;
                    event.relatedTarget = relatedTarget;
                    
                    return event;
                },
                createUIEvent: function(doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },
                fireEvent: function(target, type, event) {
                    target.fireEvent('on' + type, event);
                }
            };
        }
        
        
        Ext.Object.each({
            load: [
                false,
                false
            ],
            unload: [
                false,
                false
            ],
            select: [
                true,
                false
            ],
            change: [
                true,
                false
            ],
            submit: [
                true,
                true
            ],
            reset: [
                true,
                false
            ],
            resize: [
                true,
                false
            ],
            scroll: [
                true,
                false
            ]
        }, function(name, value) {
            var bubbles = value[0],
                cancelable = value[1];
            dispatchers[name] = function(targetEl, srcEvent) {
                var e = API.createHtmlEvent(name, bubbles, cancelable);
                API.fireEvent(targetEl, name, e);
            };
        });
        
        
        function createMouseEventDispatcher(type, detail) {
            var cancelable = (type != 'mousemove');
            return function(targetEl, srcEvent) {
                var xy = srcEvent.getXY(),
                    e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable, detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey, srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button, srcEvent.relatedTarget);
                API.fireEvent(targetEl, type, e);
            };
        }
        Ext.each([
            'click',
            'dblclick',
            'mousedown',
            'mouseup',
            'mouseover',
            'mousemove',
            'mouseout'
        ], function(eventName) {
            dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
        });
        
        
        Ext.Object.each({
            focusin: [
                true,
                false
            ],
            focusout: [
                true,
                false
            ],
            activate: [
                true,
                true
            ],
            focus: [
                false,
                false
            ],
            blur: [
                false,
                false
            ]
        }, function(name, value) {
            var bubbles = value[0],
                cancelable = value[1];
            dispatchers[name] = function(targetEl, srcEvent) {
                var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
                API.fireEvent(targetEl, name, e);
            };
        });
        
        if (!API) {
            
            dispatchers = {};
            
            API = {};
        }
        function cannotInject(target, srcEvent) {}
        
        return function(target) {
            var me = this,
                dispatcher = dispatchers[me.type] || cannotInject,
                t = target ? (target.dom || target) : me.getTarget();
            dispatcher(t, me);
        };
    }()),
    
    preventDefault: function() {
        var me = this,
            event = me.browserEvent,
            unselectable, target;
        
        
        
        if (typeof event.type !== 'unknown') {
            if (event.preventDefault) {
                event.preventDefault();
            } else {
                
                
                
                
                
                
                
                
                if (event.type === 'mousedown') {
                    target = event.target;
                    unselectable = target.getAttribute('unselectable');
                    if (unselectable !== 'on') {
                        target.setAttribute('unselectable', 'on');
                        Ext.defer(function() {
                            target.setAttribute('unselectable', unselectable);
                        }, 1);
                    }
                }
                
                event.returnValue = false;
                
                
                if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
                    event.keyCode = -1;
                }
            }
        }
        return me;
    },
    stopPropagation: function() {
        var me = this,
            event = me.browserEvent;
        
        
        
        if (typeof event.type !== 'unknown') {
            if (me.mousedownEvents[me.type]) {
                
                
                Ext.GlobalEvents.fireMouseDown(me);
            }
            me.callParent();
        }
        return me;
    },
    deprecated: {
        '5.0': {
            methods: {
                
                clone: function() {
                    return new this.self(this.browserEvent,this);
                }
            }
        }
    }
}, function() {
    var Event = this,
        btnMap,
        onKeyDown = function(e) {
            if (e.keyCode === 9) {
                Event.forwardTab = !e.shiftKey;
            }
        },
        onKeyUp = function(e) {
            if (e.keyCode === 9) {
                delete Event.forwardTab;
            }
        };
    if (Ext.isIE9m) {
        btnMap = {
            0: 0,
            1: 0,
            4: 1,
            2: 2
        };
        Event.override({
            statics: {
                
                enableIEAsync: function(browserEvent) {
                    var name,
                        fakeEvent = {};
                    for (name in browserEvent) {
                        fakeEvent[name] = browserEvent[name];
                    }
                    return fakeEvent;
                }
            },
            constructor: function(event, info, touchesMap, identifiers) {
                var me = this;
                me.callParent([
                    event,
                    info,
                    touchesMap,
                    identifiers
                ]);
                me.button = btnMap[event.button];
                if (event.type === 'contextmenu') {
                    me.button = 2;
                }
                
                
                
                
                me.toElement = event.toElement;
                me.fromElement = event.fromElement;
            },
            mouseLeaveRe: /(mouseout|mouseleave)/,
            mouseEnterRe: /(mouseover|mouseenter)/,
            
            enableIEAsync: function(browserEvent) {
                this.browserEvent = this.self.enableIEAsync(browserEvent);
            },
            getRelatedTarget: function(selector, maxDepth, returnEl) {
                var me = this,
                    type, target;
                if (!me.relatedTarget) {
                    type = me.type;
                    if (me.mouseLeaveRe.test(type)) {
                        target = me.toElement;
                    } else if (me.mouseEnterRe.test(type)) {
                        target = me.fromElement;
                    }
                    if (target) {
                        me.relatedTarget = me.self.resolveTextNode(target);
                    }
                }
                return me.callParent([
                    selector,
                    maxDepth,
                    returnEl
                ]);
            }
        });
        
        
        
        document.attachEvent('onkeydown', onKeyDown);
        document.attachEvent('onkeyup', onKeyUp);
        window.attachEvent('onunload', function() {
            document.detachEvent('onkeydown', onKeyDown);
            document.detachEvent('onkeyup', onKeyUp);
        });
    } else if (document.addEventListener) {
        document.addEventListener('keydown', onKeyDown, true);
        document.addEventListener('keyup', onKeyUp, true);
    }
});


Ext.define('Ext.event.publisher.Dom', {
    extend: 'Ext.event.publisher.Publisher',
    requires: [
        'Ext.event.Event'
    ],
    type: 'dom',
    
    handledDomEvents: [],
    reEnterCount: 0,
    
    
    
    captureEvents: {
        resize: 1,
        focus: 1,
        blur: 1,
        paste: 1,
        input: 1,
        change: 1,
        animationstart: 1,
        animationend: 1,
        scroll: 1
    },
    
    
    directEvents: {
        mouseenter: 1,
        mouseleave: 1,
        pointerenter: 1,
        pointerleave: 1,
        MSPointerEnter: 1,
        MSPointerLeave: 1,
        load: 1,
        unload: 1,
        beforeunload: 1,
        error: 1,
        DOMContentLoaded: 1,
        DOMFrameContentLoaded: 1,
        hashchange: 1
    },
    
    blockedPointerEvents: {
        pointerover: 1,
        pointerout: 1,
        pointerenter: 1,
        pointerleave: 1,
        MSPointerOver: 1,
        MSPointerOut: 1,
        MSPointerEnter: 1,
        MSPointerLeave: 1
    },
    
    blockedCompatibilityMouseEvents: {
        mouseenter: 1,
        mouseleave: 1
    },
    constructor: function() {
        var me = this;
        me.bubbleSubscribers = {};
        me.captureSubscribers = {};
        me.directSubscribers = {};
        me.directCaptureSubscribers = {};
        
        
        
        me.delegatedListeners = {};
        me.initHandlers();
        Ext.onReady(me.onReady, me);
        me.callParent();
    },
    registerEvents: function() {
        var me = this,
            publishersByEvent = Ext.event.publisher.Publisher.publishersByEvent,
            domEvents = me.handledDomEvents,
            ln = domEvents.length,
            i = 0,
            eventName;
        for (; i < ln; i++) {
            eventName = domEvents[i];
            me.handles[eventName] = 1;
            publishersByEvent[eventName] = me;
        }
        this.callParent();
    },
    onReady: function() {
        var me = this,
            domEvents = me.handledDomEvents,
            ln, i;
        if (domEvents) {
            
            
            
            
            
            for (i = 0 , ln = domEvents.length; i < ln; i++) {
                me.addDelegatedListener(domEvents[i]);
            }
        }
        Ext.getWin().on('unload', me.destroy, me);
    },
    initHandlers: function() {
        var me = this;
        me.onDelegatedEvent = Ext.bind(me.onDelegatedEvent, me);
        me.onDirectEvent = Ext.bind(me.onDirectEvent, me);
        me.onDirectCaptureEvent = Ext.bind(me.onDirectCaptureEvent, me);
    },
    addDelegatedListener: function(eventName) {
        this.delegatedListeners[eventName] = 1;
        this.target.addEventListener(eventName, this.onDelegatedEvent, !!this.captureEvents[eventName]);
    },
    removeDelegatedListener: function(eventName) {
        delete this.delegatedListeners[eventName];
        this.target.removeEventListener(eventName, this.onDelegatedEvent, !!this.captureEvents[eventName]);
    },
    addDirectListener: function(eventName, element, capture) {
        element.dom.addEventListener(eventName, capture ? this.onDirectCaptureEvent : this.onDirectEvent, capture);
    },
    removeDirectListener: function(eventName, element, capture) {
        element.dom.removeEventListener(eventName, capture ? this.onDirectCaptureEvent : this.onDirectEvent, capture);
    },
    subscribe: function(element, eventName, delegated, capture) {
        var me = this,
            subscribers, id;
        if (delegated && !me.directEvents[eventName]) {
            
            subscribers = capture ? me.captureSubscribers : me.bubbleSubscribers;
            if (!me.handles[eventName] && !me.delegatedListeners[eventName]) {
                
                
                me.addDelegatedListener(eventName);
            }
            if (subscribers[eventName]) {
                ++subscribers[eventName];
            } else {
                subscribers[eventName] = 1;
            }
        } else {
            subscribers = capture ? me.directCaptureSubscribers : me.directSubscribers;
            id = element.id;
            
            
            
            
            subscribers = subscribers[eventName] || (subscribers[eventName] = {});
            if (subscribers[id]) {
                ++subscribers[id];
            } else {
                subscribers[id] = 1;
                me.addDirectListener(eventName, element, capture);
            }
        }
    },
    unsubscribe: function(element, eventName, delegated, capture) {
        var me = this,
            captureSubscribers, bubbleSubscribers, subscribers, id;
        if (delegated && !me.directEvents[eventName]) {
            captureSubscribers = me.captureSubscribers;
            bubbleSubscribers = me.bubbleSubscribers;
            subscribers = capture ? captureSubscribers : bubbleSubscribers;
            if (subscribers[eventName]) {
                --subscribers[eventName];
            }
            if (!me.handles[eventName] && !bubbleSubscribers[eventName] && !captureSubscribers[eventName]) {
                
                
                this.removeDelegatedListener(eventName);
            }
        } else {
            subscribers = capture ? me.directCaptureSubscribers : me.directSubscribers;
            id = element.id;
            subscribers = subscribers[eventName];
            if (subscribers[id]) {
                --subscribers[id];
            }
            if (!subscribers[id]) {
                
                
                delete subscribers[id];
                me.removeDirectListener(eventName, element, capture);
            }
        }
    },
    getPropagatingTargets: function(target) {
        var currentNode = target,
            targets = [],
            parentNode;
        while (currentNode) {
            targets.push(currentNode);
            parentNode = currentNode.parentNode;
            if (!parentNode) {
                
                
                
                
                
                parentNode = currentNode.defaultView;
            }
            currentNode = parentNode;
        }
        return targets;
    },
    publish: function(eventName, target, e) {
        var me = this,
            targets, el, i, ln;
        if (Ext.isArray(target)) {
            
            targets = target;
        } else if (me.captureEvents[eventName]) {
            el = Ext.cache[target.id];
            targets = el ? [
                el
            ] : [];
        } else {
            targets = me.getPropagatingTargets(target);
        }
        ln = targets.length;
        
        
        
        
        
        
        
        
        if (me.captureSubscribers[eventName]) {
            for (i = ln; i--; ) {
                el = Ext.cache[targets[i].id];
                if (el) {
                    me.fire(el, eventName, e, false, true);
                    if (e.isStopped) {
                        break;
                    }
                }
            }
        }
        
        
        if (!e.isStopped && me.bubbleSubscribers[eventName]) {
            for (i = 0; i < ln; i++) {
                el = Ext.cache[targets[i].id];
                if (el) {
                    me.fire(el, eventName, e, false, false);
                    if (e.isStopped) {
                        break;
                    }
                }
            }
        }
    },
    fire: function(element, eventName, e, direct, capture) {
        var event;
        if (element.hasListeners[eventName]) {
            event = element.events[eventName];
            if (event) {
                if (capture && direct) {
                    event = event.directCaptures;
                } else if (capture) {
                    event = event.captures;
                } else if (direct) {
                    event = event.directs;
                }
                
                
                if (event) {
                    e.setCurrentTarget(element.dom);
                    event.fire(e, e.target);
                }
            }
        }
    },
    onDelegatedEvent: function(e) {
        if (Ext.elevateFunction) {
            
            
            Ext.elevateFunction(this.doDelegatedEvent, this, [
                e
            ]);
        } else {
            this.doDelegatedEvent(e);
        }
    },
    doDelegatedEvent: function(e, invokeAfter) {
        var me = this,
            timeStamp = e.timeStamp;
        e = new Ext.event.Event(e);
        if (me.isEventBlocked(e)) {
            return false;
        }
        me.beforeEvent(e);
        Ext.frameStartTime = timeStamp;
        me.reEnterCount++;
        me.publish(e.type, e.target, e);
        me.reEnterCount--;
        if (invokeAfter !== false) {
            me.afterEvent(e);
        }
        return e;
    },
    
    onDirectEvent: function(e) {
        if (Ext.elevateFunction) {
            
            
            Ext.elevateFunction(this.doDirectEvent, this, [
                e,
                false
            ]);
        } else {
            this.doDirectEvent(e, false);
        }
    },
    
    
    onDirectCaptureEvent: function(e) {
        if (Ext.elevateFunction) {
            
            
            Ext.elevateFunction(this.doDirectEvent, this, [
                e,
                true
            ]);
        } else {
            this.doDirectEvent(e, true);
        }
    },
    doDirectEvent: function(e, capture) {
        var me = this,
            currentTarget = e.currentTarget,
            timeStamp = e.timeStamp,
            currentTarget;
        e = new Ext.event.Event(e);
        if (me.isEventBlocked(e)) {
            return;
        }
        me.beforeEvent(e);
        Ext.frameStartTime = timeStamp;
        
        
        me.reEnterCount++;
        me.fire(Ext.cache[currentTarget.id], e.type, e, true, capture);
        me.reEnterCount--;
        me.afterEvent(e);
    },
    beforeEvent: function(e) {
        var browserEvent = e.browserEvent,
            
            
            self = Ext.event.publisher.Dom,
            touches, touch;
        if (browserEvent.type === 'touchstart') {
            touches = browserEvent.touches;
            if (touches.length === 1) {
                
                
                touch = touches[0];
                self.lastTouchStartX = touch.pageX;
                self.lastTouchStartY = touch.pageY;
            }
        }
    },
    afterEvent: function(e) {
        var browserEvent = e.browserEvent,
            type = browserEvent.type,
            
            
            self = Ext.event.publisher.Dom,
            GlobalEvents = Ext.GlobalEvents;
        
        
        
        
        
        
        if (e.self.pointerEvents[type] && e.pointerType !== 'mouse') {
            
            
            
            
            
            self.lastScreenPointerEventTime = Ext.now();
        }
        if (type === 'touchend') {
            
            
            
            self.lastTouchEndTime = Ext.now();
        }
        if (!this.reEnterCount && GlobalEvents.hasListeners.idle && !GlobalEvents.idleEventMask[type]) {
            GlobalEvents.fireEvent('idle');
        }
    },
    
    isEventBlocked: function(e) {
        var me = this,
            type = e.type,
            
            
            self = Ext.event.publisher.Dom,
            now = Ext.now();
        
        
        return (me.blockedPointerEvents[type] && e.pointerType !== 'mouse') || 
        
        
        
        
        (me.blockedCompatibilityMouseEvents[type] && (now - self.lastScreenPointerEventTime < 1000)) || (Ext.supports.TouchEvents && e.self.mouseEvents[e.type] && 
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        Math.abs(e.pageX - self.lastTouchStartX) < 15 && Math.abs(e.pageY - self.lastTouchStartY) < 15 && 
        
        
        
        
        
        (Ext.now() - self.lastTouchEndTime) < 1000);
    },
    destroy: function() {
        var eventName;
        for (eventName in this.delegatedListeners) {
            this.removeDelegatedListener(eventName);
        }
    },
    
    reset: function() {
        
        
        var self = Ext.event.publisher.Dom;
        
        
        
        self.lastScreenPointerEventTime = self.lastTouchEndTime = self.lastTouchStartX = self.lastTouchStartY = undefined;
    }
}, function(Dom) {
    var doc = document,
        defaultView = doc.defaultView,
        prototype = Dom.prototype;
    if ((Ext.os.is.iOS && Ext.os.version.getMajor() < 5) || Ext.browser.is.AndroidStock || !(defaultView && defaultView.addEventListener)) {
        
        
        
        
        
        
        
        prototype.target = doc;
    } else {
        
        prototype.target = defaultView;
    }
    Dom.instance = new Dom();
});

Ext.define('Ext.overrides.event.publisher.Dom', {
    override: 'Ext.event.publisher.Dom'
}, function() {
    var DomPublisher = Ext.event.publisher.Dom,
        prototype = DomPublisher.prototype,
        docBody = document.body,
        prototype, onDirectEvent;
    if (Ext.isIE9m) {
        prototype = DomPublisher.prototype;
        prototype.target = document;
        prototype.directBoundListeners = {};
        
        
        onDirectEvent = function(e, publisher, capture) {
            e.target = e.srcElement || window;
            e.currentTarget = this;
            if (capture) {
                
                
                publisher.onDirectCaptureEvent(e);
            } else {
                publisher.onDirectEvent(e);
            }
        };
        onDirectCaptureEvent = function(e, publisher) {
            e.target = e.srcElement || window;
            e.currentTarget = this;
            
            publisher.onDirectCaptureEvent(e);
        };
        DomPublisher.override({
            addDelegatedListener: function(eventName) {
                this.delegatedListeners[eventName] = 1;
                
                
                this.target.attachEvent('on' + eventName, this.onDelegatedEvent);
            },
            removeDelegatedListener: function(eventName) {
                delete this.delegatedListeners[eventName];
                this.target.detachEvent('on' + eventName, this.onDelegatedEvent);
            },
            addDirectListener: function(eventName, element, capture) {
                var me = this,
                    dom = element.dom,
                    
                    
                    boundFn = Ext.Function.bind(onDirectEvent, dom, [
                        me,
                        capture
                    ], true),
                    directBoundListeners = me.directBoundListeners,
                    handlers = directBoundListeners[eventName] || (directBoundListeners[eventName] = {});
                handlers[dom.id] = boundFn;
                
                
                if (dom.attachEvent) {
                    dom.attachEvent('on' + eventName, boundFn);
                } else {
                    me.callParent(arguments);
                }
            },
            removeDirectListener: function(eventName, element) {
                var dom = element.dom;
                if (dom.detachEvent) {
                    dom.detachEvent('on' + eventName, this.directBoundListeners[eventName][dom.id]);
                } else {
                    this.callParent(arguments);
                }
            },
            doDelegatedEvent: function(e, invokeAfter) {
                e.target = e.srcElement || window;
                if (e.type === 'focusin') {
                    e.relatedTarget = e.fromElement === docBody ? null : e.fromElement;
                } else if (e.type === 'focusout') {
                    e.relatedTarget = e.toElement === docBody ? null : e.toElement;
                }
                return this.callParent([
                    e,
                    invokeAfter
                ]);
            }
        });
        
        
        Ext.apply(prototype.directEvents, prototype.captureEvents);
        prototype.captureEvents = {};
    }
});


Ext.define('Ext.event.publisher.Gesture', {
    extend: 'Ext.event.publisher.Dom',
    requires: [
        'Ext.util.Point',
        'Ext.AnimationQueue'
    ],
    uses: 'Ext.event.gesture.*',
    type: 'gesture',
    config: {
        
        async: true
    },
    isCancelEvent: {
        touchcancel: 1,
        pointercancel: 1,
        MSPointerCancel: 1
    },
    handledEvents: [],
    handledDomEvents: [],
    constructor: function(config) {
        var me = this,
            handledDomEvents = me.handledDomEvents,
            supports = Ext.supports,
            supportsTouchEvents = supports.TouchEvents,
            Fn = Ext.Function,
            onTouchStart = me.onTouchStart,
            onTouchMove = me.onTouchMove,
            onTouchEnd = me.onTouchEnd,
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            asyncTouchStart = Fn.createAnimationFrame(me.onTouchStart, me, null, 1),
            asyncTouchMove = Fn.createAnimationFrame(me.onTouchMove, me),
            asyncTouchEnd = Fn.createAnimationFrame(me.onTouchEnd, me, null, 1);
        
        
        me._handlers = {
            touchstart: onTouchStart,
            touchmove: onTouchMove,
            touchend: onTouchEnd,
            touchcancel: onTouchEnd,
            pointerdown: onTouchStart,
            pointermove: onTouchMove,
            pointerup: onTouchEnd,
            pointercancel: onTouchEnd,
            MSPointerDown: onTouchStart,
            MSPointerMove: onTouchMove,
            MSPointerUp: onTouchEnd,
            MSPointerCancel: onTouchEnd,
            mousedown: onTouchStart,
            mousemove: onTouchMove,
            mouseup: onTouchEnd
        };
        me._asyncHandlers = {
            touchstart: asyncTouchStart,
            touchmove: asyncTouchMove,
            touchend: asyncTouchEnd,
            touchcancel: asyncTouchEnd,
            pointerdown: asyncTouchStart,
            pointermove: asyncTouchMove,
            pointerup: asyncTouchEnd,
            pointercancel: asyncTouchEnd,
            MSPointerDown: asyncTouchStart,
            MSPointerMove: asyncTouchMove,
            MSPointerUp: asyncTouchEnd,
            MSPointerCancel: asyncTouchEnd,
            mousedown: asyncTouchStart,
            mousemove: asyncTouchMove,
            mouseup: asyncTouchEnd
        };
        
        me.activeTouchesMap = {};
        me.activeTouches = [];
        me.changedTouches = [];
        me.recognizers = [];
        if (supportsTouchEvents) {
            
            me.onTargetTouchMove = me.onTargetTouchMove.bind(me);
            me.onTargetTouchEnd = me.onTargetTouchEnd.bind(me);
        }
        if (supports.PointerEvents) {
            handledDomEvents.push('pointerdown', 'pointermove', 'pointerup', 'pointercancel');
        } else if (supports.MSPointerEvents) {
            
            handledDomEvents.push('MSPointerDown', 'MSPointerMove', 'MSPointerUp', 'MSPointerCancel');
        } else if (supportsTouchEvents) {
            handledDomEvents.push('touchstart', 'touchmove', 'touchend', 'touchcancel');
        }
        if (!handledDomEvents.length || (supportsTouchEvents && Ext.isWebKit && Ext.os.is.Desktop)) {
            
            
            
            
            handledDomEvents.push('mousedown', 'mousemove', 'mouseup');
        }
        me.initConfig(config);
        return me.callParent();
    },
    onReady: function() {
        this.callParent();
        Ext.Array.sort(this.recognizers, function(recognizerA, recognizerB) {
            var a = recognizerA.priority,
                b = recognizerB.priority;
            return (a > b) ? 1 : (a < b) ? -1 : 0;
        });
    },
    registerRecognizer: function(recognizer) {
        var me = this,
            handledEvents = recognizer.handledEvents,
            ln = handledEvents.length,
            i;
        
        
        recognizer.setOnRecognized(me.onRecognized);
        recognizer.setCallbackScope(me);
        
        
        for (i = 0; i < ln; i++) {
            me.handledEvents.push(handledEvents[i]);
        }
        me.registerEvents(handledEvents);
        me.recognizers.push(recognizer);
    },
    onRecognized: function(eventName, e, info) {
        var me = this,
            changedTouches = e.changedTouches,
            ln = changedTouches.length,
            targetGroups, targets, i, touch;
        info = info || {};
        
        
        
        
        
        
        info.type = eventName;
        
        
        
        
        
        
        
        
        
        
        info.target = changedTouches[0].target;
        
        
        info.isStopped = false;
        e = e.chain(info);
        if (ln > 1) {
            targetGroups = [];
            for (i = 0; i < ln; i++) {
                touch = changedTouches[i];
                targetGroups.push(touch.targets);
            }
            targets = me.getCommonTargets(targetGroups);
        } else {
            targets = changedTouches[0].targets;
        }
        me.publish(eventName, targets, e);
    },
    getCommonTargets: function(targetGroups) {
        var firstTargetGroup = targetGroups[0],
            ln = targetGroups.length;
        if (ln === 1) {
            return firstTargetGroup;
        }
        var commonTargets = [],
            i = 1,
            target, targets, j;
        while (true) {
            target = firstTargetGroup[firstTargetGroup.length - i];
            if (!target) {
                return commonTargets;
            }
            for (j = 1; j < ln; j++) {
                targets = targetGroups[j];
                if (targets[targets.length - i] !== target) {
                    return commonTargets;
                }
            }
            commonTargets.unshift(target);
            i++;
        }
        return commonTargets;
    },
    invokeRecognizers: function(methodName, e) {
        var recognizers = this.recognizers,
            ln = recognizers.length,
            i, recognizer;
        if (methodName === 'onStart') {
            for (i = 0; i < ln; i++) {
                recognizers[i].isActive = true;
            }
        }
        for (i = 0; i < ln; i++) {
            recognizer = recognizers[i];
            if (recognizer.isActive && recognizer[methodName].call(recognizer, e) === false) {
                recognizer.isActive = false;
            }
        }
    },
    updateTouches: function(e, isEnd) {
        var me = this,
            browserEvent = e.browserEvent,
            
            
            
            
            touchSources = browserEvent.changedTouches || [
                browserEvent
            ],
            activeTouches = me.activeTouches,
            activeTouchesMap = me.activeTouchesMap,
            changedTouches = [],
            touchSource, identifier, touch, target, i, ln, x, y;
        for (i = 0 , ln = touchSources.length; i < ln; i++) {
            touchSource = touchSources[i];
            if ('identifier' in touchSource) {
                
                
                identifier = touchSource.identifier;
            } else if ('pointerId' in touchSource) {
                
                identifier = touchSource.pointerId;
            } else {
                
                
                identifier = 1;
            }
            touch = activeTouchesMap[identifier];
            if (!touch) {
                target = Ext.event.Event.resolveTextNode(touchSource.target);
                touch = activeTouchesMap[identifier] = {
                    identifier: identifier,
                    target: target,
                    
                    
                    
                    
                    
                    
                    
                    
                    targets: me.getPropagatingTargets(target)
                };
                activeTouches.push(touch);
            }
            if (isEnd) {
                delete activeTouchesMap[identifier];
                Ext.Array.remove(activeTouches, touch);
            }
            x = touchSource.pageX;
            y = touchSource.pageY;
            touch.pageX = x;
            touch.pageY = y;
            
            touch.point = new Ext.util.Point(x,y);
            changedTouches.push(touch);
        }
        
        
        
        e.touches = Ext.Array.clone(activeTouches);
        
        e.changedTouches = changedTouches;
    },
    doDelegatedEvent: function(e) {
        var me = this;
        
        
        e = me.callParent([
            e,
            false
        ]);
        
        
        
        
        
        if (e) {
            if (!e.button || e.button < 1) {
                
                
                
                me.handlers[e.type].call(me, e);
            }
            
            
            
            me.afterEvent(e);
        }
    },
    onTouchStart: function(e) {
        var me = this,
            target = e.target;
        if (e.browserEvent.type === 'touchstart') {
            
            
            
            
            
            
            target.addEventListener('touchmove', me.onTargetTouchMove);
            target.addEventListener('touchend', me.onTargetTouchEnd);
            target.addEventListener('touchcancel', me.onTargetTouchEnd);
        }
        me.updateTouches(e);
        if (!me.isStarted) {
            
            
            me.isStarted = true;
            me.invokeRecognizers('onStart', e);
            
            
            
            if (Ext.enableGarbageCollector) {
                Ext.dom.GarbageCollector.pause();
            }
        }
        me.invokeRecognizers('onTouchStart', e);
    },
    onTouchMove: function(e) {
        if (this.isStarted) {
            this.updateTouches(e);
            if (e.changedTouches.length > 0) {
                this.invokeRecognizers('onTouchMove', e);
            }
        }
    },
    
    
    
    onTouchEnd: function(e) {
        var me = this;
        if (!me.isStarted) {
            return;
        }
        me.updateTouches(e, true);
        me.invokeRecognizers(me.isCancelEvent[e.type] ? 'onTouchCancel' : 'onTouchEnd', e);
        if (!me.activeTouches.length) {
            
            me.isStarted = false;
            me.invokeRecognizers('onEnd', e);
            
            
            if (Ext.enableGarbageCollector) {
                Ext.dom.GarbageCollector.resume();
            }
        }
    },
    onTargetTouchMove: function(e) {
        if (Ext.elevateFunction) {
            
            
            Ext.elevateFunction(this.doTargetTouchMove, this, [
                e
            ]);
        } else {
            this.doTargetTouchMove(e);
        }
    },
    doTargetTouchMove: function(e) {
        
        
        if (!Ext.getBody().contains(e.target)) {
            this.onTouchMove(new Ext.event.Event(e));
        }
    },
    onTargetTouchEnd: function(e) {
        if (Ext.elevateFunction) {
            
            
            Ext.elevateFunction(this.doTargetTouchEnd, this, [
                e
            ]);
        } else {
            this.doTargetTouchEnd(e);
        }
    },
    doTargetTouchEnd: function(e) {
        var me = this,
            target = e.target;
        target.removeEventListener('touchmove', me.onTargetTouchMove);
        target.removeEventListener('touchend', me.onTargetTouchEnd);
        target.removeEventListener('touchcancel', me.onTargetTouchEnd);
        
        
        
        
        
        
        
        
        
        
        
        
        if (!Ext.getBody().contains(target)) {
            me.onTouchEnd(new Ext.event.Event(e));
        }
    },
    updateAsync: function(async) {
        this.handlers = async ? this._asyncHandlers : this._handlers;
    },
    
    reset: function() {
        var me = this,
            recognizers = me.recognizers,
            ln = recognizers.length,
            i, recognizer;
        me.activeTouchesMap = {};
        me.activeTouches = [];
        me.changedTouches = [];
        for (i = 0; i < ln; i++) {
            recognizer = recognizers[i];
            recognizer.reset();
            recognizer.isActive = false;
        }
        this.callParent();
    }
}, function(Gesture) {
    Gesture.instance = new Gesture();
});

Ext.define('Ext.overrides.event.publisher.Gesture', {
    override: 'Ext.event.publisher.Gesture'
}, function() {
    if (Ext.isIE9m) {
        this.override({
            updateTouches: function(e, isEnd) {
                var browserEvent = e.browserEvent,
                    xy = e.getXY();
                
                
                browserEvent.pageX = xy[0];
                browserEvent.pageY = xy[1];
                this.callParent([
                    e,
                    isEnd
                ]);
            },
            doDelegatedEvent: function(e) {
                
                
                
                this.callParent([
                    Ext.event.Event.enableIEAsync(e)
                ]);
            }
        });
    }
});


Ext.define('Ext.event.publisher.Focus', {
    extend: 'Ext.event.publisher.Dom',
    type: 'focus',
    handledEvents: [
        'focusenter',
        'focusleave'
    ],
    handledDomEvents: [
        'focusin',
        'focusout'
    ],
    doDelegatedEvent: function(e, invokeAfter) {
        var me = this,
            relatedTarget;
        e = me.callParent([
            e,
            false
        ]);
        if (e) {
            if (e.type === 'focusout') {
                
                
                if (e.relatedTarget == null) {
                    me.processFocusIn(e, e.target, document.body, invokeAfter);
                }
            } else {
                relatedTarget = e.relatedTarget;
                
                
                me.processFocusIn(e, (relatedTarget == null || !relatedTarget.tagName) ? document.body : relatedTarget, e.target, invokeAfter);
            }
        }
    },
    processFocusIn: function(e, fromElement, toElement, invokeAfter) {
        var me = this,
            commonAncestor = Ext.Element.getCommonAncestor(toElement, fromElement, true),
            node,
            targets = [],
            event, el, id;
        
        for (node = fromElement; node && node !== commonAncestor; node = node.parentNode) {
            targets.push(node);
        }
        
        if (targets.length) {
            event = me.createSyntheticEvent('focusleave', e, fromElement, toElement);
            me.publish('focusleave', targets, event);
            targets.length = 0;
            if (event.isStopped) {
                return;
            }
        }
        
        for (node = toElement; node !== commonAncestor; node = node.parentNode) {
            targets.push(node);
        }
        
        event = me.createSyntheticEvent('focusenter', e, toElement, fromElement);
        me.publish('focusenter', targets, event);
        if (event.isStopped) {
            return;
        }
        if (invokeAfter) {
            me.afterEvent(e);
        }
        Ext.GlobalEvents.fireEvent('focus', {
            event: event,
            toElement: toElement,
            fromElement: fromElement
        });
    },
    createSyntheticEvent: function(eventName, browserEvent, target, relatedTarget) {
        var event = new Ext.event.Event(browserEvent);
        event.type = eventName;
        event.relatedTarget = relatedTarget;
        event.target = target;
        return event;
    }
}, function(Focus) {
    var focusTimeout;
    
    
    if (!Ext.supports.FocusinFocusoutEvents) {
        
        
        this.override({
            handledDomEvents: [
                'focus',
                'blur'
            ],
            doDelegatedEvent: function(e, invokeAfter) {
                var me = this;
                e = me.callSuper([
                    e,
                    false
                ]);
                if (e) {
                    clearTimeout(focusTimeout);
                    focusTimeout = 0;
                    if (e.type === 'blur') {
                        var blurredEl = e.target === window ? document.body : e.target;
                        
                        
                        focusTimeout = setTimeout(function() {
                            focusTimeout = 0;
                            me.processFocusIn(e, blurredEl, document.body, invokeAfter);
                            Focus.previousActiveElement = null;
                        }, 0);
                        if (e.target === window || e.target === document) {
                            Focus.previousActiveElement = null;
                        } else {
                            Focus.previousActiveElement = e.target;
                        }
                    } else {
                        me.processFocusIn(e, Focus.previousActiveElement || document.body, e.target === window ? document.body : e.target, invokeAfter);
                    }
                }
            }
        });
    }
    Focus.instance = new Focus();
});


Ext.define('Ext.mixin.Templatable', {
    extend: 'Ext.Mixin',
    mixinConfig: {
        id: 'templatable'
    },
    referenceAttributeName: 'reference',
    referenceSelector: '[reference]',
    getElementConfig: function() {
        return {
            reference: 'element'
        };
    },
    getElementTemplate: function() {
        var elementTemplate = document.createDocumentFragment();
        elementTemplate.appendChild(Ext.Element.create(this.getElementConfig(), true));
        return elementTemplate;
    },
    initElement: function() {
        var prototype = this.self.prototype;
        prototype.elementTemplate = this.getElementTemplate();
        prototype.initElement = prototype.doInitElement;
        this.initElement.apply(this, arguments);
    },
    linkElement: function(reference, node) {
        this.link(reference, node);
    },
    doInitElement: function() {
        var referenceAttributeName = this.referenceAttributeName,
            renderElement, referenceNodes, i, ln, referenceNode, reference;
        renderElement = this.elementTemplate.cloneNode(true);
        referenceNodes = renderElement.querySelectorAll(this.referenceSelector);
        for (i = 0 , ln = referenceNodes.length; i < ln; i++) {
            referenceNode = referenceNodes[i];
            reference = referenceNode.getAttribute(referenceAttributeName);
            referenceNode.removeAttribute(referenceAttributeName);
            this.linkElement(reference, referenceNode);
        }
    }
});


Ext.define('Ext.TaskQueue', {
    requires: 'Ext.AnimationQueue',
    singleton: true,
    pending: false,
    mode: true,
    constructor: function() {
        this.readQueue = [];
        this.writeQueue = [];
        this.run = Ext.Function.bind(this.run, this);
        
        
        
        
        
        if (Ext.os.is.iOS) {
            Ext.interval(this.watch, 500, this);
        }
    },
    requestRead: function(fn, scope, args) {
        this.request(true);
        this.readQueue.push(arguments);
    },
    requestWrite: function(fn, scope, args) {
        this.request(false);
        this.writeQueue.push(arguments);
    },
    request: function(mode) {
        if (!this.pending) {
            this.pendingTime = Date.now();
            this.pending = true;
            this.mode = mode;
            if (mode) {
                Ext.defer(this.run, 1, this);
            } else {
                Ext.Function.requestAnimationFrame(this.run);
            }
        }
    },
    watch: function() {
        if (this.pending && Date.now() - this.pendingTime >= 500) {
            this.run();
        }
    },
    run: function() {
        this.pending = false;
        var readQueue = this.readQueue,
            writeQueue = this.writeQueue,
            request = null,
            queue;
        if (this.mode) {
            queue = readQueue;
            if (writeQueue.length > 0) {
                request = false;
            }
        } else {
            queue = writeQueue;
            if (readQueue.length > 0) {
                request = true;
            }
        }
        var tasks = queue.slice(),
            i, ln, task, fn, scope;
        queue.length = 0;
        for (i = 0 , ln = tasks.length; i < ln; i++) {
            task = tasks[i];
            fn = task[0];
            scope = task[1];
            if (typeof fn == 'string') {
                fn = scope[fn];
            }
            if (task.length > 2) {
                fn.apply(scope, task[2]);
            } else {
                fn.call(scope);
            }
        }
        tasks.length = 0;
        if (request !== null) {
            this.request(request);
        }
    }
});


Ext.define('Ext.util.sizemonitor.Abstract', {
    mixins: [
        'Ext.mixin.Templatable'
    ],
    requires: [
        'Ext.TaskQueue'
    ],
    config: {
        element: null,
        callback: Ext.emptyFn,
        scope: null,
        args: []
    },
    width: 0,
    height: 0,
    contentWidth: 0,
    contentHeight: 0,
    constructor: function(config) {
        this.refresh = Ext.Function.bind(this.refresh, this);
        this.info = {
            width: 0,
            height: 0,
            contentWidth: 0,
            contentHeight: 0,
            flag: 0
        };
        this.initElement();
        this.initConfig(config);
        this.bindListeners(true);
    },
    bindListeners: Ext.emptyFn,
    applyElement: function(element) {
        if (element) {
            return Ext.get(element);
        }
    },
    updateElement: function(element) {
        element.append(this.detectorsContainer);
        element.addCls(Ext.baseCSSPrefix + 'size-monitored');
    },
    applyArgs: function(args) {
        return args.concat([
            this.info
        ]);
    },
    refreshMonitors: Ext.emptyFn,
    forceRefresh: function() {
        Ext.TaskQueue.requestRead('refresh', this);
    },
    getContentBounds: function() {
        return this.detectorsContainer.getBoundingClientRect();
    },
    getContentWidth: function() {
        return this.detectorsContainer.offsetWidth;
    },
    getContentHeight: function() {
        return this.detectorsContainer.offsetHeight;
    },
    refreshSize: function() {
        var element = this.getElement();
        if (!element || element.isDestroyed) {
            return false;
        }
        var width = element.getWidth(),
            height = element.getHeight(),
            contentWidth = this.getContentWidth(),
            contentHeight = this.getContentHeight(),
            currentContentWidth = this.contentWidth,
            currentContentHeight = this.contentHeight,
            info = this.info,
            resized = false,
            flag;
        this.width = width;
        this.height = height;
        this.contentWidth = contentWidth;
        this.contentHeight = contentHeight;
        flag = ((currentContentWidth !== contentWidth ? 1 : 0) + (currentContentHeight !== contentHeight ? 2 : 0));
        if (flag > 0) {
            info.width = width;
            info.height = height;
            info.contentWidth = contentWidth;
            info.contentHeight = contentHeight;
            info.flag = flag;
            resized = true;
            this.getCallback().apply(this.getScope(), this.getArgs());
        }
        return resized;
    },
    refresh: function(force) {
        if (this.refreshSize() || force) {
            Ext.TaskQueue.requestWrite('refreshMonitors', this);
        }
    },
    destroy: function() {
        var element = this.getElement();
        this.bindListeners(false);
        if (element && !element.isDestroyed) {
            element.removeCls(Ext.baseCSSPrefix + 'size-monitored');
        }
        delete this._element;
        this.callParent();
    }
});


Ext.define('Ext.util.sizemonitor.Default', {
    extend: 'Ext.util.sizemonitor.Abstract',
    updateElement: function(element) {},
    bindListeners: function(bind) {
        var element = this.getElement().dom;
        if (!element) {
            return;
        }
        if (bind) {
            element.onresize = this.refresh;
        } else {
            delete element.onresize;
        }
    },
    getContentBounds: function() {
        return this.getElement().dom.getBoundingClientRect();
    },
    getContentWidth: function() {
        return this.getElement().getWidth();
    },
    getContentHeight: function() {
        return this.getElement().getHeight();
    }
});


Ext.define('Ext.util.sizemonitor.Scroll', {
    extend: 'Ext.util.sizemonitor.Abstract',
    getElementConfig: function() {
        return {
            reference: 'detectorsContainer',
            classList: [
                Ext.baseCSSPrefix + 'size-monitors',
                'scroll'
            ],
            children: [
                {
                    reference: 'expandMonitor',
                    className: 'expand'
                },
                {
                    reference: 'shrinkMonitor',
                    className: 'shrink'
                }
            ]
        };
    },
    constructor: function(config) {
        this.onScroll = Ext.Function.bind(this.onScroll, this);
        this.callParent(arguments);
    },
    bindListeners: function(bind) {
        var method = bind ? 'addEventListener' : 'removeEventListener';
        this.expandMonitor[method]('scroll', this.onScroll, true);
        this.shrinkMonitor[method]('scroll', this.onScroll, true);
    },
    forceRefresh: function() {
        Ext.TaskQueue.requestRead('refresh', this, [
            true
        ]);
    },
    onScroll: function() {
        Ext.TaskQueue.requestRead('refresh', this);
    },
    refreshMonitors: function() {
        var expandMonitor = this.expandMonitor,
            shrinkMonitor = this.shrinkMonitor,
            end = 1000000;
        if (expandMonitor && !expandMonitor.isDestroyed) {
            expandMonitor.scrollLeft = end;
            expandMonitor.scrollTop = end;
        }
        if (shrinkMonitor && !shrinkMonitor.isDestroyed) {
            shrinkMonitor.scrollLeft = end;
            shrinkMonitor.scrollTop = end;
        }
    }
});


Ext.define('Ext.util.sizemonitor.OverflowChange', {
    extend: 'Ext.util.sizemonitor.Abstract',
    constructor: function(config) {
        this.onExpand = Ext.Function.bind(this.onExpand, this);
        this.onShrink = Ext.Function.bind(this.onShrink, this);
        this.callParent(arguments);
    },
    getElementConfig: function() {
        return {
            reference: 'detectorsContainer',
            classList: [
                Ext.baseCSSPrefix + 'size-monitors',
                'overflowchanged'
            ],
            children: [
                {
                    reference: 'expandMonitor',
                    className: 'expand',
                    children: [
                        {
                            reference: 'expandHelper'
                        }
                    ]
                },
                {
                    reference: 'shrinkMonitor',
                    className: 'shrink',
                    children: [
                        {
                            reference: 'shrinkHelper'
                        }
                    ]
                }
            ]
        };
    },
    bindListeners: function(bind) {
        var method = bind ? 'addEventListener' : 'removeEventListener';
        this.expandMonitor[method](Ext.browser.is.Firefox ? 'underflow' : 'overflowchanged', this.onExpand, true);
        this.shrinkMonitor[method](Ext.browser.is.Firefox ? 'overflow' : 'overflowchanged', this.onShrink, true);
    },
    onExpand: function(e) {
        if (Ext.browser.is.Webkit && e.horizontalOverflow && e.verticalOverflow) {
            return;
        }
        Ext.TaskQueue.requestRead('refresh', this);
    },
    onShrink: function(e) {
        if (Ext.browser.is.Webkit && !e.horizontalOverflow && !e.verticalOverflow) {
            return;
        }
        Ext.TaskQueue.requestRead('refresh', this);
    },
    refreshMonitors: function() {
        if (this.isDestroyed) {
            return;
        }
        var expandHelper = this.expandHelper,
            shrinkHelper = this.shrinkHelper,
            contentBounds = this.getContentBounds(),
            width = contentBounds.width,
            height = contentBounds.height,
            style;
        if (expandHelper && !expandHelper.isDestroyed) {
            style = expandHelper.style;
            style.width = (width + 1) + 'px';
            style.height = (height + 1) + 'px';
        }
        if (shrinkHelper && !shrinkHelper.isDestroyed) {
            style = shrinkHelper.style;
            style.width = width + 'px';
            style.height = height + 'px';
        }
        Ext.TaskQueue.requestRead('refresh', this);
    }
});


Ext.define('Ext.util.SizeMonitor', {
    requires: [
        'Ext.util.sizemonitor.Default',
        'Ext.util.sizemonitor.Scroll',
        'Ext.util.sizemonitor.OverflowChange'
    ],
    constructor: function(config) {
        var namespace = Ext.util.sizemonitor;
        if (Ext.browser.is.Firefox) {
            return new namespace.OverflowChange(config);
        } else if (Ext.browser.is.WebKit) {
            if (!Ext.browser.is.Silk && Ext.browser.engineVersion.gtEq('535')) {
                return new namespace.OverflowChange(config);
            } else {
                return new namespace.Scroll(config);
            }
        } else if (Ext.browser.is.IE11) {
            return new namespace.Scroll(config);
        } else {
            return new namespace.Default(config);
        }
    }
});


Ext.define('Ext.event.publisher.ElementSize', {
    extend: 'Ext.event.publisher.Publisher',
    requires: [
        'Ext.util.SizeMonitor'
    ],
    type: 'size',
    handledEvents: [
        'resize'
    ],
    constructor: function() {
        this.monitors = {};
        this.subscribers = {};
        this.callParent(arguments);
    },
    subscribe: function(element) {
        var id = element.id,
            subscribers = this.subscribers,
            monitors = this.monitors;
        if (subscribers[id]) {
            ++subscribers[id];
        } else {
            subscribers[id] = 1;
            monitors[id] = new Ext.util.SizeMonitor({
                element: element,
                callback: this.onElementResize,
                scope: this,
                args: [
                    element
                ]
            });
        }
        element.on('painted', 'forceRefresh', monitors[id]);
        return true;
    },
    unsubscribe: function(element) {
        var id = element.id,
            subscribers = this.subscribers,
            monitors = this.monitors,
            sizeMonitor;
        if (subscribers[id] && !--subscribers[id]) {
            delete subscribers[id];
            sizeMonitor = monitors[id];
            element.un('painted', 'forceRefresh', sizeMonitor);
            sizeMonitor.destroy();
            delete monitors[id];
        }
    },
    onElementResize: function(element, info) {
        Ext.TaskQueue.requestRead('fire', this, [
            element,
            'resize',
            [
                element,
                info
            ]
        ]);
    }
}, function(ElementSize) {
    ElementSize.instance = new ElementSize();
});


Ext.define('Ext.util.paintmonitor.Abstract', {
    config: {
        element: null,
        callback: Ext.emptyFn,
        scope: null,
        args: []
    },
    eventName: '',
    monitorClass: '',
    constructor: function(config) {
        this.onElementPainted = Ext.Function.bind(this.onElementPainted, this);
        this.initConfig(config);
    },
    bindListeners: function(bind) {
        this.monitorElement[bind ? 'addEventListener' : 'removeEventListener'](this.eventName, this.onElementPainted, true);
    },
    applyElement: function(element) {
        if (element) {
            return Ext.get(element);
        }
    },
    updateElement: function(element) {
        this.monitorElement = Ext.Element.create({
            classList: [
                Ext.baseCSSPrefix + 'paint-monitor',
                this.monitorClass
            ]
        }, true);
        element.appendChild(this.monitorElement);
        element.addCls(Ext.baseCSSPrefix + 'paint-monitored');
        this.bindListeners(true);
    },
    onElementPainted: function() {},
    destroy: function() {
        var monitorElement = this.monitorElement,
            parentNode = monitorElement.parentNode,
            element = this.getElement();
        this.bindListeners(false);
        delete this.monitorElement;
        if (element && !element.isDestroyed) {
            element.removeCls(Ext.baseCSSPrefix + 'paint-monitored');
            delete this._element;
        }
        if (parentNode) {
            parentNode.removeChild(monitorElement);
        }
        this.callParent();
    }
});


Ext.define('Ext.util.paintmonitor.CssAnimation', {
    extend: 'Ext.util.paintmonitor.Abstract',
    eventName: Ext.browser.is.WebKit ? 'webkitAnimationEnd' : 'animationend',
    monitorClass: 'cssanimation',
    onElementPainted: function(e) {
        if (e.animationName === Ext.baseCSSPrefix + 'paint-monitor-helper') {
            this.getCallback().apply(this.getScope(), this.getArgs());
        }
    }
});


Ext.define('Ext.util.paintmonitor.OverflowChange', {
    extend: 'Ext.util.paintmonitor.Abstract',
    eventName: Ext.browser.is.Firefox ? 'overflow' : 'overflowchanged',
    monitorClass: 'overflowchange',
    onElementPainted: function(e) {
        this.getCallback().apply(this.getScope(), this.getArgs());
    }
});


Ext.define('Ext.util.PaintMonitor', {
    requires: [
        'Ext.util.paintmonitor.CssAnimation',
        'Ext.util.paintmonitor.OverflowChange'
    ],
    constructor: function(config) {
        if (Ext.browser.is.Firefox || (Ext.browser.is.WebKit && Ext.browser.engineVersion.gtEq('536') && !Ext.os.is.Blackberry)) {
            return new Ext.util.paintmonitor.OverflowChange(config);
        } else {
            return new Ext.util.paintmonitor.CssAnimation(config);
        }
    }
});


Ext.define('Ext.event.publisher.ElementPaint', {
    extend: 'Ext.event.publisher.Publisher',
    requires: [
        'Ext.util.PaintMonitor',
        'Ext.TaskQueue'
    ],
    type: 'paint',
    handledEvents: [
        'painted'
    ],
    constructor: function() {
        this.monitors = {};
        this.subscribers = {};
        this.callParent(arguments);
    },
    subscribe: function(element) {
        var id = element.id,
            subscribers = this.subscribers;
        if (subscribers[id]) {
            ++subscribers[id];
        } else {
            subscribers[id] = 1;
            this.monitors[id] = new Ext.util.PaintMonitor({
                element: element,
                callback: this.onElementPainted,
                scope: this,
                args: [
                    element
                ]
            });
        }
    },
    unsubscribe: function(element) {
        var id = element.id,
            subscribers = this.subscribers,
            monitors = this.monitors;
        if (subscribers[id] && !--subscribers[id]) {
            delete subscribers[id];
            monitors[id].destroy();
            delete monitors[id];
        }
    },
    onElementPainted: function(element) {
        Ext.TaskQueue.requestRead('fire', this, [
            element,
            'painted',
            [
                element
            ]
        ]);
    }
}, function(ElementPaint) {
    ElementPaint.instance = new ElementPaint();
});


Ext.define('Ext.dom.Element', function(Element) {
    var WIN = window,
        DOC = document,
        windowId = 'ext-window',
        documentId = 'ext-document',
        WIDTH = 'width',
        HEIGHT = 'height',
        MIN_WIDTH = 'min-width',
        MIN_HEIGHT = 'min-height',
        MAX_WIDTH = 'max-width',
        MAX_HEIGHT = 'max-height',
        TOP = 'top',
        RIGHT = 'right',
        BOTTOM = 'bottom',
        LEFT = 'left',
        VISIBILITY = 'visibility',
        HIDDEN = 'hidden',
        DISPLAY = "display",
        NONE = "none",
        ZINDEX = "z-index",
        POSITION = "position",
        RELATIVE = "relative",
        STATIC = "static",
        SEPARATOR = '-',
        wordsRe = /\w/g,
        spacesRe = /\s+/,
        classNameSplitRegex = /[\s]+/,
        transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
        adjustDirect2DTableRe = /table-row|table-.*-group/,
        borders = {
            t: 'border-top-width',
            r: 'border-right-width',
            b: 'border-bottom-width',
            l: 'border-left-width'
        },
        paddings = {
            t: 'padding-top',
            r: 'padding-right',
            b: 'padding-bottom',
            l: 'padding-left'
        },
        margins = {
            t: 'margin-top',
            r: 'margin-right',
            b: 'margin-bottom',
            l: 'margin-left'
        },
        paddingsTLRB = [
            paddings.l,
            paddings.r,
            paddings.t,
            paddings.b
        ],
        bordersTLRB = [
            borders.l,
            borders.r,
            borders.t,
            borders.b
        ],
        numberRe = /\d+$/,
        unitRe = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
        defaultUnit = 'px',
        camelRe = /(-[a-z])/gi,
        cssRe = /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
        pxRe = /^\d+(?:\.\d*)?px$/i,
        propertyCache = {},
        camelReplaceFn = function(m, a) {
            return a.charAt(1).toUpperCase();
        },
        visibilityCls = Ext.baseCSSPrefix + 'hidden-visibility',
        displayCls = Ext.baseCSSPrefix + 'hidden-display',
        offsetsCls = Ext.baseCSSPrefix + 'hidden-offsets',
        noTouchScrollCls = Ext.baseCSSPrefix + 'no-touch-scroll',
        CREATE_ATTRIBUTES = {
            style: 'style',
            className: 'className',
            cls: 'cls',
            classList: 'classList',
            text: 'text',
            hidden: 'hidden',
            html: 'html',
            children: 'children'
        },
        visFly, scrollFly, caFly;
    return {
        alternateClassName: [
            'Ext.Element'
        ],
        mixins: [
            'Ext.util.Positionable',
            'Ext.mixin.Observable'
        ],
        requires: [
            'Ext.dom.Shadow',
            'Ext.dom.Shim',
            'Ext.dom.ElementEvent',
            'Ext.event.publisher.Dom',
            'Ext.event.publisher.Gesture',
            'Ext.event.publisher.Focus',
            'Ext.event.publisher.ElementSize',
            'Ext.event.publisher.ElementPaint'
        ],
        uses: [
            'Ext.dom.Helper',
            'Ext.dom.CompositeElement',
            'Ext.dom.Fly'
        ],
        observableType: 'element',
        isElement: true,
        skipGarbageCollection: true,
        identifiablePrefix: 'ext-element-',
        styleHooks: {},
        validIdRe: Ext.validIdRe,
        blockedEvents: Ext.supports.EmulatedMouseOver ? {
            
            
            
            
            
            mouseover: 1
        } : {},
        longpressEvents: {
            longpress: 1,
            taphold: 1
        },
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        constructor: function(dom) {
            var me = this,
                id;
            if (typeof dom === 'string') {
                dom = DOC.getElementById(dom);
            }
            if (!dom) {
                Ext.Error.raise("Invalid domNode reference or an id of an existing domNode: " + dom);
                return null;
            }
            if (Ext.cache[dom.id]) {
                Ext.Error.raise("Element cache already contains an entry for id '" + dom.id + "'.  Use Ext.get() to create or retrieve Element instances.");
            }
            
            me.dom = dom;
            id = dom.id;
            if (id) {
                me.id = id;
            } else {
                id = dom.id = me.getUniqueId();
            }
            if (!me.validIdRe.test(me.id)) {
                Ext.Error.raise('Invalid Element "id": "' + me.id + '"');
            }
            
            
            
            me.el = me;
            Ext.cache[id] = me;
            me.mixins.observable.constructor.call(me);
        },
        inheritableStatics: {
            cache: Ext.cache = {},
            
            VISIBILITY: 1,
            
            DISPLAY: 2,
            
            OFFSETS: 3,
            unitRe: unitRe,
            
            useDelegatedEvents: true,
            
            validNodeTypes: {
                1: 1,
                
                9: 1
            },
            
            
            addUnits: function(size, units) {
                
                if (typeof size === 'number') {
                    return size + (units || defaultUnit);
                }
                
                
                
                
                
                if (size === "" || size === "auto" || size == null) {
                    return size || '';
                }
                
                
                if (numberRe.test(size)) {
                    return size + (units || defaultUnit);
                }
                
                if (!unitRe.test(size)) {
                    Ext.Logger.warn("Warning, size detected (" + size + ") not a valid property value on Element.addUnits.");
                    return size || '';
                }
                return size;
            },
            
            
            
            
            
            
            
            
            
            
            create: function(attributes, domNode) {
                var me = this,
                    hidden = CREATE_ATTRIBUTES.hidden,
                    element, elementStyle, tag, value, name, i, ln, className;
                if (!attributes) {
                    attributes = {};
                }
                if (attributes.isElement) {
                    return domNode ? attributes.dom : attributes;
                } else if ('nodeType' in attributes) {
                    return domNode ? attributes : Ext.get(attributes);
                }
                if (typeof attributes === 'string') {
                    return DOC.createTextNode(attributes);
                }
                tag = attributes.tag;
                if (!tag) {
                    tag = 'div';
                }
                if (attributes.namespace) {
                    element = DOC.createElementNS(attributes.namespace, tag);
                } else {
                    element = DOC.createElement(tag);
                }
                elementStyle = element.style;
                if (attributes[hidden]) {
                    className = attributes.className;
                    className = (className == null) ? '' : className + ' ';
                    attributes.className = className + displayCls;
                    delete attributes[hidden];
                }
                for (name in attributes) {
                    if (name !== 'tag') {
                        value = attributes[name];
                        switch (name) {
                            case CREATE_ATTRIBUTES.style:
                                if (typeof value === 'string') {
                                    element.setAttribute(name, value);
                                } else {
                                    for (i in value) {
                                        if (value.hasOwnProperty(i)) {
                                            elementStyle[i] = value[i];
                                        }
                                    }
                                };
                                break;
                            case CREATE_ATTRIBUTES.className:
                            case CREATE_ATTRIBUTES.cls:
                                element.className = value;
                                break;
                            case CREATE_ATTRIBUTES.classList:
                                element.className = value.join(' ');
                                break;
                            case CREATE_ATTRIBUTES.text:
                                element.textContent = value;
                                break;
                            case CREATE_ATTRIBUTES.html:
                                element.innerHTML = value;
                                break;
                            case CREATE_ATTRIBUTES.children:
                                for (i = 0 , ln = value.length; i < ln; i++) {
                                    element.appendChild(me.create(value[i], true));
                                };
                                break;
                            default:
                                if (value != null) {
                                    
                                    element.setAttribute(name, value);
                                };
                        }
                    }
                }
                if (domNode) {
                    return element;
                } else {
                    return me.get(element);
                }
            },
            
            detach: function() {
                var dom = this.dom;
                if (dom && dom.parentNode && dom.tagName !== 'BODY') {
                    dom.parentNode.removeChild(dom);
                }
                return this;
            },
            
            fly: function(dom, named) {
                return Ext.fly(dom, named);
            },
            
            fromPoint: function(x, y) {
                return Ext.get(DOC.elementFromPoint(x, y));
            },
            
            get: function(el) {
                var me = this,
                    cache = Ext.cache,
                    nodeType, dom, id, entry, isDoc, isWin, isValidNodeType;
                if (!el) {
                    return null;
                }
                function warnDuplicate(id) {
                    Ext.Error.raise("DOM element with id " + id + " in Element cache is not the same as element in the DOM. " + "Make sure to clean up Element instances using destroy()");
                }
                
                if (el.isFly) {
                    el = el.dom;
                }
                if (typeof el === 'string') {
                    id = el;
                    if (cache.hasOwnProperty(id)) {
                        entry = cache[id];
                        if (entry.skipGarbageCollection || !Ext.isGarbage(entry.dom)) {
                            dom = Ext.getElementById ? Ext.getElementById(id) : DOC.getElementById(id);
                            if (dom && (dom !== entry.dom)) {
                                warnDuplicate(id);
                            }
                            return entry;
                        } else {
                            entry.destroy();
                        }
                    }
                    if (id === windowId) {
                        return Element.get(WIN);
                    } else if (id === documentId) {
                        return Element.get(DOC);
                    }
                    
                    
                    dom = Ext.getElementById ? Ext.getElementById(id) : DOC.getElementById(id);
                    if (dom) {
                        return new Element(dom);
                    }
                }
                nodeType = el.nodeType;
                if (nodeType) {
                    isDoc = (nodeType === 9);
                    isValidNodeType = me.validNodeTypes[nodeType];
                } else {
                    
                    
                    
                    isWin = (el.window == el);
                }
                
                
                
                if (isValidNodeType || isWin) {
                    id = el.id;
                    if (cache.hasOwnProperty(id)) {
                        entry = cache[id];
                        if (entry.skipGarbageCollection || el === entry.dom || !Ext.isGarbage(entry.dom)) {
                            if (el !== entry.dom) {
                                warnDuplicate(id);
                            }
                            return entry;
                        } else {
                            entry.destroy();
                        }
                    }
                    if (el === DOC) {
                        el.id = documentId;
                    }
                    
                    if (el == WIN) {
                        el.id = windowId;
                    }
                    el = new Element(el);
                    if (isWin || isDoc) {
                        
                        el.skipGarbageCollection = true;
                    }
                    return el;
                }
                if (el.isElement) {
                    return el;
                }
                if (el.isComposite) {
                    return el;
                }
                
                
                if (Ext.isIterable(el)) {
                    return me.select(el);
                }
                return null;
            },
            
            getActiveElement: function() {
                var active = DOC.activeElement;
                
                
                
                
                if (!active || !active.focus) {
                    active = DOC.body;
                }
                return active;
            },
            
            getDocumentHeight: function() {
                return Math.max(!Ext.isStrict ? DOC.body.scrollHeight : DOC.documentElement.scrollHeight, this.getViewportHeight());
            },
            
            getDocumentWidth: function() {
                return Math.max(!Ext.isStrict ? DOC.body.scrollWidth : DOC.documentElement.scrollWidth, this.getViewportWidth());
            },
            
            getOrientation: function() {
                if (Ext.supports.OrientationChange) {
                    return (WIN.orientation == 0) ? 'portrait' : 'landscape';
                }
                return (WIN.innerHeight > WIN.innerWidth) ? 'portrait' : 'landscape';
            },
            
            getViewportHeight: function() {
                return WIN.innerHeight;
            },
            
            getViewportWidth: function() {
                return WIN.innerWidth;
            },
            
            getViewSize: function() {
                return {
                    width: Element.getViewportWidth(),
                    height: Element.getViewportHeight()
                };
            },
            
            normalize: function(prop) {
                return propertyCache[prop] || (propertyCache[prop] = prop.replace(camelRe, camelReplaceFn));
            },
            
            parseBox: function(box) {
                box = box || 0;
                var type = typeof box,
                    parts, ln;
                if (type === 'number') {
                    return {
                        top: box,
                        right: box,
                        bottom: box,
                        left: box
                    };
                } else if (type !== 'string') {
                    
                    return box;
                }
                parts = box.split(' ');
                ln = parts.length;
                if (ln === 1) {
                    parts[1] = parts[2] = parts[3] = parts[0];
                } else if (ln === 2) {
                    parts[2] = parts[0];
                    parts[3] = parts[1];
                } else if (ln === 3) {
                    parts[3] = parts[1];
                }
                return {
                    top: parseFloat(parts[0]) || 0,
                    right: parseFloat(parts[1]) || 0,
                    bottom: parseFloat(parts[2]) || 0,
                    left: parseFloat(parts[3]) || 0
                };
            },
            
            parseStyles: function(styles) {
                var out = {},
                    matches;
                if (styles) {
                    
                    
                    
                    
                    cssRe.lastIndex = 0;
                    while ((matches = cssRe.exec(styles))) {
                        out[matches[1]] = matches[2] || '';
                    }
                }
                return out;
            },
            
            select: function(selector, composite, root) {
                return Ext.fly(root || DOC).select(selector, composite);
            },
            
            query: function(selector, asDom, root) {
                return Ext.fly(root || DOC).query(selector, asDom);
            },
            
            unitizeBox: function(box, units) {
                var me = this;
                box = me.parseBox(box);
                return me.addUnits(box.top, units) + ' ' + me.addUnits(box.right, units) + ' ' + me.addUnits(box.bottom, units) + ' ' + me.addUnits(box.left, units);
            },
            
            serializeForm: function(form) {
                var fElements = form.elements || (DOC.forms[form] || Ext.getDom(form)).elements,
                    hasSubmit = false,
                    encoder = encodeURIComponent,
                    data = '',
                    eLen = fElements.length,
                    element, name, type, options, hasValue, e, o, oLen, opt;
                for (e = 0; e < eLen; e++) {
                    element = fElements[e];
                    name = element.name;
                    type = element.type;
                    options = element.options;
                    if (!element.disabled && name) {
                        if (/select-(one|multiple)/i.test(type)) {
                            oLen = options.length;
                            for (o = 0; o < oLen; o++) {
                                opt = options[o];
                                if (opt.selected) {
                                    hasValue = opt.hasAttribute('value');
                                    data += Ext.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);
            },
            
            getCommonAncestor: function(nodeA, nodeB, returnDom) {
                caFly = caFly || new Ext.dom.Fly();
                caFly.attach(Ext.getDom(nodeA));
                while (!caFly.isAncestor(nodeB)) {
                    if (caFly.dom.parentNode) {
                        caFly.attach(caFly.dom.parentNode);
                    } else 
                    {
                        caFly.attach(document.body);
                        break;
                    }
                }
                return returnDom ? caFly.dom : Ext.get(caFly);
            }
        },
        
        
        addCls: function(names, prefix, suffix) {
            var me = this,
                hasNewCls, dom, map, classList, i, ln, name,
                elementData = me.getData();
            if (!names) {
                return me;
            }
            if (!elementData.isSynchronized) {
                me.synchronize();
            }
            dom = me.dom;
            map = elementData.classMap;
            classList = elementData.classList;
            prefix = prefix ? prefix + SEPARATOR : '';
            suffix = suffix ? SEPARATOR + suffix : '';
            if (typeof names === 'string') {
                names = names.split(spacesRe);
            }
            for (i = 0 , ln = names.length; i < ln; i++) {
                name = prefix + names[i] + suffix;
                if (!map[name]) {
                    map[name] = true;
                    classList.push(name);
                    hasNewCls = true;
                }
            }
            if (hasNewCls) {
                dom.className = classList.join(' ');
            }
            return me;
        },
        addStyles: function(sides, styles) {
            var totalSize = 0,
                sidesArr = (sides || '').match(wordsRe),
                i,
                len = sidesArr.length,
                side,
                styleSides = [];
            if (len === 1) {
                totalSize = Math.abs(parseFloat(this.getStyle(styles[sidesArr[0]])) || 0);
            } else if (len) {
                for (i = 0; i < len; i++) {
                    side = sidesArr[i];
                    styleSides.push(styles[side]);
                }
                
                styleSides = this.getStyle(styleSides);
                for (i = 0; i < len; i++) {
                    side = sidesArr[i];
                    totalSize += parseFloat(styleSides[styles[side]]) || 0;
                }
            }
            return totalSize;
        },
        addUnits: function(size, units) {
            return Element.addUnits(size, units);
        },
        
        adjustDirect2DDimension: function(dimension) {
            var me = this,
                dom = me.dom,
                display = me.getStyle('display'),
                inlineDisplay = dom.style.display,
                inlinePosition = dom.style.position,
                originIndex = dimension === WIDTH ? 0 : 1,
                currentStyle = dom.currentStyle,
                floating;
            if (display === 'inline') {
                dom.style.display = 'inline-block';
            }
            dom.style.position = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
            
            
            
            
            floating = (parseFloat(currentStyle[dimension]) || parseFloat(currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
            dom.style.position = inlinePosition;
            if (display === 'inline') {
                dom.style.display = inlineDisplay;
            }
            return floating;
        },
        
        
        
        animate: function(animation) {
            animation = new Ext.fx.Animation(animation);
            animation.setElement(this);
            this._activeAnimation = animation;
            animation.on({
                animationend: this._onAnimationEnd
            });
            Ext.Animator.run(animation);
        },
        _onAnimationEnd: function() {
            this._activeAnimation = null;
        },
        getActiveAnimation: function() {
            return this._activeAnimation;
        },
        append: function() {
            this.appendChild.apply(this, arguments);
        },
        
        appendChild: function(el, returnDom) {
            var me = this,
                insertEl, eLen, e;
            if (el.nodeType || el.dom || typeof el === 'string') {
                
                el = Ext.getDom(el);
                me.dom.appendChild(el);
                return !returnDom ? Ext.get(el) : el;
            } else if (el.length) {
                
                insertEl = Ext.fly(document.createDocumentFragment());
                eLen = el.length;
                for (e = 0; e < eLen; e++) {
                    insertEl.appendChild(el[e], returnDom);
                }
                me.dom.appendChild(insertEl.dom);
                return returnDom ? insertEl.dom : insertEl;
            } else {
                
                return me.createChild(el, null, returnDom);
            }
        },
        
        appendTo: function(el) {
            Ext.getDom(el).appendChild(this.dom);
            return this;
        },
        
        applyStyles: function(styles) {
            if (styles) {
                if (typeof styles === "function") {
                    styles = styles.call();
                }
                if (typeof styles === "string") {
                    styles = Element.parseStyles(styles);
                }
                if (typeof styles === "object") {
                    this.setStyle(styles);
                }
            }
            return this;
        },
        
        blur: function() {
            var me = this,
                dom = me.dom;
            
            
            if (dom !== DOC.body) {
                try {
                    dom.blur();
                } catch (e) {}
                return me;
            } else {
                return me.focus(undefined, dom);
            }
        },
        
        cacheScrollValues: function() {
            var me = this,
                scrollValues = [],
                scrolledDescendants = [],
                descendants, descendant, i, len;
            scrollFly = scrollFly || new Ext.dom.Fly();
            descendants = me.query('*');
            for (i = 0 , len = descendants.length; i < len; i++) {
                descendant = descendants[i];
                
                
                if (descendant.scrollTop > 0 || descendant.scrollLeft !== 0) {
                    scrolledDescendants.push(descendant);
                    scrollValues.push(scrollFly.attach(descendant).getScroll());
                }
            }
            return function() {
                var scroll, i, len;
                for (i = 0 , len = scrolledDescendants.length; i < len; i++) {
                    scroll = scrollValues[i];
                    scrollFly.attach(scrolledDescendants[i]);
                    scrollFly.setScrollLeft(scroll.left);
                    scrollFly.setScrollTop(scroll.top);
                }
            };
        },
        
        center: function(centerIn) {
            return this.alignTo(centerIn || DOC, 'c-c');
        },
        
        child: function(selector, returnDom) {
            var me = this,
                
                
                id = Ext.get(me).id;
            return me.selectNode(Ext.makeIdSelector(id) + " > " + selector, !!returnDom);
        },
        constrainScrollLeft: function(left) {
            var dom = this.dom;
            return Math.max(Math.min(left, dom.scrollWidth - dom.clientWidth), 0);
        },
        constrainScrollTop: function(top) {
            var dom = this.dom;
            return Math.max(Math.min(top, dom.scrollHeight - dom.clientHeight), 0);
        },
        
        createChild: function(config, insertBefore, returnDom) {
            config = config || {
                tag: 'div'
            };
            if (insertBefore) {
                return Ext.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
            } else {
                return Ext.DomHelper.append(this.dom, config, returnDom !== true);
            }
        },
        
        contains: function(element) {
            if (!element) {
                return false;
            }
            var me = this,
                dom = Ext.getDom(element);
            
            
            return (dom === me.dom) || me.isAncestor(dom);
        },
        
        destroy: function() {
            var me = this,
                dom = me.dom;
            if (me.isDestroyed) {
                Ext.Logger.warn("Cannot destroy Element \"" + me.id + "\". Already destroyed.");
                return;
            }
            if (dom) {
                if (dom === DOC.body) {
                    Ext.Error.raise("Cannot destroy body element.");
                } else if (dom === DOC) {
                    Ext.Error.raise("Cannot destroy document object.");
                } else if (dom === WIN) {
                    Ext.Error.raise("Cannot destroy window object");
                }
            }
            if (dom && dom.parentNode) {
                dom.parentNode.removeChild(dom);
            }
            me.collect();
        },
        detach: function() {
            var dom = this.dom;
            if (dom && dom.parentNode && dom.tagName !== 'BODY') {
                dom.parentNode.removeChild(dom);
            }
            return this;
        },
        
        disableShadow: function() {
            var shadow = this.shadow;
            if (shadow) {
                shadow.hide();
                shadow.disabled = true;
            }
        },
        
        disableShim: function() {
            var shim = this.shim;
            if (shim) {
                shim.hide();
                shim.disabled = true;
            }
        },
        
        disableTouchContextMenu: function() {
            this._contextMenuListenerRemover = this.on({
                MSHoldVisual: function(e) {
                    
                    e.preventDefault();
                },
                destroyable: true,
                delegated: false
            });
        },
        
        disableTouchScroll: function() {
            
            this.addCls(noTouchScrollCls);
            
            
            
            this.on({
                touchmove: function(e) {
                    e.preventDefault();
                },
                translate: false
            });
        },
        
        doReplaceWith: function(element) {
            var dom = this.dom;
            dom.parentNode.replaceChild(Ext.getDom(element), dom);
        },
        
        
        
        doScrollIntoView: function(container, hscroll, animate, highlight, getScrollX, scrollTo) {
            scrollFly = scrollFly || new Ext.dom.Fly();
            var me = this,
                dom = me.dom,
                scrollX = scrollFly.attach(container)[getScrollX](),
                scrollY = container.scrollTop,
                position = me.getScrollIntoViewXY(container, scrollX, scrollY),
                newScrollX = position.x,
                newScrollY = position.y;
            
            if (highlight) {
                if (animate) {
                    animate = Ext.apply({
                        listeners: {
                            afteranimate: function() {
                                scrollFly.attach(dom).highlight();
                            }
                        }
                    }, animate);
                } else {
                    scrollFly.attach(dom).highlight();
                }
            }
            if (newScrollY !== scrollY) {
                scrollFly.attach(container).scrollTo('top', newScrollY, animate);
            }
            if (hscroll !== false && (newScrollX !== scrollX)) {
                scrollFly.attach(container)[scrollTo]('left', newScrollX, animate);
            }
            return me;
        },
        
        down: function(selector, returnDom) {
            return this.selectNode(selector, !!returnDom);
        },
        
        enableShadow: function(options, 
        isVisible) {
            var me = this,
                shadow = me.shadow || (me.shadow = new Ext.dom.Shadow(Ext.apply({
                    target: me
                }, options))),
                shim = me.shim;
            if (shim) {
                shim.offsets = shadow.outerOffsets;
                shim.shadow = shadow;
                shadow.shim = shim;
            }
            
            
            if (isVisible === true || (isVisible !== false && me.isVisible())) {
                
                
                shadow.show();
            } else {
                shadow.hide();
            }
            shadow.disabled = false;
        },
        
        enableShim: function(options, 
        isVisible) {
            var me = this,
                shim = me.shim || (me.shim = new Ext.dom.Shim(Ext.apply({
                    target: me
                }, options))),
                shadow = me.shadow;
            if (shadow) {
                shim.offsets = shadow.outerOffsets;
                shim.shadow = shadow;
                shadow.shim = shim;
            }
            
            
            if (isVisible === true || (isVisible !== false && me.isVisible())) {
                
                
                shim.show();
            } else {
                shim.hide();
            }
            shim.disabled = false;
        },
        
        findParent: function(simpleSelector, limit, returnEl) {
            var me = this,
                target = me.dom,
                topmost = DOC.documentElement,
                depth = 0;
            if (limit || limit === 0) {
                if (typeof limit !== 'number') {
                    topmost = Ext.getDom(limit);
                    limit = Number.MAX_VALUE;
                }
            } else {
                
                limit = 50;
            }
            while (target && target.nodeType === 1 && depth < limit && target !== topmost) {
                if (Ext.fly(target).is(simpleSelector)) {
                    return returnEl ? Ext.get(target) : target;
                }
                depth++;
                target = target.parentNode;
            }
            return null;
        },
        
        findParentNode: function(simpleSelector, limit, returnEl) {
            var p = Ext.fly(this.dom.parentNode);
            return p ? p.findParent(simpleSelector, limit, returnEl) : null;
        },
        
        first: function(selector, returnDom) {
            return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
        },
        
        focus: function(defer, 
        dom) {
            var me = this;
            dom = dom || me.dom;
            try {
                if (Number(defer)) {
                    Ext.defer(me.focus, defer, me, [
                        null,
                        dom
                    ]);
                } else {
                    Ext.GlobalEvents.fireEvent('beforefocus', dom);
                    dom.focus();
                }
            } catch (e) {}
            return me;
        },
        
        collect: function() {
            var me = this,
                dom = me.dom,
                shadow = me.shadow,
                shim = me.shim;
            if (!me.isFly) {
                me.clearListeners();
                delete Ext.cache[me.id];
                me.isDestroyed = true;
            }
            if (dom) {
                dom._extData = me.dom = null;
            }
            
            
            if (shadow) {
                shadow.hide();
                me.shadow = null;
            }
            if (shim) {
                shim.hide();
                me.shim = null;
            }
        },
        getAnchorToXY: function(el, anchor, local, mySize) {
            return el.getAnchorXY(anchor, local, mySize);
        },
        
        getAttribute: function(name, namespace) {
            var dom = this.dom;
            return namespace ? (dom.getAttributeNS(namespace, name) || dom.getAttribute(namespace + ":" + name)) : (dom.getAttribute(name) || dom[name] || null);
        },
        
        getAttributes: function() {
            var attributes = this.dom.attributes,
                result = {},
                attr, i, len;
            for (i = 0 , len = attributes.length; i < len; i++) {
                attr = attributes[i];
                result[attr.name] = attr.value;
            }
            return result;
        },
        
        getBottom: function(local) {
            return (local ? this.getLocalY() : this.getY()) + this.getHeight();
        },
        
        getById: function(id, asDom) {
            
            
            var dom = DOC.getElementById(id) || this.dom.querySelector(Ext.makeIdSelector(id));
            return asDom ? dom : (dom ? Ext.get(dom) : null);
        },
        getBorderPadding: function() {
            var paddingWidth = this.getStyle(paddingsTLRB),
                bordersWidth = this.getStyle(bordersTLRB);
            return {
                beforeX: (parseFloat(bordersWidth[borders.l]) || 0) + (parseFloat(paddingWidth[paddings.l]) || 0),
                afterX: (parseFloat(bordersWidth[borders.r]) || 0) + (parseFloat(paddingWidth[paddings.r]) || 0),
                beforeY: (parseFloat(bordersWidth[borders.t]) || 0) + (parseFloat(paddingWidth[paddings.t]) || 0),
                afterY: (parseFloat(bordersWidth[borders.b]) || 0) + (parseFloat(paddingWidth[paddings.b]) || 0)
            };
        },
        
        getBorderWidth: function(side) {
            return this.addStyles(side, borders);
        },
        getData: function(preventCreate) {
            var dom = this.dom,
                data;
            if (dom) {
                data = dom._extData;
                if (!data && !preventCreate) {
                    dom._extData = data = {};
                }
            }
            return data;
        },
        getFirstChild: function() {
            return Ext.get(this.dom.firstElementChild);
        },
        
        getHeight: function(contentHeight, preciseHeight) {
            var me = this,
                hidden = me.isStyle('display', 'none'),
                height, floating;
            if (hidden) {
                return 0;
            }
            height = me.dom.offsetHeight;
            
            if (Ext.supports.Direct2DBug) {
                floating = me.adjustDirect2DDimension(HEIGHT);
                if (preciseHeight) {
                    height += floating;
                } else if (floating > 0 && floating < 0.5) {
                    height++;
                }
            }
            if (contentHeight) {
                height -= me.getBorderWidth("tb") + me.getPadding("tb");
            }
            return (height < 0) ? 0 : height;
        },
        
        getHtml: function() {
            return this.dom ? this.dom.innerHTML : '';
        },
        
        getLeft: function(local) {
            return local ? this.getLocalX() : this.getX();
        },
        getLocalX: function() {
            var me = this,
                offsetParent,
                x = me.getStyle('left');
            if (!x || x === 'auto') {
                x = 0;
            } else if (pxRe.test(x)) {
                x = parseFloat(x);
            } else {
                x = me.getX();
                
                
                offsetParent = me.dom.offsetParent;
                if (offsetParent) {
                    x -= Ext.fly(offsetParent).getX();
                }
            }
            return x;
        },
        getLocalXY: function() {
            var me = this,
                offsetParent,
                style = me.getStyle([
                    'left',
                    'top'
                ]),
                x = style.left,
                y = style.top;
            if (!x || x === 'auto') {
                x = 0;
            } else if (pxRe.test(x)) {
                x = parseFloat(x);
            } else {
                x = me.getX();
                
                
                offsetParent = me.dom.offsetParent;
                if (offsetParent) {
                    x -= Ext.fly(offsetParent).getX();
                }
            }
            if (!y || y === 'auto') {
                y = 0;
            } else if (pxRe.test(y)) {
                y = parseFloat(y);
            } else {
                y = me.getY();
                
                
                offsetParent = me.dom.offsetParent;
                if (offsetParent) {
                    y -= Ext.fly(offsetParent).getY();
                }
            }
            return [
                x,
                y
            ];
        },
        getLocalY: function() {
            var me = this,
                offsetParent,
                y = me.getStyle('top');
            if (!y || y === 'auto') {
                y = 0;
            } else if (pxRe.test(y)) {
                y = parseFloat(y);
            } else {
                y = me.getY();
                
                
                offsetParent = me.dom.offsetParent;
                if (offsetParent) {
                    y -= Ext.fly(offsetParent).getY();
                }
            }
            return y;
        },
        
        getMargin: (function() {
            var hash = {
                    t: "top",
                    l: "left",
                    r: "right",
                    b: "bottom"
                },
                allMargins = [
                    'margin-top',
                    'margin-left',
                    'margin-right',
                    'margin-bottom'
                ];
            return function(side) {
                var me = this,
                    style, key, o;
                if (!side) {
                    style = me.getStyle(allMargins);
                    o = {};
                    if (style && typeof style === 'object') {
                        o = {};
                        for (key in margins) {
                            o[key] = o[hash[key]] = parseFloat(style[margins[key]]) || 0;
                        }
                    }
                } else {
                    o = me.addStyles(side, margins);
                }
                return o;
            };
        })(),
        
        getPadding: function(side) {
            return this.addStyles(side, paddings);
        },
        getParent: function() {
            return Ext.get(this.dom.parentNode);
        },
        
        getRight: function(local) {
            return (local ? this.getLocalX() : this.getX()) + this.getWidth();
        },
        
        getScroll: function() {
            var me = this,
                dom = me.dom,
                docElement = DOC.documentElement,
                left, top,
                body = document.body;
            if (dom === DOC || dom === body) {
                
                
                
                
                
                
                
                
                left = docElement.scrollLeft || (body ? body.scrollLeft : 0);
                top = docElement.scrollTop || (body ? body.scrollTop : 0);
            } else {
                left = dom.scrollLeft;
                top = dom.scrollTop;
            }
            return {
                left: left,
                top: top
            };
        },
        
        getScrollIntoViewXY: function(container, scrollX, scrollY) {
            var dom = this.dom,
                ct = Ext.getDom(container),
                offsets = this.getOffsetsTo(ct),
                width = dom.offsetWidth,
                height = dom.offsetHeight,
                left = offsets[0] + scrollX,
                top = offsets[1] + scrollY,
                bottom = top + height,
                right = left + width,
                viewHeight = ct.clientHeight,
                viewWidth = ct.clientWidth,
                viewLeft = scrollX,
                viewTop = scrollY,
                viewBottom = viewTop + viewHeight,
                viewRight = viewLeft + viewWidth;
            if (height > viewHeight || top < viewTop) {
                scrollY = top;
            } else if (bottom > viewBottom) {
                scrollY = bottom - viewHeight;
            }
            if (width > viewWidth || left < viewLeft) {
                scrollX = left;
            } else if (right > viewRight) {
                scrollX = right - viewWidth;
            }
            return {
                x: scrollX,
                y: scrollY
            };
        },
        
        getScrollLeft: function() {
            var dom = this.dom;
            if (dom === DOC || dom === document.body) {
                return this.getScroll().left;
            } else {
                return dom.scrollLeft;
            }
        },
        
        getScrollTop: function() {
            var dom = this.dom;
            if (dom === DOC || dom === document.body) {
                return this.getScroll().top;
            } else {
                return dom.scrollTop;
            }
        },
        
        getSize: function(contentSize) {
            return {
                width: this.getWidth(contentSize),
                height: this.getHeight(contentSize)
            };
        },
        
        getStyle: function(property, inline) {
            var me = this,
                dom = me.dom,
                multiple = typeof property !== 'string',
                hooks = me.styleHooks,
                prop = property,
                props = prop,
                len = 1,
                domStyle, camel, values, hook, out, style, i;
            if (multiple) {
                values = {};
                prop = props[0];
                i = 0;
                if (!(len = props.length)) {
                    return values;
                }
            }
            if (!dom || dom.documentElement) {
                return values || '';
            }
            domStyle = dom.style;
            if (inline) {
                style = domStyle;
            } else {
                
                
                
                
                style = dom.ownerDocument.defaultView.getComputedStyle(dom, null);
                
                if (!style) {
                    inline = true;
                    style = domStyle;
                }
            }
            do {
                hook = hooks[prop];
                if (!hook) {
                    hooks[prop] = hook = {
                        name: Element.normalize(prop)
                    };
                }
                if (hook.get) {
                    out = hook.get(dom, me, inline, style);
                } else {
                    camel = hook.name;
                    out = style[camel];
                }
                if (!multiple) {
                    return out;
                }
                values[prop] = out;
                prop = props[++i];
            } while (i < len);
            return values;
        },
        getStyleValue: function(name) {
            return this.dom.style.getPropertyValue(name);
        },
        
        getTop: function(local) {
            return local ? this.getLocalY() : this.getY();
        },
        
        getValue: function(asNumber) {
            var value = this.dom.value;
            return asNumber ? parseInt(value, 10) : value;
        },
        
        getViewSize: function() {
            var dom = this.dom;
            if (dom === DOC || dom === DOC.body) {
                return {
                    width: Element.getViewportWidth(),
                    height: Element.getViewportHeight()
                };
            } else {
                return {
                    width: dom.clientWidth,
                    height: dom.clientHeight
                };
            }
        },
        getVisibilityMode: function() {
            var me = this,
                data = me.getData(),
                mode = data.visibilityMode;
            if (mode === undefined) {
                data.visibilityMode = mode = Element.DISPLAY;
            }
            return mode;
        },
        
        getWidth: function(contentWidth, preciseWidth) {
            var me = this,
                dom = me.dom,
                hidden = me.isStyle('display', 'none'),
                rect, width, floating;
            if (hidden) {
                return 0;
            }
            
            
            
            
            
            
            if (Ext.supports.BoundingClientRect) {
                rect = dom.getBoundingClientRect();
                width = (me.vertical && !Ext.supports.RotatedBoundingClientRect) ? (rect.bottom - rect.top) : (rect.right - rect.left);
                width = preciseWidth ? width : Math.ceil(width);
            } else {
                width = dom.offsetWidth;
            }
            
            
            
            if (Ext.supports.Direct2DBug && !me.vertical) {
                
                floating = me.adjustDirect2DDimension(WIDTH);
                if (preciseWidth) {
                    width += floating;
                }
                
                
                
                else if (floating > 0 && floating < 0.5) {
                    width++;
                }
            }
            if (contentWidth) {
                width -= me.getBorderWidth("lr") + me.getPadding("lr");
            }
            return (width < 0) ? 0 : width;
        },
        
        getX: function() {
            return this.getXY()[0];
        },
        
        getXY: function() {
            var round = Math.round,
                dom = this.dom,
                x = 0,
                y = 0,
                box, scroll;
            if (dom !== DOC && dom !== DOC.body) {
                
                
                try {
                    box = dom.getBoundingClientRect();
                } catch (ex) {
                    box = {
                        left: 0,
                        top: 0
                    };
                }
                x = round(box.left);
                y = round(box.top);
                scroll = Ext.getDoc().getScroll();
                x += scroll.left;
                y += scroll.top;
            }
            return [
                x,
                y
            ];
        },
        
        getY: function() {
            return this.getXY()[1];
        },
        
        getZIndex: function() {
            return parseInt(this.getStyle('z-index'), 10);
        },
        
        hasCls: function(name) {
            var elementData = this.getData();
            if (!elementData.isSynchronized) {
                this.synchronize();
            }
            return elementData.classMap.hasOwnProperty(name);
        },
        
        hide: function() {
            this.setVisible(false);
            return this;
        },
        
        insertAfter: function(el) {
            el = Ext.getDom(el);
            el.parentNode.insertBefore(this.dom, el.nextSibling);
            return this;
        },
        
        insertBefore: function(el) {
            el = Ext.getDom(el);
            el.parentNode.insertBefore(this.dom, el);
            return this;
        },
        
        insertFirst: function(el, returnDom) {
            el = el || {};
            if (el.nodeType || el.dom || typeof el === 'string') {
                
                el = Ext.getDom(el);
                this.dom.insertBefore(el, this.dom.firstChild);
                return !returnDom ? Ext.get(el) : el;
            } else {
                
                return this.createChild(el, this.dom.firstChild, returnDom);
            }
        },
        
        insertHtml: function(where, html, returnEl) {
            var el = Ext.DomHelper.insertHtml(where, this.dom, html);
            return returnEl ? Ext.get(el) : el;
        },
        
        insertSibling: function(el, where, returnDom) {
            var me = this,
                DomHelper = Ext.DomHelper,
                isAfter = (where || 'before').toLowerCase() === 'after',
                rt, insertEl, eLen, e;
            if (Ext.isIterable(el)) {
                eLen = el.length;
                insertEl = Ext.fly(document.createDocumentFragment());
                
                if (Ext.isArray(el)) {
                    for (e = 0; e < eLen; e++) {
                        rt = insertEl.appendChild(el[e], returnDom);
                    }
                } else 
                {
                    for (e = 0; e < eLen; e++) {
                        insertEl.dom.appendChild(rt = el[0]);
                    }
                    if (returnDom === false) {
                        rt = Ext.get(rt);
                    }
                }
                
                me.dom.parentNode.insertBefore(insertEl.dom, isAfter ? me.dom.nextSibling : me.dom);
                return rt;
            }
            el = el || {};
            if (el.nodeType || el.dom) {
                rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
                if (!returnDom) {
                    rt = Ext.get(rt);
                }
            } else {
                if (isAfter && !me.dom.nextSibling) {
                    rt = DomHelper.append(me.dom.parentNode, el, !returnDom);
                } else {
                    rt = DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
                }
            }
            return rt;
        },
        
        is: function(selector) {
            var dom = this.dom,
                is;
            if (!selector) {
                
                
                
                
                
                
                
                is = true;
            } else if (!dom.tagName) {
                
                is = false;
            } else if (Ext.isFunction(selector)) {
                is = selector(dom);
            } else {
                is = dom[Ext.supports.matchesSelector](selector);
            }
            return is;
        },
        
        isAncestor: function(el) {
            var ret = false,
                dom = this.dom,
                child = Ext.getDom(el);
            if (dom && child) {
                if (dom.contains) {
                    return dom.contains(child);
                } else if (dom.compareDocumentPosition) {
                    return !!(dom.compareDocumentPosition(child) & 16);
                } else {
                    while ((child = child.parentNode)) {
                        ret = child === dom || ret;
                    }
                }
            }
            return ret;
        },
        isPainted: (function() {
            return !Ext.browser.is.IE ? function() {
                var dom = this.dom;
                return Boolean(dom && dom.offsetParent);
            } : function() {
                var dom = this.dom;
                return Boolean(dom && (dom.offsetHeight !== 0 && dom.offsetWidth !== 0));
            };
        })(),
        
        isStyle: function(style, val) {
            return this.getStyle(style) === val;
        },
        
        isVisible: function(deep) {
            var dom = this.dom,
                end;
            if (!dom) {
                return false;
            }
            if (!visFly) {
                visFly = new Ext.dom.Fly();
            }
            for (end = dom.ownerDocument.documentElement; dom !== end; dom = dom.parentNode) {
                
                
                if (!dom || dom.nodeType === 11 || (visFly.attach(dom)).isStyle(VISIBILITY, HIDDEN) || visFly.isStyle(DISPLAY, NONE)) {
                    return false;
                }
                
                if (!deep) {
                    break;
                }
            }
            return true;
        },
        
        last: function(selector, returnDom) {
            return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
        },
        matchNode: function(dir, start, selector, returnDom) {
            var dom = this.dom,
                n;
            if (!dom) {
                return null;
            }
            n = dom[start];
            while (n) {
                if (n.nodeType === 1 && (!selector || Ext.fly(n, '_matchNode').is(selector))) {
                    return !returnDom ? Ext.get(n) : n;
                }
                n = n[dir];
            }
            return null;
        },
        
        next: function(selector, returnDom) {
            return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
        },
        
        parent: function(selector, returnDom) {
            return this.matchNode('parentNode', 'parentNode', selector, returnDom);
        },
        
        position: function(pos, zIndex, x, y) {
            var me = this;
            if (!pos && me.isStyle(POSITION, STATIC)) {
                me.setStyle(POSITION, RELATIVE);
            } else if (pos) {
                me.setStyle(POSITION, pos);
            }
            if (zIndex) {
                me.setStyle(ZINDEX, zIndex);
            }
            if (x || y) {
                me.setXY([
                    x || false,
                    y || false
                ]);
            }
        },
        
        prev: function(selector, returnDom) {
            return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
        },
        
        query: function(selector, asDom, 
        single) {
            var dom = this.dom,
                results, len, nlen, node, nodes, i, j;
            if (!dom) {
                return null;
            }
            asDom = (asDom !== false);
            selector = selector.split(",");
            if (!single) {
                
                
                results = [];
            }
            for (i = 0 , len = selector.length; i < len; i++) {
                if (typeof selector[i] === 'string') {
                    if (single) {
                        
                        node = dom.querySelector(selector[i]);
                        return asDom ? node : Ext.get(node);
                    }
                    nodes = dom.querySelectorAll(selector[i]);
                    for (j = 0 , nlen = nodes.length; j < nlen; j++) {
                        results.push(asDom ? nodes[j] : Ext.get(nodes[j]));
                    }
                }
            }
            return results;
        },
        
        radioCls: function(className) {
            var cn = this.dom.parentNode.childNodes,
                v;
            className = Ext.isArray(className) ? className : [
                className
            ];
            for (var i = 0,
                len = cn.length; i < len; i++) {
                v = cn[i];
                if (v && v.nodeType === 1) {
                    Ext.fly(v).removeCls(className);
                }
            }
            return this.addCls(className);
        },
        redraw: function() {
            var dom = this.dom,
                domStyle = dom.style;
            domStyle.display = 'none';
            dom.offsetHeight;
            domStyle.display = '';
        },
        
        remove: function() {
            this.destroy();
        },
        removeChild: function(element) {
            this.dom.removeChild(Ext.getDom(element));
            return this;
        },
        
        removeCls: function(names, prefix, suffix) {
            var me = this,
                hasNewCls, dom, map, classList, i, ln, name,
                elementData = me.getData();
            if (!names) {
                return me;
            }
            if (!elementData.isSynchronized) {
                me.synchronize();
            }
            if (!suffix) {
                suffix = '';
            }
            dom = me.dom;
            map = elementData.classMap;
            classList = elementData.classList;
            prefix = prefix ? prefix + SEPARATOR : '';
            suffix = suffix ? SEPARATOR + suffix : '';
            if (typeof names === 'string') {
                names = names.split(spacesRe);
            }
            for (i = 0 , ln = names.length; i < ln; i++) {
                name = prefix + names[i] + suffix;
                if (map[name]) {
                    delete map[name];
                    Ext.Array.remove(classList, name);
                    hasNewCls = true;
                }
            }
            if (hasNewCls) {
                dom.className = classList.join(' ');
            }
            return me;
        },
        
        repaint: function() {
            var me = this;
            me.addCls(Ext.baseCSSPrefix + 'repaint');
            Ext.defer(function() {
                if (me.dom) {
                    
                    Ext.fly(me.dom).removeCls(Ext.baseCSSPrefix + 'repaint');
                }
            }, 1);
            return me;
        },
        
        replace: function(el, destroy) {
            el = Ext.getDom(el);
            var parentNode = el.parentNode,
                id = el.id,
                dom = this.dom;
            if (!parentNode) {
                Ext.Error.raise('Cannot replace element "' + id + '". It is not attached to a parent node.');
            }
            if (destroy !== false && id && Ext.cache[id]) {
                parentNode.insertBefore(dom, el);
                Ext.get(el).destroy();
            } else {
                parentNode.replaceChild(dom, el);
            }
            return this;
        },
        
        replaceCls: function(oldName, newName, prefix, suffix) {
            var me = this,
                dom, map, classList, i, ln, name,
                elementData = me.getData();
            if (!oldName && !newName) {
                return me;
            }
            oldName = oldName || [];
            newName = newName || [];
            if (!elementData.isSynchronized) {
                me.synchronize();
            }
            if (!suffix) {
                suffix = '';
            }
            dom = me.dom;
            map = elementData.classMap;
            classList = elementData.classList;
            prefix = prefix ? prefix + SEPARATOR : '';
            suffix = suffix ? SEPARATOR + suffix : '';
            if (typeof oldName === 'string') {
                oldName = oldName.split(spacesRe);
            }
            if (typeof newName === 'string') {
                newName = newName.split(spacesRe);
            }
            for (i = 0 , ln = oldName.length; i < ln; i++) {
                name = prefix + oldName[i] + suffix;
                if (map[name]) {
                    delete map[name];
                    Ext.Array.remove(classList, name);
                }
            }
            for (i = 0 , ln = newName.length; i < ln; i++) {
                name = prefix + newName[i] + suffix;
                if (!map[name]) {
                    map[name] = true;
                    classList.push(name);
                }
            }
            dom.className = classList.join(' ');
            return me;
        },
        
        replaceWith: function(el) {
            var me = this,
                dom = me.dom,
                parent = dom.parentNode,
                cache = Ext.cache,
                newDom;
            me.clearListeners();
            if (el.nodeType || el.dom || typeof el === 'string') {
                el = Ext.get(el);
                newDom = parent.insertBefore(el.dom, dom);
            } else {
                
                newDom = Ext.DomHelper.insertBefore(dom, el);
            }
            parent.removeChild(dom);
            me.dom = newDom;
            if (!me.isFly) {
                delete cache[me.id];
                cache[me.id = Ext.id(newDom)] = me;
            }
            return me;
        },
        resolveListenerScope: function(defaultScope) {
            
            var component = this.component;
            return component ? component.resolveListenerScope(defaultScope) : this;
        },
        
        scroll: function(direction, distance, animate) {
            if (!this.isScrollable()) {
                return false;
            }
            
            
            direction = direction.charAt(0);
            var me = this,
                dom = me.dom,
                side = direction === 'r' || direction === 'l' ? 'left' : 'top',
                scrolled = false,
                currentScroll, constrainedScroll;
            if (direction === 'l' || direction === 't' || direction === 'u') {
                distance = -distance;
            }
            if (side === 'left') {
                currentScroll = dom.scrollLeft;
                constrainedScroll = me.constrainScrollLeft(currentScroll + distance);
            } else {
                currentScroll = dom.scrollTop;
                constrainedScroll = me.constrainScrollTop(currentScroll + distance);
            }
            if (constrainedScroll !== currentScroll) {
                this.scrollTo(side, constrainedScroll, animate);
                scrolled = true;
            }
            return scrolled;
        },
        
        scrollBy: function(deltaX, deltaY, animate) {
            var me = this,
                dom = me.dom;
            
            if (deltaX.length) {
                animate = deltaY;
                deltaY = deltaX[1];
                deltaX = deltaX[0];
            } else if (typeof deltaX != 'number') {
                
                animate = deltaY;
                deltaY = deltaX.y;
                deltaX = deltaX.x;
            }
            if (deltaX) {
                me.scrollTo('left', me.constrainScrollLeft(dom.scrollLeft + deltaX), animate);
            }
            if (deltaY) {
                me.scrollTo('top', me.constrainScrollTop(dom.scrollTop + deltaY), animate);
            }
            return me;
        },
        
        scrollChildIntoView: function(child, hscroll) {
            
            Ext.fly(child).scrollIntoView(this, hscroll);
        },
        
        scrollIntoView: function(container, hscroll, animate, highlight) {
            container = Ext.getDom(container) || Ext.getBody().dom;
            return this.doScrollIntoView(container, hscroll, animate, highlight, 'getScrollLeft', 'scrollTo');
        },
        
        scrollTo: function(side, value, animate) {
            
            var top = /top/i.test(side),
                me = this,
                prop = top ? 'scrollTop' : 'scrollLeft',
                dom = me.dom,
                animCfg;
            if (!animate || !me.anim) {
                
                dom[prop] = value;
                
                dom[prop] = value;
            } else {
                animCfg = {
                    to: {}
                };
                animCfg.to[prop] = value;
                if (Ext.isObject(animate)) {
                    Ext.applyIf(animCfg, animate);
                }
                me.animate(animCfg);
            }
            return me;
        },
        
        select: function(selector, composite) {
            var isElementArray, elements;
            if (typeof selector === "string") {
                elements = this.query(selector, !composite);
            } else if (selector.length === undefined) {
                Ext.Error.raise("Invalid selector specified: " + selector);
            } else {
                
                
                elements = selector;
                isElementArray = true;
            }
            
            
            
            
            
            
            return composite ? new Ext.CompositeElement(elements,!isElementArray) : new Ext.CompositeElementLite(elements,true);
        },
        
        selectNode: function(selector, asDom) {
            return this.query(selector, asDom, true);
        },
        
        set: function(attributes, useSet) {
            var me = this,
                dom = me.dom,
                attribute, value;
            for (attribute in attributes) {
                if (attributes.hasOwnProperty(attribute)) {
                    value = attributes[attribute];
                    if (attribute === 'style') {
                        me.applyStyles(value);
                    } else if (attribute === 'cls') {
                        dom.className = value;
                    } else if (useSet !== false) {
                        if (value === undefined) {
                            dom.removeAttribute(attribute);
                        } else {
                            dom.setAttribute(attribute, value);
                        }
                    } else {
                        dom[attribute] = value;
                    }
                }
            }
            return me;
        },
        
        setBottom: function(bottom) {
            this.dom.style[BOTTOM] = Element.addUnits(bottom);
            return this;
        },
        setBorder: function(border) {
            var me = this,
                domStyle = me.dom.style;
            if (border || border === 0) {
                border = me.self.unitizeBox((border === true) ? 1 : border);
                domStyle.setProperty('border-width', border, 'important');
            } else {
                domStyle.removeProperty('border-top-width');
                domStyle.removeProperty('border-right-width');
                domStyle.removeProperty('border-bottom-width');
                domStyle.removeProperty('border-left-width');
            }
        },
        
        setCls: function(className) {
            var me = this,
                elementData = me.getData(),
                map = elementData.classMap,
                i, ln, name;
            if (typeof className === 'string') {
                className = className.split(spacesRe);
            }
            for (i = 0 , ln = className.length; i < ln; i++) {
                name = className[i];
                if (!map[name]) {
                    map[name] = true;
                }
            }
            elementData.classList = className.slice();
            me.dom.className = className.join(' ');
        },
        
        setHeight: function(height) {
            var me = this;
            me.dom.style[HEIGHT] = Element.addUnits(height);
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            return me;
        },
        
        setHtml: function(html) {
            if (this.dom) {
                this.dom.innerHTML = html;
            }
        },
        setId: function(id) {
            var me = this,
                currentId = me.id,
                cache = Ext.cache;
            if (currentId) {
                delete cache[currentId];
            }
            me.dom.id = id;
            
            me.id = id;
            cache[id] = me;
            return me;
        },
        
        setLeft: function(left) {
            var me = this;
            me.dom.style[LEFT] = Element.addUnits(left);
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            return me;
        },
        setLocalX: function(x) {
            var me = this,
                style = me.dom.style;
            
            style.right = 'auto';
            style.left = (x === null) ? 'auto' : x + 'px';
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            return me;
        },
        setLocalXY: function(x, y) {
            var me = this,
                style = me.dom.style;
            
            style.right = 'auto';
            if (x && x.length) {
                y = x[1];
                x = x[0];
            }
            if (x === null) {
                style.left = 'auto';
            } else if (x !== undefined) {
                style.left = x + 'px';
            }
            if (y === null) {
                style.top = 'auto';
            } else if (y !== undefined) {
                style.top = y + 'px';
            }
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            return me;
        },
        setLocalY: function(y) {
            var me = this;
            me.dom.style.top = (y === null) ? 'auto' : y + 'px';
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            return me;
        },
        setMargin: function(margin) {
            var me = this,
                domStyle = me.dom.style;
            if (margin || margin === 0) {
                margin = me.self.unitizeBox((margin === true) ? 5 : margin);
                domStyle.setProperty('margin', margin, 'important');
            } else {
                domStyle.removeProperty('margin-top');
                domStyle.removeProperty('margin-right');
                domStyle.removeProperty('margin-bottom');
                domStyle.removeProperty('margin-left');
            }
        },
        
        setMaxHeight: function(height) {
            this.dom.style[MAX_HEIGHT] = Element.addUnits(height);
            return this;
        },
        
        setMaxWidth: function(width) {
            this.dom.style[MAX_WIDTH] = Element.addUnits(width);
            return this;
        },
        
        setMinHeight: function(height) {
            this.dom.style[MIN_HEIGHT] = Element.addUnits(height);
            return this;
        },
        
        setMinWidth: function(width) {
            this.dom.style[MIN_WIDTH] = Element.addUnits(width);
            return this;
        },
        setPadding: function(padding) {
            var me = this,
                domStyle = me.dom.style;
            if (padding || padding === 0) {
                padding = me.self.unitizeBox((padding === true) ? 5 : padding);
                domStyle.setProperty('padding', padding, 'important');
            } else {
                domStyle.removeProperty('padding-top');
                domStyle.removeProperty('padding-right');
                domStyle.removeProperty('padding-bottom');
                domStyle.removeProperty('padding-left');
            }
        },
        
        setRight: function(right) {
            this.dom.style[RIGHT] = Element.addUnits(right);
            return this;
        },
        
        setScrollLeft: function(left) {
            this.dom.scrollLeft = left;
            return this;
        },
        
        setScrollTop: function(top) {
            this.dom.scrollTop = top;
            return this;
        },
        
        setSize: function(width, height) {
            var me = this,
                style = me.dom.style;
            if (Ext.isObject(width)) {
                
                height = width.height;
                width = width.width;
            }
            style.width = Element.addUnits(width);
            style.height = Element.addUnits(height);
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            return me;
        },
        setSizeState: function(state) {
            var me = this,
                classes = [
                    Ext.baseCSSPrefix + 'sized',
                    Ext.baseCSSPrefix + 'unsized',
                    Ext.baseCSSPrefix + 'stretched'
                ],
                states = [
                    true,
                    false,
                    null
                ],
                index = states.indexOf(state),
                addedClass;
            if (index !== -1) {
                addedClass = classes[index];
                classes.splice(index, 1);
                me.addCls(addedClass);
            }
            me.removeCls(classes);
            return me;
        },
        
        setStyle: function(prop, value) {
            var me = this,
                dom = me.dom,
                hooks = me.styleHooks,
                style = dom.style,
                name = prop,
                hook;
            
            if (typeof name === 'string') {
                hook = hooks[name];
                if (!hook) {
                    hooks[name] = hook = {
                        name: Element.normalize(name)
                    };
                }
                value = (value == null) ? '' : value;
                
                if (hook.set) {
                    hook.set(dom, value, me);
                } else {
                    style[hook.name] = value;
                }
                if (hook.afterSet) {
                    hook.afterSet(dom, value, me);
                }
            } else {
                for (name in prop) {
                    if (prop.hasOwnProperty(name)) {
                        hook = hooks[name];
                        if (!hook) {
                            hooks[name] = hook = {
                                name: Element.normalize(name)
                            };
                        }
                        value = prop[name];
                        value = (value == null) ? '' : value;
                        
                        if (hook.set) {
                            hook.set(dom, value, me);
                        } else {
                            style[hook.name] = value;
                        }
                        if (hook.afterSet) {
                            hook.afterSet(dom, value, me);
                        }
                    }
                }
            }
            return me;
        },
        setText: function(text) {
            this.dom.textContent = text;
        },
        
        setTop: function(top) {
            var me = this;
            me.dom.style[TOP] = Element.addUnits(top);
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            return me;
        },
        setUnderlaysVisible: function(visible) {
            var shadow = this.shadow,
                shim = this.shim;
            if (shadow && !shadow.disabled) {
                if (visible) {
                    shadow.show();
                } else {
                    shadow.hide();
                }
            }
            if (shim && !shim.disabled) {
                if (visible) {
                    shim.show();
                } else {
                    shim.hide();
                }
            }
        },
        
        setVisibility: function(isVisible) {
            var domStyle = this.dom.style;
            if (isVisible) {
                domStyle.removeProperty('visibility');
            } else {
                domStyle.setProperty('visibility', 'hidden', 'important');
            }
        },
        
        setVisibilityMode: function(mode) {
            if (mode !== 1 && mode !== 2 && mode !== 3) {
                Ext.Error.raise("visibilityMode must be one of the following: " + "Ext.Element.DISPLAY, Ext.Element.VISIBILITY, or Ext.Element.OFFSETS");
            }
            this.getData().visibilityMode = mode;
            return this;
        },
        
        setVisible: function(visible) {
            var me = this,
                mode = me.getVisibilityMode(),
                method = visible ? 'removeCls' : 'addCls';
            switch (mode) {
                case Element.DISPLAY:
                    me.removeCls([
                        visibilityCls,
                        offsetsCls
                    ]);
                    me[method](displayCls);
                    break;
                case Element.VISIBILITY:
                    me.removeCls([
                        displayCls,
                        offsetsCls
                    ]);
                    me[method](visibilityCls);
                    break;
                case Element.OFFSETS:
                    me.removeCls([
                        visibilityCls,
                        displayCls
                    ]);
                    me[method](offsetsCls);
                    break;
            }
            if (me.shadow || me.shim) {
                me.setUnderlaysVisible(visible);
            }
            return me;
        },
        
        setWidth: function(width) {
            var me = this;
            me.dom.style[WIDTH] = Element.addUnits(width);
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            return me;
        },
        
        setX: function(x) {
            return this.setXY([
                x,
                false
            ]);
        },
        
        setXY: function(xy) {
            var me = this,
                pts = me.translatePoints(xy),
                style = me.dom.style,
                pos;
            me.position();
            
            
            style.right = 'auto';
            for (pos in pts) {
                if (!isNaN(pts[pos])) {
                    style[pos] = pts[pos] + 'px';
                }
            }
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            return me;
        },
        
        setY: function(y) {
            return this.setXY([
                false,
                y
            ]);
        },
        
        setZIndex: function(zindex) {
            var me = this;
            if (me.shadow) {
                me.shadow.setZIndex(zindex);
            }
            if (me.shim) {
                me.shim.setZIndex(zindex);
            }
            return me.setStyle('z-index', zindex);
        },
        
        show: function() {
            this.setVisible(true);
            return this;
        },
        
        swapCls: function(firstClass, secondClass, flag, prefix) {
            if (flag === undefined) {
                flag = true;
            }
            var me = this,
                addedClass = flag ? firstClass : secondClass,
                removedClass = flag ? secondClass : firstClass;
            if (removedClass) {
                me.removeCls(prefix ? prefix + '-' + removedClass : removedClass);
            }
            if (addedClass) {
                me.addCls(prefix ? prefix + '-' + addedClass : addedClass);
            }
            return me;
        },
        
        synchronize: function() {
            var me = this,
                dom = me.dom,
                hasClassMap = {},
                className = dom.className,
                classList, i, ln, name,
                elementData = me.getData();
            if (className && className.length > 0) {
                classList = dom.className.split(classNameSplitRegex);
                for (i = 0 , ln = classList.length; i < ln; i++) {
                    name = classList[i];
                    hasClassMap[name] = true;
                }
            } else {
                classList = [];
            }
            elementData.classList = classList;
            elementData.classMap = hasClassMap;
            elementData.isSynchronized = true;
            return me;
        },
        
        syncUnderlays: function() {
            var me = this,
                shadow = me.shadow,
                shim = me.shim,
                dom = me.dom,
                xy, x, y, w, h;
            if (me.isVisible()) {
                xy = me.getXY();
                x = xy[0];
                y = xy[1];
                w = dom.offsetWidth;
                h = dom.offsetHeight;
                if (shadow && !shadow.hidden) {
                    shadow.realign(x, y, w, h);
                }
                if (shim && !shim.hidden) {
                    shim.realign(x, y, w, h);
                }
            }
        },
        
        toggleCls: function(className, force) {
            if (typeof force !== 'boolean') {
                force = !this.hasCls(className);
            }
            return (force) ? this.addCls(className) : this.removeCls(className);
        },
        
        toggle: function() {
            this.setVisible(!this.isVisible());
            return this;
        },
        translate: function() {
            var transformStyleName = 'webkitTransform' in DOC.createElement('div').style ? 'webkitTransform' : 'transform';
            return function(x, y, z) {
                this.dom.style[transformStyleName] = 'translate3d(' + (x || 0) + 'px, ' + (y || 0) + 'px, ' + (z || 0) + 'px)';
            };
        }(),
        
        unwrap: function() {
            var dom = this.dom,
                parentNode = dom.parentNode,
                grandparentNode;
            if (parentNode) {
                grandparentNode = parentNode.parentNode;
                grandparentNode.insertBefore(dom, parentNode);
                grandparentNode.removeChild(parentNode);
            } else {
                grandparentNode = document.createDocumentFragment();
                grandparentNode.appendChild(dom);
            }
            return this;
        },
        
        up: function(simpleSelector, limit, returnDom) {
            return this.findParentNode(simpleSelector, limit, !returnDom);
        },
        
        update: function(html) {
            return this.setHtml(html);
        },
        
        wrap: function(config, returnDom, selector) {
            var me = this,
                dom = me.dom,
                newEl = Ext.DomHelper.insertBefore(dom, config || {
                    tag: "div"
                }, !returnDom),
                target = newEl;
            if (selector) {
                target = newEl.selectNode(selector, returnDom);
            }
            target.appendChild(dom);
            return newEl;
        },
        privates: {
            doAddListener: function(eventName, fn, scope, options, order, caller, manager) {
                var me = this,
                    observableDoAddListener, additiveEventName, translatedEventName;
                
                
                if (!me.blockedEvents[eventName]) {
                    observableDoAddListener = me.mixins.observable.doAddListener;
                    options = options || {};
                    if (me.longpressEvents[eventName]) {
                        me.disableTouchContextMenu();
                    }
                    if (Element.useDelegatedEvents === false) {
                        options.delegated = options.delegated || false;
                    }
                    if (options.translate !== false) {
                        
                        
                        additiveEventName = me.additiveEvents[eventName];
                        if (additiveEventName) {
                            
                            
                            
                            
                            options.type = eventName;
                            eventName = additiveEventName;
                            observableDoAddListener.call(me, eventName, fn, scope, options, order, caller, manager);
                        }
                        translatedEventName = me.eventMap[eventName];
                        if (translatedEventName) {
                            
                            options.type = options.type || eventName;
                            eventName = translatedEventName;
                        }
                    }
                    observableDoAddListener.call(me, eventName, fn, scope, options, order, caller, manager);
                    
                    
                    
                    
                    delete options.type;
                }
            },
            doRemoveListener: function(eventName, fn, scope) {
                var me = this,
                    observableDoRemoveListener, translatedEventName, additiveEventName, contextMenuListenerRemover;
                
                
                if (!me.blockedEvents[eventName]) {
                    observableDoRemoveListener = me.mixins.observable.doRemoveListener;
                    if (me.longpressEvents[eventName]) {
                        contextMenuListenerRemover = this._contextMenuListenerRemover;
                        if (contextMenuListenerRemover) {
                            contextMenuListenerRemover.destroy();
                        }
                    }
                    
                    
                    additiveEventName = me.additiveEvents[eventName];
                    if (additiveEventName) {
                        
                        
                        
                        
                        eventName = additiveEventName;
                        observableDoRemoveListener.call(me, eventName, fn, scope);
                    }
                    translatedEventName = me.eventMap[eventName];
                    if (translatedEventName) {
                        observableDoRemoveListener.call(me, translatedEventName, fn, scope);
                    }
                    
                    
                    observableDoRemoveListener.call(me, eventName, fn, scope);
                }
            },
            _initEvent: function(eventName) {
                return (this.events[eventName] = new Ext.dom.ElementEvent(this,eventName));
            },
            
            _getPublisher: function(eventName) {
                var Publisher = Ext.event.publisher.Publisher,
                    publisher = Publisher.publishersByEvent[eventName];
                
                
                
                
                if (!publisher || (this.dom === window && eventName === 'resize')) {
                    publisher = Publisher.publishers.dom;
                }
                return publisher;
            }
        },
        deprecated: {
            '5.0': {
                methods: {
                    
                    cssTranslate: null,
                    
                    getHTML: 'getHtml',
                    
                    getOuterHeight: null,
                    
                    getOuterWidth: null,
                    
                    getPageBox: function(getRegion) {
                        var me = this,
                            dom = me.dom,
                            isDoc = dom.nodeName === 'BODY',
                            w = isDoc ? Element.getViewportWidth() : dom.offsetWidth,
                            h = isDoc ? Element.getViewportHeight() : dom.offsetHeight,
                            xy = me.getXY(),
                            t = xy[1],
                            r = xy[0] + w,
                            b = xy[1] + h,
                            l = xy[0];
                        if (getRegion) {
                            return new Ext.util.Region(t,r,b,l);
                        } else {
                            return {
                                left: l,
                                top: t,
                                width: w,
                                height: h,
                                right: r,
                                bottom: b
                            };
                        }
                    },
                    
                    getScrollParent: null,
                    
                    isDescendent: null,
                    
                    isTransparent: function(prop) {
                        var value = this.getStyle(prop);
                        return value ? transparentRe.test(value) : false;
                    },
                    
                    purgeAllListeners: 'clearListeners',
                    
                    removeAllListeners: 'clearListeners',
                    
                    setHTML: 'setHtml',
                    
                    setTopLeft: null
                }
            }
        }
    };
}, function(Element) {
    var DOC = document,
        prototype = Element.prototype,
        supports = Ext.supports,
        pointerdown = 'pointerdown',
        pointermove = 'pointermove',
        pointerup = 'pointerup',
        pointercancel = 'pointercancel',
        MSPointerDown = 'MSPointerDown',
        MSPointerMove = 'MSPointerMove',
        MSPointerUp = 'MSPointerUp',
        MSPointerCancel = 'MSPointerCancel',
        mousedown = 'mousedown',
        mousemove = 'mousemove',
        mouseup = 'mouseup',
        mouseover = 'mouseover',
        mouseout = 'mouseout',
        mouseenter = 'mouseenter',
        mouseleave = 'mouseleave',
        touchstart = 'touchstart',
        touchmove = 'touchmove',
        touchend = 'touchend',
        touchcancel = 'touchcancel',
        click = 'click',
        dblclick = 'dblclick',
        tap = 'tap',
        doubletap = 'doubletap',
        eventMap = prototype.eventMap = {},
        additiveEvents = prototype.additiveEvents = {},
        oldId = Ext.id,
        eventOptions;
    
    Ext.id = function(obj, prefix) {
        var el = Ext.getDom(obj, true),
            sandboxPrefix, id;
        if (!el) {
            id = oldId(obj, prefix);
        } else if (!(id = el.id)) {
            id = oldId(null, prefix || Element.prototype.identifiablePrefix);
            if (Ext.isSandboxed) {
                sandboxPrefix = Ext.sandboxPrefix || (Ext.sandboxPrefix = Ext.sandboxName.toLowerCase() + '-');
                id = sandboxPrefix + id;
            }
            el.id = id;
        }
        return id;
    };
    if (supports.PointerEvents) {
        eventMap[mousedown] = pointerdown;
        eventMap[mousemove] = pointermove;
        eventMap[mouseup] = pointerup;
        eventMap[touchstart] = pointerdown;
        eventMap[touchmove] = pointermove;
        eventMap[touchend] = pointerup;
        eventMap[touchcancel] = pointercancel;
        eventMap[click] = tap;
        eventMap[dblclick] = doubletap;
        
        
        
        
        
        
        
        
        
        eventMap[mouseover] = 'pointerover';
        eventMap[mouseout] = 'pointerout';
        eventMap[mouseenter] = 'pointerenter';
        eventMap[mouseleave] = 'pointerleave';
    } else if (supports.MSPointerEvents) {
        
        eventMap[pointerdown] = MSPointerDown;
        eventMap[pointermove] = MSPointerMove;
        eventMap[pointerup] = MSPointerUp;
        eventMap[pointercancel] = MSPointerCancel;
        eventMap[mousedown] = MSPointerDown;
        eventMap[mousemove] = MSPointerMove;
        eventMap[mouseup] = MSPointerUp;
        eventMap[touchstart] = MSPointerDown;
        eventMap[touchmove] = MSPointerMove;
        eventMap[touchend] = MSPointerUp;
        eventMap[touchcancel] = MSPointerCancel;
        eventMap[click] = tap;
        eventMap[dblclick] = doubletap;
        
        
        eventMap[mouseover] = 'MSPointerOver';
        eventMap[mouseout] = 'MSPointerOut';
    } else if (supports.TouchEvents) {
        eventMap[pointerdown] = touchstart;
        eventMap[pointermove] = touchmove;
        eventMap[pointerup] = touchend;
        eventMap[pointercancel] = touchcancel;
        eventMap[mousedown] = touchstart;
        eventMap[mousemove] = touchmove;
        eventMap[mouseup] = touchend;
        eventMap[click] = tap;
        eventMap[dblclick] = doubletap;
        if (Ext.isWebKit && Ext.os.is.Desktop) {
            
            
            
            
            
            
            
            
            
            
            
            eventMap[touchstart] = mousedown;
            eventMap[touchmove] = mousemove;
            eventMap[touchend] = mouseup;
            eventMap[touchcancel] = mouseup;
            additiveEvents[mousedown] = mousedown;
            additiveEvents[mousemove] = mousemove;
            additiveEvents[mouseup] = mouseup;
            additiveEvents[touchstart] = touchstart;
            additiveEvents[touchmove] = touchmove;
            additiveEvents[touchend] = touchend;
            additiveEvents[touchcancel] = touchcancel;
            additiveEvents[pointerdown] = mousedown;
            additiveEvents[pointermove] = mousemove;
            additiveEvents[pointerup] = mouseup;
            additiveEvents[pointercancel] = mouseup;
        }
    } else {
        
        
        eventMap[pointerdown] = mousedown;
        eventMap[pointermove] = mousemove;
        eventMap[pointerup] = mouseup;
        eventMap[pointercancel] = mouseup;
        eventMap[touchstart] = mousedown;
        eventMap[touchmove] = mousemove;
        eventMap[touchend] = mouseup;
        eventMap[touchcancel] = mouseup;
    }
    if (Ext.isWebKit) {
        
        
        eventMap.transitionend = Ext.browser.getVendorProperyName('transitionEnd');
        eventMap.animationstart = Ext.browser.getVendorProperyName('animationStart');
        eventMap.animationend = Ext.browser.getVendorProperyName('animationEnd');
    }
    if (!Ext.supports.MouseWheel && !Ext.isOpera) {
        eventMap.mousewheel = 'DOMMouseScroll';
    }
    eventOptions = prototype.$eventOptions = Ext.Object.chain(prototype.$eventOptions);
    eventOptions.translate = eventOptions.capture = eventOptions.delegate = eventOptions.delegated = eventOptions.stopEvent = eventOptions.preventDefault = eventOptions.stopPropagation = 
    
    
    
    
    eventOptions.element = 1;
    
    prototype.getTrueXY = prototype.getXY;
    
    Ext.select = Element.select;
    
    Ext.query = Element.query;
    Ext.apply(Ext, {
        
        get: function(element) {
            return Element.get(element);
        },
        
        getDom: function(el) {
            if (!el || !DOC) {
                return null;
            }
            return el.dom || (typeof el === 'string' ? Ext.getElementById(el) : el);
        },
        
        getBody: function() {
            if (!Ext._bodyEl) {
                if (!DOC.body) {
                    throw new Error("[Ext.getBody] document.body does not yet exist");
                }
                Ext._bodyEl = Ext.get(DOC.body);
            }
            return Ext._bodyEl;
        },
        
        getHead: function() {
            if (!Ext._headEl) {
                Ext._headEl = Ext.get(DOC.head || DOC.getElementsByTagName('head')[0]);
            }
            return Ext._headEl;
        },
        
        getDoc: function() {
            if (!Ext._docEl) {
                Ext._docEl = Ext.get(DOC);
            }
            return Ext._docEl;
        },
        
        getWin: function() {
            if (!Ext._winEl) {
                Ext._winEl = Ext.get(window);
            }
            return Ext._winEl;
        },
        
        removeNode: function(node) {
            var id = node && node.id,
                el = Ext.cache[id],
                parent;
            if (el) {
                el.destroy();
            } else if (node && node.tagName.toUpperCase() !== 'BODY') {
                parent = node.parentNode;
                if (parent) {
                    parent.removeChild(node);
                }
            }
        }
    });
    
    
    Ext.isGarbage = function(dom) {
        
        
        return dom && 
        dom.nodeType === 1 && dom.tagName !== 'BODY' && dom.tagName !== 'HTML' && 
        
        (!dom.parentNode || 
        
        (!dom.offsetParent && 
        
        
        
        
        
        
        ((Ext.isIE8 ? DOC.all[dom.id] : DOC.getElementById(dom.id)) !== dom) && 
        
        !(Ext.detachedBodyEl && Ext.detachedBodyEl.isAncestor(dom))));
    };
});


Ext.define('Ext.util.Filter', {
    isFilter: true,
    config: {
        
        property: null,
        
        value: null,
        
        filterFn: null,
        
        id: null,
        
        anyMatch: false,
        
        exactMatch: false,
        
        caseSensitive: false,
        
        disabled: false,
        
        disableOnEmpty: false,
        
        operator: null,
        
        root: null,
        
        serializer: null,
        
        convert: null
    },
    
    scope: null,
    
    
    $configStrict: false,
    statics: {
        
        createFilterFn: function(filters) {
            if (!filters) {
                return Ext.returnTrue;
            }
            return function(candidate) {
                var items = filters.isCollection ? filters.items : filters,
                    length = items.length,
                    match = true,
                    i, filter;
                for (i = 0; match && i < length; i++) {
                    filter = items[i];
                    
                    if (!filter.getDisabled()) {
                        match = filter.filter(candidate);
                    }
                }
                return match;
            };
        },
        
        isInvalid: function(cfg) {
            if (!cfg.filterFn) {
                
                if (!cfg.property) {
                    return 'A Filter requires either a property or a filterFn to be set';
                }
                if (!cfg.hasOwnProperty('value') && !cfg.operator) {
                    return 'A Filter requires either a property and value, or a filterFn to be set';
                }
            }
            return false;
        }
    },
    
    constructor: function(config) {
        var warn = Ext.util.Filter.isInvalid(config);
        if (warn) {
            Ext.log.warn(warn);
        }
        this.initConfig(config);
    },
    preventConvert: {
        'in': 1
    },
    filter: function(item) {
        var me = this,
            filterFn = me._filterFn || me.getFilterFn(),
            convert = me.getConvert(),
            value = me._value;
        me._filterValue = value;
        me.isDateValue = Ext.isDate(value);
        if (me.isDateValue) {
            me.dateValue = value.getTime();
        }
        if (convert && !me.preventConvert[me.getOperator()]) {
            me._filterValue = convert.call(me.scope || me, value);
        }
        return filterFn.call(me.scope || me, item);
    },
    getId: function() {
        var id = this._id;
        if (!id) {
            id = this.getProperty();
            if (!id) {
                id = Ext.id(null, 'ext-filter-');
            }
            this._id = id;
        }
        return id;
    },
    getFilterFn: function() {
        var me = this,
            filterFn = me._filterFn,
            operator;
        if (!filterFn) {
            operator = me.getOperator();
            if (operator) {
                filterFn = me.operatorFns[operator];
            } else {
                
                
                
                filterFn = me.createRegexFilter();
            }
            me._filterFn = filterFn;
        }
        return filterFn;
    },
    
    createRegexFilter: function() {
        var me = this,
            anyMatch = !!me.getAnyMatch(),
            exact = !!me.getExactMatch(),
            value = me.getValue(),
            matcher = Ext.String.createRegex(value, !anyMatch, 
            !anyMatch && exact, 
            !me.getCaseSensitive());
        return function(item) {
            var val = me.getPropertyValue(item);
            return matcher ? matcher.test(val) : (val == null);
        };
    },
    
    getPropertyValue: function(item) {
        var root = this._root,
            value = (root == null) ? item : item[root];
        return value[this._property];
    },
    
    getState: function() {
        var config = this.getInitialConfig(),
            result = {},
            name;
        for (name in config) {
            
            
            if (config.hasOwnProperty(name)) {
                result[name] = config[name];
            }
        }
        delete result.root;
        result.value = this.getValue();
        return result;
    },
    getScope: function() {
        return this.scope;
    },
    
    serialize: function() {
        var result = this.getState(),
            serializer = this.getSerializer();
        delete result.id;
        delete result.serializer;
        if (serializer) {
            serializer.call(this, result);
        }
        return result;
    },
    updateOperator: function() {
        this._filterFn = null;
    },
    updateValue: function(value) {
        this._filterFn = null;
        if (this.getDisableOnEmpty()) {
            this.setDisabled(Ext.isEmpty(value));
        }
    },
    updateDisableOnEmpty: function(disableOnEmpty) {
        var disabled = false;
        if (disableOnEmpty) {
            disabled = Ext.isEmpty(this.getValue());
        }
        this.setDisabled(disabled);
    },
    privates: {
        getCandidateValue: function(candidate, v, preventCoerce) {
            var me = this,
                convert = me._convert,
                result = me.getPropertyValue(candidate);
            if (convert) {
                result = convert.call(me.scope || me, result);
            } else if (!preventCoerce) {
                result = Ext.coerce(result, v);
            }
            return result;
        }
    }
}, function() {
    var prototype = this.prototype,
        operatorFns = (prototype.operatorFns = {
            "<": function(candidate) {
                var v = this._filterValue;
                return this.getCandidateValue(candidate, v) < v;
            },
            "<=": function(candidate) {
                var v = this._filterValue;
                return this.getCandidateValue(candidate, v) <= v;
            },
            "=": function(candidate) {
                var me = this,
                    v = me._filterValue;
                candidate = me.getCandidateValue(candidate, v);
                if (me.isDateValue && candidate instanceof Date) {
                    candidate = candidate.getTime();
                    v = me.dateValue;
                }
                return candidate == v;
            },
            "===": function(candidate) {
                var me = this,
                    v = me._filterValue;
                candidate = me.getCandidateValue(candidate, v, true);
                if (me.isDateValue && candidate instanceof Date) {
                    candidate = candidate.getTime();
                    v = me.dateValue;
                }
                return candidate === v;
            },
            ">=": function(candidate) {
                var v = this._filterValue;
                return this.getCandidateValue(candidate, v) >= v;
            },
            ">": function(candidate) {
                var v = this._filterValue;
                return this.getCandidateValue(candidate, v) > v;
            },
            "!=": function(candidate) {
                var me = this,
                    v = me._filterValue;
                candidate = me.getCandidateValue(candidate, v);
                if (me.isDateValue && candidate instanceof Date) {
                    candidate = candidate.getTime();
                    v = me.dateValue;
                }
                return candidate != v;
            },
            "!==": function(candidate) {
                var me = this,
                    v = me._filterValue;
                candidate = me.getCandidateValue(candidate, v, true);
                if (me.isDateValue && candidate instanceof Date) {
                    candidate = candidate.getTime();
                    v = me.dateValue;
                }
                return candidate !== v;
            },
            "in": function(candidate) {
                var v = this._filterValue;
                return Ext.Array.contains(v, this.getCandidateValue(candidate, v));
            },
            like: function(candidate) {
                var v = this._filterValue;
                return v && this.getCandidateValue(candidate, v).toLowerCase().indexOf(v.toLowerCase()) > -1;
            }
        });
    
    operatorFns['=='] = operatorFns['='];
    operatorFns.gt = operatorFns['>'];
    operatorFns.ge = operatorFns['>='];
    operatorFns.lt = operatorFns['<'];
    operatorFns.le = operatorFns['<='];
    operatorFns.eq = operatorFns['='];
    operatorFns.ne = operatorFns['!='];
});


Ext.define('Ext.util.Observable', {
    extend: 'Ext.mixin.Observable',
    
    
    $applyConfigs: true
});


Ext.define('Ext.util.AbstractMixedCollection', {
    requires: [
        'Ext.util.Filter'
    ],
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    isMixedCollection: true,
    
    generation: 0,
    
    indexGeneration: 0,
    constructor: function(allowFunctions, keyFn) {
        var me = this;
        
        if (arguments.length === 1 && Ext.isObject(allowFunctions)) {
            me.initialConfig = allowFunctions;
            Ext.apply(me, allowFunctions);
        } else 
        {
            me.allowFunctions = allowFunctions === true;
            if (keyFn) {
                me.getKey = keyFn;
            }
            me.initialConfig = {
                allowFunctions: me.allowFunctions,
                getKey: me.getKey
            };
        }
        me.items = [];
        me.map = {};
        me.keys = [];
        me.indexMap = {};
        me.length = 0;
        
        
        
        
        me.mixins.observable.constructor.call(me);
    },
    
    allowFunctions: false,
    
    add: function(key, obj) {
        var len = this.length,
            out;
        if (arguments.length === 1) {
            out = this.insert(len, key);
        } else {
            out = this.insert(len, key, obj);
        }
        return out;
    },
    
    getKey: function(o) {
        return o.id;
    },
    
    replace: function(key, o) {
        var me = this,
            old, index;
        if (arguments.length == 1) {
            o = arguments[0];
            key = me.getKey(o);
        }
        old = me.map[key];
        if (typeof key == 'undefined' || key === null || typeof old == 'undefined') {
            return me.add(key, o);
        }
        me.generation++;
        index = me.indexOfKey(key);
        me.items[index] = o;
        me.map[key] = o;
        if (me.hasListeners.replace) {
            me.fireEvent('replace', key, old, o);
        }
        return o;
    },
    
    updateKey: function(oldKey, newKey) {
        var me = this,
            map = me.map,
            index = me.indexOfKey(oldKey),
            
            indexMap = me.indexMap,
            item;
        if (index > -1) {
            item = map[oldKey];
            delete map[oldKey];
            delete indexMap[oldKey];
            map[newKey] = item;
            indexMap[newKey] = index;
            me.keys[index] = newKey;
            
            
            me.indexGeneration = ++me.generation;
        }
    },
    
    addAll: function(objs) {
        var me = this,
            key;
        if (arguments.length > 1 || Ext.isArray(objs)) {
            me.insert(me.length, arguments.length > 1 ? arguments : objs);
        } else {
            for (key in objs) {
                if (objs.hasOwnProperty(key)) {
                    if (me.allowFunctions || typeof objs[key] != 'function') {
                        me.add(key, objs[key]);
                    }
                }
            }
        }
    },
    
    each: function(fn, scope) {
        var items = Ext.Array.push([], this.items),
            
            i = 0,
            len = items.length,
            item;
        for (; i < len; i++) {
            item = items[i];
            if (fn.call(scope || item, item, i, len) === false) {
                break;
            }
        }
    },
    
    eachKey: function(fn, scope) {
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = keys.length;
        for (; i < len; i++) {
            fn.call(scope || window, keys[i], items[i], i, len);
        }
    },
    
    findBy: function(fn, scope) {
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = items.length;
        for (; i < len; i++) {
            if (fn.call(scope || window, items[i], keys[i])) {
                return items[i];
            }
        }
        return null;
    },
    
    find: function() {
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.util.MixedCollection: find has been deprecated. Use findBy instead.');
        }
        return this.findBy.apply(this, arguments);
    },
    
    insert: function(index, key, obj) {
        var out;
        if (Ext.isIterable(key)) {
            out = this.doInsert(index, key, obj);
        } else {
            if (arguments.length > 2) {
                out = this.doInsert(index, [
                    key
                ], [
                    obj
                ]);
            } else {
                out = this.doInsert(index, [
                    key
                ]);
            }
            out = out[0];
        }
        return out;
    },
    
    doInsert: function(index, keys, objects) {
        var me = this,
            itemKey, removeIndex, i,
            len = keys.length,
            deDupedLen = len,
            fireAdd = me.hasListeners.add,
            syncIndices,
            newKeys = {},
            passedDuplicates, oldKeys, oldObjects;
        
        
        if (objects != null) {
            me.useLinearSearch = true;
        } else 
        {
            objects = keys;
            keys = new Array(len);
            for (i = 0; i < len; i++) {
                keys[i] = this.getKey(objects[i]);
            }
        }
        
        me.suspendEvents();
        for (i = 0; i < len; i++) {
            itemKey = keys[i];
            
            removeIndex = me.indexOfKey(itemKey);
            if (removeIndex !== -1) {
                if (removeIndex < index) {
                    index--;
                }
                me.removeAt(removeIndex);
            }
            if (itemKey != null) {
                
                if (newKeys[itemKey] != null) {
                    passedDuplicates = true;
                    deDupedLen--;
                }
                newKeys[itemKey] = i;
            }
        }
        me.resumeEvents();
        
        if (passedDuplicates) {
            oldKeys = keys;
            oldObjects = objects;
            keys = new Array(deDupedLen);
            objects = new Array(deDupedLen);
            i = 0;
            
            
            for (itemKey in newKeys) {
                keys[i] = oldKeys[newKeys[itemKey]];
                objects[i] = oldObjects[newKeys[itemKey]];
                i++;
            }
            len = deDupedLen;
        }
        
        syncIndices = index === me.length && me.indexGeneration === me.generation;
        
        Ext.Array.insert(me.items, index, objects);
        Ext.Array.insert(me.keys, index, keys);
        me.length += len;
        me.generation++;
        if (syncIndices) {
            me.indexGeneration = me.generation;
        }
        for (i = 0; i < len; i++ , index++) {
            itemKey = keys[i];
            if (itemKey != null) {
                me.map[itemKey] = objects[i];
                
                if (syncIndices) {
                    me.indexMap[itemKey] = index;
                }
            }
            if (fireAdd) {
                me.fireEvent('add', index, objects[i], itemKey);
            }
        }
        return objects;
    },
    
    remove: function(o) {
        var me = this,
            removeKey, index;
        
        
        
        
        
        if (!me.useLinearSearch && (removeKey = me.getKey(o))) {
            index = me.indexOfKey(removeKey);
        } else 
        {
            index = Ext.Array.indexOf(me.items, o);
        }
        return (index === -1) ? false : me.removeAt(index);
    },
    
    removeAll: function(items) {
        var me = this,
            i;
        if (items || me.hasListeners.remove) {
            
            if (items) {
                for (i = items.length - 1; i >= 0; --i) {
                    me.remove(items[i]);
                }
            } else {
                while (me.length) {
                    me.removeAt(0);
                }
            }
        } else {
            me.length = me.items.length = me.keys.length = 0;
            me.map = {};
            me.indexMap = {};
            me.generation++;
            me.indexGeneration = me.generation;
        }
    },
    
    removeAt: function(index) {
        var me = this,
            o, key;
        if (index < me.length && index >= 0) {
            me.length--;
            o = me.items[index];
            Ext.Array.erase(me.items, index, 1);
            key = me.keys[index];
            if (typeof key != 'undefined') {
                delete me.map[key];
            }
            Ext.Array.erase(me.keys, index, 1);
            if (me.hasListeners.remove) {
                me.fireEvent('remove', o, key);
            }
            me.generation++;
            return o;
        }
        return false;
    },
    
    removeRange: function(index, removeCount) {
        var me = this,
            o, key, i, limit, syncIndices, trimming;
        if (index < me.length && index >= 0) {
            if (!removeCount) {
                removeCount = 1;
            }
            limit = Math.min(index + removeCount, me.length);
            removeCount = limit - index;
            
            trimming = limit === me.length;
            syncIndices = trimming && me.indexGeneration === me.generation;
            
            for (i = index; i < limit; i++) {
                key = me.keys[i];
                if (key != null) {
                    delete me.map[key];
                    if (syncIndices) {
                        delete me.indexMap[key];
                    }
                }
            }
            
            o = me.items[i - 1];
            me.length -= removeCount;
            me.generation++;
            if (syncIndices) {
                me.indexGeneration = me.generation;
            }
            
            
            
            
            if (trimming) {
                me.items.length = me.keys.length = me.length;
            } else {
                me.items.splice(index, removeCount);
                me.keys.splice(index, removeCount);
            }
            
            return o;
        }
        return false;
    },
    
    removeAtKey: function(key) {
        var me = this,
            keys = me.keys,
            i;
        
        if (key == null) {
            for (i = keys.length - 1; i >= 0; i--) {
                if (keys[i] == null) {
                    me.removeAt(i);
                }
            }
        } else 
        {
            return me.removeAt(me.indexOfKey(key));
        }
    },
    
    getCount: function() {
        return this.length;
    },
    
    indexOf: function(o) {
        var me = this,
            key;
        if (o != null) {
            
            
            
            
            
            if (!me.useLinearSearch && (key = me.getKey(o))) {
                return this.indexOfKey(key);
            }
            
            return Ext.Array.indexOf(me.items, o);
        }
        
        return -1;
    },
    
    indexOfKey: function(key) {
        if (!this.map.hasOwnProperty(key)) {
            return -1;
        }
        if (this.indexGeneration !== this.generation) {
            this.rebuildIndexMap();
        }
        return this.indexMap[key];
    },
    rebuildIndexMap: function() {
        var me = this,
            indexMap = me.indexMap = {},
            keys = me.keys,
            len = keys.length,
            i;
        for (i = 0; i < len; i++) {
            indexMap[keys[i]] = i;
        }
        me.indexGeneration = me.generation;
    },
    
    get: function(key) {
        var me = this,
            mk = me.map[key],
            item = mk !== undefined ? mk : (typeof key == 'number') ? me.items[key] : undefined;
        return typeof item != 'function' || me.allowFunctions ? item : null;
    },
    
    
    getAt: function(index) {
        return this.items[index];
    },
    
    getByKey: function(key) {
        return this.map[key];
    },
    
    contains: function(o) {
        var me = this,
            key;
        if (o != null) {
            
            
            
            
            
            if (!me.useLinearSearch && (key = me.getKey(o))) {
                return this.map[key] != null;
            }
            
            return Ext.Array.indexOf(this.items, o) !== -1;
        }
        return false;
    },
    
    containsKey: function(key) {
        return this.map.hasOwnProperty(key);
    },
    
    clear: function() {
        var me = this;
        
        if (me.generation) {
            me.length = 0;
            me.items = [];
            me.keys = [];
            me.map = {};
            me.indexMap = {};
            me.generation++;
            me.indexGeneration = me.generation;
        }
        if (me.hasListeners.clear) {
            me.fireEvent('clear');
        }
    },
    
    first: function() {
        return this.items[0];
    },
    
    last: function() {
        return this.items[this.length - 1];
    },
    
    sum: function(property, root, start, end) {
        var values = this.extractValues(property, root),
            length = values.length,
            sum = 0,
            i;
        start = start || 0;
        end = (end || end === 0) ? end : length - 1;
        for (i = start; i <= end; i++) {
            sum += values[i];
        }
        return sum;
    },
    
    collect: function(property, root, allowNull) {
        var values = this.extractValues(property, root),
            length = values.length,
            hits = {},
            unique = [],
            value, strValue, i;
        for (i = 0; i < length; i++) {
            value = values[i];
            strValue = String(value);
            if ((allowNull || !Ext.isEmpty(value)) && !hits[strValue]) {
                hits[strValue] = true;
                unique.push(value);
            }
        }
        return unique;
    },
    
    extractValues: function(property, root) {
        var values = this.items;
        if (root) {
            values = Ext.Array.pluck(values, root);
        }
        return Ext.Array.pluck(values, property);
    },
    
    hasRange: function(start, end) {
        return (end < this.length);
    },
    
    getRange: function(start, end) {
        var me = this,
            items = me.items,
            range = [],
            len = items.length,
            tmp, reverse;
        if (len < 1) {
            return range;
        }
        if (start > end) {
            reverse = true;
            tmp = start;
            start = end;
            end = tmp;
        }
        if (start < 0) {
            start = 0;
        }
        if (end == null || end >= len) {
            end = len - 1;
        }
        range = items.slice(start, end + 1);
        if (reverse && range.length) {
            range.reverse();
        }
        return range;
    },
    
    filter: function(property, value, anyMatch, caseSensitive) {
        var filters = [];
        
        if (Ext.isString(property)) {
            filters.push(new Ext.util.Filter({
                property: property,
                value: value,
                anyMatch: anyMatch,
                caseSensitive: caseSensitive
            }));
        } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) {
            filters = filters.concat(property);
        }
        
        
        
        return this.filterBy(Ext.util.Filter.createFilterFn(filters));
    },
    
    filterBy: function(fn, scope) {
        var me = this,
            newMC = new me.self(me.initialConfig),
            keys = me.keys,
            items = me.items,
            length = items.length,
            i;
        newMC.getKey = me.getKey;
        for (i = 0; i < length; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                newMC.add(keys[i], items[i]);
            }
        }
        
        
        
        newMC.useLinearSearch = me.useLinearSearch;
        return newMC;
    },
    
    findIndex: function(property, value, start, anyMatch, caseSensitive) {
        if (Ext.isEmpty(value, false)) {
            return -1;
        }
        value = this.createValueMatcher(value, anyMatch, caseSensitive);
        return this.findIndexBy(function(o) {
            return o && value.test(o[property]);
        }, null, start);
    },
    
    findIndexBy: function(fn, scope, start) {
        var me = this,
            keys = me.keys,
            items = me.items,
            i = start || 0,
            len = items.length;
        for (; i < len; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                return i;
            }
        }
        return -1;
    },
    
    createValueMatcher: function(value, anyMatch, caseSensitive, exactMatch) {
        if (!value.exec) {
            
            var er = Ext.String.escapeRegex;
            value = String(value);
            if (anyMatch === true) {
                value = er(value);
            } else {
                value = '^' + er(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value,caseSensitive ? '' : 'i');
        }
        return value;
    },
    
    clone: function() {
        var me = this,
            copy = new me.self(me.initialConfig);
        copy.add(me.keys, me.items);
        
        
        
        copy.useLinearSearch = me.useLinearSearch;
        return copy;
    }
});


Ext.define('Ext.util.Sorter', {
    isSorter: true,
    config: {
        
        property: null,
        
        sorterFn: null,
        
        root: null,
        
        transform: null,
        
        direction: "ASC",
        
        id: undefined
    },
    statics: {
        
        createComparator: function(sorters, nextFn) {
            nextFn = nextFn || 0;
            return function(lhs, rhs) {
                var items = sorters.isCollection ? sorters.items : sorters,
                    n = items.length,
                    comp, i;
                for (i = 0; i < n; ++i) {
                    comp = items[i].sort(lhs, rhs);
                    if (comp) {
                        return comp;
                    }
                }
                return nextFn && nextFn(lhs, rhs);
            };
        }
    },
    
    multiplier: 1,
    constructor: function(config) {
        if (config && !this.isGrouper) {
            if (!config.property === !config.sorterFn) {
                
                Ext.Error.raise("A Sorter requires either a property or a sorterFn.");
            }
        }
        this.initConfig(config);
    },
    getId: function() {
        var id = this._id;
        if (!id) {
            id = this.getProperty();
            if (!id) {
                id = Ext.id(null, 'ext-sorter-');
            }
            this._id = id;
        }
        return id;
    },
    sort: function(lhs, rhs) {
        return this.multiplier * this.sortFn(lhs, rhs);
    },
    
    sortFn: function(item1, item2) {
        var me = this,
            transform = me._transform,
            root = me._root,
            property = me._property,
            lhs, rhs;
        if (root) {
            item1 = item1[root];
            item2 = item2[root];
        }
        lhs = item1[property];
        rhs = item2[property];
        if (transform) {
            lhs = transform(lhs);
            rhs = transform(rhs);
        }
        return (lhs > rhs) ? 1 : (lhs < rhs ? -1 : 0);
    },
    applyDirection: function(direction) {
        return direction ? direction : 'ASC';
    },
    updateDirection: function(direction) {
        this.multiplier = (direction.toUpperCase() === "DESC") ? -1 : 1;
    },
    updateProperty: function(property) {
        if (property) {
            
            delete this.sortFn;
        }
    },
    updateSorterFn: function(sorterFn) {
        
        this.sortFn = sorterFn;
    },
    
    toggle: function() {
        this.setDirection(Ext.String.toggle(this.getDirection(), "ASC", "DESC"));
    },
    
    getState: function() {
        return {
            root: this.getRoot(),
            property: this.getProperty(),
            direction: this.getDirection()
        };
    },
    
    serialize: function() {
        return {
            property: this.getProperty(),
            direction: this.getDirection()
        };
    }
});


Ext.define("Ext.util.Sortable", {
    
    isSortable: true,
    $configPrefixed: false,
    $configStrict: false,
    config: {
        
        sorters: null
    },
    
    defaultSortDirection: "ASC",
    requires: [
        'Ext.util.Sorter'
    ],
    
    
    multiSortLimit: 3,
    statics: {
        
        createComparator: function(sorters) {
            return sorters && sorters.length ? function(r1, r2) {
                var result = sorters[0].sort(r1, r2),
                    length = sorters.length,
                    i = 1;
                
                
                for (; !result && i < length; i++) {
                    result = sorters[i].sort.call(this, r1, r2);
                }
                return result;
            } : function() {
                return 0;
            };
        }
    },
    
    applySorters: function(sorters) {
        var me = this,
            sortersCollection = me.getSorters() || new Ext.util.MixedCollection(false,Ext.returnId);
        
        if (sorters) {
            sortersCollection.addAll(me.decodeSorters(sorters));
        }
        return sortersCollection;
    },
    
    sort: function(sorters, direction, insertionPosition, doSort) {
        var me = this,
            sorter, overFlow,
            currentSorters = me.getSorters();
        if (!currentSorters) {
            me.setSorters(null);
            currentSorters = me.getSorters();
        }
        if (Ext.isArray(sorters)) {
            doSort = insertionPosition;
            insertionPosition = direction;
        } else if (Ext.isObject(sorters)) {
            sorters = [
                sorters
            ];
            doSort = insertionPosition;
            insertionPosition = direction;
        } else if (Ext.isString(sorters)) {
            sorter = currentSorters.get(sorters);
            if (!sorter) {
                sorter = {
                    property: sorters,
                    direction: direction
                };
            } else if (direction == null) {
                sorter.toggle();
            } else {
                sorter.setDirection(direction);
            }
            sorters = [
                sorter
            ];
        }
        if (sorters && sorters.length) {
            sorters = me.decodeSorters(sorters);
            switch (insertionPosition) {
                
                
                
                
                case "multi":
                    
                    currentSorters.insert(0, sorters[0]);
                    
                    overFlow = currentSorters.getCount() - me.multiSortLimit;
                    if (overFlow > 0) {
                        currentSorters.removeRange(me.multiSortLimit, overFlow);
                    };
                    break;
                case "prepend":
                    currentSorters.insert(0, sorters);
                    break;
                case "append":
                    currentSorters.addAll(sorters);
                    break;
                case undefined:
                case null:
                case "replace":
                    currentSorters.clear();
                    currentSorters.addAll(sorters);
                    break;
                default:
                    Ext.Error.raise('Sorter insertion point must be "multi", "prepend", "append" or "replace"');
            }
        }
        if (doSort !== false) {
            me.fireEvent('beforesort', me, sorters);
            me.onBeforeSort(sorters);
            if (me.getSorterCount()) {
                
                me.doSort(me.generateComparator());
            }
        }
        return sorters;
    },
    
    getSorterCount: function() {
        return this.getSorters().items.length;
    },
    
    generateComparator: function() {
        var sorters = this.getSorters().getRange();
        return sorters.length ? this.createComparator(sorters) : this.emptyComparator;
    },
    emptyComparator: function() {
        return 0;
    },
    onBeforeSort: Ext.emptyFn,
    
    decodeSorters: function(sorters) {
        if (!Ext.isArray(sorters)) {
            if (sorters === undefined) {
                sorters = [];
            } else {
                sorters = [
                    sorters
                ];
            }
        }
        var length = sorters.length,
            Sorter = Ext.util.Sorter,
            model = this.getModel ? this.getModel() : this.model,
            field, config, i;
        for (i = 0; i < length; i++) {
            config = sorters[i];
            if (!(config instanceof Sorter)) {
                if (Ext.isString(config)) {
                    config = {
                        property: config
                    };
                }
                Ext.applyIf(config, {
                    root: this.sortRoot,
                    direction: "ASC"
                });
                
                if (config.fn) {
                    config.sorterFn = config.fn;
                }
                
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }
                
                if (model && !config.transform) {
                    field = model.getField(config.property);
                    config.transform = field && field.sortType !== Ext.identityFn ? field.sortType : undefined;
                }
                sorters[i] = new Ext.util.Sorter(config);
            }
        }
        return sorters;
    },
    
    getFirstSorter: function() {
        var sorters = this.getSorters().items,
            len = sorters.length,
            i = 0,
            sorter;
        for (; i < len; ++i) {
            sorter = sorters[i];
            if (!sorter.isGrouper) {
                return sorter;
            }
        }
        return null;
    }
}, function() {
    
    this.prototype.createComparator = this.createComparator;
});


Ext.define('Ext.util.MixedCollection', {
    extend: 'Ext.util.AbstractMixedCollection',
    mixins: {
        sortable: 'Ext.util.Sortable'
    },
    
    
    
    constructor: function() {
        var me = this;
        me.initConfig();
        me.callParent(arguments);
    },
    doSort: function(sorterFn) {
        this.sortBy(sorterFn);
    },
    
    _sort: function(property, dir, fn) {
        var me = this,
            i, len,
            dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
            
            c = [],
            keys = me.keys,
            items = me.items,
            o;
        
        fn = fn || function(a, b) {
            return a - b;
        };
        
        for (i = 0 , len = items.length; i < len; i++) {
            c[c.length] = {
                key: keys[i],
                value: items[i],
                index: i
            };
        }
        
        Ext.Array.sort(c, function(a, b) {
            return fn(a[property], b[property]) * dsc || 
            (a.index < b.index ? -1 : 1);
        });
        
        
        for (i = 0 , len = c.length; i < len; i++) {
            o = c[i];
            items[i] = o.value;
            keys[i] = o.key;
            me.indexMap[o.key] = i;
        }
        me.generation++;
        me.indexGeneration = me.generation;
        me.fireEvent('sort', me);
    },
    
    sortBy: function(sorterFn) {
        var me = this,
            items = me.items,
            item,
            keys = me.keys,
            key,
            length = items.length,
            i;
        
        for (i = 0; i < length; i++) {
            items[i].$extCollectionIndex = i;
        }
        Ext.Array.sort(items, function(a, b) {
            return sorterFn(a, b) || 
            (a.$extCollectionIndex < b.$extCollectionIndex ? -1 : 1);
        });
        
        for (i = 0; i < length; i++) {
            item = items[i];
            key = me.getKey(item);
            keys[i] = key;
            me.indexMap[key] = i;
            delete items.$extCollectionIndex;
        }
        me.generation++;
        me.indexGeneration = me.generation;
        me.fireEvent('sort', me, items, keys);
    },
    
    findInsertionIndex: function(newItem, sorterFn) {
        var me = this,
            items = me.items,
            start = 0,
            end = items.length - 1,
            middle, comparison;
        if (!sorterFn) {
            sorterFn = me.generateComparator();
        }
        while (start <= end) {
            middle = (start + end) >> 1;
            comparison = sorterFn(newItem, items[middle]);
            if (comparison >= 0) {
                start = middle + 1;
            } else if (comparison < 0) {
                end = middle - 1;
            }
        }
        return start;
    },
    
    reorder: function(mapping) {
        var me = this,
            items = me.items,
            index = 0,
            length = items.length,
            order = [],
            remaining = [],
            oldIndex;
        me.suspendEvents();
        
        for (oldIndex in mapping) {
            order[mapping[oldIndex]] = items[oldIndex];
        }
        for (index = 0; index < length; index++) {
            if (mapping[index] == undefined) {
                remaining.push(items[index]);
            }
        }
        for (index = 0; index < length; index++) {
            if (order[index] == undefined) {
                order[index] = remaining.shift();
            }
        }
        me.clear();
        me.addAll(order);
        me.resumeEvents();
        me.fireEvent('sort', me);
    },
    
    sortByKey: function(dir, fn) {
        this._sort('key', dir, fn || function(a, b) {
            var v1 = String(a).toUpperCase(),
                v2 = String(b).toUpperCase();
            return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
        });
    }
});



Ext.define('Ext.util.TaskRunner', {
    
    
    
    interval: 10,
    
    timerId: null,
    constructor: function(interval) {
        var me = this;
        if (typeof interval == 'number') {
            me.interval = interval;
        } else if (interval) {
            Ext.apply(me, interval);
        }
        me.tasks = [];
        me.timerFn = Ext.Function.bind(me.onTick, me);
    },
    
    newTask: function(config) {
        var task = new Ext.util.TaskRunner.Task(config);
        task.manager = this;
        return task;
    },
    
    start: function(task) {
        var me = this,
            now = Ext.Date.now();
        if (!task.pending) {
            me.tasks.push(task);
            task.pending = true;
        }
        
        task.stopped = false;
        
        task.taskStartTime = now;
        task.taskRunTime = task.fireOnStart !== false ? 0 : task.taskStartTime;
        task.taskRunCount = 0;
        if (!me.firing) {
            if (task.fireOnStart !== false) {
                me.startTimer(0, now);
            } else {
                me.startTimer(task.interval, now);
            }
        }
        return task;
    },
    
    stop: function(task) {
        
        
        
        if (!task.stopped) {
            task.stopped = true;
            if (task.onStop) {
                task.onStop.call(task.scope || task, task);
            }
        }
        return task;
    },
    
    stopAll: function() {
        
        Ext.each(this.tasks, this.stop, this);
    },
    
    firing: false,
    nextExpires: 1.0E99,
    
    onTick: function() {
        var me = this,
            tasks = me.tasks,
            now = Ext.Date.now(),
            nextExpires = 1.0E99,
            len = tasks.length,
            globalEvents = Ext.GlobalEvents,
            expires, newTasks, i, task, rt, remove, fireIdleEvent;
        me.timerId = null;
        me.firing = true;
        
        
        
        
        
        for (i = 0; i < len || i < (len = tasks.length); ++i) {
            task = tasks[i];
            if (!(remove = task.stopped)) {
                expires = task.taskRunTime + task.interval;
                if (expires <= now) {
                    rt = 1;
                    
                    
                    if (task.hasOwnProperty('fireIdleEvent')) {
                        fireIdleEvent = task.fireIdleEvent;
                    } else {
                        fireIdleEvent = me.fireIdleEvent;
                    }
                    try {
                        rt = task.run.apply(task.scope || task, task.args || [
                            ++task.taskRunCount
                        ]);
                    } catch (taskError) {
                        try {
                            Ext.log({
                                msg: taskError,
                                level: 'error'
                            });
                            if (task.onError) {
                                rt = task.onError.call(task.scope || task, task, taskError);
                            }
                        } catch (ignore) {}
                    }
                    task.taskRunTime = now;
                    if (rt === false || task.taskRunCount === task.repeat) {
                        me.stop(task);
                        remove = true;
                    } else {
                        remove = task.stopped;
                        
                        expires = now + task.interval;
                    }
                }
                if (!remove && task.duration && task.duration <= (now - task.taskStartTime)) {
                    me.stop(task);
                    remove = true;
                }
            }
            if (remove) {
                task.pending = false;
                
                
                
                
                
                
                if (!newTasks) {
                    newTasks = tasks.slice(0, i);
                }
            } else 
            
            
            {
                if (newTasks) {
                    newTasks.push(task);
                }
                
                if (nextExpires > expires) {
                    nextExpires = expires;
                }
            }
        }
        
        if (newTasks) {
            
            
            me.tasks = newTasks;
        }
        me.firing = false;
        
        if (me.tasks.length) {
            
            
            
            me.startTimer(nextExpires - now, Ext.Date.now());
        }
        
        if (fireIdleEvent !== false && globalEvents.hasListeners.idle) {
            globalEvents.fireEvent('idle');
        }
    },
    
    startTimer: function(timeout, now) {
        var me = this,
            expires = now + timeout,
            timerId = me.timerId;
        
        
        if (timerId && me.nextExpires - expires > me.interval) {
            clearTimeout(timerId);
            timerId = null;
        }
        if (!timerId) {
            if (timeout < me.interval) {
                timeout = me.interval;
            }
            me.timerId = Ext.defer(me.timerFn, timeout);
            me.nextExpires = expires;
        }
    }
}, function() {
    var me = this,
        proto = me.prototype;
    
    proto.destroy = proto.stopAll;
    
    me.Task = new Ext.Class({
        isTask: true,
        
        stopped: true,
        
        
        fireOnStart: false,
        constructor: function(config) {
            Ext.apply(this, config);
        },
        
        restart: function(interval) {
            if (interval !== undefined) {
                this.interval = interval;
            }
            this.manager.start(this);
        },
        
        start: function(interval) {
            if (this.stopped) {
                this.restart(interval);
            }
        },
        
        stop: function() {
            this.manager.stop(this);
        }
    });
    proto = me.Task.prototype;
    
    proto.destroy = proto.stop;
});


Ext.define('Ext.fx.target.Target', {
    isAnimTarget: true,
    
    constructor: function(target) {
        this.target = target;
        this.id = this.getId();
    },
    getId: function() {
        return this.target.id;
    },
    remove: function() {
        Ext.destroy(this.target);
    }
});


Ext.define('Ext.fx.target.Element', {
    
    extend: 'Ext.fx.target.Target',
    
    type: 'element',
    getElVal: function(el, attr, val) {
        if (val == undefined) {
            if (attr === 'x') {
                val = el.getX();
            } else if (attr === 'y') {
                val = el.getY();
            } else if (attr === 'scrollTop') {
                val = el.getScroll().top;
            } else if (attr === 'scrollLeft') {
                val = el.getScroll().left;
            } else if (attr === 'height') {
                val = el.getHeight();
            } else if (attr === 'width') {
                val = el.getWidth();
            } else {
                val = el.getStyle(attr);
            }
        }
        return val;
    },
    getAttr: function(attr, val) {
        var el = this.target;
        return [
            [
                el,
                this.getElVal(el, attr, val)
            ]
        ];
    },
    setAttr: function(targetData) {
        var target = this.target,
            ln = targetData.length,
            attrs, attr, o, i, j, ln2;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                if (attrs.hasOwnProperty(attr)) {
                    ln2 = attrs[attr].length;
                    for (j = 0; j < ln2; j++) {
                        o = attrs[attr][j];
                        this.setElVal(o[0], attr, o[1]);
                    }
                }
            }
        }
    },
    setElVal: function(element, attr, value) {
        if (attr === 'x') {
            element.setX(value);
        } else if (attr === 'y') {
            element.setY(value);
        } else if (attr === 'scrollTop') {
            element.scrollTo('top', value);
        } else if (attr === 'scrollLeft') {
            element.scrollTo('left', value);
        } else if (attr === 'width') {
            element.setWidth(value);
        } else if (attr === 'height') {
            element.setHeight(value);
        } else {
            element.setStyle(attr, value);
        }
    }
});


Ext.define('Ext.fx.target.ElementCSS', {
    
    extend: 'Ext.fx.target.Element',
    
    setAttr: function(targetData, isFirstFrame) {
        var cssArr = {
                attrs: [],
                duration: [],
                easing: []
            },
            ln = targetData.length,
            attributes, attrs, attr, easing, duration, o, i, j, ln2;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i];
            duration = attrs.duration;
            easing = attrs.easing;
            attrs = attrs.attrs;
            for (attr in attrs) {
                if (Ext.Array.indexOf(cssArr.attrs, attr) == -1) {
                    cssArr.attrs.push(attr.replace(/[A-Z]/g, function(v) {
                        return '-' + v.toLowerCase();
                    }));
                    cssArr.duration.push(duration + 'ms');
                    cssArr.easing.push(easing);
                }
            }
        }
        attributes = cssArr.attrs.join(',');
        duration = cssArr.duration.join(',');
        easing = cssArr.easing.join(', ');
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                ln2 = attrs[attr].length;
                for (j = 0; j < ln2; j++) {
                    o = attrs[attr][j];
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', isFirstFrame ? '' : attributes);
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', isFirstFrame ? '' : duration);
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', isFirstFrame ? '' : easing);
                    o[0].setStyle(attr, o[1]);
                    
                    if (isFirstFrame) {
                        o = o[0].dom.offsetWidth;
                    } else {
                        
                        o[0].on(Ext.supports.CSS3TransitionEnd, function() {
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', null);
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', null);
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', null);
                        }, o[0], {
                            single: true
                        });
                    }
                }
            }
        }
    }
});


Ext.define('Ext.fx.target.CompositeElement', {
    
    extend: 'Ext.fx.target.Element',
    
    
    isComposite: true,
    constructor: function(target) {
        target.id = target.id || Ext.id(null, 'ext-composite-');
        this.callParent([
            target
        ]);
    },
    getAttr: function(attr, val) {
        var out = [],
            target = this.target,
            elements = target.elements,
            length = elements.length,
            i, el;
        for (i = 0; i < length; i++) {
            el = elements[i];
            if (el) {
                el = target.getElement(el);
                out.push([
                    el,
                    this.getElVal(el, attr, val)
                ]);
            }
        }
        return out;
    },
    setAttr: function(targetData) {
        var target = this.target,
            ln = targetData.length,
            elements = target.elements,
            ln3 = elements.length,
            value, k, attrs, attr, o, i, j, ln2;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                if (attrs.hasOwnProperty(attr)) {
                    ln2 = attrs[attr].length;
                    for (j = 0; j < ln2; j++) {
                        value = attrs[attr][j][1];
                        for (k = 0; k < ln3; ++k) {
                            el = elements[k];
                            if (el) {
                                el = target.getElement(el);
                                this.setElVal(el, attr, value);
                            }
                        }
                    }
                }
            }
        }
    },
    remove: function() {
        this.target.destroy();
    }
});


Ext.define('Ext.fx.target.CompositeElementCSS', {
    
    extend: 'Ext.fx.target.CompositeElement',
    requires: [
        'Ext.fx.target.ElementCSS'
    ],
    
    setAttr: function() {
        return Ext.fx.target.ElementCSS.prototype.setAttr.apply(this, arguments);
    }
});


Ext.define('Ext.fx.target.Sprite', {
    
    extend: 'Ext.fx.target.Target',
    
    type: 'draw',
    getFromPrim: function(sprite, attr) {
        var obj;
        switch (attr) {
            case 'rotate':
            case 'rotation':
                obj = sprite.attr.rotation;
                return {
                    x: obj.x || 0,
                    y: obj.y || 0,
                    degrees: obj.degrees || 0
                };
            case 'scale':
            case 'scaling':
                obj = sprite.attr.scaling;
                return {
                    x: obj.x || 1,
                    y: obj.y || 1,
                    cx: obj.cx || 0,
                    cy: obj.cy || 0
                };
            case 'translate':
            case 'translation':
                obj = sprite.attr.translation;
                return {
                    x: obj.x || 0,
                    y: obj.y || 0
                };
            default:
                return sprite.attr[attr];
        }
    },
    getAttr: function(attr, val) {
        return [
            [
                this.target,
                val != undefined ? val : this.getFromPrim(this.target, attr)
            ]
        ];
    },
    setAttr: function(targetData) {
        var ln = targetData.length,
            spriteArr = [],
            attrsConf, attr, attrArr, attrs, sprite, idx, value, i, j, x, y, ln2;
        for (i = 0; i < ln; i++) {
            attrsConf = targetData[i].attrs;
            for (attr in attrsConf) {
                attrArr = attrsConf[attr];
                ln2 = attrArr.length;
                for (j = 0; j < ln2; j++) {
                    sprite = attrArr[j][0];
                    attrs = attrArr[j][1];
                    if (attr === 'translate' || attr === 'translation') {
                        value = {
                            x: attrs.x,
                            y: attrs.y
                        };
                    } else if (attr === 'rotate' || attr === 'rotation') {
                        x = attrs.x;
                        if (isNaN(x)) {
                            x = null;
                        }
                        y = attrs.y;
                        if (isNaN(y)) {
                            y = null;
                        }
                        value = {
                            degrees: attrs.degrees,
                            x: x,
                            y: y
                        };
                    } else if (attr === 'scale' || attr === 'scaling') {
                        x = attrs.x;
                        if (isNaN(x)) {
                            x = null;
                        }
                        y = attrs.y;
                        if (isNaN(y)) {
                            y = null;
                        }
                        value = {
                            x: x,
                            y: y,
                            cx: attrs.cx,
                            cy: attrs.cy
                        };
                    } else if (attr === 'width' || attr === 'height' || attr === 'x' || attr === 'y') {
                        value = parseFloat(attrs);
                    } else {
                        value = attrs;
                    }
                    idx = Ext.Array.indexOf(spriteArr, sprite);
                    if (idx == -1) {
                        spriteArr.push([
                            sprite,
                            {}
                        ]);
                        idx = spriteArr.length - 1;
                    }
                    spriteArr[idx][1][attr] = value;
                }
            }
        }
        ln = spriteArr.length;
        for (i = 0; i < ln; i++) {
            spriteArr[i][0].setAttributes(spriteArr[i][1]);
        }
        this.target.redraw();
    }
});


Ext.define('Ext.fx.target.CompositeSprite', {
    
    extend: 'Ext.fx.target.Sprite',
    
    getAttr: function(attr, val) {
        var out = [],
            sprites = [].concat(this.target.items),
            length = sprites.length,
            i, sprite;
        for (i = 0; i < length; i++) {
            sprite = sprites[i];
            out.push([
                sprite,
                val != undefined ? val : this.getFromPrim(sprite, attr)
            ]);
        }
        return out;
    }
});


Ext.define('Ext.fx.target.Component', {
    
    extend: 'Ext.fx.target.Target',
    
    type: 'component',
    
    getPropMethod: {
        top: function() {
            return this.getPosition(true)[1];
        },
        left: function() {
            return this.getPosition(true)[0];
        },
        x: function() {
            return this.getPosition()[0];
        },
        y: function() {
            return this.getPosition()[1];
        },
        height: function() {
            return this.getHeight();
        },
        width: function() {
            return this.getWidth();
        },
        opacity: function() {
            return this.el.getStyle('opacity');
        }
    },
    setMethods: {
        top: 'setPosition',
        left: 'setPosition',
        x: 'setPagePosition',
        y: 'setPagePosition',
        height: 'setSize',
        width: 'setSize',
        opacity: 'setOpacity'
    },
    
    getAttr: function(attr, val) {
        return [
            [
                this.target,
                val !== undefined ? val : this.getPropMethod[attr].call(this.target)
            ]
        ];
    },
    setAttr: function(targetData, isFirstFrame, isLastFrame) {
        var me = this,
            ln = targetData.length,
            attrs, attr, o, i, j, targets, left, top, w, h,
            methodsToCall = {},
            methodProps;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                targets = attrs[attr].length;
                for (j = 0; j < targets; j++) {
                    o = attrs[attr][j];
                    methodProps = methodsToCall[me.setMethods[attr]] || (methodsToCall[me.setMethods[attr]] = {});
                    methodProps.target = o[0];
                    methodProps[attr] = o[1];
                }
            }
            
            if (methodsToCall.setPosition) {
                o = methodsToCall.setPosition;
                left = (o.left === undefined) ? undefined : parseFloat(o.left);
                top = (o.top === undefined) ? undefined : parseFloat(o.top);
                o.target.setPosition(left, top);
            }
            if (methodsToCall.setPagePosition) {
                o = methodsToCall.setPagePosition;
                o.target.setPagePosition(o.x, o.y);
            }
            if (methodsToCall.setSize) {
                o = methodsToCall.setSize;
                
                w = (o.width === undefined) ? o.target.getWidth() : parseFloat(o.width);
                h = (o.height === undefined) ? o.target.getHeight() : parseFloat(o.height);
                
                
                
                
                
                
                
                o.target.el.setSize(w, h);
                if (isLastFrame || me.dynamic) {
                    
                    
                    
                    Ext.GlobalEvents.on({
                        idle: Ext.Function.bind(o.target.setSize, o.target, [
                            w,
                            h
                        ]),
                        single: true
                    });
                }
            }
            if (methodsToCall.setOpacity) {
                o = methodsToCall.setOpacity;
                o.target.el.setStyle('opacity', o.opacity);
            }
        }
    }
});


Ext.define('Ext.fx.Queue', {
    requires: [
        'Ext.util.HashMap'
    ],
    constructor: function() {
        this.targets = new Ext.util.HashMap();
        this.fxQueue = {};
    },
    
    getFxDefaults: function(targetId) {
        var target = this.targets.get(targetId);
        if (target) {
            return target.fxDefaults;
        }
        return {};
    },
    
    setFxDefaults: function(targetId, obj) {
        var target = this.targets.get(targetId);
        if (target) {
            target.fxDefaults = Ext.apply(target.fxDefaults || {}, obj);
        }
    },
    
    stopAnimation: function(targetId) {
        var me = this,
            queue = me.getFxQueue(targetId),
            ln = queue.length;
        while (ln) {
            queue[ln - 1].end();
            ln--;
        }
    },
    
    getActiveAnimation: function(targetId) {
        var queue = this.getFxQueue(targetId);
        return (queue && !!queue.length) ? queue[0] : false;
    },
    
    hasFxBlock: function(targetId) {
        var queue = this.getFxQueue(targetId);
        return queue && queue[0] && queue[0].block;
    },
    
    getFxQueue: function(targetId) {
        if (!targetId) {
            return false;
        }
        var me = this,
            queue = me.fxQueue[targetId],
            target = me.targets.get(targetId);
        if (!target) {
            return false;
        }
        if (!queue) {
            me.fxQueue[targetId] = [];
            
            if (target.type != 'element') {
                target.target.on('destroy', function() {
                    me.fxQueue[targetId] = [];
                });
            }
        }
        return me.fxQueue[targetId];
    },
    
    queueFx: function(anim) {
        var me = this,
            target = anim.target,
            queue, ln;
        if (!target) {
            return;
        }
        queue = me.getFxQueue(target.getId());
        ln = queue.length;
        if (ln) {
            if (anim.concurrent) {
                anim.paused = false;
            } else {
                queue[ln - 1].on('afteranimate', function() {
                    anim.paused = false;
                });
            }
        } else {
            anim.paused = false;
        }
        anim.on('afteranimate', function() {
            Ext.Array.remove(queue, anim);
            if (queue.length === 0) {
                me.targets.remove(anim.target);
            }
            if (anim.remove) {
                if (target.type == 'element') {
                    var el = Ext.get(target.id);
                    if (el) {
                        el.destroy();
                    }
                }
            }
        }, me, {
            single: true
        });
        queue.push(anim);
    }
});


Ext.define('Ext.fx.Manager', {
    
    singleton: true,
    requires: [
        'Ext.util.MixedCollection',
        'Ext.util.TaskRunner',
        'Ext.fx.target.Element',
        'Ext.fx.target.ElementCSS',
        'Ext.fx.target.CompositeElement',
        'Ext.fx.target.CompositeElementCSS',
        'Ext.fx.target.Sprite',
        'Ext.fx.target.CompositeSprite',
        'Ext.fx.target.Component'
    ],
    mixins: {
        queue: 'Ext.fx.Queue'
    },
    
    constructor: function() {
        var me = this;
        me.items = new Ext.util.MixedCollection();
        me.targetArr = {};
        me.mixins.queue.constructor.call(me);
        
        
        me.taskRunner = new Ext.util.TaskRunner();
    },
    
    interval: 16,
    
    forceJS: true,
    
    createTarget: function(target) {
        var me = this,
            useCSS3 = !me.forceJS && Ext.supports.Transitions,
            targetObj;
        me.useCSS3 = useCSS3;
        if (target) {
            
            if (target.tagName || Ext.isString(target) || target.isFly) {
                target = Ext.get(target);
                targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
            }
            
            else if (target.dom) {
                targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
            }
            
            else if (target.isComposite) {
                targetObj = new Ext.fx.target['CompositeElement' + (useCSS3 ? 'CSS' : '')](target);
            }
            
            else if (target.isSprite) {
                targetObj = new Ext.fx.target.Sprite(target);
            }
            
            else if (target.isCompositeSprite) {
                targetObj = new Ext.fx.target.CompositeSprite(target);
            }
            
            else if (target.isComponent) {
                targetObj = new Ext.fx.target.Component(target);
            } else if (target.isAnimTarget) {
                return target;
            } else {
                return null;
            }
            me.targets.add(targetObj);
            return targetObj;
        } else {
            return null;
        }
    },
    
    addAnim: function(anim) {
        var me = this,
            items = me.items,
            task = me.task;
        
        
        
        items.add(anim.id, anim);
        
        
        if (!task && items.length) {
            task = me.task = {
                run: me.runner,
                interval: me.interval,
                scope: me
            };
            
            me.taskRunner.start(task);
        }
    },
    
    removeAnim: function(anim) {
        var me = this,
            items = me.items,
            task = me.task;
        items.removeAtKey(anim.id);
        
        
        if (task && !items.length) {
            
            me.taskRunner.stop(task);
            delete me.task;
        }
    },
    
    runner: function() {
        var me = this,
            items = me.items.getRange(),
            i = 0,
            len = items.length,
            anim;
        
        me.targetArr = {};
        
        me.timestamp = new Date();
        
        
        
        
        
        
        
        
        
        
        for (; i < len; i++) {
            anim = items[i];
            if (anim.isReady()) {
                
                me.startAnim(anim);
            }
        }
        for (i = 0; i < len; i++) {
            anim = items[i];
            if (anim.isRunning()) {
                
                me.runAnim(anim);
            } else if (!me.useCSS3) {}
        }
        
        
        
        
        
        
        me.applyPendingAttrs();
    },
    
    startAnim: function(anim) {
        anim.start(this.timestamp);
    },
    
    runAnim: function(anim, forceEnd) {
        if (!anim) {
            return;
        }
        var me = this,
            useCSS3 = me.useCSS3 && anim.target.type == 'element',
            elapsedTime = me.timestamp - anim.startTime,
            lastFrame = (elapsedTime >= anim.duration),
            target, o;
        if (forceEnd) {
            elapsedTime = anim.duration;
            lastFrame = true;
        }
        target = this.collectTargetData(anim, elapsedTime, useCSS3, lastFrame);
        
        
        if (useCSS3) {
            
            
            anim.target.setAttr(target.anims[anim.id].attributes, true);
            
            me.collectTargetData(anim, anim.duration, useCSS3, lastFrame);
            
            anim.paused = true;
            target = anim.target.target;
            
            if (anim.target.isComposite) {
                target = anim.target.target.last();
            }
            
            o = {};
            o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
            o.scope = anim;
            o.single = true;
            target.on(o);
        }
        return target;
    },
    jumpToEnd: function(anim) {
        var target = this.runAnim(anim, true);
        this.applyAnimAttrs(target, target.anims[anim.id]);
    },
    
    collectTargetData: function(anim, elapsedTime, useCSS3, isLastFrame) {
        var targetId = anim.target.getId(),
            target = this.targetArr[targetId];
        if (!target) {
            
            
            
            
            target = this.targetArr[targetId] = {
                id: targetId,
                el: anim.target,
                anims: {}
            };
        }
        
        
        
        
        target.anims[anim.id] = {
            id: anim.id,
            anim: anim,
            elapsed: elapsedTime,
            isLastFrame: isLastFrame,
            
            attributes: [
                {
                    duration: anim.duration,
                    easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,
                    
                    
                    attrs: anim.runAnim(elapsedTime)
                }
            ]
        };
        return target;
    },
    
    
    
    applyAnimAttrs: function(target, animWrap) {
        var anim = animWrap.anim;
        if (animWrap.attributes && anim.isRunning()) {
            target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame);
            
            if (animWrap.isLastFrame) {
                anim.lastFrame();
            }
        }
    },
    
    applyPendingAttrs: function() {
        var targetArr = this.targetArr,
            target, targetId, animWrap, anim, animId;
        
        for (targetId in targetArr) {
            if (targetArr.hasOwnProperty(targetId)) {
                target = targetArr[targetId];
                
                for (animId in target.anims) {
                    if (target.anims.hasOwnProperty(animId)) {
                        animWrap = target.anims[animId];
                        anim = animWrap.anim;
                        
                        if (animWrap.attributes && anim.isRunning()) {
                            
                            target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame);
                            
                            if (animWrap.isLastFrame) {
                                
                                anim.lastFrame();
                            }
                        }
                    }
                }
            }
        }
    }
});


Ext.define('Ext.fx.Animator', {
    
    mixins: {
        observable: 'Ext.util.Observable'
    },
    requires: [
        'Ext.fx.Manager'
    ],
    
    
    isAnimator: true,
    
    duration: 250,
    
    delay: 0,
    
    delayStart: 0,
    
    dynamic: false,
    
    easing: 'ease',
    
    running: false,
    
    paused: false,
    
    damper: 1,
    
    iterations: 1,
    
    currentIteration: 0,
    
    keyframeStep: 0,
    
    animKeyFramesRE: /^(from|to|\d+%?)$/,
    
    
    
    
    
    constructor: function(config) {
        var me = this;
        config = Ext.apply(me, config || {});
        me.config = config;
        me.id = Ext.id(null, 'ext-animator-');
        me.mixins.observable.constructor.call(me, config);
        me.timeline = [];
        me.createTimeline(me.keyframes);
        if (me.target) {
            me.applyAnimator(me.target);
            Ext.fx.Manager.addAnim(me);
        }
    },
    
    sorter: function(a, b) {
        return a.pct - b.pct;
    },
    
    createTimeline: function(keyframes) {
        var me = this,
            attrs = [],
            to = me.to || {},
            duration = me.duration,
            prevMs, ms, i, ln, pct, attr;
        for (pct in keyframes) {
            if (keyframes.hasOwnProperty(pct) && me.animKeyFramesRE.test(pct)) {
                attr = {
                    attrs: Ext.apply(keyframes[pct], to)
                };
                
                if (pct == "from") {
                    pct = 0;
                } else if (pct == "to") {
                    pct = 100;
                }
                
                attr.pct = parseInt(pct, 10);
                attrs.push(attr);
            }
        }
        
        Ext.Array.sort(attrs, me.sorter);
        
        
        
        
        ln = attrs.length;
        for (i = 0; i < ln; i++) {
            prevMs = (attrs[i - 1]) ? duration * (attrs[i - 1].pct / 100) : 0;
            ms = duration * (attrs[i].pct / 100);
            me.timeline.push({
                duration: ms - prevMs,
                attrs: attrs[i].attrs
            });
        }
    },
    
    applyAnimator: function(target) {
        var me = this,
            anims = [],
            timeline = me.timeline,
            ln = timeline.length,
            anim, easing, damper, attrs, i;
        if (me.fireEvent('beforeanimate', me) !== false) {
            for (i = 0; i < ln; i++) {
                anim = timeline[i];
                attrs = anim.attrs;
                easing = attrs.easing || me.easing;
                damper = attrs.damper || me.damper;
                delete attrs.easing;
                delete attrs.damper;
                anim = new Ext.fx.Anim({
                    target: target,
                    easing: easing,
                    damper: damper,
                    duration: anim.duration,
                    paused: true,
                    to: attrs
                });
                anims.push(anim);
            }
            me.animations = anims;
            me.target = anim.target;
            for (i = 0; i < ln - 1; i++) {
                anim = anims[i];
                anim.nextAnim = anims[i + 1];
                anim.on('afteranimate', function() {
                    this.nextAnim.paused = false;
                });
                anim.on('afteranimate', function() {
                    this.fireEvent('keyframe', this, ++this.keyframeStep);
                }, me);
            }
            anims[ln - 1].on('afteranimate', function() {
                this.lastFrame();
            }, me);
        }
    },
    
    start: function(startTime) {
        var me = this,
            delay = me.delay,
            delayStart = me.delayStart,
            delayDelta;
        if (delay) {
            if (!delayStart) {
                me.delayStart = startTime;
                return;
            } else {
                delayDelta = startTime - delayStart;
                if (delayDelta < delay) {
                    return;
                } else {
                    
                    startTime = new Date(delayStart.getTime() + delay);
                }
            }
        }
        if (me.fireEvent('beforeanimate', me) !== false) {
            me.startTime = startTime;
            me.running = true;
            me.animations[me.keyframeStep].paused = false;
        }
    },
    
    lastFrame: function() {
        var me = this,
            iter = me.iterations,
            iterCount = me.currentIteration;
        iterCount++;
        if (iterCount < iter) {
            me.startTime = new Date();
            me.currentIteration = iterCount;
            me.keyframeStep = 0;
            me.applyAnimator(me.target);
            me.animations[me.keyframeStep].paused = false;
        } else {
            me.currentIteration = 0;
            me.end();
        }
    },
    
    end: function() {
        var me = this;
        me.fireEvent('afteranimate', me, me.startTime, new Date() - me.startTime);
    },
    isReady: function() {
        return this.paused === false && this.running === false && this.iterations > 0;
    },
    isRunning: function() {
        
        return false;
    }
});


Ext.define('Ext.fx.CubicBezier', {
    
    singleton: true,
    
    cubicBezierAtTime: function(t, p1x, p1y, p2x, p2y, duration) {
        var cx = 3 * p1x,
            bx = 3 * (p2x - p1x) - cx,
            ax = 1 - cx - bx,
            cy = 3 * p1y,
            by = 3 * (p2y - p1y) - cy,
            ay = 1 - cy - by;
        function sampleCurveX(t) {
            return ((ax * t + bx) * t + cx) * t;
        }
        function solve(x, epsilon) {
            var t = solveCurveX(x, epsilon);
            return ((ay * t + by) * t + cy) * t;
        }
        function solveCurveX(x, epsilon) {
            var t0, t1, t2, x2, d2, i;
            for (t2 = x , i = 0; i < 8; i++) {
                x2 = sampleCurveX(t2) - x;
                if (Math.abs(x2) < epsilon) {
                    return t2;
                }
                d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
                if (Math.abs(d2) < 1.0E-6) {
                    break;
                }
                t2 = t2 - x2 / d2;
            }
            t0 = 0;
            t1 = 1;
            t2 = x;
            if (t2 < t0) {
                return t0;
            }
            if (t2 > t1) {
                return t1;
            }
            while (t0 < t1) {
                x2 = sampleCurveX(t2);
                if (Math.abs(x2 - x) < epsilon) {
                    return t2;
                }
                if (x > x2) {
                    t0 = t2;
                } else {
                    t1 = t2;
                }
                t2 = (t1 - t0) / 2 + t0;
            }
            return t2;
        }
        return solve(t, 1 / (200 * duration));
    },
    cubicBezier: function(x1, y1, x2, y2) {
        var fn = function(pos) {
                return Ext.fx.CubicBezier.cubicBezierAtTime(pos, x1, y1, x2, y2, 1);
            };
        fn.toCSS3 = function() {
            return 'cubic-bezier(' + [
                x1,
                y1,
                x2,
                y2
            ].join(',') + ')';
        };
        fn.reverse = function() {
            return Ext.fx.CubicBezier.cubicBezier(1 - x2, 1 - y2, 1 - x1, 1 - y1);
        };
        return fn;
    }
});



Ext.require('Ext.fx.CubicBezier', function() {
    var math = Math,
        pi = math.PI,
        pow = math.pow,
        sin = math.sin,
        sqrt = math.sqrt,
        abs = math.abs,
        backInSeed = 1.70158;
    Ext.define('Ext.fx.Easing', {
        singleton: true,
        linear: Ext.identityFn,
        ease: function(n) {
            var q = 0.07813 - n / 2,
                alpha = -0.25,
                Q = sqrt(0.0066 + q * q),
                x = Q - q,
                X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
                y = -Q - q,
                Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
                t = X + Y + 0.25;
            return pow(1 - t, 2) * 3 * t * 0.1 + (1 - t) * 3 * t * t + t * t * t;
        },
        easeIn: function(n) {
            return pow(n, 1.7);
        },
        easeOut: function(n) {
            return pow(n, 0.48);
        },
        easeInOut: function(n) {
            var q = 0.48 - n / 1.04,
                Q = sqrt(0.1734 + q * q),
                x = Q - q,
                X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
                y = -Q - q,
                Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
                t = X + Y + 0.5;
            return (1 - t) * 3 * t * t + t * t * t;
        },
        backIn: function(n) {
            return n * n * ((backInSeed + 1) * n - backInSeed);
        },
        backOut: function(n) {
            n = n - 1;
            return n * n * ((backInSeed + 1) * n + backInSeed) + 1;
        },
        elasticIn: function(n) {
            if (n === 0 || n === 1) {
                return n;
            }
            var p = 0.3,
                s = p / 4;
            return pow(2, -10 * n) * sin((n - s) * (2 * pi) / p) + 1;
        },
        elasticOut: function(n) {
            return 1 - Ext.fx.Easing.elasticIn(1 - n);
        },
        bounceIn: function(n) {
            return 1 - Ext.fx.Easing.bounceOut(1 - n);
        },
        bounceOut: function(n) {
            var s = 7.5625,
                p = 2.75,
                l;
            if (n < (1 / p)) {
                l = s * n * n;
            } else {
                if (n < (2 / p)) {
                    n -= (1.5 / p);
                    l = s * n * n + 0.75;
                } else {
                    if (n < (2.5 / p)) {
                        n -= (2.25 / p);
                        l = s * n * n + 0.9375;
                    } else {
                        n -= (2.625 / p);
                        l = s * n * n + 0.984375;
                    }
                }
            }
            return l;
        }
    }, function() {
        var easing = Ext.fx.Easing.self,
            proto = easing.prototype;
        easing.addMembers({
            'back-in': proto.backIn,
            'back-out': proto.backOut,
            'ease-in': proto.easeIn,
            'ease-out': proto.easeOut,
            'elastic-in': proto.elasticIn,
            'elastic-out': proto.elasticOut,
            'bounce-in': proto.bounceIn,
            'bounce-out': proto.bounceOut,
            'ease-in-out': proto.easeInOut
        });
    });
});


Ext.define('Ext.fx.DrawPath', {
    
    singleton: true,
    
    pathToStringRE: /,?([achlmqrstvxz]),?/gi,
    pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
    pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
    stopsRE: /^(\d+%?)$/,
    radian: Math.PI / 180,
    is: function(o, type) {
        type = String(type).toLowerCase();
        return (type == "object" && o === Object(o)) || (type == "undefined" && typeof o == type) || (type == "null" && o === null) || (type == "array" && Array.isArray && Array.isArray(o)) || (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;
    },
    
    path2string: function() {
        return this.join(",").replace(Ext.fx.DrawPath.pathToStringRE, "$1");
    },
    
    pathToString: function(arrayPath) {
        return arrayPath.join(",").replace(Ext.fx.DrawPath.pathToStringRE, "$1");
    },
    parsePathString: function(pathString) {
        if (!pathString) {
            return null;
        }
        var paramCounts = {
                a: 7,
                c: 6,
                h: 1,
                l: 2,
                m: 2,
                q: 4,
                s: 4,
                t: 2,
                v: 1,
                z: 0
            },
            data = [],
            me = this;
        if (me.is(pathString, "array") && me.is(pathString[0], "array")) {
            
            data = me.pathClone(pathString);
        }
        if (!data.length) {
            String(pathString).replace(me.pathCommandRE, function(a, b, c) {
                var params = [],
                    name = b.toLowerCase();
                c.replace(me.pathValuesRE, function(a, b) {
                    b && params.push(+b);
                });
                if (name == "m" && params.length > 2) {
                    data.push([
                        b
                    ].concat(Ext.Array.splice(params, 0, 2)));
                    name = "l";
                    b = (b == "m") ? "l" : "L";
                }
                while (params.length >= paramCounts[name]) {
                    data.push([
                        b
                    ].concat(Ext.Array.splice(params, 0, paramCounts[name])));
                    if (!paramCounts[name]) {
                        break;
                    }
                }
            });
        }
        data.toString = me.path2string;
        return data;
    },
    pathClone: function(pathArray) {
        var res = [],
            j, jj, i, ii;
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) {
            
            pathArray = this.parsePathString(pathArray);
        }
        for (i = 0 , ii = pathArray.length; i < ii; i++) {
            res[i] = [];
            for (j = 0 , jj = pathArray[i].length; j < jj; j++) {
                res[i][j] = pathArray[i][j];
            }
        }
        res.toString = this.path2string;
        return res;
    },
    pathToAbsolute: function(pathArray) {
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) {
            
            pathArray = this.parsePathString(pathArray);
        }
        var res = [],
            x = 0,
            y = 0,
            mx = 0,
            my = 0,
            i = 0,
            ln = pathArray.length,
            r, pathSegment, j, ln2;
        
        if (ln && pathArray[0][0] == "M") {
            x = +pathArray[0][1];
            y = +pathArray[0][2];
            mx = x;
            my = y;
            i++;
            res[0] = [
                "M",
                x,
                y
            ];
        }
        for (; i < ln; i++) {
            r = res[i] = [];
            pathSegment = pathArray[i];
            if (pathSegment[0] != pathSegment[0].toUpperCase()) {
                r[0] = pathSegment[0].toUpperCase();
                switch (r[0]) {
                    
                    case "A":
                        r[1] = pathSegment[1];
                        r[2] = pathSegment[2];
                        r[3] = pathSegment[3];
                        r[4] = pathSegment[4];
                        r[5] = pathSegment[5];
                        r[6] = +(pathSegment[6] + x);
                        r[7] = +(pathSegment[7] + y);
                        break;
                    
                    case "V":
                        r[1] = +pathSegment[1] + y;
                        break;
                    
                    case "H":
                        r[1] = +pathSegment[1] + x;
                        break;
                    case "M":
                        
                        mx = +pathSegment[1] + x;
                        my = +pathSegment[2] + y;
                    default:
                        j = 1;
                        ln2 = pathSegment.length;
                        for (; j < ln2; j++) {
                            r[j] = +pathSegment[j] + ((j % 2) ? x : y);
                        };
                }
            } else {
                j = 0;
                ln2 = pathSegment.length;
                for (; j < ln2; j++) {
                    res[i][j] = pathSegment[j];
                }
            }
            switch (r[0]) {
                
                case "Z":
                    x = mx;
                    y = my;
                    break;
                
                case "H":
                    x = r[1];
                    break;
                
                case "V":
                    y = r[1];
                    break;
                
                case "M":
                    pathSegment = res[i];
                    ln2 = pathSegment.length;
                    mx = pathSegment[ln2 - 2];
                    my = pathSegment[ln2 - 1];
                default:
                    pathSegment = res[i];
                    ln2 = pathSegment.length;
                    x = pathSegment[ln2 - 2];
                    y = pathSegment[ln2 - 1];
            }
        }
        res.toString = this.path2string;
        return res;
    },
    interpolatePaths: function(path, path2) {
        var me = this,
            p = me.pathToAbsolute(path),
            p2 = me.pathToAbsolute(path2),
            attrs = {
                x: 0,
                y: 0,
                bx: 0,
                by: 0,
                X: 0,
                Y: 0,
                qx: null,
                qy: null
            },
            attrs2 = {
                x: 0,
                y: 0,
                bx: 0,
                by: 0,
                X: 0,
                Y: 0,
                qx: null,
                qy: null
            },
            fixArc = function(pp, i) {
                if (pp[i].length > 7) {
                    pp[i].shift();
                    var pi = pp[i];
                    while (pi.length) {
                        Ext.Array.splice(pp, i++, 0, [
                            "C"
                        ].concat(Ext.Array.splice(pi, 0, 6)));
                    }
                    Ext.Array.erase(pp, i, 1);
                    ii = Math.max(p.length, p2.length || 0);
                }
            },
            fixM = function(path1, path2, a1, a2, i) {
                if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
                    Ext.Array.splice(path2, i, 0, [
                        "M",
                        a2.x,
                        a2.y
                    ]);
                    a1.bx = 0;
                    a1.by = 0;
                    a1.x = path1[i][1];
                    a1.y = path1[i][2];
                    ii = Math.max(p.length, p2.length || 0);
                }
            },
            i, ii, seg, seg2, seglen, seg2len;
        for (i = 0 , ii = Math.max(p.length, p2.length || 0); i < ii; i++) {
            p[i] = me.command2curve(p[i], attrs);
            fixArc(p, i);
            (p2[i] = me.command2curve(p2[i], attrs2));
            fixArc(p2, i);
            fixM(p, p2, attrs, attrs2, i);
            fixM(p2, p, attrs2, attrs, i);
            seg = p[i];
            seg2 = p2[i];
            seglen = seg.length;
            seg2len = seg2.length;
            attrs.x = seg[seglen - 2];
            attrs.y = seg[seglen - 1];
            attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
            attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
            attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);
            attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);
            attrs2.x = seg2[seg2len - 2];
            attrs2.y = seg2[seg2len - 1];
        }
        return [
            p,
            p2
        ];
    },
    
    command2curve: function(pathCommand, d) {
        var me = this;
        if (!pathCommand) {
            return [
                "C",
                d.x,
                d.y,
                d.x,
                d.y,
                d.x,
                d.y
            ];
        }
        if (pathCommand[0] != "T" && pathCommand[0] != "Q") {
            d.qx = d.qy = null;
        }
        switch (pathCommand[0]) {
            case "M":
                d.X = pathCommand[1];
                d.Y = pathCommand[2];
                break;
            case "A":
                pathCommand = [
                    "C"
                ].concat(me.arc2curve.apply(me, [
                    d.x,
                    d.y
                ].concat(pathCommand.slice(1))));
                break;
            case "S":
                pathCommand = [
                    "C",
                    d.x + (d.x - (d.bx || d.x)),
                    d.y + (d.y - (d.by || d.y))
                ].concat(pathCommand.slice(1));
                break;
            case "T":
                d.qx = d.x + (d.x - (d.qx || d.x));
                d.qy = d.y + (d.y - (d.qy || d.y));
                pathCommand = [
                    "C"
                ].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));
                break;
            case "Q":
                d.qx = pathCommand[1];
                d.qy = pathCommand[2];
                pathCommand = [
                    "C"
                ].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));
                break;
            case "L":
                pathCommand = [
                    "C"
                ].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);
                break;
            case "H":
                pathCommand = [
                    "C"
                ].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);
                break;
            case "V":
                pathCommand = [
                    "C"
                ].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);
                break;
            case "Z":
                pathCommand = [
                    "C"
                ].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);
                break;
        }
        return pathCommand;
    },
    quadratic2curve: function(x1, y1, ax, ay, x2, y2) {
        var _13 = 1 / 3,
            _23 = 2 / 3;
        return [
            _13 * x1 + _23 * ax,
            _13 * y1 + _23 * ay,
            _13 * x2 + _23 * ax,
            _13 * y2 + _23 * ay,
            x2,
            y2
        ];
    },
    rotate: function(x, y, rad) {
        var cos = Math.cos(rad),
            sin = Math.sin(rad),
            X = x * cos - y * sin,
            Y = x * sin + y * cos;
        return {
            x: X,
            y: Y
        };
    },
    arc2curve: function(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
        
        
        var me = this,
            PI = Math.PI,
            radian = me.radian,
            _120 = PI * 120 / 180,
            rad = radian * (+angle || 0),
            res = [],
            math = Math,
            mcos = math.cos,
            msin = math.sin,
            msqrt = math.sqrt,
            mabs = math.abs,
            masin = math.asin,
            xy, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2, t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;
        if (!recursive) {
            xy = me.rotate(x1, y1, -rad);
            x1 = xy.x;
            y1 = xy.y;
            xy = me.rotate(x2, y2, -rad);
            x2 = xy.x;
            y2 = xy.y;
            x = (x1 - x2) / 2;
            y = (y1 - y2) / 2;
            h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
            if (h > 1) {
                h = msqrt(h);
                rx = h * rx;
                ry = h * ry;
            }
            rx2 = rx * rx;
            ry2 = ry * ry;
            k = (large_arc_flag == sweep_flag ? -1 : 1) * msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
            cx = k * rx * y / ry + (x1 + x2) / 2;
            cy = k * -ry * x / rx + (y1 + y2) / 2;
            f1 = masin(((y1 - cy) / ry).toFixed(7));
            f2 = masin(((y2 - cy) / ry).toFixed(7));
            f1 = x1 < cx ? PI - f1 : f1;
            f2 = x2 < cx ? PI - f2 : f2;
            if (f1 < 0) {
                f1 = PI * 2 + f1;
            }
            if (f2 < 0) {
                f2 = PI * 2 + f2;
            }
            if (sweep_flag && f1 > f2) {
                f1 = f1 - PI * 2;
            }
            if (!sweep_flag && f2 > f1) {
                f2 = f2 - PI * 2;
            }
        } else {
            f1 = recursive[0];
            f2 = recursive[1];
            cx = recursive[2];
            cy = recursive[3];
        }
        df = f2 - f1;
        if (mabs(df) > _120) {
            f2old = f2;
            x2old = x2;
            y2old = y2;
            f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
            x2 = cx + rx * mcos(f2);
            y2 = cy + ry * msin(f2);
            res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [
                f2,
                f2old,
                cx,
                cy
            ]);
        }
        df = f2 - f1;
        c1 = mcos(f1);
        s1 = msin(f1);
        c2 = mcos(f2);
        s2 = msin(f2);
        t = math.tan(df / 4);
        hx = 4 / 3 * rx * t;
        hy = 4 / 3 * ry * t;
        m1 = [
            x1,
            y1
        ];
        m2 = [
            x1 + hx * s1,
            y1 - hy * c1
        ];
        m3 = [
            x2 + hx * s2,
            y2 - hy * c2
        ];
        m4 = [
            x2,
            y2
        ];
        m2[0] = 2 * m1[0] - m2[0];
        m2[1] = 2 * m1[1] - m2[1];
        if (recursive) {
            return [
                m2,
                m3,
                m4
            ].concat(res);
        } else {
            res = [
                m2,
                m3,
                m4
            ].concat(res).join().split(",");
            newres = [];
            ln = res.length;
            for (i = 0; i < ln; i++) {
                newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;
            }
            return newres;
        }
    }
});


Ext.define('Ext.fx.PropertyHandler', {
    
    requires: [
        'Ext.fx.DrawPath'
    ],
    statics: {
        defaultHandler: {
            pixelDefaultsRE: /width|height|top$|bottom$|left$|right$/i,
            unitRE: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/,
            scrollRE: /^scroll/i,
            computeDelta: function(from, end, damper, initial, attr) {
                damper = (typeof damper == 'number') ? damper : 1;
                var unitRE = this.unitRE,
                    match = unitRE.exec(from),
                    start, units;
                if (match) {
                    from = match[1];
                    units = match[2];
                    if (!this.scrollRE.test(attr) && !units && this.pixelDefaultsRE.test(attr)) {
                        units = 'px';
                    }
                }
                from = +from || 0;
                match = unitRE.exec(end);
                if (match) {
                    end = match[1];
                    units = match[2] || units;
                }
                end = +end || 0;
                start = (initial != null) ? initial : from;
                return {
                    from: from,
                    delta: (end - start) * damper,
                    units: units
                };
            },
            get: function(from, end, damper, initialFrom, attr) {
                var ln = from.length,
                    out = [],
                    i, initial, res, j, len;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    if (Ext.isArray(from[i][1]) && Ext.isArray(end)) {
                        res = [];
                        j = 0;
                        len = from[i][1].length;
                        for (; j < len; j++) {
                            res.push(this.computeDelta(from[i][1][j], end[j], damper, initial, attr));
                        }
                        out.push([
                            from[i][0],
                            res
                        ]);
                    } else {
                        out.push([
                            from[i][0],
                            this.computeDelta(from[i][1], end, damper, initial, attr)
                        ]);
                    }
                }
                return out;
            },
            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, res, len, j;
                for (i = 0; i < ln; i++) {
                    val = values[i][1];
                    if (Ext.isArray(val)) {
                        res = [];
                        j = 0;
                        len = val.length;
                        for (; j < len; j++) {
                            res.push(val[j].from + val[j].delta * easing + (val[j].units || 0));
                        }
                        out.push([
                            values[i][0],
                            res
                        ]);
                    } else {
                        out.push([
                            values[i][0],
                            val.from + val.delta * easing + (val.units || 0)
                        ]);
                    }
                }
                return out;
            }
        },
        stringHandler: {
            computeDelta: function(from, end, damper, initial, attr) {
                return {
                    from: from,
                    delta: end
                };
            },
            get: function(from, end, damper, initialFrom, attr) {
                var ln = from.length,
                    out = [],
                    i, initial, res, j, len;
                for (i = 0; i < ln; i++) {
                    out.push([
                        from[i][0],
                        this.computeDelta(from[i][1], end, damper, initial, attr)
                    ]);
                }
                return out;
            },
            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, res, len, j;
                for (i = 0; i < ln; i++) {
                    val = values[i][1];
                    out.push([
                        values[i][0],
                        val.delta
                    ]);
                }
                return out;
            }
        },
        color: {
            rgbRE: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
            hexRE: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
            hex3RE: /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i,
            parseColor: function(color, damper) {
                damper = (typeof damper == 'number') ? damper : 1;
                var out = false,
                    reList = [
                        this.hexRE,
                        this.rgbRE,
                        this.hex3RE
                    ],
                    length = reList.length,
                    match, base, re, i;
                for (i = 0; i < length; i++) {
                    re = reList[i];
                    base = (i % 2 === 0) ? 16 : 10;
                    match = re.exec(color);
                    if (match && match.length === 4) {
                        if (i === 2) {
                            match[1] += match[1];
                            match[2] += match[2];
                            match[3] += match[3];
                        }
                        out = {
                            red: parseInt(match[1], base),
                            green: parseInt(match[2], base),
                            blue: parseInt(match[3], base)
                        };
                        break;
                    }
                }
                return out || color;
            },
            computeDelta: function(from, end, damper, initial) {
                from = this.parseColor(from);
                end = this.parseColor(end, damper);
                var start = initial ? initial : from,
                    tfrom = typeof start,
                    tend = typeof end;
                
                if (tfrom == 'string' || tfrom == 'undefined' || tend == 'string' || tend == 'undefined') {
                    return end || start;
                }
                return {
                    from: from,
                    delta: {
                        red: Math.round((end.red - start.red) * damper),
                        green: Math.round((end.green - start.green) * damper),
                        blue: Math.round((end.blue - start.blue) * damper)
                    }
                };
            },
            get: function(start, end, damper, initialFrom) {
                var ln = start.length,
                    out = [],
                    i, initial;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    out.push([
                        start[i][0],
                        this.computeDelta(start[i][1], end, damper, initial)
                    ]);
                }
                return out;
            },
            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, parsedString, from, delta;
                for (i = 0; i < ln; i++) {
                    val = values[i][1];
                    if (val) {
                        from = val.from;
                        delta = val.delta;
                        
                        val = (typeof val == 'object' && 'red' in val) ? 'rgb(' + val.red + ', ' + val.green + ', ' + val.blue + ')' : val;
                        val = (typeof val == 'object' && val.length) ? val[0] : val;
                        if (typeof val == 'undefined') {
                            return [];
                        }
                        parsedString = typeof val == 'string' ? val : 'rgb(' + [
                            (from.red + Math.round(delta.red * easing)) % 256,
                            (from.green + Math.round(delta.green * easing)) % 256,
                            (from.blue + Math.round(delta.blue * easing)) % 256
                        ].join(',') + ')';
                        out.push([
                            values[i][0],
                            parsedString
                        ]);
                    }
                }
                return out;
            }
        },
        object: {
            interpolate: function(prop, damper) {
                damper = (typeof damper == 'number') ? damper : 1;
                var out = {},
                    p;
                for (p in prop) {
                    out[p] = parseFloat(prop[p]) * damper;
                }
                return out;
            },
            computeDelta: function(from, end, damper, initial) {
                from = this.interpolate(from);
                end = this.interpolate(end, damper);
                var start = initial ? initial : from,
                    delta = {},
                    p;
                for (p in end) {
                    delta[p] = end[p] - start[p];
                }
                return {
                    from: from,
                    delta: delta
                };
            },
            get: function(start, end, damper, initialFrom) {
                var ln = start.length,
                    out = [],
                    i, initial;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    out.push([
                        start[i][0],
                        this.computeDelta(start[i][1], end, damper, initial)
                    ]);
                }
                return out;
            },
            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    outObject = {},
                    i, from, delta, val, p;
                for (i = 0; i < ln; i++) {
                    val = values[i][1];
                    from = val.from;
                    delta = val.delta;
                    for (p in from) {
                        outObject[p] = from[p] + delta[p] * easing;
                    }
                    out.push([
                        values[i][0],
                        outObject
                    ]);
                }
                return out;
            }
        },
        path: {
            computeDelta: function(from, end, damper, initial) {
                damper = (typeof damper == 'number') ? damper : 1;
                var start;
                from = +from || 0;
                end = +end || 0;
                start = (initial != null) ? initial : from;
                return {
                    from: from,
                    delta: (end - start) * damper
                };
            },
            forcePath: function(path) {
                if (!Ext.isArray(path) && !Ext.isArray(path[0])) {
                    path = Ext.fx.DrawPath.parsePathString(path);
                }
                return path;
            },
            get: function(start, end, damper, initialFrom) {
                var endPath = this.forcePath(end),
                    out = [],
                    startLn = start.length,
                    startPathLn, pointsLn, i, deltaPath, initial, j, k, path, startPath;
                for (i = 0; i < startLn; i++) {
                    startPath = this.forcePath(start[i][1]);
                    deltaPath = Ext.fx.DrawPath.interpolatePaths(startPath, endPath);
                    startPath = deltaPath[0];
                    endPath = deltaPath[1];
                    startPathLn = startPath.length;
                    path = [];
                    for (j = 0; j < startPathLn; j++) {
                        deltaPath = [
                            startPath[j][0]
                        ];
                        pointsLn = startPath[j].length;
                        for (k = 1; k < pointsLn; k++) {
                            initial = initialFrom && initialFrom[0][1][j][k].from;
                            deltaPath.push(this.computeDelta(startPath[j][k], endPath[j][k], damper, initial));
                        }
                        path.push(deltaPath);
                    }
                    out.push([
                        start[i][0],
                        path
                    ]);
                }
                return out;
            },
            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, j, k, newPath, calcPath, deltaPath, deltaPathLn, pointsLn;
                for (i = 0; i < ln; i++) {
                    deltaPath = values[i][1];
                    newPath = [];
                    deltaPathLn = deltaPath.length;
                    for (j = 0; j < deltaPathLn; j++) {
                        calcPath = [
                            deltaPath[j][0]
                        ];
                        pointsLn = deltaPath[j].length;
                        for (k = 1; k < pointsLn; k++) {
                            calcPath.push(deltaPath[j][k].from + deltaPath[j][k].delta * easing);
                        }
                        newPath.push(calcPath.join(','));
                    }
                    out.push([
                        values[i][0],
                        newPath.join(',')
                    ]);
                }
                return out;
            }
        }
    }
}, 
function() {
    
    var props = [
            'outlineColor',
            'backgroundColor',
            'borderColor',
            'borderTopColor',
            'borderRightColor',
            'borderBottomColor',
            'borderLeftColor',
            'fill',
            'stroke'
        ],
        length = props.length,
        i = 0,
        prop;
    for (; i < length; i++) {
        prop = props[i];
        this[prop] = this.color;
    }
    
    props = [
        'cursor'
    ];
    length = props.length;
    i = 0;
    for (; i < length; i++) {
        prop = props[i];
        this[prop] = this.stringHandler;
    }
});


Ext.define('Ext.fx.Anim', {
    
    mixins: {
        observable: 'Ext.util.Observable'
    },
    requires: [
        'Ext.fx.Manager',
        'Ext.fx.Animator',
        'Ext.fx.Easing',
        'Ext.fx.CubicBezier',
        'Ext.fx.PropertyHandler'
    ],
    
    
    isAnimation: true,
    
    
    
    
    duration: 250,
    
    delay: 0,
    
    delayStart: 0,
    
    dynamic: false,
    
    easing: 'ease',
    
    
    damper: 1,
    
    bezierRE: /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
    
    reverse: false,
    
    running: false,
    
    paused: false,
    
    iterations: 1,
    
    autoEnd: false,
    
    alternate: false,
    
    currentIteration: 0,
    
    startTime: 0,
    
    
    
    
    
    frameCount: 0,
    
    
    
    
    constructor: function(config) {
        var me = this,
            curve;
        config = config || {};
        
        if (config.keyframes) {
            return new Ext.fx.Animator(config);
        }
        Ext.apply(me, config);
        if (me.from === undefined) {
            me.from = {};
        }
        me.propHandlers = {};
        me.config = config;
        me.target = Ext.fx.Manager.createTarget(me.target);
        me.easingFn = Ext.fx.Easing[me.easing];
        me.target.dynamic = me.dynamic;
        
        if (!me.easingFn) {
            me.easingFn = String(me.easing).match(me.bezierRE);
            if (me.easingFn && me.easingFn.length == 5) {
                curve = me.easingFn;
                me.easingFn = Ext.fx.CubicBezier.cubicBezier(+curve[1], +curve[2], +curve[3], +curve[4]);
            }
        }
        me.id = Ext.id(null, 'ext-anim-');
        me.mixins.observable.constructor.call(me);
        Ext.fx.Manager.addAnim(me);
        if (config.autoEnd) {
            me.running = true;
            me.jumpToEnd();
        }
    },
    
    setAttr: function(attr, value) {
        return Ext.fx.Manager.items.get(this.id).setAttr(this.target, attr, value);
    },
    
    initAttrs: function() {
        var me = this,
            from = me.from,
            to = me.to,
            initialFrom = me.initialFrom || {},
            out = {},
            start, end, propHandler, attr;
        for (attr in to) {
            if (to.hasOwnProperty(attr)) {
                start = me.target.getAttr(attr, from[attr]);
                end = to[attr];
                
                if (!Ext.fx.PropertyHandler[attr]) {
                    if (Ext.isObject(end)) {
                        propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.object;
                    } else {
                        propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.defaultHandler;
                    }
                } else 
                {
                    propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler[attr];
                }
                out[attr] = propHandler.get(start, end, me.damper, initialFrom[attr], attr);
            }
        }
        me.currentAttrs = out;
    },
    
    start: function(startTime) {
        var me = this,
            delay = me.delay,
            delayStart = me.delayStart,
            delayDelta;
        if (delay) {
            if (!delayStart) {
                me.delayStart = startTime;
                return;
            } else {
                delayDelta = startTime - delayStart;
                if (delayDelta < delay) {
                    return;
                } else {
                    
                    startTime = new Date(delayStart.getTime() + delay);
                }
            }
        }
        if (me.fireEvent('beforeanimate', me) !== false) {
            me.startTime = startTime;
            if (!me.paused && !me.currentAttrs) {
                me.initAttrs();
            }
            me.running = true;
            me.frameCount = 0;
        }
    },
    
    jumpToEnd: function() {
        var me = this;
        if (!me.endWasCalled) {
            if (!me.currentAttrs) {
                me.initAttrs();
            }
            Ext.fx.Manager.jumpToEnd(me);
            me.end();
        }
    },
    
    runAnim: function(elapsedTime) {
        var me = this,
            attrs = me.currentAttrs,
            duration = me.duration,
            easingFn = me.easingFn,
            propHandlers = me.propHandlers,
            ret = {},
            easing, values, attr, lastFrame;
        if (elapsedTime >= duration) {
            elapsedTime = duration;
            lastFrame = true;
        }
        if (me.reverse) {
            elapsedTime = duration - elapsedTime;
        }
        for (attr in attrs) {
            if (attrs.hasOwnProperty(attr)) {
                values = attrs[attr];
                easing = lastFrame ? 1 : easingFn(elapsedTime / duration);
                ret[attr] = propHandlers[attr].set(values, easing);
            }
        }
        me.frameCount++;
        return ret;
    },
    
    lastFrame: function() {
        var me = this,
            iter = me.iterations,
            iterCount = me.currentIteration;
        iterCount++;
        if (iterCount < iter) {
            if (me.alternate) {
                me.reverse = !me.reverse;
            }
            me.startTime = new Date();
            me.currentIteration = iterCount;
            
            me.paused = false;
        } else {
            me.currentIteration = 0;
            me.end();
            me.fireEvent('lastframe', me, me.startTime);
        }
    },
    endWasCalled: 0,
    
    end: function() {
        var me = this;
        if (me.endWasCalled++) {
            return;
        }
        me.startTime = 0;
        me.paused = false;
        me.running = false;
        Ext.fx.Manager.removeAnim(me);
        me.fireEvent('afteranimate', me, me.startTime);
        Ext.callback(me.callback, me.scope, [
            me,
            me.startTime
        ]);
        if (me.remove) {
            me.target.destroy();
        }
    },
    isReady: function() {
        return this.paused === false && this.running === false && this.iterations > 0;
    },
    isRunning: function() {
        return this.paused === false && this.running === true && this.isAnimator !== true;
    }
});

Ext.enableFx = true;


Ext.define('Ext.util.Animate', {
    mixinId: 'animate',
    requires: [
        'Ext.fx.Manager',
        'Ext.fx.Anim'
    ],
    isAnimate: true,
    
    animate: function(animObj) {
        var me = this;
        if (Ext.fx.Manager.hasFxBlock(me.id)) {
            return me;
        }
        Ext.fx.Manager.queueFx(new Ext.fx.Anim(me.anim(animObj)));
        return this;
    },
    
    anim: function(config) {
        if (!Ext.isObject(config)) {
            return (config) ? {} : false;
        }
        var me = this;
        if (config.stopAnimation) {
            me.stopAnimation();
        }
        Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
        return Ext.apply({
            target: me,
            paused: true
        }, config);
    },
    
    getAnimationProps: function() {
        var me = this,
            layout = me.layout;
        return layout && layout.animate ? layout.animate : {};
    },
    
    stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'),
    
    stopAnimation: function() {
        Ext.fx.Manager.stopAnimation(this.id);
        return this;
    },
    
    syncFx: function() {
        Ext.fx.Manager.setFxDefaults(this.id, {
            concurrent: true
        });
        return this;
    },
    
    sequenceFx: function() {
        Ext.fx.Manager.setFxDefaults(this.id, {
            concurrent: false
        });
        return this;
    },
    
    hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'),
    
    getActiveAnimation: function() {
        return Ext.fx.Manager.getActiveAnimation(this.id);
    }
});


Ext.define('Ext.dom.Fly', {
    extend: 'Ext.dom.Element',
    alternateClassName: 'Ext.dom.Element.Fly',
    
    
    
    
    validNodeTypes: {
        1: 1,
        
        9: 1,
        
        11: 1
    },
    
    
    isFly: true,
    constructor: function(dom) {
        this.dom = dom;
        
        
        
        this.el = this;
    },
    attach: function(dom) {
        var me = this;
        if (!dom) {
            return me.detach();
        }
        me.dom = dom;
        
        
        if (!Ext.cache[dom.id]) {
            me.getData().isSynchronized = false;
        }
        return me;
    },
    detach: function() {
        this.dom = null;
    },
    addListener: function() {
        Ext.Error.raise("Cannot use addListener() on Ext.dom.Fly instances. " + "Please use Ext.get() to retrieve an Ext.dom.Element instance instead.");
    } || null,
    removeListener: function() {
        Ext.Error.raise("Cannot use removeListener() on Ext.dom.Fly instances. " + "Please use Ext.get() to retrieve an Ext.dom.Element instance instead.");
    } || null
}, function(Fly) {
    var flyweights = {};
    
    Fly.cache = flyweights;
    
    Ext.fly = function(dom, named) {
        var fly = null,
            fn = Ext.fly,
            nodeType, data;
        
        named = named || (fn.caller && fn.caller.$name) || '_global';
        dom = Ext.getDom(dom);
        if (dom) {
            nodeType = dom.nodeType;
            
            
            
            
            
            
            if (Fly.prototype.validNodeTypes[nodeType] || (!nodeType && (dom.window == dom))) {
                fly = Ext.cache[dom.id];
                
                if (!fly || fly.dom !== dom) {
                    fly = flyweights[named] || (flyweights[named] = new Fly());
                    fly.dom = dom;
                    data = fly.getData(true);
                    if (data) {
                        data.isSynchronized = false;
                    }
                }
            }
        }
        return fly;
    };
});


Ext.define('Ext.dom.CompositeElementLite', {
    alternateClassName: [
        'Ext.CompositeElementLite'
    ],
    requires: [
        'Ext.dom.Fly'
    ],
    
    isComposite: true,
    
    isLite: true,
    
    
    
    
    
    
    
    statics: {
        
        
        
        
        importElementMethods: function() {
            var Element = Ext.dom.Element,
                prototype = this.prototype;
            Ext.Object.each(Element.prototype, function(name, member) {
                if (typeof member === 'function' && !prototype[name]) {
                    prototype[name] = function() {
                        return this.invoke(name, arguments);
                    };
                }
            });
        }
    },
    constructor: function(elements, 
    skipValidation) {
        
        if (skipValidation) {
            
            
            
            
            this.elements = elements || [];
        } else {
            this.elements = [];
            this.add(elements);
        }
    },
    
    getElement: function(el) {
        
        var fly = this._fly || (this._fly = new Ext.dom.Fly());
        return fly.attach(el);
    },
    
    transformElement: function(el) {
        return Ext.getDom(el);
    },
    
    getCount: function() {
        return this.elements.length;
    },
    
    add: function(els, root) {
        var elements = this.elements,
            i, ln;
        if (!els) {
            return this;
        }
        if (typeof els == "string") {
            els = Ext.fly(root || document).query(els);
        } else if (els.isComposite) {
            els = els.elements;
        } else if (!Ext.isIterable(els)) {
            els = [
                els
            ];
        }
        for (i = 0 , ln = els.length; i < ln; ++i) {
            elements.push(this.transformElement(els[i]));
        }
        return this;
    },
    invoke: function(fn, args) {
        var me = this,
            elements = me.elements,
            ln = elements.length,
            prototype, element, i;
        if (i !== 0) {
            
            
            prototype = (me.isLite ? Ext.dom.Fly : Ext.dom.Element).prototype;
            for (i = 0; i < ln; i++) {
                element = elements[i];
                if (element) {
                    prototype[fn].apply(me.getElement(element), args);
                }
            }
        }
        return me;
    },
    
    item: function(index) {
        var el = this.elements[index],
            out = null;
        if (el) {
            out = this.getElement(el);
        }
        return out;
    },
    
    slice: function(start, end) {
        return Ext.Array.slice(this.elements, start, end);
    },
    
    each: function(fn, scope) {
        var me = this,
            els = me.elements,
            len = els.length,
            i, e;
        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                e = this.getElement(e);
                if (fn.call(scope || e, e, me, i) === false) {
                    break;
                }
            }
        }
        return me;
    },
    
    fill: function(els) {
        var me = this;
        me.elements = [];
        me.add(els);
        return me;
    },
    insert: function(index, nodes) {
        Ext.Array.insert(this.elements, index, nodes);
    },
    
    filter: function(selector) {
        var me = this,
            els = me.elements,
            len = els.length,
            out = [],
            i = 0,
            isFunc = typeof selector == 'function',
            add, el;
        for (; i < len; i++) {
            el = els[i];
            add = false;
            if (el) {
                el = me.getElement(el);
                if (isFunc) {
                    add = selector.call(el, el, me, i) !== false;
                } else {
                    add = el.is(selector);
                }
                if (add) {
                    out.push(me.transformElement(el));
                }
            }
        }
        me.elements = out;
        return me;
    },
    
    indexOf: function(el) {
        return Ext.Array.indexOf(this.elements, this.transformElement(el));
    },
    
    replaceElement: function(el, replacement, domReplace) {
        var index = !isNaN(el) ? el : this.indexOf(el),
            d;
        if (index > -1) {
            replacement = Ext.getDom(replacement);
            if (domReplace) {
                d = this.elements[index];
                d.parentNode.insertBefore(replacement, d);
                Ext.removeNode(d);
            }
            Ext.Array.splice(this.elements, index, 1, replacement);
        }
        return this;
    },
    
    clear: function(removeDom) {
        var me = this,
            els = me.elements,
            i = els.length - 1;
        if (removeDom) {
            for (; i >= 0; i--) {
                Ext.removeNode(els[i]);
            }
        }
        this.elements = [];
    },
    addElements: function(els, root) {
        if (!els) {
            return this;
        }
        if (typeof els === "string") {
            els = Ext.dom.Element.selectorFunction(els, root);
        }
        var yels = this.elements,
            eLen = els.length,
            e;
        for (e = 0; e < eLen; e++) {
            yels.push(Ext.get(els[e]));
        }
        return this;
    },
    
    first: function() {
        return this.item(0);
    },
    
    last: function() {
        return this.item(this.getCount() - 1);
    },
    
    contains: function(el) {
        return this.indexOf(el) != -1;
    },
    
    removeElement: function(keys, removeDom) {
        keys = [].concat(keys);
        var me = this,
            elements = me.elements,
            kLen = keys.length,
            val, el, k;
        for (k = 0; k < kLen; k++) {
            val = keys[k];
            if ((el = (elements[val] || elements[val = me.indexOf(val)]))) {
                if (removeDom) {
                    if (el.dom) {
                        el.destroy();
                    } else {
                        Ext.removeNode(el);
                    }
                }
                Ext.Array.erase(elements, val, 1);
            }
        }
        return me;
    },
    destroy: function() {
        
        return this.invoke('destroy', arguments);
    }
}, function(CompositeElementLite) {
    var prototype = CompositeElementLite.prototype;
    CompositeElementLite.importElementMethods();
    prototype.on = prototype.addListener;
});


Ext.define('Ext.overrides.dom.Element', (function() {
    var Element,
        
        WIN = window,
        DOC = document,
        HIDDEN = 'hidden',
        ISCLIPPED = 'isClipped',
        OVERFLOW = 'overflow',
        OVERFLOWX = 'overflow-x',
        OVERFLOWY = 'overflow-y',
        ORIGINALCLIP = 'originalClip',
        HEIGHT = 'height',
        WIDTH = 'width',
        VISIBILITY = 'visibility',
        DISPLAY = 'display',
        NONE = 'none',
        HIDDEN = 'hidden',
        OFFSETS = 'offsets',
        ORIGINALDISPLAY = 'originalDisplay',
        VISMODE = 'visibilityMode',
        ISVISIBLE = 'isVisible',
        OFFSETCLASS = Ext.baseCSSPrefix + 'hidden-offsets',
        boxMarkup = [
            '<div class="{0}-tl" role="presentation">',
            '<div class="{0}-tr" role="presentation">',
            '<div class="{0}-tc" role="presentation"></div>',
            '</div>',
            '</div>',
            '<div class="{0}-ml" role="presentation">',
            '<div class="{0}-mr" role="presentation">',
            '<div class="{0}-mc" role="presentation"></div>',
            '</div>',
            '</div>',
            '<div class="{0}-bl" role="presentation">',
            '<div class="{0}-br" role="presentation">',
            '<div class="{0}-bc" role="presentation"></div>',
            '</div>',
            '</div>'
        ].join(''),
        scriptTagRe = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        replaceScriptTagRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        srcRe = /\ssrc=([\'\"])(.*?)\1/i,
        nonSpaceRe = /\S/,
        typeRe = /\stype=([\'\"])(.*?)\1/i,
        msRe = /^-ms-/,
        camelRe = /(-[a-z])/gi,
        camelReplaceFn = function(m, a) {
            return a.charAt(1).toUpperCase();
        },
        XMASKED = Ext.baseCSSPrefix + "masked",
        XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
        EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
        mouseEnterLeaveRe = /^(?:mouseenter|mouseleave)$/,
        bodyRe = /^body/i,
        propertyCache = {},
        getDisplay = function(el) {
            var data = el.getData(),
                display = data[ORIGINALDISPLAY];
            if (display === undefined) {
                data[ORIGINALDISPLAY] = display = '';
            }
            return display;
        },
        getVisMode = function(el) {
            var data = el.getData(),
                visMode = data[VISMODE];
            if (visMode === undefined) {
                data[VISMODE] = visMode = Element.VISIBILITY;
            }
            return visMode;
        },
        garbageBin,
        emptyRange = DOC.createRange ? DOC.createRange() : null,
        inputTags = {
            INPUT: true,
            TEXTAREA: true
        };
    return {
        override: 'Ext.dom.Element',
        mixins: [
            'Ext.util.Animate'
        ],
        uses: [
            'Ext.dom.GarbageCollector',
            'Ext.dom.Fly',
            'Ext.event.publisher.MouseEnterLeave',
            'Ext.fx.Manager',
            'Ext.fx.Anim'
        ],
        skipGarbageCollection: false,
        _init: function(E) {
            Element = E;
        },
        
        statics: {
            selectableCls: Ext.baseCSSPrefix + 'selectable',
            unselectableCls: Ext.baseCSSPrefix + 'unselectable',
            
            tabIndexAttributeName: Ext.isIE8 ? 'tabIndex' : 'tabindex',
            tabbableSelector: 'a[href],button,iframe,input,select,textarea,[tabindex],[contenteditable="true"]',
            
            
            
            naturallyFocusableTags: {
                BUTTON: true,
                IFRAME: true,
                EMBED: true,
                INPUT: true,
                OBJECT: true,
                SELECT: true,
                TEXTAREA: true,
                HTML: Ext.isIE ? true : false
            },
            
            naturallyTabbableTags: {
                BUTTON: true,
                IFRAME: true,
                INPUT: true,
                SELECT: true,
                TEXTAREA: true,
                OBJECT: Ext.isIE8m ? true : false
            },
            tabbableSavedFlagAttribute: 'data-tabindexsaved',
            tabbableSavedAttribute: 'data-savedtabindex',
            normalize: function(prop) {
                if (prop === 'float') {
                    prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
                }
                
                return propertyCache[prop] || (propertyCache[prop] = prop.replace(msRe, 'ms-').replace(camelRe, camelReplaceFn));
            },
            getViewportHeight: function() {
                return Ext.isIE9m ? DOC.documentElement.clientHeight : WIN.innerHeight;
            },
            getViewportWidth: function() {
                return (!Ext.isStrict && !Ext.isOpera) ? document.body.clientWidth : Ext.isIE9m ? DOC.documentElement.clientWidth : WIN.innerWidth;
            }
        },
        
        addClsOnClick: function(className, testFn, scope) {
            var me = this,
                dom = me.dom,
                hasTest = Ext.isFunction(testFn);
            me.on("mousedown", function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return false;
                }
                Ext.fly(dom).addCls(className);
                var d = Ext.getDoc(),
                    fn = function() {
                        Ext.fly(dom).removeCls(className);
                        d.removeListener("mouseup", fn);
                    };
                d.on("mouseup", fn);
            });
            return me;
        },
        
        addClsOnFocus: function(className, testFn, scope) {
            var me = this,
                dom = me.dom,
                hasTest = Ext.isFunction(testFn);
            me.on("focus", function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return false;
                }
                Ext.fly(dom).addCls(className);
            });
            me.on("blur", function() {
                Ext.fly(dom).removeCls(className);
            });
            return me;
        },
        
        addClsOnOver: function(className, testFn, scope) {
            var me = this,
                dom = me.dom,
                hasTest = Ext.isFunction(testFn);
            me.hover(function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return;
                }
                Ext.fly(dom).addCls(className);
            }, function() {
                Ext.fly(dom).removeCls(className);
            });
            return me;
        },
        
        addKeyListener: function(key, fn, scope) {
            var config;
            if (typeof key !== 'object' || Ext.isArray(key)) {
                config = {
                    target: this,
                    key: key,
                    fn: fn,
                    scope: scope
                };
            } else {
                config = {
                    target: this,
                    key: key.key,
                    shift: key.shift,
                    ctrl: key.ctrl,
                    alt: key.alt,
                    fn: fn,
                    scope: scope
                };
            }
            return new Ext.util.KeyMap(config);
        },
        
        addKeyMap: function(config) {
            return new Ext.util.KeyMap(Ext.apply({
                target: this
            }, config));
        },
        
        afterAnimate: function() {
            var shadow = this.shadow;
            if (shadow && !shadow.disabled && !shadow.animate) {
                shadow.show();
            }
        },
        
        anchorAnimX: function(anchor) {
            var xName = (anchor === 'l') ? 'right' : 'left';
            this.dom.style[xName] = '0px';
        },
        
        anim: function(config) {
            if (!Ext.isObject(config)) {
                return (config) ? {} : false;
            }
            var me = this,
                duration = config.duration || Ext.fx.Anim.prototype.duration,
                easing = config.easing || 'ease',
                animConfig;
            if (config.stopAnimation) {
                me.stopAnimation();
            }
            Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
            
            Ext.fx.Manager.setFxDefaults(me.id, {
                delay: 0
            });
            animConfig = {
                
                target: me.dom,
                remove: config.remove,
                alternate: config.alternate || false,
                duration: duration,
                easing: easing,
                callback: config.callback,
                listeners: config.listeners,
                iterations: config.iterations || 1,
                scope: config.scope,
                block: config.block,
                concurrent: config.concurrent,
                delay: config.delay || 0,
                paused: true,
                keyframes: config.keyframes,
                from: config.from || {},
                to: Ext.apply({}, config)
            };
            Ext.apply(animConfig.to, config.to);
            
            delete animConfig.to.to;
            delete animConfig.to.from;
            delete animConfig.to.remove;
            delete animConfig.to.alternate;
            delete animConfig.to.keyframes;
            delete animConfig.to.iterations;
            delete animConfig.to.listeners;
            delete animConfig.to.target;
            delete animConfig.to.paused;
            delete animConfig.to.callback;
            delete animConfig.to.scope;
            delete animConfig.to.duration;
            delete animConfig.to.easing;
            delete animConfig.to.concurrent;
            delete animConfig.to.block;
            delete animConfig.to.stopAnimation;
            delete animConfig.to.delay;
            return animConfig;
        },
        
        animate: function(config) {
            var me = this,
                animId = me.dom.id || Ext.id(me.dom),
                listeners, anim, end;
            if (!Ext.fx.Manager.hasFxBlock(animId)) {
                
                if (config.listeners) {
                    listeners = config.listeners;
                    delete config.listeners;
                }
                if (config.internalListeners) {
                    config.listeners = config.internalListeners;
                    delete config.internalListeners;
                }
                end = config.autoEnd;
                delete config.autoEnd;
                anim = new Ext.fx.Anim(me.anim(config));
                anim.on({
                    afteranimate: 'afterAnimate',
                    beforeanimate: 'beforeAnimate',
                    scope: me,
                    single: true
                });
                if (listeners) {
                    anim.on(listeners);
                }
                Ext.fx.Manager.queueFx(anim);
                if (end) {
                    anim.jumpToEnd();
                }
            }
            return me;
        },
        
        beforeAnimate: function() {
            var shadow = this.shadow;
            if (shadow && !shadow.disabled && !shadow.animate) {
                shadow.hide();
            }
        },
        
        boxWrap: function(cls) {
            cls = cls || Ext.baseCSSPrefix + 'box';
            var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "' role='presentation'>" + Ext.String.format(boxMarkup, cls) + "</div>"));
            el.selectNode('.' + cls + '-mc').appendChild(this.dom);
            return el;
        },
        
        clean: function(forceReclean) {
            var me = this,
                dom = me.dom,
                data = me.getData(),
                n = dom.firstChild,
                ni = -1,
                nx;
            if (data.isCleaned && forceReclean !== true) {
                return me;
            }
            while (n) {
                nx = n.nextSibling;
                if (n.nodeType === 3) {
                    
                    if (!(nonSpaceRe.test(n.nodeValue))) {
                        dom.removeChild(n);
                    }
                    
                    else if (nx && nx.nodeType === 3) {
                        n.appendData(Ext.String.trim(nx.data));
                        dom.removeChild(nx);
                        nx = n.nextSibling;
                        n.nodeIndex = ++ni;
                    }
                } else {
                    
                    Ext.fly(n, '_clean').clean();
                    n.nodeIndex = ++ni;
                }
                n = nx;
            }
            data.isCleaned = true;
            return me;
        },
        
        empty: emptyRange ? function() {
            var dom = this.dom;
            if (dom.firstChild) {
                emptyRange.setStartBefore(dom.firstChild);
                emptyRange.setEndAfter(dom.lastChild);
                emptyRange.deleteContents();
            }
        } : function() {
            var dom = this.dom;
            while (dom.lastChild) {
                dom.removeChild(dom.lastChild);
            }
        },
        clearListeners: function() {
            this.removeAnchor();
            this.callParent();
        },
        
        clearPositioning: function(value) {
            value = value || '';
            return this.setStyle({
                left: value,
                right: value,
                top: value,
                bottom: value,
                'z-index': '',
                position: 'static'
            });
        },
        
        createProxy: function(config, renderTo, matchBox) {
            config = (typeof config === 'object') ? config : {
                tag: "div",
                role: 'presentation',
                cls: config
            };
            var me = this,
                proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) : Ext.DomHelper.insertBefore(me.dom, config, true);
            proxy.setVisibilityMode(Element.DISPLAY);
            proxy.hide();
            if (matchBox && me.setBox && me.getBox) {
                
                proxy.setBox(me.getBox());
            }
            return proxy;
        },
        
        clearOpacity: function() {
            return this.setOpacity('');
        },
        
        clip: function() {
            var me = this,
                data = me.getData(),
                style;
            if (!data[ISCLIPPED]) {
                data[ISCLIPPED] = true;
                style = me.getStyle([
                    OVERFLOW,
                    OVERFLOWX,
                    OVERFLOWY
                ]);
                data[ORIGINALCLIP] = {
                    o: style[OVERFLOW],
                    x: style[OVERFLOWX],
                    y: style[OVERFLOWY]
                };
                me.setStyle(OVERFLOW, HIDDEN);
                me.setStyle(OVERFLOWX, HIDDEN);
                me.setStyle(OVERFLOWY, HIDDEN);
            }
            return me;
        },
        destroy: function() {
            var me = this,
                dom = me.dom,
                data = me.getData(),
                maskEl, maskMsg;
            if (dom && me.isAnimate) {
                me.stopAnimation();
            }
            me.callParent();
            
            
            
            
            
            if (dom && Ext.isIE8 && (dom.window != dom) && (dom.nodeType !== 9) && (dom.tagName !== 'BODY') && (dom.tagName !== 'HTML')) {
                garbageBin = garbageBin || DOC.createElement('div');
                garbageBin.appendChild(dom);
                garbageBin.innerHTML = '';
            }
            if (data) {
                maskEl = data.maskEl;
                maskMsg = data.maskMsg;
                if (maskEl) {
                    maskEl.destroy();
                }
                if (maskMsg) {
                    maskMsg.destroy();
                }
            }
        },
        
        enableDisplayMode: function(display) {
            var me = this;
            me.setVisibilityMode(Element.DISPLAY);
            if (display !== undefined) {
                me.getData()[ORIGINALDISPLAY] = display;
            }
            return me;
        },
        
        fadeIn: function(o) {
            var me = this,
                dom = me.dom;
            me.animate(Ext.apply({}, o, {
                opacity: 1,
                internalListeners: {
                    beforeanimate: function(anim) {
                        
                        
                        var el = Ext.fly(dom, '_anim');
                        if (el.isStyle('display', 'none')) {
                            el.setDisplayed('');
                        } else {
                            el.show();
                        }
                    }
                }
            }));
            return this;
        },
        
        fadeOut: function(o) {
            var me = this,
                dom = me.dom;
            o = Ext.apply({
                opacity: 0,
                internalListeners: {
                    afteranimate: function(anim) {
                        if (dom && anim.to.opacity === 0) {
                            var el = Ext.fly(dom, '_anim');
                            if (o.useDisplay) {
                                el.setDisplayed(false);
                            } else {
                                el.hide();
                            }
                        }
                    }
                }
            }, o);
            me.animate(o);
            return me;
        },
        
        fixDisplay: function() {
            var me = this;
            if (me.isStyle(DISPLAY, NONE)) {
                me.setStyle(VISIBILITY, HIDDEN);
                me.setStyle(DISPLAY, getDisplay(me));
                
                if (me.isStyle(DISPLAY, NONE)) {
                    
                    me.setStyle(DISPLAY, "block");
                }
            }
        },
        
        frame: function(color, count, obj) {
            var me = this,
                dom = me.dom,
                beforeAnim;
            color = color || '#C3DAF9';
            count = count || 1;
            obj = obj || {};
            beforeAnim = function() {
                var el = Ext.fly(dom, '_anim'),
                    animScope = this,
                    box, proxy, proxyAnim;
                el.show();
                box = el.getBox();
                proxy = Ext.getBody().createChild({
                    role: 'presentation',
                    id: el.dom.id + '-anim-proxy',
                    style: {
                        position: 'absolute',
                        'pointer-events': 'none',
                        'z-index': 35000,
                        border: '0px solid ' + color
                    }
                });
                proxyAnim = new Ext.fx.Anim({
                    target: proxy,
                    duration: obj.duration || 1000,
                    iterations: count,
                    from: {
                        top: box.y,
                        left: box.x,
                        borderWidth: 0,
                        opacity: 1,
                        height: box.height,
                        width: box.width
                    },
                    to: {
                        top: box.y - 20,
                        left: box.x - 20,
                        borderWidth: 10,
                        opacity: 0,
                        height: box.height + 40,
                        width: box.width + 40
                    }
                });
                proxyAnim.on('afteranimate', function() {
                    proxy.destroy();
                    
                    animScope.end();
                });
            };
            me.animate({
                
                duration: (Math.max(obj.duration, 500) * 2) || 2000,
                listeners: {
                    beforeanimate: {
                        fn: beforeAnim
                    }
                },
                callback: obj.callback,
                scope: obj.scope
            });
            return me;
        },
        
        getColor: function(attr, defaultValue, prefix) {
            var v = this.getStyle(attr),
                color = prefix || prefix === '' ? prefix : '#',
                h, len,
                i = 0;
            if (!v || (/transparent|inherit/.test(v))) {
                return defaultValue;
            }
            if (/^r/.test(v)) {
                v = v.slice(4, v.length - 1).split(',');
                len = v.length;
                for (; i < len; i++) {
                    h = parseInt(v[i], 10);
                    color += (h < 16 ? '0' : '') + h.toString(16);
                }
            } else {
                v = v.replace('#', '');
                color += v.length === 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
            }
            return (color.length > 5 ? color.toLowerCase() : defaultValue);
        },
        
        getLoader: function() {
            var me = this,
                data = me.getData(),
                loader = data.loader;
            if (!loader) {
                data.loader = loader = new Ext.ElementLoader({
                    target: me
                });
            }
            return loader;
        },
        
        getPositioning: function(autoPx) {
            var styles = this.getStyle([
                    'left',
                    'top',
                    'position',
                    'z-index'
                ]),
                dom = this.dom;
            if (autoPx) {
                if (styles.left === 'auto') {
                    styles.left = dom.offsetLeft + 'px';
                }
                if (styles.top === 'auto') {
                    styles.top = dom.offsetTop + 'px';
                }
            }
            return styles;
        },
        
        ghost: function(anchor, obj) {
            var me = this,
                dom = me.dom,
                beforeAnim;
            anchor = anchor || "b";
            beforeAnim = function() {
                var el = Ext.fly(dom, '_anim'),
                    width = el.getWidth(),
                    height = el.getHeight(),
                    xy = el.getXY(),
                    position = el.getPositioning(),
                    to = {
                        opacity: 0
                    };
                switch (anchor) {
                    case 't':
                        to.y = xy[1] - height;
                        break;
                    case 'l':
                        to.x = xy[0] - width;
                        break;
                    case 'r':
                        to.x = xy[0] + width;
                        break;
                    case 'b':
                        to.y = xy[1] + height;
                        break;
                    case 'tl':
                        to.x = xy[0] - width;
                        to.y = xy[1] - height;
                        break;
                    case 'bl':
                        to.x = xy[0] - width;
                        to.y = xy[1] + height;
                        break;
                    case 'br':
                        to.x = xy[0] + width;
                        to.y = xy[1] + height;
                        break;
                    case 'tr':
                        to.x = xy[0] + width;
                        to.y = xy[1] - height;
                        break;
                }
                this.to = to;
                this.on('afteranimate', function() {
                    var el = Ext.fly(dom, '_anim');
                    if (el) {
                        el.hide();
                        el.clearOpacity();
                        el.setPositioning(position);
                    }
                });
            };
            me.animate(Ext.applyIf(obj || {}, {
                duration: 500,
                easing: 'ease-out',
                listeners: {
                    beforeanimate: beforeAnim
                }
            }));
            return me;
        },
        
        hide: function(animate) {
            
            if (typeof animate === 'string') {
                this.setVisible(false, animate);
                return this;
            }
            this.setVisible(false, this.anim(animate));
            return this;
        },
        
        highlight: function(color, o) {
            var me = this,
                dom = me.dom,
                from = {},
                restore, to, attr, lns, event, fn;
            o = o || {};
            lns = o.listeners || {};
            attr = o.attr || 'backgroundColor';
            from[attr] = color || 'ffff9c';
            if (!o.to) {
                to = {};
                to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
            } else {
                to = o.to;
            }
            
            o.listeners = Ext.apply(Ext.apply({}, lns), {
                beforeanimate: function() {
                    restore = dom.style[attr];
                    var el = Ext.fly(dom, '_anim');
                    el.clearOpacity();
                    el.show();
                    event = lns.beforeanimate;
                    if (event) {
                        fn = event.fn || event;
                        return fn.apply(event.scope || lns.scope || WIN, arguments);
                    }
                },
                afteranimate: function() {
                    if (dom) {
                        dom.style[attr] = restore;
                    }
                    event = lns.afteranimate;
                    if (event) {
                        fn = event.fn || event;
                        fn.apply(event.scope || lns.scope || WIN, arguments);
                    }
                }
            });
            me.animate(Ext.apply({}, o, {
                duration: 1000,
                easing: 'ease-in',
                from: from,
                to: to
            }));
            return me;
        },
        
        hover: function(overFn, outFn, scope, options) {
            var me = this;
            me.on('mouseenter', overFn, scope || me.dom, options);
            me.on('mouseleave', outFn, scope || me.dom, options);
            return me;
        },
        
        initDD: function(group, config, overrides) {
            var dd = new Ext.dd.DD(Ext.id(this.dom),group,config);
            return Ext.apply(dd, overrides);
        },
        
        initDDProxy: function(group, config, overrides) {
            var dd = new Ext.dd.DDProxy(Ext.id(this.dom),group,config);
            return Ext.apply(dd, overrides);
        },
        
        initDDTarget: function(group, config, overrides) {
            var dd = new Ext.dd.DDTarget(Ext.id(this.dom),group,config);
            return Ext.apply(dd, overrides);
        },
        
        isFocusable: function() {
            var dom = this.dom,
                focusable = false,
                nodeName;
            if (dom && !dom.disabled) {
                nodeName = dom.nodeName;
                
                focusable = !!Ext.Element.naturallyFocusableTags[nodeName] || ((nodeName === 'A' || nodeName === 'LINK') && !!dom.href) || dom.getAttribute('tabindex') != null || dom.contentEditable === 'true';
                
                
                if (Ext.isIE8 && nodeName === 'INPUT' && dom.type === 'hidden') {
                    focusable = false;
                }
                
                focusable = focusable && this.isVisible(true);
            }
            return focusable;
        },
        
        isInputField: function() {
            var dom = this.dom,
                contentEditable = dom.contentEditable;
            
            
            
            
            
            if ((inputTags[dom.tagName] && dom.type !== 'button') || (contentEditable === '' || contentEditable === 'true')) {
                return true;
            }
            return false;
        },
        
        isTabbable: function() {
            var dom = this.dom,
                tabbable = false,
                nodeName, hasIndex, tabIndex;
            if (dom && !dom.disabled) {
                nodeName = dom.nodeName;
                
                
                
                tabIndex = dom.getAttribute('tabindex');
                hasIndex = tabIndex != null;
                tabIndex -= 0;
                
                
                if (nodeName === 'A' || nodeName === 'LINK') {
                    if (dom.href) {
                        
                        
                        tabbable = hasIndex && tabIndex < 0 ? false : true;
                    } else 
                    
                    {
                        if (dom.contentEditable === 'true') {
                            tabbable = !hasIndex || (hasIndex && tabIndex >= 0) ? true : false;
                        } else {
                            tabbable = hasIndex && tabIndex >= 0 ? true : false;
                        }
                    }
                }
                
                
                else if (dom.contentEditable === 'true' || Ext.Element.naturallyTabbableTags[nodeName]) {
                    tabbable = hasIndex && tabIndex < 0 ? false : true;
                } else 
                
                {
                    if (hasIndex && tabIndex >= 0) {
                        tabbable = true;
                    }
                }
                
                
                if (Ext.isIE8 && nodeName === 'INPUT' && dom.type === 'hidden') {
                    tabbable = false;
                }
                
                
                
                tabbable = tabbable && (!this.component || this.component.isVisible(true)) && this.isVisible(true);
            }
            return tabbable;
        },
        
        isMasked: function(deep) {
            var me = this,
                data = me.getData(),
                maskEl = data.maskEl,
                maskMsg = data.maskMsg,
                hasMask = false,
                parent;
            if (maskEl && maskEl.isVisible()) {
                if (maskMsg) {
                    maskMsg.center(me);
                }
                hasMask = true;
            } else if (deep) {
                parent = me.findParentNode();
                if (parent) {
                    return Ext.fly(parent).isMasked(deep);
                }
            }
            return hasMask;
        },
        
        isScrollable: function() {
            var dom = this.dom;
            return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
        },
        
        load: function(options) {
            this.getLoader().load(options);
            return this;
        },
        
        mask: function(msg, msgCls, 
        elHeight) {
            var me = this,
                dom = me.dom,
                data = me.getData(),
                maskEl = data.maskEl,
                maskMsg;
            if (!(bodyRe.test(dom.tagName) && me.getStyle('position') === 'static')) {
                me.addCls(XMASKEDRELATIVE);
            }
            
            if (maskEl) {
                maskEl.destroy();
            }
            maskEl = Ext.DomHelper.append(dom, {
                role: 'presentation',
                cls: Ext.baseCSSPrefix + "mask " + Ext.baseCSSPrefix + "border-box",
                children: {
                    role: 'presentation',
                    cls: msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG,
                    cn: {
                        tag: 'div',
                        role: 'presentation',
                        cls: Ext.baseCSSPrefix + 'mask-msg-inner',
                        cn: {
                            tag: 'div',
                            role: 'presentation',
                            cls: Ext.baseCSSPrefix + 'mask-msg-text',
                            html: msg || ''
                        }
                    }
                }
            }, true);
            maskMsg = Ext.get(maskEl.dom.firstChild);
            data.maskEl = maskEl;
            me.addCls(XMASKED);
            maskEl.setDisplayed(true);
            if (typeof msg === 'string') {
                maskMsg.setDisplayed(true);
                maskMsg.center(me);
            } else {
                maskMsg.setDisplayed(false);
            }
            
            if (dom === DOC.body) {
                maskEl.addCls(Ext.baseCSSPrefix + 'mask-fixed');
            } else {
                me.saveTabbableState();
            }
            me.saveChildrenTabbableState();
            
            if (Ext.isIE9m && dom !== DOC.body && me.isStyle('height', 'auto')) {
                maskEl.setSize(undefined, elHeight || me.getHeight());
            }
            return maskEl;
        },
        
        monitorMouseLeave: function(delay, handler, scope) {
            var me = this,
                timer,
                listeners = {
                    mouseleave: function(e) {
                        if (Ext.isIE9m) {
                            e.enableIEAsync();
                        }
                        timer = Ext.defer(handler, delay, scope || me, [
                            e
                        ]);
                    },
                    mouseenter: function() {
                        clearTimeout(timer);
                    }
                };
            me.on(listeners);
            return listeners;
        },
        
        puff: function(obj) {
            var me = this,
                dom = me.dom,
                beforeAnim,
                box = me.getBox(),
                originalStyles = me.getStyle([
                    'width',
                    'height',
                    'left',
                    'right',
                    'top',
                    'bottom',
                    'position',
                    'z-index',
                    'font-size',
                    'opacity'
                ], true);
            obj = Ext.applyIf(obj || {}, {
                easing: 'ease-out',
                duration: 500,
                useDisplay: false
            });
            beforeAnim = function() {
                var el = Ext.fly(dom, '_anim');
                el.clearOpacity();
                el.show();
                this.to = {
                    width: box.width * 2,
                    height: box.height * 2,
                    x: box.x - (box.width / 2),
                    y: box.y - (box.height / 2),
                    opacity: 0,
                    fontSize: '200%'
                };
                this.on('afteranimate', function() {
                    var el = Ext.fly(dom, '_anim');
                    if (el) {
                        if (obj.useDisplay) {
                            el.setDisplayed(false);
                        } else {
                            el.hide();
                        }
                        el.setStyle(originalStyles);
                        Ext.callback(obj.callback, obj.scope);
                    }
                });
            };
            me.animate({
                duration: obj.duration,
                easing: obj.easing,
                listeners: {
                    beforeanimate: {
                        fn: beforeAnim
                    }
                }
            });
            return me;
        },
        
        selectable: function() {
            var me = this;
            
            
            me.dom.unselectable = '';
            me.removeCls(Element.unselectableCls);
            me.addCls(Element.selectableCls);
            return me;
        },
        
        
        
        
        
        
        setCapture: function() {
            var dom = this.dom;
            if (Ext.isIE9m && dom.setCapture) {
                dom.setCapture();
            }
        },
        
        setDisplayed: function(value) {
            var me = this;
            if (typeof value === "boolean") {
                value = value ? getDisplay(me) : NONE;
            }
            me.setStyle(DISPLAY, value);
            if (me.shadow || me.shim) {
                me.setUnderlaysVisible(value !== NONE);
            }
            return me;
        },
        
        setHeight: function(height, animate) {
            var me = this;
            if (!animate || !me.anim) {
                me.callParent(arguments);
            } else {
                if (!Ext.isObject(animate)) {
                    animate = {};
                }
                me.animate(Ext.applyIf({
                    to: {
                        height: height
                    }
                }, animate));
            }
            return me;
        },
        
        setHorizontal: function() {
            var me = this,
                cls = me.verticalCls;
            delete me.vertical;
            if (cls) {
                delete me.verticalCls;
                me.removeCls(cls);
            }
            
            delete me.setWidth;
            delete me.setHeight;
            if (!Ext.isIE8) {
                delete me.getWidth;
                delete me.getHeight;
            }
            
            delete me.styleHooks;
        },
        
        updateText: function(text) {
            var me = this,
                dom, textNode;
            if (dom) {
                textNode = dom.firstChild;
                if (!textNode || (textNode.nodeType !== 3 || textNode.nextSibling)) {
                    textNode = DOC.createTextNode();
                    me.empty();
                    dom.appendChild(textNode);
                }
                if (text) {
                    textNode.data = text;
                }
            }
        },
        
        setHtml: function(html, loadScripts, callback) {
            var me = this,
                id, dom, interval;
            if (!me.dom) {
                return me;
            }
            html = html || '';
            dom = me.dom;
            if (loadScripts !== true) {
                dom.innerHTML = html;
                Ext.callback(callback, me);
                return me;
            }
            id = Ext.id();
            html += '<span id="' + id + '" role="presentation"></span>';
            interval = Ext.interval(function() {
                var hd, match, attrs, srcMatch, typeMatch, el, s;
                if (!(el = DOC.getElementById(id))) {
                    return false;
                }
                clearInterval(interval);
                Ext.removeNode(el);
                hd = Ext.getHead().dom;
                while ((match = scriptTagRe.exec(html))) {
                    attrs = match[1];
                    srcMatch = attrs ? attrs.match(srcRe) : false;
                    if (srcMatch && srcMatch[2]) {
                        s = DOC.createElement("script");
                        s.src = srcMatch[2];
                        typeMatch = attrs.match(typeRe);
                        if (typeMatch && typeMatch[2]) {
                            s.type = typeMatch[2];
                        }
                        hd.appendChild(s);
                    } else if (match[2] && match[2].length > 0) {
                        if (WIN.execScript) {
                            WIN.execScript(match[2]);
                        } else {
                            WIN.eval(match[2]);
                        }
                    }
                }
                Ext.callback(callback, me);
            }, 20);
            dom.innerHTML = html.replace(replaceScriptTagRe, '');
            return me;
        },
        
        setOpacity: function(opacity, animate) {
            var me = this;
            if (!me.dom) {
                return me;
            }
            if (!animate || !me.anim) {
                me.setStyle('opacity', opacity);
            } else {
                if (typeof animate != 'object') {
                    animate = {
                        duration: 350,
                        easing: 'ease-in'
                    };
                }
                me.animate(Ext.applyIf({
                    to: {
                        opacity: opacity
                    }
                }, animate));
            }
            return me;
        },
        
        setPositioning: function(pc) {
            return this.setStyle(pc);
        },
        
        setVertical: function(angle, cls) {
            var me = this,
                proto = Element.prototype;
            me.vertical = true;
            if (cls) {
                me.addCls(me.verticalCls = cls);
            }
            me.setWidth = proto.setHeight;
            me.setHeight = proto.setWidth;
            if (!Ext.isIE8) {
                
                
                
                
                me.getWidth = proto.getHeight;
                me.getHeight = proto.getWidth;
            }
            
            me.styleHooks = (angle === 270) ? proto.verticalStyleHooks270 : proto.verticalStyleHooks90;
        },
        
        setSize: function(width, height, animate) {
            var me = this;
            if (Ext.isObject(width)) {
                
                animate = height;
                height = width.height;
                width = width.width;
            }
            if (!animate || !me.anim) {
                me.dom.style.width = Element.addUnits(width);
                me.dom.style.height = Element.addUnits(height);
                if (me.shadow || me.shim) {
                    me.syncUnderlays();
                }
            } else {
                if (animate === true) {
                    animate = {};
                }
                me.animate(Ext.applyIf({
                    to: {
                        width: width,
                        height: height
                    }
                }, animate));
            }
            return me;
        },
        
        setVisible: function(visible, animate) {
            var me = this,
                dom = me.dom,
                visMode = getVisMode(me);
            
            if (typeof animate === 'string') {
                switch (animate) {
                    case DISPLAY:
                        visMode = Element.DISPLAY;
                        break;
                    case VISIBILITY:
                        visMode = Element.VISIBILITY;
                        break;
                    case OFFSETS:
                        visMode = Element.OFFSETS;
                        break;
                }
                me.setVisibilityMode(visMode);
                animate = false;
            }
            if (!animate || !me.anim) {
                if (visMode === Element.DISPLAY) {
                    return me.setDisplayed(visible);
                } else if (visMode === Element.OFFSETS) {
                    me[visible ? 'removeCls' : 'addCls'](OFFSETCLASS);
                } else if (visMode === Element.VISIBILITY) {
                    me.fixDisplay();
                    
                    dom.style.visibility = visible ? '' : HIDDEN;
                }
            } else {
                
                if (visible) {
                    me.setOpacity(0.01);
                    me.setVisible(true);
                }
                if (!Ext.isObject(animate)) {
                    animate = {
                        duration: 350,
                        easing: 'ease-in'
                    };
                }
                me.animate(Ext.applyIf({
                    callback: function() {
                        if (!visible) {
                            
                            Ext.fly(dom).setVisible(false).setOpacity(1);
                        }
                    },
                    to: {
                        opacity: (visible) ? 1 : 0
                    }
                }, animate));
            }
            me.getData()[ISVISIBLE] = visible;
            if (me.shadow || me.shim) {
                me.setUnderlaysVisible(visible);
            }
            return me;
        },
        
        setWidth: function(width, animate) {
            var me = this;
            if (!animate || !me.anim) {
                me.callParent(arguments);
            } else {
                if (!Ext.isObject(animate)) {
                    animate = {};
                }
                me.animate(Ext.applyIf({
                    to: {
                        width: width
                    }
                }, animate));
            }
            return me;
        },
        setX: function(x, animate) {
            return this.setXY([
                x,
                this.getY()
            ], animate);
        },
        setXY: function(xy, animate) {
            var me = this;
            if (!animate || !me.anim) {
                me.callParent([
                    xy
                ]);
            } else {
                if (!Ext.isObject(animate)) {
                    animate = {};
                }
                me.animate(Ext.applyIf({
                    to: {
                        x: xy[0],
                        y: xy[1]
                    }
                }, animate));
            }
            return this;
        },
        setY: function(y, animate) {
            return this.setXY([
                this.getX(),
                y
            ], animate);
        },
        
        show: function(animate) {
            
            if (typeof animate === 'string') {
                this.setVisible(true, animate);
                return this;
            }
            this.setVisible(true, this.anim(animate));
            return this;
        },
        
        slideIn: function(anchor, obj, slideOut) {
            var me = this,
                dom = me.dom,
                elStyle = dom.style,
                beforeAnim, wrapAnim, restoreScroll, wrapDomParentNode;
            anchor = anchor || "t";
            obj = obj || {};
            beforeAnim = function() {
                var animScope = this,
                    listeners = obj.listeners,
                    el = Ext.fly(dom, '_anim'),
                    box, originalStyles, anim, wrap;
                if (!slideOut) {
                    el.fixDisplay();
                }
                box = el.getBox();
                if ((anchor == 't' || anchor == 'b') && box.height === 0) {
                    box.height = dom.scrollHeight;
                } else if ((anchor == 'l' || anchor == 'r') && box.width === 0) {
                    box.width = dom.scrollWidth;
                }
                originalStyles = el.getStyle([
                    'width',
                    'height',
                    'left',
                    'right',
                    'top',
                    'bottom',
                    'position',
                    'z-index'
                ], true);
                el.setSize(box.width, box.height);
                
                if (obj.preserveScroll) {
                    restoreScroll = el.cacheScrollValues();
                }
                wrap = el.wrap({
                    role: 'presentation',
                    id: Ext.id() + '-anim-wrap-for-' + el.dom.id,
                    style: {
                        visibility: slideOut ? 'visible' : 'hidden'
                    }
                });
                wrapDomParentNode = wrap.dom.parentNode;
                wrap.setPositioning(el.getPositioning(true));
                if (wrap.isStyle('position', 'static')) {
                    wrap.position('relative');
                }
                el.clearPositioning('auto');
                wrap.clip();
                
                if (restoreScroll) {
                    restoreScroll();
                }
                
                
                
                el.setStyle({
                    visibility: '',
                    position: 'absolute'
                });
                if (slideOut) {
                    wrap.setSize(box.width, box.height);
                }
                switch (anchor) {
                    case 't':
                        anim = {
                            from: {
                                width: box.width + 'px',
                                height: '0px'
                            },
                            to: {
                                width: box.width + 'px',
                                height: box.height + 'px'
                            }
                        };
                        elStyle.bottom = '0px';
                        break;
                    case 'l':
                        anim = {
                            from: {
                                width: '0px',
                                height: box.height + 'px'
                            },
                            to: {
                                width: box.width + 'px',
                                height: box.height + 'px'
                            }
                        };
                        me.anchorAnimX(anchor);
                        break;
                    case 'r':
                        anim = {
                            from: {
                                x: box.x + box.width,
                                width: '0px',
                                height: box.height + 'px'
                            },
                            to: {
                                x: box.x,
                                width: box.width + 'px',
                                height: box.height + 'px'
                            }
                        };
                        me.anchorAnimX(anchor);
                        break;
                    case 'b':
                        anim = {
                            from: {
                                y: box.y + box.height,
                                width: box.width + 'px',
                                height: '0px'
                            },
                            to: {
                                y: box.y,
                                width: box.width + 'px',
                                height: box.height + 'px'
                            }
                        };
                        break;
                    case 'tl':
                        anim = {
                            from: {
                                x: box.x,
                                y: box.y,
                                width: '0px',
                                height: '0px'
                            },
                            to: {
                                width: box.width + 'px',
                                height: box.height + 'px'
                            }
                        };
                        elStyle.bottom = '0px';
                        me.anchorAnimX('l');
                        break;
                    case 'bl':
                        anim = {
                            from: {
                                y: box.y + box.height,
                                width: '0px',
                                height: '0px'
                            },
                            to: {
                                y: box.y,
                                width: box.width + 'px',
                                height: box.height + 'px'
                            }
                        };
                        me.anchorAnimX('l');
                        break;
                    case 'br':
                        anim = {
                            from: {
                                x: box.x + box.width,
                                y: box.y + box.height,
                                width: '0px',
                                height: '0px'
                            },
                            to: {
                                x: box.x,
                                y: box.y,
                                width: box.width + 'px',
                                height: box.height + 'px'
                            }
                        };
                        me.anchorAnimX('r');
                        break;
                    case 'tr':
                        anim = {
                            from: {
                                x: box.x + box.width,
                                width: '0px',
                                height: '0px'
                            },
                            to: {
                                x: box.x,
                                width: box.width + 'px',
                                height: box.height + 'px'
                            }
                        };
                        elStyle.bottom = '0px';
                        me.anchorAnimX('r');
                        break;
                }
                wrap.show();
                wrapAnim = Ext.apply({}, obj);
                delete wrapAnim.listeners;
                wrapAnim = new Ext.fx.Anim(Ext.applyIf(wrapAnim, {
                    target: wrap,
                    duration: 500,
                    easing: 'ease-out',
                    from: slideOut ? anim.to : anim.from,
                    to: slideOut ? anim.from : anim.to
                }));
                
                wrapAnim.on('afteranimate', function() {
                    var el = Ext.fly(dom, '_anim');
                    el.setStyle(originalStyles);
                    if (slideOut) {
                        if (obj.useDisplay) {
                            el.setDisplayed(false);
                        } else {
                            el.hide();
                        }
                    }
                    if (wrap.dom) {
                        if (wrap.dom.parentNode) {
                            wrap.dom.parentNode.insertBefore(el.dom, wrap.dom);
                        } else {
                            wrapDomParentNode.appendChild(el.dom);
                        }
                        wrap.destroy();
                    }
                    
                    if (restoreScroll) {
                        restoreScroll();
                    }
                    
                    animScope.end();
                });
                
                if (listeners) {
                    wrapAnim.on(listeners);
                }
            };
            me.animate({
                
                duration: obj.duration ? Math.max(obj.duration, 500) * 2 : 1000,
                listeners: {
                    beforeanimate: beforeAnim
                }
            });
            
            return me;
        },
        
        slideOut: function(anchor, o) {
            return this.slideIn(anchor, o, true);
        },
        
        swallowEvent: function(eventName, preventDefault) {
            var me = this,
                e, eLen,
                fn = function(e) {
                    e.stopPropagation();
                    if (preventDefault) {
                        e.preventDefault();
                    }
                };
            if (Ext.isArray(eventName)) {
                eLen = eventName.length;
                for (e = 0; e < eLen; e++) {
                    me.on(eventName[e], fn);
                }
                return me;
            }
            me.on(eventName, fn);
            return me;
        },
        
        switchOff: function(obj) {
            var me = this,
                dom = me.dom,
                beforeAnim;
            obj = Ext.applyIf(obj || {}, {
                easing: 'ease-in',
                duration: 500,
                remove: false,
                useDisplay: false
            });
            beforeAnim = function() {
                var el = Ext.fly(dom, '_anim'),
                    animScope = this,
                    size = el.getSize(),
                    xy = el.getXY(),
                    keyframe, position;
                el.clearOpacity();
                el.clip();
                position = el.getPositioning();
                keyframe = new Ext.fx.Animator({
                    target: dom,
                    duration: obj.duration,
                    easing: obj.easing,
                    keyframes: {
                        33: {
                            opacity: 0.3
                        },
                        66: {
                            height: 1,
                            y: xy[1] + size.height / 2
                        },
                        100: {
                            width: 1,
                            x: xy[0] + size.width / 2
                        }
                    }
                });
                keyframe.on('afteranimate', function() {
                    var el = Ext.fly(dom, '_anim');
                    if (obj.useDisplay) {
                        el.setDisplayed(false);
                    } else {
                        el.hide();
                    }
                    el.clearOpacity();
                    el.setPositioning(position);
                    el.setSize(size);
                    
                    animScope.end();
                });
            };
            me.animate({
                
                duration: (Math.max(obj.duration, 500) * 2),
                listeners: {
                    beforeanimate: {
                        fn: beforeAnim
                    }
                },
                callback: obj.callback,
                scope: obj.scope
            });
            return me;
        },
        
        syncContent: function(source) {
            source = Ext.getDom(source);
            var sourceNodes = source.childNodes,
                sourceLen = sourceNodes.length,
                dest = this.dom,
                destNodes = dest.childNodes,
                destLen = destNodes.length,
                i, destNode, sourceNode, nodeType, newAttrs, attLen, attName,
                elData = dest._extData;
            
            
            
            
            if (Ext.isIE9m && dest.mergeAttributes) {
                dest.mergeAttributes(source, true);
                
                
                dest.src = source.src;
            } else {
                newAttrs = source.attributes;
                attLen = newAttrs.length;
                for (i = 0; i < attLen; i++) {
                    attName = newAttrs[i].name;
                    if (attName !== 'id') {
                        dest.setAttribute(attName, newAttrs[i].value);
                    }
                }
            }
            
            if (elData) {
                elData.isSynchronized = false;
            }
            
            if (sourceLen !== destLen) {
                dest.innerHTML = source.innerHTML;
                return;
            }
            
            
            for (i = 0; i < sourceLen; i++) {
                sourceNode = sourceNodes[i];
                destNode = destNodes[i];
                nodeType = sourceNode.nodeType;
                
                if (nodeType !== destNode.nodeType || (nodeType === 1 && sourceNode.tagName !== destNode.tagName)) {
                    dest.innerHTML = source.innerHTML;
                    return;
                }
                
                if (nodeType === 3) {
                    destNode.data = sourceNode.data;
                } else 
                {
                    if (sourceNode.id && destNode.id !== sourceNode.id) {
                        destNode.id = sourceNode.id;
                    }
                    destNode.style.cssText = sourceNode.style.cssText;
                    destNode.className = sourceNode.className;
                    Ext.fly(destNode, '_syncContent').syncContent(sourceNode);
                }
            }
        },
        
        toggle: function(animate) {
            var me = this;
            me.setVisible(!me.isVisible(), me.anim(animate));
            return me;
        },
        
        unmask: function() {
            var me = this,
                data = me.getData(),
                maskEl = data.maskEl,
                style;
            if (maskEl) {
                style = maskEl.dom.style;
                
                if (style.clearExpression) {
                    style.clearExpression('width');
                    style.clearExpression('height');
                }
                if (maskEl) {
                    maskEl.destroy();
                    delete data.maskEl;
                }
                me.removeCls([
                    XMASKED,
                    XMASKEDRELATIVE
                ]);
            }
            me.restoreChildrenTabbableState();
            if (me.dom !== DOC.body) {
                me.restoreTabbableState();
            }
        },
        
        unclip: function() {
            var me = this,
                data = me.getData(),
                clip;
            if (data[ISCLIPPED]) {
                data[ISCLIPPED] = false;
                clip = data[ORIGINALCLIP];
                if (clip.o) {
                    me.setStyle(OVERFLOW, clip.o);
                }
                if (clip.x) {
                    me.setStyle(OVERFLOWX, clip.x);
                }
                if (clip.y) {
                    me.setStyle(OVERFLOWY, clip.y);
                }
            }
            return me;
        },
        translate: function(x, y, z) {
            if (Ext.supports.CssTransforms && !Ext.isIE9m) {
                this.callParent(arguments);
            } else {
                if (x != null) {
                    this.dom.style.left = x + 'px';
                }
                if (y != null) {
                    this.dom.style.top = y + 'px';
                }
            }
        },
        
        unselectable: function() {
            
            
            
            
            
            
            
            var me = this;
            
            
            
            
            
            if (Ext.isOpera) {
                me.dom.unselectable = 'on';
            }
            
            
            
            
            
            
            
            
            
            me.removeCls(Element.selectableCls);
            me.addCls(Element.unselectableCls);
            return me;
        },
        privates: {
            
            needsTabIndex: function() {
                var dom = this.dom,
                    nodeName, isFocusable;
                if (dom) {
                    nodeName = dom.nodeName;
                    
                    
                    isFocusable = !!Ext.Element.naturallyFocusableTags[nodeName] || ((nodeName === 'A' || nodeName === 'LINK') && !!dom.href) || dom.getAttribute('tabindex') != null || dom.contentEditable === 'true';
                    
                    return !isFocusable;
                }
            },
            
            
            
            
            findTabbableElements: function(asDom, selector, 
            limit, backward) {
                asDom = asDom != undefined ? asDom : true;
                var me = this,
                    selection;
                selection = me.selectTabbableElements(asDom, selector, limit, backward);
                if (me.isTabbable()) {
                    selection.unshift(asDom ? me.dom : me);
                }
                return selection;
            },
            
            selectTabbableElements: function(asDom, selector, 
            limit, backward) {
                var selection = [],
                    nodes, node, el, i, len, to, step, tabIndex;
                asDom = asDom != undefined ? asDom : true;
                nodes = this.dom.querySelectorAll(selector || Ext.Element.tabbableSelector);
                len = nodes.length;
                if (!len) {
                    return selection;
                }
                if (backward) {
                    i = len - 1;
                    to = 0;
                    step = -1;
                } else {
                    i = 0;
                    to = len - 1;
                    step = 1;
                }
                
                
                
                for (; ; i += step) {
                    if ((step > 0 && i > to) || (step < 0 && i < to)) {
                        break;
                    }
                    node = nodes[i];
                    
                    
                    
                    
                    
                    
                    tabIndex = node.getAttribute('tabindex') - 0;
                    
                    
                    
                    
                    
                    
                    if (!(tabIndex < 0)) {
                        el = asDom ? Ext.fly(node) : Ext.get(node);
                        if (el.isTabbable()) {
                            selection.push(asDom ? node : el);
                        }
                    }
                    if (selection.length >= limit) {
                        return selection;
                    }
                }
                return selection;
            },
            
            selectFirstTabbableElement: function(asDom, selector) {
                var els = this.selectTabbableElements(asDom, selector, 1, false);
                return els[0];
            },
            
            selectLastTabbableElement: function(asDom, selector) {
                var el = this.selectTabbableElements(true, selector, 1, true)[0];
                return (asDom !== false) ? el : Ext.get(el);
            },
            
            saveTabbableState: function(attribute) {
                var tabbableSavedFlagAttribute = Ext.Element.tabbableSavedFlagAttribute,
                    dom = this.dom;
                
                if (dom.hasAttribute(tabbableSavedFlagAttribute)) {
                    return;
                }
                attribute = attribute || Ext.Element.tabbableSavedAttribute;
                
                
                if (dom.hasAttribute('tabindex')) {
                    dom.setAttribute(attribute, dom.getAttribute('tabindex'));
                } else 
                {
                    dom.setAttribute(attribute, 'none');
                }
                
                
                dom.setAttribute('tabindex', -1);
                dom.setAttribute(tabbableSavedFlagAttribute, true);
                return this;
            },
            
            restoreTabbableState: function(attribute) {
                var tabbableSavedFlagAttribute = Ext.Element.tabbableSavedFlagAttribute,
                    dom = this.dom,
                    idx;
                attribute = attribute || Ext.Element.tabbableSavedAttribute;
                if (!dom.hasAttribute(tabbableSavedFlagAttribute) || !dom.hasAttribute(attribute)) {
                    return;
                }
                idx = dom.getAttribute(attribute);
                
                if (idx === 'none') {
                    dom.removeAttribute('tabindex');
                } else {
                    dom.setAttribute('tabindex', idx);
                }
                dom.removeAttribute(attribute);
                dom.removeAttribute(tabbableSavedFlagAttribute);
                return this;
            },
            
            saveChildrenTabbableState: function(attribute) {
                var children, child, i, len;
                if (this.dom) {
                    children = this.selectTabbableElements();
                    for (i = 0 , len = children.length; i < len; i++) {
                        child = Ext.fly(children[i]);
                        child.saveTabbableState(attribute);
                    }
                }
                return children;
            },
            
            restoreChildrenTabbableState: function(attribute, children) {
                var child, i, len;
                if (this.dom) {
                    attribute = attribute || Ext.Element.tabbableSavedAttribute;
                    children = children || this.dom.querySelectorAll('[' + attribute + ']');
                    for (i = 0 , len = children.length; i < len; i++) {
                        child = Ext.fly(children[i]);
                        child.restoreTabbableState(attribute);
                    }
                }
                return children;
            }
        },
        deprecated: {
            '4.0': {
                methods: {
                    
                    pause: function(ms) {
                        var me = this;
                        Ext.fx.Manager.setFxDefaults(me.id, {
                            delay: ms
                        });
                        return me;
                    },
                    
                    scale: function(w, h, o) {
                        this.animate(Ext.apply({}, o, {
                            width: w,
                            height: h
                        }));
                        return this;
                    },
                    
                    shift: function(config) {
                        this.animate(config);
                        return this;
                    }
                }
            },
            '4.2': {
                methods: {
                    
                    moveTo: function(x, y, animate) {
                        return this.setXY([
                            x,
                            y
                        ], animate);
                    },
                    
                    setBounds: function(x, y, width, height, animate) {
                        return this.setBox({
                            x: x,
                            y: y,
                            width: width,
                            height: height
                        }, animate);
                    },
                    
                    setLeftTop: function(left, top) {
                        var me = this,
                            style = me.dom.style;
                        style.left = Element.addUnits(left);
                        style.top = Element.addUnits(top);
                        if (me.shadow || me.shim) {
                            me.syncUnderlays();
                        }
                        return me;
                    },
                    
                    setLocation: function(x, y, animate) {
                        return this.setXY([
                            x,
                            y
                        ], animate);
                    }
                }
            },
            '5.0': {
                methods: {
                    
                    getAttributeNS: function(namespace, name) {
                        return this.getAttribute(name, namespace);
                    },
                    
                    getCenterXY: function() {
                        return this.getAlignToXY(DOC, 'c-c');
                    },
                    
                    getComputedHeight: function() {
                        return Math.max(this.dom.offsetHeight, this.dom.clientHeight) || parseFloat(this.getStyle(HEIGHT)) || 0;
                    },
                    
                    getComputedWidth: function() {
                        return Math.max(this.dom.offsetWidth, this.dom.clientWidth) || parseFloat(this.getStyle(WIDTH)) || 0;
                    },
                    
                    getStyleSize: function() {
                        var me = this,
                            d = this.dom,
                            isDoc = (d === DOC || d === DOC.body),
                            s, w, h;
                        
                        if (isDoc) {
                            return {
                                width: Element.getViewportWidth(),
                                height: Element.getViewportHeight()
                            };
                        }
                        s = me.getStyle([
                            'height',
                            'width'
                        ], true);
                        
                        
                        if (s.width && s.width !== 'auto') {
                            w = parseFloat(s.width);
                        }
                        
                        if (s.height && s.height !== 'auto') {
                            h = parseFloat(s.height);
                        }
                        
                        return {
                            width: w || me.getWidth(true),
                            height: h || me.getHeight(true)
                        };
                    },
                    
                    isBorderBox: function() {
                        return true;
                    },
                    
                    isDisplayed: function() {
                        return !this.isStyle('display', 'none');
                    },
                    
                    focusable: 'isFocusable'
                }
            }
        }
    };
})(), function() {
    var Element = Ext.dom.Element,
        proto = Element.prototype,
        useDocForId = !Ext.isIE8,
        DOC = document,
        view = DOC.defaultView,
        opacityRe = /alpha\(opacity=(.*)\)/i,
        trimRe = /^\s+|\s+$/g,
        styleHooks = proto.styleHooks,
        supports = Ext.supports,
        removeNode, garbageBin, verticalStyleHooks90, verticalStyleHooks270, edges, k, edge, borderWidth;
    proto._init(Element);
    delete proto._init;
    Ext.plainTableCls = Ext.baseCSSPrefix + 'table-plain';
    Ext.plainListCls = Ext.baseCSSPrefix + 'list-plain';
    
    if (Ext.CompositeElementLite) {
        Ext.CompositeElementLite.importElementMethods();
    }
    styleHooks.opacity = {
        name: 'opacity',
        afterSet: function(dom, value, el) {
            var shadow = el.shadow;
            if (shadow) {
                shadow.setOpacity(value);
            }
        }
    };
    if (!supports.Opacity && Ext.isIE) {
        Ext.apply(styleHooks.opacity, {
            get: function(dom) {
                var filter = dom.style.filter,
                    match, opacity;
                if (filter.match) {
                    match = filter.match(opacityRe);
                    if (match) {
                        opacity = parseFloat(match[1]);
                        if (!isNaN(opacity)) {
                            return opacity ? opacity / 100 : 0;
                        }
                    }
                }
                return 1;
            },
            set: function(dom, value) {
                var style = dom.style,
                    val = style.filter.replace(opacityRe, '').replace(trimRe, '');
                style.zoom = 1;
                
                
                if (typeof (value) === 'number' && value >= 0 && value < 1) {
                    value *= 100;
                    style.filter = val + (val.length ? ' ' : '') + 'alpha(opacity=' + value + ')';
                } else {
                    style.filter = val;
                }
            }
        });
    }
    if (!supports.matchesSelector) {
        
        var simpleSelectorRe = /^([a-z]+|\*)?(?:\.([a-z][a-z\-_0-9]*))?$/i,
            dashRe = /\-/g,
            fragment,
            classMatcher = function(tag, cls) {
                var classRe = new RegExp('(?:^|\\s+)' + cls.replace(dashRe, '\\-') + '(?:\\s+|$)');
                if (tag && tag !== '*') {
                    tag = tag.toUpperCase();
                    return function(el) {
                        return el.tagName === tag && classRe.test(el.className);
                    };
                }
                return function(el) {
                    return classRe.test(el.className);
                };
            },
            tagMatcher = function(tag) {
                tag = tag.toUpperCase();
                return function(el) {
                    return el.tagName === tag;
                };
            },
            cache = {};
        proto.matcherCache = cache;
        proto.is = function(selector) {
            
            if (!selector) {
                return true;
            }
            var dom = this.dom,
                cls, match, testFn, root, isOrphan, is, tag;
            
            if (dom.nodeType !== 1) {
                return false;
            }
            if (!(testFn = Ext.isFunction(selector) ? selector : cache[selector])) {
                if (!(match = selector.match(simpleSelectorRe))) {
                    
                    root = dom.parentNode;
                    if (!root) {
                        isOrphan = true;
                        root = fragment || (fragment = DOC.createDocumentFragment());
                        fragment.appendChild(dom);
                    }
                    is = Ext.Array.indexOf(Ext.fly(root, '_is').query(selector), dom) !== -1;
                    if (isOrphan) {
                        fragment.removeChild(dom);
                    }
                    return is;
                }
                tag = match[1];
                cls = match[2];
                cache[selector] = testFn = cls ? classMatcher(tag, cls) : tagMatcher(tag);
            }
            return testFn(dom);
        };
    }
    
    if (!view || !view.getComputedStyle) {
        proto.getStyle = function(property, inline) {
            var me = this,
                dom = me.dom,
                multiple = typeof property !== 'string',
                prop = property,
                props = prop,
                len = 1,
                isInline = inline,
                styleHooks = me.styleHooks,
                camel, domStyle, values, hook, out, style, i;
            if (multiple) {
                values = {};
                prop = props[0];
                i = 0;
                if (!(len = props.length)) {
                    return values;
                }
            }
            if (!dom || dom.documentElement) {
                return values || '';
            }
            domStyle = dom.style;
            if (inline) {
                style = domStyle;
            } else {
                style = dom.currentStyle;
                
                if (!style) {
                    isInline = true;
                    style = domStyle;
                }
            }
            do {
                hook = styleHooks[prop];
                if (!hook) {
                    styleHooks[prop] = hook = {
                        name: Element.normalize(prop)
                    };
                }
                if (hook.get) {
                    out = hook.get(dom, me, isInline, style);
                } else {
                    camel = hook.name;
                    out = style[camel];
                }
                if (!multiple) {
                    return out;
                }
                values[prop] = out;
                prop = props[++i];
            } while (i < len);
            return values;
        };
    }
    
    if (Ext.isIE8) {
        function getBorderWidth(dom, el, inline, style) {
            if (style[this.styleName] === 'none') {
                return '0px';
            }
            return style[this.name];
        }
        edges = [
            'Top',
            'Right',
            'Bottom',
            'Left'
        ];
        k = edges.length;
        while (k--) {
            edge = edges[k];
            borderWidth = 'border' + edge + 'Width';
            styleHooks['border-' + edge.toLowerCase() + '-width'] = styleHooks[borderWidth] = {
                name: borderWidth,
                styleName: 'border' + edge + 'Style',
                get: getBorderWidth
            };
        }
    }
    Ext.apply(Ext, {
        
        enableGarbageCollector: true,
        
        
        isBorderBox: true,
        
        useShims: false,
        
        getDetachedBody: function() {
            var detachedEl = Ext.detachedBodyEl;
            if (!detachedEl) {
                detachedEl = DOC.createElement('div');
                Ext.detachedBodyEl = detachedEl = new Ext.dom.Fly(detachedEl);
                detachedEl.isDetachedBody = true;
            }
            return detachedEl;
        },
        getElementById: function(id) {
            var el = DOC.getElementById(id),
                detachedBodyEl;
            if (!el && (detachedBodyEl = Ext.detachedBodyEl)) {
                el = detachedBodyEl.dom.querySelector(Ext.makeIdSelector(id));
            }
            return el;
        },
        
        addBehaviors: function(o) {
            if (!Ext.isReady) {
                Ext.onReady(function() {
                    Ext.addBehaviors(o);
                });
            } else {
                var cache = {},
                    
                    parts, b, s;
                for (b in o) {
                    if ((parts = b.split('@'))[1]) {
                        
                        s = parts[0];
                        if (!cache[s]) {
                            cache[s] = Ext.fly(document).select(s, true);
                        }
                        cache[s].on(parts[1], o[b]);
                    }
                }
                cache = null;
            }
        }
    });
    if (Ext.isIE8) {
        
        removeNode = Ext.removeNode;
        Ext.removeNode = function(node) {
            removeNode(node);
            
            
            garbageBin = garbageBin || DOC.createElement('div');
            garbageBin.appendChild(node);
            garbageBin.innerHTML = '';
        };
    }
    if (Ext.isIE9m) {
        Ext.getElementById = function(id) {
            var el = DOC.getElementById(id),
                detachedBodyEl;
            if (!el && (detachedBodyEl = Ext.detachedBodyEl)) {
                el = detachedBodyEl.dom.all[id];
            }
            return el;
        };
        proto.getById = function(id, asDom) {
            var dom = this.dom,
                ret = null,
                entry, el;
            if (dom) {
                
                
                el = (useDocForId && DOC.getElementById(id)) || dom.all[id];
                if (el) {
                    if (asDom) {
                        ret = el;
                    } else {
                        
                        
                        entry = Ext.cache[id];
                        if (entry) {
                            if (entry.skipGarbageCollection || !Ext.isGarbage(entry.dom)) {
                                ret = entry;
                            } else {
                                Ext.Error.raise("Stale Element with id '" + el.id + "' found in Element cache. " + "Make sure to clean up Element instances using destroy()");
                                entry.destroy();
                            }
                        }
                        ret = ret || new Ext.Element(el);
                    }
                }
            }
            return ret;
        };
    } else if (!DOC.querySelector) {
        Ext.getDetachedBody = Ext.getBody;
        Ext.getElementById = function(id) {
            return DOC.getElementById(id);
        };
        proto.getById = function(id, asDom) {
            var dom = DOC.getElementById(id);
            return asDom ? dom : (dom ? Ext.get(dom) : null);
        };
    }
    if (Ext.isIE && !(Ext.isIE9p && DOC.documentMode >= 9)) {
        
        
        
        
        
        
        
        
        
        proto.getAttribute = function(name, ns) {
            var d = this.dom,
                type;
            if (ns) {
                type = typeof d[ns + ":" + name];
                if (type != 'undefined' && type != 'unknown') {
                    return d[ns + ":" + name] || null;
                }
                return null;
            }
            if (name === "for") {
                name = "htmlFor";
            }
            return d[name] || null;
        };
    }
    Ext.onReady(function() {
        var transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
            bodyCls = [],
            
            origSetWidth = proto.setWidth,
            origSetHeight = proto.setHeight,
            origSetSize = proto.setSize,
            pxRe = /^\d+(?:\.\d*)?px$/i,
            colorStyles, i, name, camel;
        if (supports.FixedTableWidthBug) {
            
            
            
            
            
            
            
            
            
            styleHooks.width = {
                name: 'width',
                set: function(dom, value, el) {
                    var style = dom.style,
                        needsFix = el._needsTableWidthFix,
                        origDisplay = style.display;
                    if (needsFix) {
                        style.display = 'none';
                    }
                    style.width = value;
                    if (needsFix) {
                        dom.scrollWidth;
                        
                        style.display = origDisplay;
                    }
                }
            };
            proto.setWidth = function(width, animate) {
                var me = this,
                    dom = me.dom,
                    style = dom.style,
                    needsFix = me._needsTableWidthFix,
                    origDisplay = style.display;
                if (needsFix && !animate) {
                    style.display = 'none';
                }
                origSetWidth.call(me, width, animate);
                if (needsFix && !animate) {
                    dom.scrollWidth;
                    
                    style.display = origDisplay;
                }
                return me;
            };
            proto.setSize = function(width, height, animate) {
                var me = this,
                    dom = me.dom,
                    style = dom.style,
                    needsFix = me._needsTableWidthFix,
                    origDisplay = style.display;
                if (needsFix && !animate) {
                    style.display = 'none';
                }
                origSetSize.call(me, width, height, animate);
                if (needsFix && !animate) {
                    dom.scrollWidth;
                    
                    style.display = origDisplay;
                }
                return me;
            };
        }
        if (Ext.isIE8) {
            styleHooks.height = {
                name: 'height',
                set: function(dom, value, el) {
                    var component = el.component,
                        frameInfo, frameBodyStyle;
                    if (component && component._syncFrameHeight && this === component.el) {
                        frameBodyStyle = component.frameBody.dom.style;
                        if (pxRe.test(value)) {
                            frameInfo = component.getFrameInfo();
                            if (frameInfo) {
                                frameBodyStyle.height = (parseInt(value, 10) - frameInfo.height) + 'px';
                            }
                        } else if (!value || value === 'auto') {
                            frameBodyStyle.height = '';
                        }
                    }
                    dom.style.height = value;
                }
            };
            proto.setHeight = function(height, animate) {
                var component = this.component,
                    frameInfo, frameBodyStyle;
                if (component && component._syncFrameHeight && this === component.el) {
                    frameBodyStyle = component.frameBody.dom.style;
                    if (!height || height === 'auto') {
                        frameBodyStyle.height = '';
                    } else {
                        frameInfo = component.getFrameInfo();
                        if (frameInfo) {
                            frameBodyStyle.height = (height - frameInfo.height) + 'px';
                        }
                    }
                }
                return origSetHeight.call(this, height, animate);
            };
            proto.setSize = function(width, height, animate) {
                var component = this.component,
                    frameInfo, frameBodyStyle;
                if (component && component._syncFrameHeight && this === component.el) {
                    frameBodyStyle = component.frameBody.dom.style;
                    if (!height || height === 'auto') {
                        frameBodyStyle.height = '';
                    } else {
                        frameInfo = component.getFrameInfo();
                        if (frameInfo) {
                            frameBodyStyle.height = (height - frameInfo.height) + 'px';
                        }
                    }
                }
                return origSetSize.call(this, width, height, animate);
            };
        }
        
        
        
        Ext.getDoc().on('selectstart', function(ev, dom) {
            var selectableCls = Element.selectableCls,
                unselectableCls = Element.unselectableCls,
                tagName = dom && dom.tagName;
            tagName = tagName && tagName.toLowerCase();
            
            
            
            if (tagName === 'input' || tagName === 'textarea') {
                return;
            }
            
            while (dom && dom.nodeType === 1 && dom !== DOC.documentElement) {
                var el = Ext.fly(dom);
                
                if (el.hasCls(selectableCls)) {
                    return;
                }
                
                if (el.hasCls(unselectableCls)) {
                    ev.stopEvent();
                    return;
                }
                dom = dom.parentNode;
            }
        });
        function fixTransparent(dom, el, inline, style) {
            var value = style[this.name] || '';
            return transparentRe.test(value) ? 'transparent' : value;
        }
        
        function makeSelectionRestoreFn(activeEl, start, end) {
            return function() {
                activeEl.selectionStart = start;
                activeEl.selectionEnd = end;
            };
        }
        
        function getRightMarginFixCleaner(target) {
            var hasInputBug = supports.DisplayChangeInputSelectionBug,
                hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug,
                activeEl, tag, start, end;
            if (hasInputBug || hasTextAreaBug) {
                activeEl = Element.getActiveElement();
                tag = activeEl && activeEl.tagName;
                if ((hasTextAreaBug && tag === 'TEXTAREA') || (hasInputBug && tag === 'INPUT' && activeEl.type === 'text')) {
                    if (Ext.fly(target).isAncestor(activeEl)) {
                        start = activeEl.selectionStart;
                        end = activeEl.selectionEnd;
                        if (Ext.isNumber(start) && Ext.isNumber(end)) {
                            
                            
                            
                            
                            
                            return makeSelectionRestoreFn(activeEl, start, end);
                        }
                    }
                }
            }
            return Ext.emptyFn;
        }
        
        function fixRightMargin(dom, el, inline, style) {
            var result = style.marginRight,
                domStyle, display;
            
            
            if (result !== '0px') {
                domStyle = dom.style;
                display = domStyle.display;
                domStyle.display = 'inline-block';
                result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, null)).marginRight;
                domStyle.display = display;
            }
            return result;
        }
        function fixRightMarginAndInputFocus(dom, el, inline, style) {
            var result = style.marginRight,
                domStyle, cleaner, display;
            if (result !== '0px') {
                domStyle = dom.style;
                cleaner = getRightMarginFixCleaner(dom);
                display = domStyle.display;
                domStyle.display = 'inline-block';
                result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, '')).marginRight;
                domStyle.display = display;
                cleaner();
            }
            return result;
        }
        
        if (!supports.RightMargin) {
            styleHooks.marginRight = styleHooks['margin-right'] = {
                name: 'marginRight',
                
                
                get: (supports.DisplayChangeInputSelectionBug || supports.DisplayChangeTextAreaSelectionBug) ? fixRightMarginAndInputFocus : fixRightMargin
            };
        }
        if (!supports.TransparentColor) {
            colorStyles = [
                'background-color',
                'border-color',
                'color',
                'outline-color'
            ];
            for (i = colorStyles.length; i--; ) {
                name = colorStyles[i];
                camel = Element.normalize(name);
                styleHooks[name] = styleHooks[camel] = {
                    name: camel,
                    get: fixTransparent
                };
            }
        }
        
        
        proto.verticalStyleHooks90 = verticalStyleHooks90 = Ext.Object.chain(styleHooks);
        proto.verticalStyleHooks270 = verticalStyleHooks270 = Ext.Object.chain(styleHooks);
        verticalStyleHooks90.width = styleHooks.height || {
            name: 'height'
        };
        verticalStyleHooks90.height = styleHooks.width || {
            name: 'width'
        };
        verticalStyleHooks90['margin-top'] = {
            name: 'marginLeft'
        };
        verticalStyleHooks90['margin-right'] = {
            name: 'marginTop'
        };
        verticalStyleHooks90['margin-bottom'] = {
            name: 'marginRight'
        };
        verticalStyleHooks90['margin-left'] = {
            name: 'marginBottom'
        };
        verticalStyleHooks90['padding-top'] = {
            name: 'paddingLeft'
        };
        verticalStyleHooks90['padding-right'] = {
            name: 'paddingTop'
        };
        verticalStyleHooks90['padding-bottom'] = {
            name: 'paddingRight'
        };
        verticalStyleHooks90['padding-left'] = {
            name: 'paddingBottom'
        };
        verticalStyleHooks90['border-top'] = {
            name: 'borderLeft'
        };
        verticalStyleHooks90['border-right'] = {
            name: 'borderTop'
        };
        verticalStyleHooks90['border-bottom'] = {
            name: 'borderRight'
        };
        verticalStyleHooks90['border-left'] = {
            name: 'borderBottom'
        };
        verticalStyleHooks270.width = styleHooks.height || {
            name: 'height'
        };
        verticalStyleHooks270.height = styleHooks.width || {
            name: 'width'
        };
        verticalStyleHooks270['margin-top'] = {
            name: 'marginRight'
        };
        verticalStyleHooks270['margin-right'] = {
            name: 'marginBottom'
        };
        verticalStyleHooks270['margin-bottom'] = {
            name: 'marginLeft'
        };
        verticalStyleHooks270['margin-left'] = {
            name: 'marginTop'
        };
        verticalStyleHooks270['padding-top'] = {
            name: 'paddingRight'
        };
        verticalStyleHooks270['padding-right'] = {
            name: 'paddingBottom'
        };
        verticalStyleHooks270['padding-bottom'] = {
            name: 'paddingLeft'
        };
        verticalStyleHooks270['padding-left'] = {
            name: 'paddingTop'
        };
        verticalStyleHooks270['border-top'] = {
            name: 'borderRight'
        };
        verticalStyleHooks270['border-right'] = {
            name: 'borderBottom'
        };
        verticalStyleHooks270['border-bottom'] = {
            name: 'borderLeft'
        };
        verticalStyleHooks270['border-left'] = {
            name: 'borderTop'
        };
        
        if (!Ext.scopeCss) {
            bodyCls.push(Ext.baseCSSPrefix + 'body');
        }
        if (supports.Touch) {
            bodyCls.push(Ext.baseCSSPrefix + 'touch');
        }
        if (Ext.isIE && Ext.isIE9m) {
            bodyCls.push(Ext.baseCSSPrefix + 'ie', Ext.baseCSSPrefix + 'ie9m');
            
            
            
            
            
            
            
            
            
            
            
            bodyCls.push(Ext.baseCSSPrefix + 'ie8p');
            if (Ext.isIE8) {
                bodyCls.push(Ext.baseCSSPrefix + 'ie8');
            } else {
                bodyCls.push(Ext.baseCSSPrefix + 'ie9', Ext.baseCSSPrefix + 'ie9p');
            }
            if (Ext.isIE8m) {
                bodyCls.push(Ext.baseCSSPrefix + 'ie8m');
            }
        }
        if (Ext.isIE10) {
            bodyCls.push(Ext.baseCSSPrefix + 'ie10');
        }
        if (Ext.isIE11) {
            bodyCls.push(Ext.baseCSSPrefix + 'ie11');
        }
        if (Ext.isGecko) {
            bodyCls.push(Ext.baseCSSPrefix + 'gecko');
        }
        if (Ext.isOpera) {
            bodyCls.push(Ext.baseCSSPrefix + 'opera');
        }
        if (Ext.isOpera12m) {
            bodyCls.push(Ext.baseCSSPrefix + 'opera12m');
        }
        if (Ext.isWebKit) {
            bodyCls.push(Ext.baseCSSPrefix + 'webkit');
        }
        if (Ext.isSafari) {
            bodyCls.push(Ext.baseCSSPrefix + 'safari');
        }
        if (Ext.isChrome) {
            bodyCls.push(Ext.baseCSSPrefix + 'chrome');
        }
        if (Ext.isMac) {
            bodyCls.push(Ext.baseCSSPrefix + 'mac');
        }
        if (Ext.isLinux) {
            bodyCls.push(Ext.baseCSSPrefix + 'linux');
        }
        if (!supports.CSS3BorderRadius) {
            bodyCls.push(Ext.baseCSSPrefix + 'nbr');
        }
        if (!supports.CSS3LinearGradient) {
            bodyCls.push(Ext.baseCSSPrefix + 'nlg');
        }
        if (supports.Touch) {
            bodyCls.push(Ext.baseCSSPrefix + 'touch');
        }
        
        Ext.getBody().addCls(bodyCls);
    }, null, {
        priority: 1500
    });
});


Ext.define('Ext.GlobalEvents', {
    extend: 'Ext.mixin.Observable',
    alternateClassName: 'Ext.globalEvents',
    
    requires: [
        'Ext.dom.Element'
    ],
    observableType: 'global',
    singleton: true,
    
    resizeBuffer: 100,
    
    
    
    
    
    
    
    
    
    
    
    
    
    idleEventMask: {
        mousemove: 1,
        touchmove: 1,
        pointermove: 1,
        MSPointerMove: 1,
        unload: 1
    },
    constructor: function() {
        var me = this;
        me.callParent();
        Ext.onReady(function() {
            
            
            me.attachListeners();
        });
    },
    attachListeners: function() {
        Ext.get(window).on('resize', this.fireResize, this, {
            buffer: this.resizeBuffer
        });
    },
    fireResize: function() {
        var me = this,
            Element = Ext.Element,
            w = Element.getViewportWidth(),
            h = Element.getViewportHeight();
        
        if (me.curHeight !== h || me.curWidth !== w) {
            me.curHeight = h;
            me.curWidth = w;
            me.fireEvent('resize', w, h);
        }
    }
}, function(GlobalEvents) {
    
    Ext.on = function() {
        return GlobalEvents.addListener.apply(GlobalEvents, arguments);
    };
    
    Ext.un = function() {
        return GlobalEvents.removeListener.apply(GlobalEvents, arguments);
    };
});



Ext.define('Ext.overrides.GlobalEvents', {
    override: 'Ext.GlobalEvents',
    
    
    attachListeners: function() {
        this.callParent();
        Ext.getDoc().on('mousedown', this.fireMouseDown, this);
    },
    fireMouseDown: function(e) {
        this.fireEvent('mousedown', e);
    },
    deprecated: {
        5: {
            methods: {
                addListener: function(ename, fn, scope, options, order, caller, eventOptions) {
                    var name, readyFn;
                    
                    
                    if (ename === 'ready') {
                        readyFn = fn;
                    } else if (typeof ename !== 'string') {
                        for (name in ename) {
                            if (name === 'ready') {
                                readyFn = ename[name];
                            }
                        }
                    }
                    if (readyFn) {
                        Ext.log.warn("Ext.on('ready', fn) is deprecated.  Please use Ext.onReady(fn) instead.");
                        Ext.onReady(readyFn);
                    }
                    this.callParent([
                        ename,
                        fn,
                        scope,
                        options,
                        order,
                        caller,
                        eventOptions
                    ]);
                }
            }
        }
    }
});


Ext.USE_NATIVE_JSON = false;

Ext.JSON = (new (function() {
    
    
    
    var me = this,
        hasNative = window.JSON && JSON.toString() == '[object JSON]',
        useHasOwn = !!{}.hasOwnProperty,
        pad = function(n) {
            return n < 10 ? "0" + n : n;
        },
        doDecode = function(json) {
            return eval("(" + json + ')');
        },
        doEncode = function(o, newline) {
            
            if (o === null || o === undefined) {
                return "null";
            } else if (Ext.isDate(o)) {
                return me.encodeDate(o);
            } else if (Ext.isString(o)) {
                if (Ext.isMSDate(o)) {
                    return me.encodeMSDate(o);
                } else {
                    return me.encodeString(o);
                }
            } else if (typeof o == "number") {
                
                return isFinite(o) ? String(o) : "null";
            } else if (Ext.isBoolean(o)) {
                return String(o);
            }
            
            
            else if (o.toJSON) {
                return o.toJSON();
            } else if (Ext.isArray(o)) {
                return encodeArray(o, newline);
            } else if (Ext.isObject(o)) {
                return encodeObject(o, newline);
            } else if (typeof o === "function") {
                return "null";
            }
            return 'undefined';
        },
        m = {
            "\b": '\\b',
            "\t": '\\t',
            "\n": '\\n',
            "\f": '\\f',
            "\r": '\\r',
            '"': '\\"',
            "\\": '\\\\',
            '\v': '\\u000b'
        },
        
        charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
        encodeString = function(s) {
            return '"' + s.replace(charToReplace, function(a) {
                var c = m[a];
                return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"';
        },
        encodeMSDate = function(o) {
            return '"' + o + '"';
        },
        encodeArrayPretty = function(o, newline) {
            var len = o.length,
                cnewline = newline + '   ',
                sep = ',' + cnewline,
                a = [
                    "[",
                    cnewline
                ],
                
                i;
            for (i = 0; i < len; i += 1) {
                a.push(me.encodeValue(o[i], cnewline), sep);
            }
            
            a[a.length - 1] = newline + ']';
            return a.join('');
        },
        encodeObjectPretty = function(o, newline) {
            var cnewline = newline + '   ',
                sep = ',' + cnewline,
                a = [
                    "{",
                    cnewline
                ],
                
                i, val;
            for (i in o) {
                val = o[i];
                if (!useHasOwn || o.hasOwnProperty(i)) {
                    
                    if (typeof val === 'function' || val === undefined) {
                        
                        continue;
                    }
                    a.push(me.encodeValue(i) + ': ' + me.encodeValue(val, cnewline), sep);
                }
            }
            
            a[a.length - 1] = newline + '}';
            return a.join('');
        },
        encodeArray = function(o, newline) {
            if (newline) {
                return encodeArrayPretty(o, newline);
            }
            var a = [
                    "[",
                    ""
                ],
                
                len = o.length,
                i;
            for (i = 0; i < len; i += 1) {
                a.push(me.encodeValue(o[i]), ',');
            }
            
            a[a.length - 1] = ']';
            return a.join("");
        },
        encodeObject = function(o, newline) {
            if (newline) {
                return encodeObjectPretty(o, newline);
            }
            var a = [
                    "{",
                    ""
                ],
                
                i, val;
            for (i in o) {
                val = o[i];
                if (!useHasOwn || o.hasOwnProperty(i)) {
                    
                    if (typeof val === 'function' || val === undefined) {
                        
                        continue;
                    }
                    a.push(me.encodeValue(i), ":", me.encodeValue(val), ',');
                }
            }
            
            a[a.length - 1] = '}';
            return a.join("");
        };
    
    me.encodeString = encodeString;
    
    me.encodeValue = doEncode;
    
    me.encodeDate = function(o) {
        return '"' + o.getFullYear() + "-" + pad(o.getMonth() + 1) + "-" + pad(o.getDate()) + "T" + pad(o.getHours()) + ":" + pad(o.getMinutes()) + ":" + pad(o.getSeconds()) + '"';
    };
    
    me.encode = function(o) {
        
        if (hasNative && Ext.USE_NATIVE_JSON) {
            return JSON.stringify(o);
        }
        return me.encodeValue(o);
    };
    
    me.decode = function(json, safe) {
        try {
            
            if (hasNative && Ext.USE_NATIVE_JSON) {
                return JSON.parse(json);
            }
            return doDecode(json);
        } catch (e) {
            if (safe) {
                return null;
            }
            Ext.Error.raise({
                sourceClass: "Ext.JSON",
                sourceMethod: "decode",
                msg: "You're trying to decode an invalid JSON String: " + json
            });
        }
    };
    me.encodeMSDate = encodeMSDate;
    //@private Alias for backwards compatibility

    if (!Ext.util) {
        Ext.util = {};
    }
    Ext.util.JSON = me;
    
    Ext.encode = me.encode;
    
    Ext.decode = me.decode;
})());


Ext.define('Ext.util.Format', function() {
    var me;
    
    return {
        requires: [
            'Ext.Error',
            'Ext.Number',
            'Ext.String',
            'Ext.Date'
        ],
        singleton: true,
        
        defaultDateFormat: 'm/d/Y',
        
        
        thousandSeparator: ',',
        
        
        
        decimalSeparator: '.',
        
        
        
        currencyPrecision: 2,
        
        
        
        currencySign: '$',
        
        
        percentSign: '%',
        
        
        currencyAtEnd: false,
        
        stripTagsRe: /<\/?[^>]+>/gi,
        stripScriptsRe: /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        nl2brRe: /\r?\n/g,
        hashRe: /#+$/,
        allHashes: /^#+$/,
        
        formatPattern: /[\d,\.#]+/,
        
        formatCleanRe: /[^\d\.#]/g,
        
        
        I18NFormatCleanRe: null,
        
        formatFns: {},
        constructor: function() {
            me = this;
        },
        
        
        undef: function(value) {
            return value !== undefined ? value : "";
        },
        
        defaultValue: function(value, defaultValue) {
            return value !== undefined && value !== '' ? value : defaultValue;
        },
        
        substr: 'ab'.substr(-1) != 'b' ? function(value, start, length) {
            var str = String(value);
            return (start < 0) ? str.substr(Math.max(str.length + start, 0), length) : str.substr(start, length);
        } : function(value, start, length) {
            return String(value).substr(start, length);
        },
        
        lowercase: function(value) {
            return String(value).toLowerCase();
        },
        
        uppercase: function(value) {
            return String(value).toUpperCase();
        },
        
        usMoney: function(v) {
            return me.currency(v, '$', 2);
        },
        
        currency: function(v, currencySign, decimals, end) {
            var negativeSign = '',
                format = ",0",
                i = 0;
            v = v - 0;
            if (v < 0) {
                v = -v;
                negativeSign = '-';
            }
            decimals = Ext.isDefined(decimals) ? decimals : me.currencyPrecision;
            format += (decimals > 0 ? '.' : '');
            for (; i < decimals; i++) {
                format += '0';
            }
            v = me.number(v, format);
            if ((end || me.currencyAtEnd) === true) {
                return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || me.currencySign);
            } else {
                return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || me.currencySign, v);
            }
        },
        
        date: function(v, format) {
            if (!v) {
                return "";
            }
            if (!Ext.isDate(v)) {
                v = new Date(Date.parse(v));
            }
            return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
        },
        
        
        dateRenderer: function(format) {
            return function(v) {
                return me.date(v, format);
            };
        },
        
        hex: function(value, digits) {
            var s = parseInt(value || 0, 10).toString(16);
            if (digits) {
                if (digits < 0) {
                    digits = -digits;
                    if (s.length > digits) {
                        s = s.substring(s.length - digits);
                    }
                }
                while (s.length < digits) {
                    s = '0' + s;
                }
            }
            return s;
        },
        
        or: function(value, orValue) {
            return value || orValue;
        },
        
        pick: function(value, firstValue, secondValue) {
            if (Ext.isNumber(value)) {
                var ret = arguments[value + 1];
                if (ret) {
                    return ret;
                }
            }
            return value ? secondValue : firstValue;
        },
        
        stripTags: function(v) {
            return !v ? v : String(v).replace(me.stripTagsRe, "");
        },
        
        stripScripts: function(v) {
            return !v ? v : String(v).replace(me.stripScriptsRe, "");
        },
        
        fileSize: (function() {
            var byteLimit = 1024,
                kbLimit = 1048576,
                mbLimit = 1073741824;
            return function(size) {
                var out;
                if (size < byteLimit) {
                    if (size === 1) {
                        out = '1 byte';
                    } else {
                        out = size + ' bytes';
                    }
                } else if (size < kbLimit) {
                    out = (Math.round(((size * 10) / byteLimit)) / 10) + ' KB';
                } else if (size < mbLimit) {
                    out = (Math.round(((size * 10) / kbLimit)) / 10) + ' MB';
                } else {
                    out = (Math.round(((size * 10) / mbLimit)) / 10) + ' GB';
                }
                return out;
            };
        })(),
        
        math: (function() {
            var fns = {};
            return function(v, a) {
                if (!fns[a]) {
                    fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
                }
                return fns[a](v);
            };
        }()),
        
        round: function(value, precision) {
            var result = Number(value);
            if (typeof precision === 'number') {
                precision = Math.pow(10, precision);
                result = Math.round(value * precision) / precision;
            } else if (precision === undefined) {
                result = Math.round(result);
            }
            return result;
        },
        
        number: function(v, formatString) {
            if (!formatString) {
                return v;
            }
            if (isNaN(v)) {
                return '';
            }
            var formatFn = me.formatFns[formatString];
            
            
            if (!formatFn) {
                var originalFormatString = formatString,
                    comma = me.thousandSeparator,
                    decimalSeparator = me.decimalSeparator,
                    precision = 0,
                    trimPart = '',
                    hasComma, splitFormat, extraChars, trimTrailingZeroes, code, len;
                
                
                
                
                if (formatString.substr(formatString.length - 2) === '/i') {
                    
                    
                    if (!me.I18NFormatCleanRe || me.lastDecimalSeparator !== decimalSeparator) {
                        me.I18NFormatCleanRe = new RegExp('[^\\d\\' + decimalSeparator + ']','g');
                        me.lastDecimalSeparator = decimalSeparator;
                    }
                    formatString = formatString.substr(0, formatString.length - 2);
                    hasComma = formatString.indexOf(comma) !== -1;
                    splitFormat = formatString.replace(me.I18NFormatCleanRe, '').split(decimalSeparator);
                } else {
                    hasComma = formatString.indexOf(',') !== -1;
                    splitFormat = formatString.replace(me.formatCleanRe, '').split('.');
                }
                extraChars = formatString.replace(me.formatPattern, '');
                if (splitFormat.length > 2) {
                    Ext.Error.raise({
                        sourceClass: "Ext.util.Format",
                        sourceMethod: "number",
                        value: v,
                        formatString: formatString,
                        msg: "Invalid number format, should have no more than 1 decimal"
                    });
                } else if (splitFormat.length === 2) {
                    precision = splitFormat[1].length;
                    
                    trimTrailingZeroes = splitFormat[1].match(me.hashRe);
                    if (trimTrailingZeroes) {
                        len = trimTrailingZeroes[0].length;
                        
                        trimPart = 'trailingZeroes=new RegExp(Ext.String.escapeRegex(utilFormat.decimalSeparator) + "*0{0,' + len + '}$")';
                    }
                }
                
                code = [
                    'var utilFormat=Ext.util.Format,extNumber=Ext.Number,neg,absVal,fnum,parts' + (hasComma ? ',thousandSeparator,thousands=[],j,n,i' : '') + (extraChars ? ',formatString="' + formatString + '",formatPattern=/[\\d,\\.#]+/' : '') + ',trailingZeroes;' + 'return function(v){' + 'if(typeof v!=="number"&&isNaN(v=extNumber.from(v,NaN)))return"";' + 'neg=v<0;',
                    'absVal=Math.abs(v);',
                    'fnum=Ext.Number.toFixed(absVal, ' + precision + ');',
                    trimPart,
                    ';'
                ];
                if (hasComma) {
                    
                    
                    if (precision) {
                        code[code.length] = 'parts=fnum.split(".");';
                        code[code.length] = 'fnum=parts[0];';
                    }
                    code[code.length] = 'if(absVal>=1000) {';
                    code[code.length] = 'thousandSeparator=utilFormat.thousandSeparator;' + 'thousands.length=0;' + 'j=fnum.length;' + 'n=fnum.length%3||3;' + 'for(i=0;i<j;i+=n){' + 'if(i!==0){' + 'n=3;' + '}' + 'thousands[thousands.length]=fnum.substr(i,n);' + '}' + 'fnum=thousands.join(thousandSeparator);' + '}';
                    if (precision) {
                        code[code.length] = 'fnum += utilFormat.decimalSeparator+parts[1];';
                    }
                } else if (precision) {
                    
                    code[code.length] = 'if(utilFormat.decimalSeparator!=="."){' + 'parts=fnum.split(".");' + 'fnum=parts[0]+utilFormat.decimalSeparator+parts[1];' + '}';
                }
                
                code[code.length] = 'if(neg&&fnum!=="' + (precision ? '0.' + Ext.String.repeat('0', precision) : '0') + '") { fnum="-"+fnum; }';
                if (trimTrailingZeroes) {
                    code[code.length] = 'fnum=fnum.replace(trailingZeroes,"");';
                }
                code[code.length] = 'return ';
                
                if (extraChars) {
                    code[code.length] = 'formatString.replace(formatPattern, fnum);';
                } else {
                    code[code.length] = 'fnum;';
                }
                code[code.length] = '};';
                formatFn = me.formatFns[originalFormatString] = Ext.functionFactory('Ext', code.join(''))(Ext);
            }
            return formatFn(v);
        },
        
        numberRenderer: function(format) {
            return function(v) {
                return me.number(v, format);
            };
        },
        
        percent: function(value, formatString) {
            return me.number(value * 100, formatString || '0') + me.percentSign;
        },
        
        attributes: function(attributes) {
            if (typeof attributes === 'object') {
                var result = [],
                    name;
                for (name in attributes) {
                    if (attributes.hasOwnProperty(name)) {
                        result.push(name, '="', name === 'style' ? Ext.DomHelper.generateStyles(attributes[name], null, true) : Ext.htmlEncode(attributes[name]), '" ');
                    }
                }
                attributes = result.join('');
            }
            return attributes || '';
        },
        
        plural: function(v, s, p) {
            return v + ' ' + (v === 1 ? s : (p ? p : s + 's'));
        },
        
        nl2br: function(v) {
            return Ext.isEmpty(v) ? '' : v.replace(me.nl2brRe, '<br/>');
        },
        
        capitalize: Ext.String.capitalize,
        
        uncapitalize: Ext.String.uncapitalize,
        
        ellipsis: Ext.String.ellipsis,
        
        escape: Ext.String.escape,
        
        escapeRegex: Ext.String.escapeRegex,
        
        htmlDecode: Ext.String.htmlDecode,
        
        htmlEncode: Ext.String.htmlEncode,
        
        leftPad: Ext.String.leftPad,
        
        toggle: Ext.String.toggle,
        
        trim: Ext.String.trim,
        
        parseBox: function(box) {
            box = box || 0;
            if (typeof box === 'number') {
                return {
                    top: box,
                    right: box,
                    bottom: box,
                    left: box
                };
            }
            var parts = box.split(' '),
                ln = parts.length;
            if (ln === 1) {
                parts[1] = parts[2] = parts[3] = parts[0];
            } else if (ln === 2) {
                parts[2] = parts[0];
                parts[3] = parts[1];
            } else if (ln === 3) {
                parts[3] = parts[1];
            }
            return {
                top: parseInt(parts[0], 10) || 0,
                right: parseInt(parts[1], 10) || 0,
                bottom: parseInt(parts[2], 10) || 0,
                left: parseInt(parts[3], 10) || 0
            };
        }
    };
});


Ext.define('Ext.Template', {
    
    
    requires: [
        
        'Ext.util.Format'
    ],
    inheritableStatics: {
        
        from: function(el, config) {
            el = Ext.getDom(el);
            return new this(el.value || el.innerHTML,config || '');
        }
    },
    
    
    
    useEval: Ext.isGecko,
    
    
    constructor: function(html) {
        var me = this,
            args = arguments,
            buffer = [],
            i,
            length = args.length,
            value;
        me.initialConfig = {};
        
        
        
        if (length === 1 && Ext.isArray(html)) {
            args = html;
            length = args.length;
        }
        if (length > 1) {
            for (i = 0; i < length; i++) {
                value = args[i];
                if (typeof value == 'object') {
                    Ext.apply(me.initialConfig, value);
                    Ext.apply(me, value);
                } else {
                    buffer.push(value);
                }
            }
        } else {
            buffer.push(html);
        }
        
        me.html = buffer.join('');
    },
    
    isTemplate: true,
    
    
    disableFormats: false,
    
    tokenRe: /\{(?:(?:(\d+)|([a-z_][\w\-]*))(?::([a-z_\.]+)(?:\(([^\)]*?)?\))?)?)\}/gi,
    
    apply: function(values) {
        var me = this;
        if (me.compiled) {
            if (!me.fn) {
                me.compile();
            }
            return me.fn(values).join('');
        }
        return me.evaluate(values);
    },
    
    
    evaluate: function(values) {
        var me = this,
            useFormat = !me.disableFormats,
            fm = Ext.util.Format,
            tpl = me;
        function fn(match, index, name, formatFn, args) {
            
            
            if (name == null || name == '') {
                name = index;
            }
            if (formatFn && useFormat) {
                if (args) {
                    args = [
                        values[name]
                    ].concat(Ext.functionFactory('return [' + args + '];')());
                } else {
                    args = [
                        values[name]
                    ];
                }
                
                if (formatFn.substr(0, 5) === "this.") {
                    return tpl[formatFn.substr(5)].apply(tpl, args);
                }
                
                else if (fm[formatFn]) {
                    return fm[formatFn].apply(fm, args);
                } else 
                {
                    return match;
                }
            } else {
                return values[name] !== undefined ? values[name] : "";
            }
        }
        return me.html.replace(me.tokenRe, fn);
    },
    
    applyOut: function(values, out) {
        var me = this;
        if (me.compiled) {
            if (!me.fn) {
                me.compile();
            }
            out.push.apply(out, me.fn(values));
        } else {
            out.push(me.apply(values));
        }
        return out;
    },
    
    applyTemplate: function() {
        return this.apply.apply(this, arguments);
    },
    
    set: function(html, compile) {
        var me = this;
        me.html = html;
        me.compiled = !!compile;
        me.fn = null;
        return me;
    },
    compileARe: /\\/g,
    compileBRe: /(\r\n|\n)/g,
    compileCRe: /'/g,
    
    compile: function() {
        var me = this,
            code;
        code = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.tokenRe, me.regexReplaceFn.bind(me));
        code = (this.disableFormats !== true ? 'var fm=Ext.util.Format;' : '') + (me.useEval ? '$=' : 'return') + " function(v){return ['" + code + "'];};";
        me.fn = me.useEval ? me.evalCompiled(code) : (new Function('Ext',code))(Ext);
        me.compiled = true;
        return me;
    },
    
    evalCompiled: function($) {
        
        
        
        
        eval($);
        return $;
    },
    regexReplaceFn: function(match, index, name, formatFn, args) {
        
        
        
        if (index == null || index == '') {
            index = '"' + name + '"';
        }
        
        
        else if (this.stringFormat) {
            index = parseInt(index) + 1;
        }
        if (formatFn && this.disableFormats !== true) {
            args = args ? ',' + args : "";
            
            if (formatFn.substr(0, 5) === "this.") {
                formatFn = formatFn + '(';
            }
            
            else if (Ext.util.Format[formatFn]) {
                formatFn = "fm." + formatFn + '(';
            } else 
            {
                return match;
            }
            return "'," + formatFn + "v[" + index + "]" + args + "),'";
        } else {
            return "',v[" + index + "] == undefined ? '' : v[" + index + "],'";
        }
    },
    
    insertFirst: function(el, values, returnElement) {
        return this.doInsert('afterBegin', el, values, returnElement);
    },
    
    insertBefore: function(el, values, returnElement) {
        return this.doInsert('beforeBegin', el, values, returnElement);
    },
    
    insertAfter: function(el, values, returnElement) {
        return this.doInsert('afterEnd', el, values, returnElement);
    },
    
    append: function(el, values, returnElement) {
        return this.doInsert('beforeEnd', el, values, returnElement);
    },
    doInsert: function(where, el, values, returnElement) {
        var newNode = Ext.DomHelper.insertHtml(where, Ext.getDom(el), this.apply(values));
        return returnElement ? Ext.get(newNode) : newNode;
    },
    
    overwrite: function(el, values, returnElement) {
        var newNode = Ext.DomHelper.overwrite(Ext.getDom(el), this.apply(values));
        return returnElement ? Ext.get(newNode) : newNode;
    }
}, function(Template) {
    var formatRe = /\{\d+\}/,
        generateFormatFn = function(format) {
            
            if (formatRe.test(format)) {
                format = new Template(format,formatTplConfig);
                return function() {
                    return format.apply(arguments);
                };
            } else 
            {
                return function() {
                    return format;
                };
            }
        },
        
        
        
        formatTplConfig = {
            useFormat: false,
            compiled: true,
            stringFormat: true
        },
        formatFns = {};
    
    
    Ext.String.format = Ext.util.Format.format = function(format) {
        var formatFn = formatFns[format] || (formatFns[format] = generateFormatFn(format));
        return formatFn.apply(this, arguments);
    };
});


Ext.define('Ext.mixin.Inheritable', {
    extend: 'Ext.Mixin',
    mixinConfig: {
        id: 'inheritable'
    },
    
    getInherited: function(inner) {
        var me = this,
            inheritedState = (inner && me.inheritedStateInner) || me.inheritedState,
            ownerCt = me.ownerCt || me.initOwnerCt,
            isContainer = me.isContainer,
            parent, inheritedStateInner, getInner;
        if (!inheritedState || inheritedState.invalid) {
            
            
            
            
            parent = me.getRefOwner();
            if (ownerCt) {
                
                
                getInner = me.ownerLayout === ownerCt.layout;
            }
            me.inheritedState = inheritedState = 
            
            
            
            
            Ext.Object.chain(parent ? parent.getInherited(getInner) : Ext.rootInheritedState);
            if (isContainer) {
                me.inheritedStateInner = inheritedStateInner = Ext.Object.chain(inheritedState);
            }
            me.initInheritedState(inheritedState, inheritedStateInner);
            
            
            inheritedState = (isContainer && inner) ? me.inheritedStateInner : me.inheritedState;
        }
        return inheritedState;
    },
    
    getInheritedConfig: function(property, skipThis) {
        var state = this.inheritedState,
            old, ret;
        
        if (!state || state.invalid) {
            state = this.getInherited();
        }
        ret = state[property];
        if (skipThis && state.hasOwnProperty(property)) {
            old = ret;
            delete state[property];
            ret = state[property];
            state[property] = old;
        }
        return ret;
    },
    
    fixReference: function() {
        var me = this,
            refHolder;
        if (me.reference) {
            refHolder = me.lookupReferenceHolder();
            if (refHolder) {
                refHolder.attachReference(me);
            }
        }
    },
    
    resolveListenerScope: function(defaultScope) {
        var me = this,
            namedScope = Ext._namedScopes[defaultScope],
            ret;
        if (!namedScope) {
            
            
            
            
            ret = me.getInheritedConfig('defaultListenerScope', true) || defaultScope || me;
        } else if (namedScope.isController) {
            
            
            
            
            ret = me.getInheritedConfig('controller', !namedScope.isSelf);
        } else if (namedScope.isSelf) {
            
            
            ret = me.getInheritedConfig('defaultListenerScope') || me;
        } else if (namedScope.isThis) {
            
            
            ret = me;
        }
        return ret || null;
    },
    
    lookupReferenceHolder: function(skipThis) {
        return this.getInheritedConfig('referenceHolder', skipThis !== false) || null;
    },
    
    getRefOwner: function() {
        
        return this.ownerCt || this.initOwnerCt || this.ownerCmp || this.floatParent;
    },
    
    
    invalidateInheritedState: function() {
        var inheritedState = this.inheritedState;
        if (inheritedState) {
            
            
            
            inheritedState.invalid = true;
            
            
            
            
            
            delete this.inheritedState;
        }
    }
}, function() {
    
    Ext.rootInheritedState = {};
});


Ext.define('Ext.mixin.Bindable', {
    mixinId: 'bindable',
    config: {
        
        bind: {
            $value: null,
            lazy: true
        },
        
        
        controller: null,
        
        defaultListenerScope: false,
        
        publishes: {
            $value: null,
            lazy: true,
            merge: function(newValue, oldValue) {
                return this.mergeSets(newValue, oldValue);
            }
        },
        
        reference: null,
        
        
        session: {
            $value: null,
            lazy: true
        },
        
        twoWayBindable: {
            $value: null,
            lazy: true,
            merge: function(newValue, oldValue) {
                return this.mergeSets(newValue, oldValue);
            }
        },
        
        
        viewModel: {
            $value: null,
            lazy: true
        }
    },
    
    defaultBindProperty: null,
    
    validRefRe: /^[a-z_][a-z0-9_]*$/i,
    
    initInheritedState: function(inheritedState) {
        var me = this,
            reference = me.getReference(),
            controller = me.getController(),
            
            
            viewModel = me.getConfig('viewModel', true),
            session = me.getConfig('session', true),
            defaultListenerScope = me.getDefaultListenerScope();
        if (controller) {
            inheritedState.controller = controller;
        }
        if (defaultListenerScope) {
            inheritedState.defaultListenerScope = me;
        } else if (controller) {
            inheritedState.defaultListenerScope = controller;
        }
        if (viewModel) {
            
            
            
            
            if (!viewModel.isViewModel) {
                viewModel = me;
            }
            inheritedState.viewModel = viewModel;
        }
        
        if (session) {
            if (!session.isSession) {
                session = me;
            }
            inheritedState.session = session;
        }
        if (reference) {
            me.referenceKey = (inheritedState.referencePath || '') + reference;
            me.viewModelKey = (inheritedState.viewModelPath || '') + reference;
        }
    },
    
    lookupController: function(skipThis) {
        return this.getInheritedConfig('controller', skipThis) || null;
    },
    
    lookupSession: function(skipThis) {
        
        var ret = skipThis ? null : this.getSession();
        
        if (!ret) {
            ret = this.getInheritedConfig('session', skipThis);
            if (ret && !ret.isSession) {
                ret = ret.getInherited().session = ret.getSession();
            }
        }
        return ret || null;
    },
    
    lookupViewModel: function(skipThis) {
        var ret = skipThis ? null : this.getViewModel();
        
        if (!ret) {
            ret = this.getInheritedConfig('viewModel', skipThis);
            
            
            
            if (ret && !ret.isViewModel) {
                ret = ret.getInherited().viewModel = ret.getViewModel();
            }
        }
        return ret || null;
    },
    
    publishState: function(property, value) {
        var me = this,
            path = me.viewModelKey,
            state = me.publishedState,
            binds = me.getBind(),
            binding = binds && property && binds[property],
            count = 0,
            name, publishes, vm;
        if (binding && !binding.syncing && !binding.isReadOnly()) {
            
            
            
            
            
            
            if (!(binding.calls === 0 && (value == null || value === me.getInitialConfig()[property]))) {
                binding.setValue(value);
            }
        }
        if (!path || !(publishes = me.getPublishes())) {
            return;
        }
        if (!(vm = me.lookupViewModel())) {
            return;
        }
        if (property && state) {
            if (!publishes[property]) {
                return;
            }
            
            
            if (!(value && value.constructor === Object) && !(value instanceof Array)) {
                if (state[property] === value) {
                    return;
                }
            }
            path += '.';
            path += property;
        } else {
            state = state || (me.publishedState = {});
            for (name in publishes) {
                ++count;
                
                
                if (name === property) {
                    state[name] = value;
                } else {
                    state[name] = me[name];
                }
            }
            if (!count) {
                
                return;
            }
            value = state;
        }
        vm.set(path, value);
    },
    
    privates: {
        
        addBindableUpdater: function(property) {
            var me = this,
                configs = me.self.$config.configs,
                cfg = configs[property],
                updateName;
            
            
            if (cfg && !me.hasOwnProperty(updateName = cfg.names.update)) {
                me[updateName] = cfg.bindableUpdater || (cfg.root.bindableUpdater = me.makeBindableUpdater(cfg));
            }
        },
        
        applyBind: function(binds, currentBindings) {
            var me = this,
                viewModel = me.lookupViewModel(),
                twoWayable = me.getTwoWayBindable(),
                getBindTemplateScope = me._getBindTemplateScope,
                b, property, descriptor;
            if (!currentBindings || typeof currentBindings === 'string') {
                currentBindings = {};
            }
            if (!viewModel) {
                Ext.Error.raise('Cannot use bind config without a viewModel');
            }
            if (Ext.isString(binds)) {
                if (!me.defaultBindProperty) {
                    Ext.Error.raise(me.$className + ' has no defaultBindProperty - ' + 'Please specify a bind object');
                }
                b = binds;
                binds = {};
                binds[me.defaultBindProperty] = b;
            }
            for (property in binds) {
                descriptor = binds[property];
                b = currentBindings[property];
                if (b && typeof b !== 'string') {
                    b.destroy();
                    b = null;
                }
                if (descriptor) {
                    b = viewModel.bind(descriptor, me.onBindNotify, me);
                    b._config = Ext.Config.get(property);
                    b.getTemplateScope = getBindTemplateScope;
                    if (!me[b._config.names.set]) {
                        Ext.Error.raise('Cannot bind ' + property + ' on ' + me.$className + ' - missing a ' + b._config.names.set + ' method.');
                    }
                }
                currentBindings[property] = b;
                if (twoWayable && twoWayable[property] && !b.isReadOnly()) {
                    me.addBindableUpdater(property);
                }
            }
            return currentBindings;
        },
        applyController: function(controller) {
            if (controller) {
                controller = Ext.Factory.controller(controller);
                controller.setView(this);
            }
            return controller;
        },
        applyPublishes: function(all) {
            if (this.lookupViewModel()) {
                for (var property in all) {
                    this.addBindableUpdater(property);
                }
            }
            return all;
        },
        applyReference: function(reference) {
            var validIdRe = this.validRefRe || Ext.validIdRe;
            if (reference && !validIdRe.test(reference)) {
                Ext.Error.raise('Invalid reference "' + reference + '" for ' + this.getId() + ' - not a valid identifier');
            }
            return reference;
        },
        
        applySession: function(session) {
            if (!session) {
                return null;
            }
            if (!session.isSession) {
                var parentSession = this.lookupSession(true),
                    
                    config = (session === true) ? {} : session;
                if (parentSession) {
                    session = parentSession.spawn(config);
                } else {
                    
                    
                    session = new Ext.data['Session'](config);
                }
            }
            return session;
        },
        
        applyViewModel: function(viewModel) {
            var me = this,
                config, session;
            if (!(viewModel && viewModel.isViewModel)) {
                config = {
                    parent: me.lookupViewModel(true)
                };
                
                config.session = me.getSession();
                if (!session && !config.parent) {
                    config.session = me.lookupSession();
                }
                if (viewModel) {
                    if (viewModel.constructor === Object) {
                        Ext.apply(config, viewModel);
                    } else if (typeof viewModel === 'string') {
                        config.type = viewModel;
                    }
                }
                viewModel = Ext.Factory.viewModel(config);
            }
            return viewModel;
        },
        _getBindTemplateScope: function() {
            
            
            return this.scope.resolveListenerScope();
        },
        
        initBindable: function() {
            this.initBindable = Ext.emptyFn;
            this.getBind();
            this.getPublishes();
        },
        
        
        
        
        
        
        
        
        
        
        
        
        
        makeBindableUpdater: function(cfg) {
            var updateName = cfg.names.update;
            return function(newValue, oldValue) {
                var me = this,
                    updater = me.self.prototype[updateName];
                if (updater) {
                    updater.call(me, newValue, oldValue);
                }
                me.publishState(cfg.name, newValue);
            };
        },
        onBindNotify: function(value, oldValue, binding) {
            binding.syncing = (binding.syncing + 1) || 1;
            this[binding._config.names.set](value);
            --binding.syncing;
        },
        removeBindings: function() {
            var bindings = this.bind,
                key, binding;
            if (bindings && typeof bindings !== 'string') {
                for (key in bindings) {
                    binding = bindings[key];
                    binding.destroy();
                    binding._config = binding.getTemplateScope = null;
                }
            }
            this.bind = null;
        },
        
        updateSession: function(session) {
            var state = this.getInherited();
            if (session) {
                state.session = session;
            } else {
                delete state.session;
            }
        },
        
        updateViewModel: function(viewModel) {
            var state = this.getInherited(),
                controller = this.getController();
            if (viewModel) {
                state.viewModel = viewModel;
                viewModel.setView(this);
                if (controller) {
                    controller.initViewModel(viewModel);
                }
            } else {
                delete state.viewModel;
            }
        }
    }
});


Ext.define('Ext.Widget', {
    extend: 'Ext.Evented',
    xtype: 'widget',
    requires: [
        'Ext.dom.Element'
    ],
    mixins: [
        'Ext.mixin.Inheritable',
        'Ext.mixin.Bindable'
    ],
    isWidget: true,
    
    element: {
        reference: 'element'
    },
    observableType: 'component',
    eventedConfig: {
        
        width: null,
        
        height: null
    },
    
    template: [],
    constructor: function(config) {
        var me = this;
        me.initId(config);
        me.initElement();
        me.mixins.observable.constructor.call(me, config);
        Ext.ComponentManager.register(me);
    },
    afterCachedConfig: function() {
        
        
        
        
        
        
        
        var me = this,
            prototype = me.self.prototype,
            referenceList = me.referenceList,
            renderElement = me.renderElement.dom,
            renderTemplate, element, i, ln, reference, elements;
        
        
        prototype.renderTemplate = renderTemplate = document.createDocumentFragment();
        renderTemplate.appendChild(renderElement.cloneNode(true));
        elements = renderTemplate.querySelectorAll('[id]');
        for (i = 0 , ln = elements.length; i < ln; i++) {
            element = elements[i];
            element.removeAttribute('id');
        }
        
        
        
        
        for (i = 0 , ln = referenceList.length; i < ln; i++) {
            reference = referenceList[i];
            me[reference].dom.removeAttribute('reference');
        }
    },
    applyWidth: function(width) {
        return this.filterLengthValue(width);
    },
    applyHeight: function(height) {
        return this.filterLengthValue(height);
    },
    destroy: function() {
        var me = this,
            referenceList = me.referenceList,
            i, ln, reference;
        
        for (i = 0 , ln = referenceList.length; i < ln; i++) {
            reference = referenceList[i];
            if (me.hasOwnProperty(reference)) {
                me[reference].destroy();
                me[reference] = null;
            }
        }
        me.callParent();
        Ext.ComponentManager.unregister(me);
    },
    
    doSetWidth: function(width) {
        this.element.setWidth(width);
    },
    
    doSetHeight: function(height) {
        this.element.setHeight(height);
    },
    
    getElementConfig: function() {
        var me = this,
            el = me.element;
        if (!('children' in el)) {
            el = Ext.apply({
                children: me.getTemplate()
            }, el);
        }
        return el;
    },
    
    getItemId: function() {
        
        return this.itemId || this.id;
    },
    
    getSize: function() {
        return {
            width: this.getWidth(),
            height: this.getHeight()
        };
    },
    getTemplate: function() {
        return this.template;
    },
    
    initElement: function() {
        var me = this,
            prototype = me.self.prototype,
            id = me.getId(),
            referenceList = me.referenceList = [],
            cleanAttributes = true,
            renderTemplate, renderElement, element, referenceNodes, i, ln, referenceNode, reference, elementConfig;
        if (prototype.hasOwnProperty('renderTemplate')) {
            
            
            
            
            renderTemplate = me.renderTemplate.cloneNode(true);
            renderElement = renderTemplate.firstChild;
        } else {
            
            
            cleanAttributes = false;
            renderTemplate = document.createDocumentFragment();
            elementConfig = me.getElementConfig();
            
            
            
            
            me.initElementListeners(elementConfig);
            renderElement = Ext.Element.create(elementConfig, true);
            renderTemplate.appendChild(renderElement);
        }
        referenceNodes = renderTemplate.querySelectorAll('[reference]');
        for (i = 0 , ln = referenceNodes.length; i < ln; i++) {
            referenceNode = referenceNodes[i];
            reference = referenceNode.getAttribute('reference');
            if (cleanAttributes) {
                
                
                
                
                
                
                
                referenceNode.removeAttribute('reference');
            }
            if (reference === 'element') {
                if (element) {
                    
                    Ext.Error.raise("Duplicate 'element' reference detected in '" + me.$className + "' template.");
                }
                referenceNode.id = id;
                
                
                element = me.el = me.addElementReference(reference, referenceNode);
            } else {
                me.addElementReferenceOnDemand(reference, referenceNode);
            }
            referenceList.push(reference);
        }
        if (!element) {
            Ext.Error.raise("No 'element' reference found in '" + me.$className + "' template.");
        }
        if (renderElement === element.dom) {
            me.renderElement = element;
        } else {
            me.addElementReferenceOnDemand('renderElement', renderElement);
        }
    },
    
    is: function(selector) {
        return Ext.ComponentQuery.is(this, selector);
    },
    
    isXType: function(xtype, shallow) {
        return shallow ? (Ext.Array.indexOf(this.xtypes, xtype) !== -1) : !!this.xtypesMap[xtype];
    },
    resolveListenerScope: function(defaultType) {
        
        return this.mixins.inheritable.resolveListenerScope.call(this, defaultType);
    },
    
    setSize: function(width, height) {
        if (width !== undefined) {
            this.setWidth(width);
        }
        if (height !== undefined) {
            this.setHeight(height);
        }
    },
    
    privates: {
        
        addElementReferenceOnDemand: function(name, domNode) {
            if (this._elementListeners[name]) {
                
                
                
                this.addElementReference(name, domNode);
            } else {
                
                
                Ext.Object.defineProperty(this, name, {
                    get: function() {
                        
                        
                        
                        delete this[name];
                        return this.addElementReference(name, domNode);
                    },
                    configurable: true
                });
            }
        },
        
        addElementReference: function(name, domNode) {
            var me = this,
                referenceEl = me[name] = Ext.get(domNode),
                listeners = me._elementListeners[name],
                eventName, listener;
            referenceEl.skipGarbageCollection = true;
            referenceEl.component = me;
            if (listeners) {
                
                
                
                
                
                
                
                
                listeners = Ext.clone(listeners);
                
                
                
                
                
                
                
                listeners.scope = me;
                
                
                
                
                
                
                
                
                
                for (eventName in listeners) {
                    listener = listeners[eventName];
                    if (typeof listener === 'object') {
                        listener.scope = me;
                    }
                }
                
                
                
                referenceEl.on(listeners);
            }
            return referenceEl;
        },
        //@private

        doAddListener: function(name, fn, scope, options, order, caller, manager) {
            if (options && 'element' in options) {
                if (this.referenceList.indexOf(options.element) === -1) {
                    Ext.Logger.error("Adding event listener with an invalid element reference of '" + options.element + "' for this component. Available values are: '" + this.referenceList.join("', '") + "'", this);
                }
                
                this[options.element].doAddListener(name, fn, scope || this, options, order);
            }
            this.callParent([
                name,
                fn,
                scope,
                options,
                order,
                caller,
                manager
            ]);
        },
        filterLengthValue: function(value) {
            if (value === 'auto' || (!value && value !== 0)) {
                return null;
            }
            return value;
        },
        getFocusEl: function() {
            return this.element;
        },
        
        initElementListeners: function(elementConfig) {
            var me = this,
                elementListeners = me._elementListeners || (me.self.prototype._elementListeners = {}),
                reference = elementConfig.reference,
                children = elementConfig.children,
                listeners, ln, i;
            if (reference) {
                listeners = elementConfig.listeners;
                if (listeners) {
                    elementListeners[reference] = listeners;
                    
                    
                    
                    elementConfig.listeners = null;
                }
            }
            if (children) {
                for (i = 0 , ln = children.length; i < ln; i++) {
                    me.initElementListeners(children[i]);
                }
            }
        },
        initId: function(config) {
            var me = this,
                defaultConfig = me.config,
                id = (config && config.id) || (defaultConfig && defaultConfig.id);
            if (id) {
                
                
                me.setId(id);
                me.id = id;
            } else {
                
                me.getId();
            }
        }
    }
}, function(Widget) {
    
    
    (Widget.prototype.$elementEventOptions = Ext.Object.chain(Ext.Element.prototype.$eventOptions)).element = 1;
});

Ext.define('Ext.overrides.Widget', {
    override: 'Ext.Widget',
    uses: [
        'Ext.Component'
    ],
    $configStrict: false,
    isComponent: true,
    liquidLayout: true,
    
    
    
    rendered: true,
    rendering: true,
    cachedConfig: {
        baseCls: Ext.baseCSSPrefix + 'widget'
    },
    constructor: function(config) {
        this.callParent([
            config
        ]);
        
        this.getComponentLayout();
    },
    addCls: function(cls) {
        this.el.addCls(cls);
    },
    addClsWithUI: function(cls) {
        this.el.addCls(cls);
    },
    afterComponentLayout: Ext.emptyFn,
    finishRender: function() {
        this.rendering = false;
        this.initBindable();
    },
    getComponentLayout: function() {
        var me = this,
            layout = me.componentLayout;
        if (!layout) {
            layout = me.componentLayout = new Ext.layout.component.Auto();
            layout.setOwner(me);
        }
        return layout;
    },
    
    getTdCls: function() {
        return Ext.baseCSSPrefix + this.getTdType() + '-' + (this.ui || 'default') + '-cell';
    },
    
    getTdType: function() {
        return this.xtype;
    },
    
    getItemId: function() {
        return this.itemId || this.id;
    },
    getSizeModel: function() {
        return Ext.Component.prototype.getSizeModel.apply(this, arguments);
    },
    onAdded: function(container, pos, instanced) {
        var me = this,
            inheritedState = me.inheritedState;
        me.ownerCt = container;
        
        
        
        if (inheritedState && instanced) {
            me.invalidateInheritedState();
        }
        if (me.reference) {
            me.fixReference();
        }
    },
    onRemoved: function(destroying) {
        var me = this,
            refHolder;
        if (me.reference) {
            refHolder = me.lookupReferenceHolder();
            if (refHolder) {
                refHolder.clearReference(me);
            }
        }
        if (!destroying) {
            me.removeBindings();
        }
        if (me.inheritedState && !destroying) {
            me.invalidateInheritedState();
        }
        me.ownerCt = me.ownerLayout = null;
    },
    parseBox: function(box) {
        return Ext.Element.parseBox(box);
    },
    render: function(container, position) {
        var element = this.element,
            nextSibling;
        if (position) {
            nextSibling = container.childNodes[position];
            if (nextSibling) {
                container.insertBefore(element, nextSibling);
                return;
            }
        }
        container.appendChild(element);
    },
    setPosition: function(x, y) {
        this.el.setLocalXY(x, y);
    },
    up: function() {
        return Ext.Component.prototype.up.apply(this, arguments);
    },
    isAncestor: function() {
        return Ext.Component.prototype.isAncestor.apply(this, arguments);
    },
    onFocusEnter: function() {
        return Ext.Component.prototype.onFocusEnter.apply(this, arguments);
    },
    onFocusLeave: function() {
        return Ext.Component.prototype.onFocusLeave.apply(this, arguments);
    }
}, function() {
    var prototype;
    if (Ext.isIE8) {
        prototype = Ext.Widget.prototype;
        
        
        prototype.addElementReferenceOnDemand = prototype.addElementReference;
    }
});


Ext.define('Ext.util.XTemplateParser', {
    requires: [
        'Ext.String'
    ],
    constructor: function(config) {
        Ext.apply(this, config);
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    doTpl: Ext.emptyFn,
    parse: function(str) {
        var me = this,
            len = str.length,
            aliases = {
                elseif: 'elif'
            },
            topRe = me.topRe,
            actionsRe = me.actionsRe,
            index, stack, s, m, t, prev, frame, subMatch, begin, end, actions, prop, expectTplNext;
        me.level = 0;
        me.stack = stack = [];
        for (index = 0; index < len; index = end) {
            topRe.lastIndex = index;
            m = topRe.exec(str);
            if (!m) {
                me.doText(str.substring(index, len));
                break;
            }
            begin = m.index;
            end = topRe.lastIndex;
            if (index < begin) {
                
                
                
                s = str.substring(index, begin);
                if (!(expectTplNext && Ext.String.trim(s) === '')) {
                    me.doText(s);
                }
            }
            expectTplNext = false;
            if (m[1]) {
                end = str.indexOf('%}', begin + 2);
                me.doEval(str.substring(begin + 2, end));
                end += 2;
            } else if (m[2]) {
                end = str.indexOf(']}', begin + 2);
                me.doExpr(str.substring(begin + 2, end));
                end += 2;
            } else if (m[3]) {
                
                me.doTag(m[3]);
            } else if (m[4]) {
                
                actions = null;
                while ((subMatch = actionsRe.exec(m[4])) !== null) {
                    s = subMatch[2] || subMatch[3];
                    if (s) {
                        s = Ext.String.htmlDecode(s);
                        
                        t = subMatch[1];
                        t = aliases[t] || t;
                        actions = actions || {};
                        prev = actions[t];
                        if (typeof prev == 'string') {
                            actions[t] = [
                                prev,
                                s
                            ];
                        } else if (prev) {
                            actions[t].push(s);
                        } else {
                            actions[t] = s;
                        }
                    }
                }
                if (!actions) {
                    if (me.elseRe.test(m[4])) {
                        me.doElse();
                    } else if (me.defaultRe.test(m[4])) {
                        me.doDefault();
                    } else {
                        me.doTpl();
                        stack.push({
                            type: 'tpl'
                        });
                    }
                } else if (actions['if']) {
                    me.doIf(actions['if'], actions);
                    stack.push({
                        type: 'if'
                    });
                } else if (actions['switch']) {
                    me.doSwitch(actions['switch'], actions);
                    stack.push({
                        type: 'switch'
                    });
                    expectTplNext = true;
                } else if (actions['case']) {
                    me.doCase(actions['case'], actions);
                } else if (actions['elif']) {
                    me.doElseIf(actions['elif'], actions);
                } else if (actions['for']) {
                    ++me.level;
                    
                    if (prop = me.propRe.exec(m[4])) {
                        actions.propName = prop[1] || prop[2];
                    }
                    me.doFor(actions['for'], actions);
                    stack.push({
                        type: 'for',
                        actions: actions
                    });
                } else if (actions['foreach']) {
                    ++me.level;
                    
                    if (prop = me.propRe.exec(m[4])) {
                        actions.propName = prop[1] || prop[2];
                    }
                    me.doForEach(actions['foreach'], actions);
                    stack.push({
                        type: 'foreach',
                        actions: actions
                    });
                } else if (actions.exec) {
                    me.doExec(actions.exec, actions);
                    stack.push({
                        type: 'exec',
                        actions: actions
                    });
                }
            }
            
            else if (m[0].length === 5) {
                
                
                stack.push({
                    type: 'tpl'
                });
            } else {
                frame = stack.pop();
                me.doEnd(frame.type, frame.actions);
                if (frame.type == 'for' || frame.type == 'foreach') {
                    --me.level;
                }
            }
        }
    },
    
    topRe: /(?:(\{\%)|(\{\[)|\{([^{}]+)\})|(?:<tpl([^>]*)\>)|(?:<\/tpl>)/g,
    actionsRe: /\s*(elif|elseif|if|for|foreach|exec|switch|case|eval|between)\s*\=\s*(?:(?:"([^"]*)")|(?:'([^']*)'))\s*/g,
    propRe: /prop=(?:(?:"([^"]*)")|(?:'([^']*)'))/,
    defaultRe: /^\s*default\s*$/,
    elseRe: /^\s*else\s*$/
});


Ext.define('Ext.util.XTemplateCompiler', {
    extend: 'Ext.util.XTemplateParser',
    
    
    
    useEval: Ext.isGecko,
    
    
    
    useIndex: Ext.isIE8m,
    useFormat: true,
    propNameRe: /^[\w\d\$]*$/,
    compile: function(tpl) {
        var me = this,
            code = me.generate(tpl);
        
        
        
        
        return me.useEval ? me.evalTpl(code) : (new Function('Ext',code))(Ext);
    },
    generate: function(tpl) {
        var me = this,
            
            definitions = 'var fm=Ext.util.Format,ts=Object.prototype.toString;',
            code;
        
        me.maxLevel = 0;
        me.body = [
            'var c0=values, a0=' + me.createArrayTest(0) + ', p0=parent, n0=xcount, i0=xindex, k0, v;\n'
        ];
        if (me.definitions) {
            if (typeof me.definitions === 'string') {
                me.definitions = [
                    me.definitions,
                    definitions
                ];
            } else {
                me.definitions.push(definitions);
            }
        } else {
            me.definitions = [
                definitions
            ];
        }
        me.switches = [];
        me.parse(tpl);
        me.definitions.push((me.useEval ? '$=' : 'return') + ' function (' + me.fnArgs + ') {', me.body.join(''), '}');
        code = me.definitions.join('\n');
        
        me.definitions.length = me.body.length = me.switches.length = 0;
        delete me.definitions;
        delete me.body;
        delete me.switches;
        return code;
    },
    
    
    doText: function(text) {
        var me = this,
            out = me.body;
        text = text.replace(me.aposRe, "\\'").replace(me.newLineRe, '\\n');
        if (me.useIndex) {
            out.push('out[out.length]=\'', text, '\'\n');
        } else {
            out.push('out.push(\'', text, '\')\n');
        }
    },
    doExpr: function(expr) {
        var out = this.body;
        out.push('if ((v=' + expr + ') != null) out');
        
        
        if (this.useIndex) {
            out.push('[out.length]=v+\'\'\n');
        } else {
            out.push('.push(v+\'\')\n');
        }
    },
    doTag: function(tag) {
        var expr = this.parseTag(tag);
        if (expr) {
            this.doExpr(expr);
        } else {
            
            this.doText('{' + tag + '}');
        }
    },
    doElse: function() {
        this.body.push('} else {\n');
    },
    doEval: function(text) {
        this.body.push(text, '\n');
    },
    doIf: function(action, actions) {
        var me = this;
        
        if (action === '.') {
            me.body.push('if (values) {\n');
        } else if (me.propNameRe.test(action)) {
            me.body.push('if (', me.parseTag(action), ') {\n');
        } else 
        {
            me.body.push('if (', me.addFn(action), me.callFn, ') {\n');
        }
        if (actions.exec) {
            me.doExec(actions.exec);
        }
    },
    doElseIf: function(action, actions) {
        var me = this;
        
        if (action === '.') {
            me.body.push('else if (values) {\n');
        } else if (me.propNameRe.test(action)) {
            me.body.push('} else if (', me.parseTag(action), ') {\n');
        } else 
        {
            me.body.push('} else if (', me.addFn(action), me.callFn, ') {\n');
        }
        if (actions.exec) {
            me.doExec(actions.exec);
        }
    },
    doSwitch: function(action) {
        var me = this,
            key;
        
        if (action === '.' || action === '#') {
            key = action === '.' ? 'values' : 'xindex';
            me.body.push('switch (', key, ') {\n');
        } else if (me.propNameRe.test(action)) {
            me.body.push('switch (', me.parseTag(action), ') {\n');
        } else 
        {
            me.body.push('switch (', me.addFn(action), me.callFn, ') {\n');
        }
        me.switches.push(0);
    },
    doCase: function(action) {
        var me = this,
            cases = Ext.isArray(action) ? action : [
                action
            ],
            n = me.switches.length - 1,
            match, i;
        if (me.switches[n]) {
            me.body.push('break;\n');
        } else {
            me.switches[n]++;
        }
        for (i = 0 , n = cases.length; i < n; ++i) {
            match = me.intRe.exec(cases[i]);
            cases[i] = match ? match[1] : ("'" + cases[i].replace(me.aposRe, "\\'") + "'");
        }
        me.body.push('case ', cases.join(': case '), ':\n');
    },
    doDefault: function() {
        var me = this,
            n = me.switches.length - 1;
        if (me.switches[n]) {
            me.body.push('break;\n');
        } else {
            me.switches[n]++;
        }
        me.body.push('default:\n');
    },
    doEnd: function(type, actions) {
        var me = this,
            L = me.level - 1;
        if (type == 'for' || type == 'foreach') {
            
            if (actions.exec) {
                me.doExec(actions.exec);
            }
            me.body.push('}\n');
            me.body.push('parent=p', L, ';values=r', L + 1, ';xcount=n' + L + ';xindex=i', L, '+1;xkey=k', L, ';\n');
        } else if (type == 'if' || type == 'switch') {
            me.body.push('}\n');
        }
    },
    doFor: function(action, actions) {
        var me = this,
            s,
            L = me.level,
            up = L - 1,
            parentAssignment;
        
        if (action === '.') {
            s = 'values';
        } else if (me.propNameRe.test(action)) {
            s = me.parseTag(action);
        } else 
        {
            s = me.addFn(action) + me.callFn;
        }
        
        
        if (me.maxLevel < L) {
            me.maxLevel = L;
            me.body.push('var ');
        }
        if (action == '.') {
            parentAssignment = 'c' + L;
        } else {
            parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up;
        }
        me.body.push('i', L, '=0,n', L, '=0,c', L, '=', s, ',a', L, '=', me.createArrayTest(L), ',r', L, '=values,p', L, ',k', L, ';\n', 'p', L, '=parent=', parentAssignment, '\n', 'if (c', L, '){if(a', L, '){n', L, '=c', L, '.length;}else if (c', L, '.isMixedCollection){c', L, '=c', L, '.items;n', L, '=c', L, '.length;}else if(c', L, '.isStore){c', L, '=c', L, '.data.items;n', L, '=c', L, '.length;}else{c', L, '=[c', L, '];n', L, '=1;}}\n', 'for (xcount=n', L, ';i', L, '<n' + L + ';++i', L, '){\n', 'values=c', L, '[i', L, ']');
        if (actions.propName) {
            me.body.push('.', actions.propName);
        }
        me.body.push('\n', 'xindex=i', L, '+1\n');
        if (actions.between) {
            me.body.push('if(xindex>1){ out.push("', actions.between, '"); } \n');
        }
    },
    doForEach: function(action, actions) {
        var me = this,
            s,
            L = me.level,
            up = L - 1,
            parentAssignment;
        
        if (action === '.') {
            s = 'values';
        } else if (me.propNameRe.test(action)) {
            s = me.parseTag(action);
        } else 
        {
            s = me.addFn(action) + me.callFn;
        }
        
        
        if (me.maxLevel < L) {
            me.maxLevel = L;
            me.body.push('var ');
        }
        if (action == '.') {
            parentAssignment = 'c' + L;
        } else {
            parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up;
        }
        me.body.push('i', L, '=-1,n', L, '=0,c', L, '=', s, ',a', L, '=', me.createArrayTest(L), ',r', L, '=values,p', L, ',k', L, ';\n', 'p', L, '=parent=', parentAssignment, '\n', 'for(k', L, ' in c', L, '){\n', 'xindex=++i', L, '+1;\n', 'xkey=k', L, ';\n', 'values=c', L, '[k', L, '];');
        if (actions.propName) {
            me.body.push('.', actions.propName);
        }
        if (actions.between) {
            me.body.push('if(xindex>1){ out.push("', actions.between, '"); } \n');
        }
    },
    createArrayTest: ('isArray' in Array) ? function(L) {
        return 'Array.isArray(c' + L + ')';
    } : function(L) {
        return 'ts.call(c' + L + ')==="[object Array]"';
    },
    doExec: function(action, actions) {
        var me = this,
            name = 'f' + me.definitions.length,
            guards = me.guards[me.strict ? 0 : 1];
        me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', guards.doTry, ' var $v = values; with($v) {', '  ' + action, ' }', guards.doCatch, '}');
        me.body.push(name + me.callFn + '\n');
    },
    
    
    guards: [
        {
            doTry: '',
            doCatch: ''
        },
        {
            doTry: 'try { ',
            doCatch: ' } catch(e) {\n' + 'Ext.log.warn("XTemplate evaluation exception: " + e.message);\n' + '}'
        }
    ],
    addFn: function(body) {
        var me = this,
            name = 'f' + me.definitions.length,
            guards = me.guards[me.strict ? 0 : 1];
        if (body === '.') {
            me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', ' return values', '}');
        } else if (body === '..') {
            me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', ' return parent', '}');
        } else {
            me.definitions.push('function ' + name + '(' + me.fnArgs + ') {', guards.doTry, ' var $v = values; with($v) {', '  return(' + body + ')', ' }', guards.doCatch, '}');
        }
        return name;
    },
    parseTag: function(tag) {
        var me = this,
            m = me.tagRe.exec(tag),
            name, format, args, math, v;
        if (!m) {
            return null;
        }
        name = m[1];
        format = m[2];
        args = m[3];
        math = m[4];
        
        if (name == '.') {
            
            if (!me.validTypes) {
                me.definitions.push('var validTypes={string:1,number:1,boolean:1};');
                me.validTypes = true;
            }
            v = 'validTypes[typeof values] || ts.call(values) === "[object Date]" ? values : ""';
        }
        
        else if (name == '#') {
            v = 'xindex';
        }
        
        else if (name == '$') {
            v = 'xkey';
        } else if (name.substr(0, 7) == "parent.") {
            v = name;
        }
        
        else if (isNaN(name) && name.indexOf('-') == -1 && name.indexOf('.') != -1) {
            v = "values." + name;
        } else 
        
        {
            v = "values['" + name + "']";
        }
        if (math) {
            v = '(' + v + math + ')';
        }
        if (format && me.useFormat) {
            args = args ? ',' + args : "";
            if (format.substr(0, 5) != "this.") {
                format = "fm." + format + '(';
            } else {
                format += '(';
            }
        } else {
            return v;
        }
        return format + v + args + ')';
    },
    
    evalTpl: function($) {
        
        
        
        
        eval($);
        return $;
    },
    newLineRe: /\r\n|\r|\n/g,
    aposRe: /[']/g,
    intRe: /^\s*(\d+)\s*$/,
    tagRe: /^([\w-\.\#\$]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?$/
}, function() {
    var proto = this.prototype;
    proto.fnArgs = 'out,values,parent,xindex,xcount,xkey';
    proto.callFn = '.call(this,' + proto.fnArgs + ')';
});


Ext.define('Ext.XTemplate', {
    extend: 'Ext.Template',
    requires: [
        'Ext.util.XTemplateCompiler'
    ],
    
    emptyObj: {},
    
    
    
    fn: null,
    
    strict: false,
    apply: function(values, parent) {
        return this.applyOut(values, [], parent).join('');
    },
    applyOut: function(values, out, parent) {
        var me = this,
            compiler;
        if (!me.fn) {
            compiler = new Ext.util.XTemplateCompiler({
                useFormat: me.disableFormats !== true,
                definitions: me.definitions,
                strict: me.strict
            });
            me.fn = compiler.compile(me.html);
        }
        if (me.strict) {
            me.fn(out, values, parent || me.emptyObj, 1, 1);
        } else {
            try {
                me.fn(out, values, parent || me.emptyObj, 1, 1);
            } catch (e) {
                Ext.log.warn('XTemplate evaluation exception: ' + e.message);
            }
        }
        return out;
    },
    
    compile: function() {
        return this;
    },
    statics: {
        
        getTpl: function(instance, name) {
            var tpl = instance[name],
                
                owner;
            if (tpl && !tpl.isTemplate) {
                
                
                tpl = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl);
                
                if (instance.hasOwnProperty(name)) {
                    
                    owner = instance;
                } else {
                    
                    for (owner = instance.self.prototype; owner && !owner.hasOwnProperty(name); owner = owner.superclass) {}
                }
                owner[name] = tpl;
                tpl.owner = owner;
            }
            
            
            return tpl || null;
        }
    }
});


Ext.define('Ext.app.EventDomain', {
    requires: [
        'Ext.util.Event'
    ],
    statics: {
        
        instances: {}
    },
    
    isEventDomain: true,
    isInstance: false,
    constructor: function() {
        var me = this;
        if (!me.isInstance) {
            Ext.app.EventDomain.instances[me.type] = me;
        }
        me.bus = {};
        me.monitoredClasses = [];
    },
    
    dispatch: function(target, ev, args) {
        ev = Ext.canonicalEventName(ev);
        var me = this,
            bus = me.bus,
            selectors = bus[ev],
            selector, controllers, id, info, events, len, i, event;
        if (!selectors) {
            return true;
        }
        
        for (selector in selectors) {
            
            
            if (selectors.hasOwnProperty(selector) && me.match(target, selector, me.controller)) {
                
                controllers = selectors[selector];
                for (id in controllers) {
                    if (controllers.hasOwnProperty(id)) {
                        info = controllers[id];
                        if (info.controller.isActive()) {
                            
                            
                            events = info.list;
                            len = events.length;
                            for (i = 0; i < len; i++) {
                                event = events[i];
                                
                                if (event.fire.apply(event, args) === false) {
                                    return false;
                                }
                            }
                        }
                    }
                }
            }
        }
        return true;
    },
    
    listen: function(selectors, controller) {
        var me = this,
            bus = me.bus,
            idProperty = me.idProperty,
            monitoredClasses = me.monitoredClasses,
            monitoredClassesCount = monitoredClasses.length,
            controllerId = controller.getId(),
            isComponentDomain = (me.type === 'component'),
            refMap = isComponentDomain ? controller.getRefMap() : null,
            i, tree, info, selector, options, listener, scope, event, listeners, ev, classHasListeners;
        for (selector in selectors) {
            listeners = selectors[selector];
            if (isComponentDomain) {
                
                
                
                
                
                
                
                
                
                
                
                
                selector = refMap[selector] || selector;
            }
            if (listeners) {
                if (idProperty) {
                    if (!/^[*#]/.test(selector)) {
                        Ext.Error.raise('Selectors containing id should begin with #');
                    }
                    selector = selector === '*' ? selector : selector.substring(1);
                }
                for (ev in listeners) {
                    options = null;
                    listener = listeners[ev];
                    scope = controller;
                    ev = Ext.canonicalEventName(ev);
                    event = new Ext.util.Event(controller,ev);
                    
                    if (Ext.isObject(listener)) {
                        options = listener;
                        listener = options.fn;
                        scope = options.scope || controller;
                        delete options.fn;
                        delete options.scope;
                    }
                    if ((!options || !options.scope) && typeof listener === 'string') {
                        
                        
                        if (!scope[listener]) {
                            Ext.Error.raise('Cannot resolve "' + listener + '" on controller.');
                        }
                        scope = null;
                    } else if (typeof listener === 'string') {
                        listener = scope[listener];
                    }
                    event.addListener(listener, scope, options);
                    for (i = 0; i < monitoredClassesCount; ++i) {
                        classHasListeners = monitoredClasses[i].hasListeners;
                        if (classHasListeners) {
                            
                            classHasListeners._incr_(ev);
                        }
                    }
                    
                    tree = bus[ev] || (bus[ev] = {});
                    tree = tree[selector] || (tree[selector] = {});
                    info = tree[controllerId] || (tree[controllerId] = {
                        controller: controller,
                        list: []
                    });
                    
                    info.list.push(event);
                }
            }
        }
    },
    
    match: function(target, selector) {
        var idProperty = this.idProperty;
        if (idProperty) {
            return selector === '*' || target[idProperty] === selector;
        }
        return false;
    },
    
    monitor: function(observable) {
        var domain = this,
            prototype = observable.isInstance ? observable : observable.prototype,
            doFireEvent = prototype.doFireEvent;
        domain.monitoredClasses.push(observable);
        prototype.doFireEvent = function(ev, args) {
            var ret = doFireEvent.apply(this, arguments);
            if (ret !== false && !this.isSuspended(ev)) {
                ret = domain.dispatch(this, ev, args);
            }
            return ret;
        };
    },
    
    unlisten: function(controllerId) {
        var bus = this.bus,
            id = controllerId,
            monitoredClasses = this.monitoredClasses,
            monitoredClassesCount = monitoredClasses.length,
            controllers, ev, events, len, item, selector, selectors, i, j, info, classHasListeners;
        if (controllerId.isController) {
            id = controllerId.getId();
        }
        for (ev in bus) {
            ev = Ext.canonicalEventName(ev);
            if (bus.hasOwnProperty(ev) && (selectors = bus[ev])) {
                for (selector in selectors) {
                    controllers = selectors[selector];
                    info = controllers[id];
                    if (info) {
                        events = info.list;
                        if (events) {
                            for (i = 0 , len = events.length; i < len; ++i) {
                                item = events[i];
                                item.clearListeners();
                                for (j = 0; j < monitoredClassesCount; ++j) {
                                    classHasListeners = monitoredClasses[j].hasListeners;
                                    if (classHasListeners) {
                                        
                                        
                                        classHasListeners._decr_(item.name);
                                    }
                                }
                            }
                            delete controllers[id];
                        }
                    }
                }
            }
        }
    },
    destroy: function() {
        this.monitoredClasses = this.bus = null;
    }
});


Ext.define('Ext.app.domain.Component', {
    extend: 'Ext.app.EventDomain',
    singleton: true,
    requires: [
        'Ext.Widget'
    ],
    type: 'component',
    constructor: function() {
        this.callParent();
        this.monitor(Ext.Widget);
    },
    dispatch: function(target, ev, args) {
        var controller = target.lookupController(false),
            
            domain, view;
        while (controller) {
            domain = controller.compDomain;
            if (domain) {
                if (domain.dispatch(target, ev, args) === false) {
                    return false;
                }
            }
            view = controller.getView();
            controller = view ? view.lookupController(true) : null;
        }
        return this.callParent(arguments);
    },
    match: function(target, selector) {
        return target.is(selector);
    }
});



Ext.define('Ext.util.ProtoElement', function() {
    var splitWords = Ext.String.splitWords,
        toMap = Ext.Array.toMap;
    return {
        isProtoEl: true,
        
        clsProp: 'cls',
        
        styleProp: 'style',
        
        removedProp: 'removed',
        
        styleIsText: false,
        constructor: function(config) {
            var me = this,
                cls, style;
            if (config) {
                Ext.apply(me, config);
                cls = me.cls;
                style = me.style;
                delete me.cls;
            }
            me.classList = cls ? splitWords(cls) : [];
            me.classMap = cls ? toMap(me.classList) : {};
            if (style) {
                if (typeof style === 'string') {
                    me.style = Ext.Element.parseStyles(style);
                } else if (Ext.isFunction(style)) {
                    me.styleFn = style;
                    delete me.style;
                } else {
                    me.style = Ext.apply({}, style);
                }
            }
        },
        
        
        flush: function() {
            this.flushClassList = [];
            this.removedClasses = {};
            
            delete this.style;
            delete this.unselectableAttr;
        },
        
        addCls: function(cls) {
            if (!cls) {
                return this;
            }
            var me = this,
                add = (typeof cls === 'string') ? splitWords(cls) : cls,
                length = add.length,
                list = me.classList,
                map = me.classMap,
                flushList = me.flushClassList,
                i = 0,
                c;
            for (; i < length; ++i) {
                c = add[i];
                if (!map[c]) {
                    map[c] = true;
                    list.push(c);
                    if (flushList) {
                        flushList.push(c);
                        delete me.removedClasses[c];
                    }
                }
            }
            return me;
        },
        
        hasCls: function(cls) {
            return cls in this.classMap;
        },
        
        removeCls: function(cls) {
            var me = this,
                list = me.classList,
                newList = (me.classList = []),
                remove = toMap(splitWords(cls)),
                length = list.length,
                map = me.classMap,
                removedClasses = me.removedClasses,
                i, c;
            for (i = 0; i < length; ++i) {
                c = list[i];
                if (remove[c]) {
                    if (removedClasses) {
                        if (map[c]) {
                            removedClasses[c] = true;
                            Ext.Array.remove(me.flushClassList, c);
                        }
                    }
                    delete map[c];
                } else {
                    newList.push(c);
                }
            }
            return me;
        },
        
        setStyle: function(prop, value) {
            var me = this,
                style = me.style || (me.style = {});
            if (typeof prop == 'string') {
                if (arguments.length === 1) {
                    me.setStyle(Ext.Element.parseStyles(prop));
                } else {
                    style[prop] = value;
                }
            } else {
                Ext.apply(style, prop);
            }
            return me;
        },
        unselectable: function() {
            
            this.addCls(Ext.dom.Element.unselectableCls);
            if (Ext.isOpera) {
                this.unselectableAttr = true;
            }
        },
        
        writeTo: function(to) {
            var me = this,
                classList = me.flushClassList || me.classList,
                removedClasses = me.removedClasses,
                style;
            if (me.styleFn) {
                style = Ext.apply({}, me.styleFn());
                Ext.apply(style, me.style);
            } else {
                style = me.style;
            }
            to[me.clsProp] = classList.join(' ');
            if (style) {
                to[me.styleProp] = me.styleIsText ? Ext.DomHelper.generateStyles(style, null, true) : style;
            }
            if (removedClasses) {
                removedClasses = Ext.Object.getKeys(removedClasses);
                if (removedClasses.length) {
                    to[me.removedProp] = removedClasses.join(' ');
                }
            }
            if (me.unselectableAttr) {
                to.unselectable = 'on';
            }
            return to;
        }
    };
});


Ext.define('Ext.dom.CompositeElement', {
    alternateClassName: 'Ext.CompositeElement',
    extend: 'Ext.dom.CompositeElementLite',
    isLite: false,
    
    getElement: function(el) {
        
        return el;
    },
    
    transformElement: function(el) {
        return Ext.get(el);
    }
});



Ext.Factory = function(type) {
    var me = this;
    me.aliasPrefix = type + '.';
    me.cache = {};
    me.name = type.replace(me.fixNameRe, me.fixNameFn);
    me.type = type;
};
Ext.Factory.prototype = {
    
    
    defaultProperty: 'type',
    
    
    instanceProp: 'isInstance',
    
    
    
    create: function(config, defaultType) {
        var me = this,
            Manager = Ext.ClassManager,
            cache = me.cache,
            alias, className, klass, suffix;
        if (config) {
            if (config[me.instanceProp]) {
                return config;
            }
            if (typeof config === 'string') {
                suffix = config;
                config = {};
                config[me.defaultProperty] = suffix;
            }
            className = config.xclass;
            suffix = config.type;
        }
        if (className) {
            if (!(klass = Manager.get(className))) {
                return Manager.instantiate(className, config);
            }
        } else {
            if (!(suffix = suffix || defaultType || me.defaultType)) {
                klass = me.defaultClass;
            }
            if (!suffix && !klass) {
                Ext.Error.raise('No type specified for ' + me.type + '.create');
            }
            if (!klass && !(klass = cache[suffix])) {
                alias = me.aliasPrefix + suffix;
                className = Manager.getNameByAlias(alias);
                
                if (!(klass = className && Manager.get(className))) {
                    return Manager.instantiateByAlias(alias, config);
                }
                cache[suffix] = klass;
            }
        }
        return klass.isInstance ? klass : new klass(config);
    },
    fixNameRe: /\.[a-z]/ig,
    fixNameFn: function(match) {
        return match.substring(1).toUpperCase();
    },
    clearCache: function() {
        this.cache = {};
    }
};

Ext.Factory.define = function(type, config) {
    var Factory = Ext.Factory,
        defaultClass, factory, fn;
    if (type.constructor === Object) {
        Ext.Object.each(type, Factory.define, Factory);
    } else {
        factory = new Ext.Factory(type);
        if (config) {
            if (config.constructor === Object) {
                Ext.apply(factory, config);
                if (typeof (defaultClass = factory.xclass) === 'string') {
                    factory.defaultClass = Ext.ClassManager.get(defaultClass);
                }
            } else {
                factory.defaultType = config;
            }
        }
        Factory[factory.name] = fn = factory.create.bind(factory);
        fn.instance = factory;
    }
    return fn;
};

Ext.define('Ext.mixin.Factoryable', {
    mixinId: 'factoryable',
    onClassMixedIn: function(targetClass) {
        var proto = targetClass.prototype,
            factoryConfig = proto.factoryConfig,
            alias = proto.alias,
            config = {},
            dot, createFn;
        alias = alias && alias.length && alias[0];
        if (alias && (dot = alias.lastIndexOf('.')) > 0) {
            config.type = alias.substring(0, dot);
            config.defaultType = alias.substring(dot + 1);
        }
        if (factoryConfig) {
            delete proto.factoryConfig;
            Ext.apply(config, factoryConfig);
        }
        createFn = Ext.Factory.define(config.type, config);
        if (targetClass.create === Ext.Base.create) {
            
            targetClass.create = createFn;
        }
    }
});


Ext.define('Ext.scroll.Scroller', {
    extend: 'Ext.Evented',
    alias: 'scroller.scroller',
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    uses: [
        'Ext.scroll.TouchScroller',
        'Ext.scroll.DomScroller'
    ],
    factoryConfig: {
        defaultType: 'dom'
    },
    isScroller: true,
    
    
    
    
    config: {
        
        direction: undefined,
        
        
        directionLock: false,
        
        disabled: null,
        
        element: undefined,
        
        indicators: null,
        
        maxPosition: null,
        
        maxUserPosition: null,
        
        minPosition: {
            x: 0,
            y: 0
        },
        
        minUserPosition: {
            x: 0,
            y: 0
        },
        
        momentumEasing: null,
        
        size: null,
        
        x: true,
        
        y: true
    },
    statics: {
        
        create: function(config) {
            return Ext.Factory.scroller(config, Ext.supports.Touch ? 'touch' : 'dom');
        }
    },
    constructor: function(config) {
        var me = this;
        me.callParent([
            config
        ]);
        me.onDomScrollEnd = Ext.Function.createBuffered(me.onDomScrollEnd, 100, me);
    },
    
    addPartner: function(partner, axis) {
        var me = this,
            partners = me._partners || (me._partners = {}),
            otherPartners = partner._partners || (partner._partners = {});
        partners[partner.getId()] = {
            scroller: partner,
            axis: axis
        };
        otherPartners[me.getId()] = {
            scroller: me,
            axis: axis
        };
    },
    applyElement: function(element) {
        var el;
        if (element) {
            if (element.isElement) {
                el = element;
            } else {
                el = Ext.get(element);
                if (!el && (typeof element === 'string')) {
                    Ext.Error.raise("Cannot create Ext.scroll.Scroller instance. " + "Element with id '" + element + "' not found.");
                }
            }
        }
        return el;
    },
    
    updateDirectionLock: Ext.emptyFn,
    updateDisabled: Ext.emptyFn,
    updateIndicators: Ext.emptyFn,
    updateMaxPosition: Ext.emptyFn,
    updateMaxUserPosition: Ext.emptyFn,
    updateMinPosition: Ext.emptyFn,
    updateMinUserPosition: Ext.emptyFn,
    updateMomenumEasing: Ext.emptyFn,
    updateSize: Ext.emptyFn,
    updateX: Ext.emptyFn,
    updateY: Ext.emptyFn,
    updateElement: function(element) {
        element.on('scroll', 'onDomScroll', this);
    },
    
    
    
    
    
    refresh: function() {
        this.fireEvent('refresh', this);
        return this;
    },
    
    removePartner: function(partner) {
        var partners = this._partners,
            otherPartners = partner._partners;
        if (partners) {
            delete partners[partner.getId()];
        }
        if (otherPartners) {
            delete (otherPartners[this.getId()]);
        }
    },
    
    scrollBy: function(deltaX, deltaY, animate) {
        var position = this.getPosition();
        if (deltaX) {
            if (deltaX.length) {
                
                animate = deltaY;
                deltaY = deltaX[1];
                deltaX = deltaX[0];
            } else if (typeof deltaX !== 'number') {
                
                animate = deltaY;
                deltaY = deltaX.y;
                deltaX = deltaX.x;
            }
        }
        deltaX = (typeof deltaX === 'number') ? deltaX + position.x : null;
        deltaY = (typeof deltaY === 'number') ? deltaY + position.y : null;
        return this.doScrollTo(deltaX, deltaY, animate);
    },
    
    scrollIntoView: function(el, hscroll, animate, highlight) {
        var me = this,
            position = me.getPosition(),
            newPosition, newX, newY;
        newPosition = Ext.fly(el).getScrollIntoViewXY(me.getElement(), position.x, position.y);
        newX = (hscroll === false) ? position.x : newPosition.x;
        newY = newPosition.y;
        if (highlight) {
            me.on({
                scrollend: 'doHighlight',
                scope: me,
                single: true,
                args: [
                    el,
                    highlight
                ]
            });
        }
        me.doScrollTo(newX, newY, animate);
    },
    
    scrollTo: function(x, y, animate) {
        var maxPosition;
        if (x) {
            if (x.length) {
                
                animate = y;
                y = x[1];
                x = x[0];
            } else if (typeof x !== 'number') {
                
                animate = y;
                y = x.y;
                x = x.x;
            }
        }
        if (x < 0 || y < 0) {
            maxPosition = this.getMaxPosition();
            if (x < 0) {
                x += maxPosition.x;
            }
            if (y < 0) {
                y += maxPosition.y;
            }
        }
        this.doScrollTo(x, y, animate);
    },
    updateDirection: function(direction) {
        var me = this,
            x, y;
        if (!direction) {
            
            
            
            x = me.getX();
            y = me.getY();
            if (x && y) {
                direction = (y === 'scroll' && x === 'scroll') ? 'both' : 'auto';
            } else if (y) {
                direction = 'vertical';
            } else if (x) {
                direction = 'horizontal';
            }
            
            
            me._direction = direction;
        } else {
            if (direction === 'auto') {
                x = true;
                y = true;
            } else if (direction === 'vertical') {
                x = false;
                y = true;
            } else if (direction === 'horizontal') {
                x = true;
                y = false;
            } else if (direction === 'both') {
                x = 'scroll';
                y = 'scroll';
            }
            me.setX(x);
            me.setY(y);
        }
    },
    deprecated: {
        '5': {
            methods: {
                
                getScroller: function() {
                    return this;
                }
            }
        },
        '5.1.0': {
            methods: {
                
                scrollToTop: function(animate) {
                    return this.scrollTo(0, 0, animate);
                },
                
                scrollToEnd: function(animate) {
                    return this.scrollTo(Infinity, Infinity, animate);
                }
            }
        }
    },
    privates: {
        
        convertX: function(x) {
            return x;
        },
        
        doHighlight: function(el, highlight) {
            if (highlight !== true) {
                
                Ext.fly(el).highlight(highlight);
            } else {
                Ext.fly(el).highlight();
            }
        },
        fireScrollStart: function(x, y) {
            var me = this,
                component = me.component;
            me.invokePartners('onPartnerScrollStart', x, y);
            if (me.hasListeners.scrollstart) {
                me.fireEvent('scrollstart', me, x, y);
            }
            if (component && component.onScrollStart) {
                component.onScrollStart(x, y);
            }
        },
        fireScroll: function(x, y) {
            var me = this,
                component = me.component;
            me.invokePartners('onPartnerScroll', x, y);
            if (me.hasListeners.scroll) {
                me.fireEvent('scroll', me, x, y);
            }
            if (component && component.onScrollMove) {
                component.onScrollMove(x, y);
            }
        },
        fireScrollEnd: function(x, y) {
            var me = this,
                component = me.component;
            me.invokePartners('onPartnerScrollEnd', x, y);
            if (me.hasListeners.scrollend) {
                me.fireEvent('scrollend', me, x, y);
            }
            if (component && component.onScrollEnd) {
                component.onScrollEnd(x, y);
            }
        },
        invokePartners: function(method, x, y) {
            var partners = this._partners,
                partner, id;
            if (!this.suspendSync) {
                for (id in partners) {
                    partner = partners[id];
                    if (!partner.suspendSync) {
                        partner.scroller[method](this, x, y);
                    }
                }
            }
        },
        suspendPartnerSync: function() {
            this.suspendSync = (this.suspendSync || 0) + 1;
        },
        resumePartnerSync: function() {
            if (this.suspendSync) {
                this.suspendSync--;
            }
        },
        
        
        
        
        
        
        onDomScroll: function() {
            var me = this,
                position = me.getPosition(),
                x = position.x,
                y = position.y;
            if (!me.isScrolling) {
                me.isScrolling = true;
                me.fireScrollStart(x, y);
            }
            me.fireScroll(x, y);
            
            
            me.onDomScrollEnd();
        },
        onDomScrollEnd: function() {
            var me = this,
                position = me.getPosition();
            me.isScrolling = false;
            me.fireScrollEnd(position.x, position.y);
        },
        onPartnerScroll: function(partner, x, y) {
            var axis = partner._partners[this.getId()].axis;
            if (axis) {
                if (axis === 'x') {
                    y = null;
                } else if (axis === 'y') {
                    x = null;
                }
            }
            this.doScrollTo(x, y, null, true);
        },
        onPartnerScrollEnd: Ext.privateFn,
        onPartnerScrollStart: Ext.privateFn
    }
});


Ext.define('Ext.fx.easing.Abstract', {
    config: {
        startTime: 0,
        startValue: 0
    },
    isEasing: true,
    isEnded: false,
    constructor: function(config) {
        this.initConfig(config);
        return this;
    },
    applyStartTime: function(startTime) {
        if (!startTime) {
            startTime = Ext.Date.now();
        }
        return startTime;
    },
    updateStartTime: function(startTime) {
        this.reset();
    },
    reset: function() {
        this.isEnded = false;
    },
    getValue: Ext.emptyFn
});


Ext.define('Ext.fx.easing.Momentum', {
    extend: 'Ext.fx.easing.Abstract',
    config: {
        acceleration: 30,
        friction: 0,
        startVelocity: 0
    },
    alpha: 0,
    updateFriction: function(friction) {
        var theta = Math.log(1 - (friction / 10));
        this.theta = theta;
        this.alpha = theta / this.getAcceleration();
    },
    updateStartVelocity: function(velocity) {
        this.velocity = velocity * this.getAcceleration();
    },
    updateAcceleration: function(acceleration) {
        this.velocity = this.getStartVelocity() * acceleration;
        this.alpha = this.theta / acceleration;
    },
    getValue: function() {
        return this.getStartValue() - this.velocity * (1 - this.getFrictionFactor()) / this.theta;
    },
    getFrictionFactor: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime();
        return Math.exp(deltaTime * this.alpha);
    },
    getVelocity: function() {
        return this.getFrictionFactor() * this.velocity;
    }
});


Ext.define('Ext.fx.easing.Bounce', {
    extend: 'Ext.fx.easing.Abstract',
    config: {
        springTension: 0.3,
        acceleration: 30,
        startVelocity: 0
    },
    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            theta = (deltaTime / this.getAcceleration()),
            powTime = theta * Math.pow(Math.E, -this.getSpringTension() * theta);
        return this.getStartValue() + (this.getStartVelocity() * powTime);
    }
});


Ext.define('Ext.fx.easing.BoundMomentum', {
    extend: 'Ext.fx.easing.Abstract',
    requires: [
        'Ext.fx.easing.Momentum',
        'Ext.fx.easing.Bounce'
    ],
    config: {
        
        momentum: null,
        
        bounce: null,
        minMomentumValue: 0,
        maxMomentumValue: 0,
        
        minVelocity: 0.01,
        
        startVelocity: 0
    },
    applyMomentum: function(config, currentEasing) {
        return Ext.factory(config, Ext.fx.easing.Momentum, currentEasing);
    },
    applyBounce: function(config, currentEasing) {
        return Ext.factory(config, Ext.fx.easing.Bounce, currentEasing);
    },
    updateStartTime: function(startTime) {
        this.getMomentum().setStartTime(startTime);
        this.callParent(arguments);
    },
    updateStartVelocity: function(startVelocity) {
        this.getMomentum().setStartVelocity(startVelocity);
    },
    updateStartValue: function(startValue) {
        this.getMomentum().setStartValue(startValue);
    },
    reset: function() {
        this.lastValue = null;
        this.isBouncingBack = false;
        this.isOutOfBound = false;
        return this.callParent(arguments);
    },
    getValue: function() {
        var momentum = this.getMomentum(),
            bounce = this.getBounce(),
            startVelocity = momentum.getStartVelocity(),
            direction = startVelocity > 0 ? 1 : -1,
            minValue = this.getMinMomentumValue(),
            maxValue = this.getMaxMomentumValue(),
            boundedValue = (direction == 1) ? maxValue : minValue,
            lastValue = this.lastValue,
            value, velocity;
        if (startVelocity === 0) {
            return this.getStartValue();
        }
        if (!this.isOutOfBound) {
            value = momentum.getValue();
            velocity = momentum.getVelocity();
            if (Math.abs(velocity) < this.getMinVelocity()) {
                this.isEnded = true;
            }
            if (value >= minValue && value <= maxValue) {
                return value;
            }
            this.isOutOfBound = true;
            bounce.setStartTime(Ext.Date.now()).setStartVelocity(velocity).setStartValue(boundedValue);
        }
        value = bounce.getValue();
        if (!this.isEnded) {
            if (!this.isBouncingBack) {
                if (lastValue !== null) {
                    if ((direction == 1 && value < lastValue) || (direction == -1 && value > lastValue)) {
                        this.isBouncingBack = true;
                    }
                }
            } else {
                if (Math.round(value) == boundedValue) {
                    this.isEnded = true;
                }
            }
        }
        this.lastValue = value;
        return value;
    }
});


Ext.define('Ext.fx.easing.Linear', {
    extend: 'Ext.fx.easing.Abstract',
    alias: 'easing.linear',
    config: {
        duration: 0,
        endValue: 0
    },
    updateStartValue: function(startValue) {
        this.distance = this.getEndValue() - startValue;
    },
    updateEndValue: function(endValue) {
        this.distance = endValue - this.getStartValue();
    },
    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            duration = this.getDuration();
        if (deltaTime > duration) {
            this.isEnded = true;
            return this.getEndValue();
        } else {
            return this.getStartValue() + ((deltaTime / duration) * this.distance);
        }
    }
});


Ext.define('Ext.fx.easing.EaseOut', {
    extend: 'Ext.fx.easing.Linear',
    alias: 'easing.ease-out',
    config: {
        exponent: 4,
        duration: 1500
    },
    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            duration = this.getDuration(),
            startValue = this.getStartValue(),
            endValue = this.getEndValue(),
            distance = this.distance,
            theta = deltaTime / duration,
            thetaC = 1 - theta,
            thetaEnd = 1 - Math.pow(thetaC, this.getExponent()),
            currentValue = startValue + (thetaEnd * distance);
        if (deltaTime >= duration) {
            this.isEnded = true;
            return endValue;
        }
        return currentValue;
    }
});


Ext.define('Ext.util.translatable.Abstract', {
    extend: 'Ext.Evented',
    requires: [
        'Ext.fx.easing.Linear'
    ],
    config: {
        useWrapper: null,
        easing: null,
        easingX: null,
        easingY: null
    },
    
    
    
    
    x: 0,
    
    y: 0,
    activeEasingX: null,
    activeEasingY: null,
    isAnimating: false,
    isTranslatable: true,
    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);
        
        
        
        this.position = {
            x: 0,
            y: 0
        };
    },
    factoryEasing: function(easing) {
        return Ext.factory(easing, Ext.fx.easing.Linear, null, 'easing');
    },
    applyEasing: function(easing) {
        if (!this.getEasingX()) {
            this.setEasingX(this.factoryEasing(easing));
        }
        if (!this.getEasingY()) {
            this.setEasingY(this.factoryEasing(easing));
        }
    },
    applyEasingX: function(easing) {
        return this.factoryEasing(easing);
    },
    applyEasingY: function(easing) {
        return this.factoryEasing(easing);
    },
    doTranslate: Ext.emptyFn,
    translate: function(x, y, animation) {
        if (animation) {
            return this.translateAnimated(x, y, animation);
        }
        if (this.isAnimating) {
            this.stopAnimation();
        }
        if (!isNaN(x) && typeof x == 'number') {
            this.x = x;
        }
        if (!isNaN(y) && typeof y == 'number') {
            this.y = y;
        }
        this.doTranslate(x, y);
    },
    translateAxis: function(axis, value, animation) {
        var x, y;
        if (axis == 'x') {
            x = value;
        } else {
            y = value;
        }
        return this.translate(x, y, animation);
    },
    
    getPosition: function() {
        var me = this,
            position = me.position;
        position.x = -me.x;
        position.y = -me.y;
        return position;
    },
    animate: function(easingX, easingY) {
        this.activeEasingX = easingX;
        this.activeEasingY = easingY;
        this.isAnimating = true;
        this.lastX = null;
        this.lastY = null;
        Ext.AnimationQueue.start(this.doAnimationFrame, this);
        this.fireEvent('animationstart', this, this.x, this.y);
        return this;
    },
    translateAnimated: function(x, y, animation) {
        var me = this;
        if (!Ext.isObject(animation)) {
            animation = {};
        }
        if (me.isAnimating) {
            me.stopAnimation();
        }
        
        me.callback = animation.callback;
        me.callbackScope = animation.scope;
        var now = Ext.Date.now(),
            easing = animation.easing,
            easingX = (typeof x == 'number') ? (animation.easingX || easing || me.getEasingX() || true) : null,
            easingY = (typeof y == 'number') ? (animation.easingY || easing || me.getEasingY() || true) : null;
        if (easingX) {
            easingX = me.factoryEasing(easingX);
            easingX.setStartTime(now);
            easingX.setStartValue(me.x);
            easingX.setEndValue(x);
            if ('duration' in animation) {
                easingX.setDuration(animation.duration);
            }
        }
        if (easingY) {
            easingY = me.factoryEasing(easingY);
            easingY.setStartTime(now);
            easingY.setStartValue(me.y);
            easingY.setEndValue(y);
            if ('duration' in animation) {
                easingY.setDuration(animation.duration);
            }
        }
        return me.animate(easingX, easingY);
    },
    doAnimationFrame: function() {
        var me = this,
            easingX = me.activeEasingX,
            easingY = me.activeEasingY,
            now = Date.now(),
            x, y;
        if (!me.isAnimating) {
            return;
        }
        me.lastRun = now;
        if (easingX === null && easingY === null) {
            me.stopAnimation();
            return;
        }
        if (easingX !== null) {
            me.x = x = Math.round(easingX.getValue());
            if (easingX.isEnded) {
                me.activeEasingX = null;
                me.fireEvent('axisanimationend', me, 'x', x);
            }
        } else {
            x = me.x;
        }
        if (easingY !== null) {
            me.y = y = Math.round(easingY.getValue());
            if (easingY.isEnded) {
                me.activeEasingY = null;
                me.fireEvent('axisanimationend', me, 'y', y);
            }
        } else {
            y = me.y;
        }
        if (me.lastX !== x || me.lastY !== y) {
            me.doTranslate(x, y);
            me.lastX = x;
            me.lastY = y;
        }
        me.fireEvent('animationframe', me, x, y);
    },
    stopAnimation: function() {
        var me = this;
        if (!me.isAnimating) {
            return;
        }
        me.activeEasingX = null;
        me.activeEasingY = null;
        me.isAnimating = false;
        Ext.AnimationQueue.stop(me.doAnimationFrame, me);
        me.fireEvent('animationend', me, me.x, me.y);
        if (me.callback) {
            me.callback.call(me.callbackScope);
            me.callback = null;
        }
    },
    refresh: function() {
        this.translate(this.x, this.y);
    },
    destroy: function() {
        if (this.isAnimating) {
            this.stopAnimation();
        }
        this.callParent(arguments);
    }
});


Ext.define('Ext.util.translatable.Dom', {
    extend: 'Ext.util.translatable.Abstract',
    config: {
        element: null
    },
    applyElement: function(element) {
        if (!element) {
            return;
        }
        return Ext.get(element);
    },
    updateElement: function() {
        this.refresh();
    }
});


Ext.define('Ext.util.translatable.CssTransform', {
    extend: 'Ext.util.translatable.Dom',
    doTranslate: function(x, y) {
        var element = this.getElement();
        if (!this.isDestroyed && !element.isDestroyed) {
            element.translate(x, y);
        }
    },
    destroy: function() {
        var element = this.getElement();
        if (element && !element.isDestroyed) {
            element.dom.style.webkitTransform = null;
        }
        this.callParent();
    }
});


Ext.define('Ext.util.translatable.ScrollPosition', {
    extend: 'Ext.util.translatable.Dom',
    type: 'scrollposition',
    config: {
        useWrapper: true
    },
    getWrapper: function() {
        var wrapper = this.wrapper,
            element = this.getElement(),
            container;
        if (!wrapper) {
            container = element.getParent();
            if (!container) {
                return null;
            }
            if (container.hasCls(Ext.baseCSSPrefix + 'translatable-hboxfix')) {
                container = container.getParent();
            }
            if (this.getUseWrapper()) {
                wrapper = element.wrap();
            } else {
                wrapper = container;
            }
            element.addCls(Ext.baseCSSPrefix + 'translatable');
            wrapper.addCls(Ext.baseCSSPrefix + 'translatable-container');
            this.wrapper = wrapper;
            wrapper.on('painted', function() {
                if (!this.isAnimating) {
                    this.refresh();
                }
            }, this);
            this.refresh();
        }
        return wrapper;
    },
    doTranslate: function(x, y) {
        var wrapper = this.getWrapper(),
            dom;
        if (wrapper) {
            dom = wrapper.dom;
            if (typeof x == 'number') {
                dom.scrollLeft = 500000 - x;
            }
            if (typeof y == 'number') {
                dom.scrollTop = 500000 - y;
            }
        }
    },
    destroy: function() {
        var element = this.getElement(),
            wrapper = this.wrapper;
        if (wrapper) {
            if (!element.isDestroyed) {
                if (this.getUseWrapper()) {
                    wrapper.doReplaceWith(element);
                }
                element.removeCls(Ext.baseCSSPrefix + 'translatable');
            }
            if (!wrapper.isDestroyed) {
                wrapper.removeCls(Ext.baseCSSPrefix + 'translatable-container');
                wrapper.un('painted', 'refresh', this);
            }
            delete this.wrapper;
            delete this._element;
        }
        this.callParent();
    }
});


Ext.define('Ext.util.translatable.ScrollParent', {
    extend: 'Ext.util.translatable.Dom',
    isScrollParent: true,
    applyElement: function(element) {
        var el = Ext.get(element);
        if (el) {
            this.parent = el.parent();
        }
        return el;
    },
    doTranslate: function(x, y) {
        var parent = this.parent;
        parent.setScrollLeft(Math.round(-x));
        parent.setScrollTop(Math.round(-y));
    },
    getPosition: function() {
        var me = this,
            position = me.position,
            parent = me.parent;
        position.x = parent.getScrollLeft();
        position.y = parent.getScrollTop();
        return position;
    }
});


Ext.define('Ext.util.translatable.CssPosition', {
    extend: 'Ext.util.translatable.Dom',
    doTranslate: function(x, y) {
        var domStyle = this.getElement().dom.style;
        if (typeof x == 'number') {
            domStyle.left = x + 'px';
        }
        if (typeof y == 'number') {
            domStyle.top = y + 'px';
        }
    },
    destroy: function() {
        var domStyle = this.getElement().dom.style;
        domStyle.left = null;
        domStyle.top = null;
        this.callParent(arguments);
    }
});


Ext.define('Ext.util.Translatable', {
    requires: [
        'Ext.util.translatable.CssTransform',
        'Ext.util.translatable.ScrollPosition',
        'Ext.util.translatable.ScrollParent',
        'Ext.util.translatable.CssPosition'
    ],
    constructor: function(config) {
        var namespace = Ext.util.translatable;
        switch (Ext.browser.getPreferredTranslationMethod(config)) {
            case 'scrollposition':
                return new namespace.ScrollPosition(config);
            case 'scrollparent':
                return new namespace.ScrollParent(config);
            case 'csstransform':
                return new namespace.CssTransform(config);
            case 'cssposition':
                return new namespace.CssPosition(config);
        }
    }
});


Ext.define('Ext.scroll.Indicator', {
    extend: 'Ext.Widget',
    xtype: 'scrollindicator',
    config: {
        
        axis: null,
        
        hideAnimation: true,
        
        hideDelay: 0,
        
        scroller: null,
        
        minLength: 24
    },
    defaultHideAnimation: {
        to: {
            opacity: 0
        },
        duration: 300
    },
    names: {
        x: {
            side: 'l',
            getSize: 'getHeight',
            setLength: 'setWidth',
            translate: 'translateX'
        },
        y: {
            side: 't',
            getSize: 'getWidth',
            setLength: 'setHeight',
            translate: 'translateY'
        }
    },
    oppositeAxis: {
        x: 'y',
        y: 'x'
    },
    cls: Ext.baseCSSPrefix + 'scroll-indicator',
    applyHideAnimation: function(hideAnimation) {
        if (hideAnimation) {
            hideAnimation = Ext.mergeIf({
                onEnd: this.onHideAnimationEnd,
                scope: this
            }, this.defaultHideAnimation, hideAnimation);
        }
        return hideAnimation;
    },
    constructor: function(config) {
        var me = this,
            axis;
        me.callParent([
            config
        ]);
        axis = me.getAxis();
        me.names = me.names[axis];
        me.element.addCls(me.cls + ' ' + me.cls + '-' + axis);
    },
    
    hide: function() {
        var me = this,
            delay = me.getHideDelay();
        if (delay) {
            me._hideTimer = Ext.defer(me.doHide, delay, me);
        } else {
            me.doHide();
        }
    },
    
    setValue: function(value) {
        var me = this,
            el = me.element,
            names = me.names,
            axis = me.getAxis(),
            scroller = me.getScroller(),
            maxScrollPosition = scroller.getMaxUserPosition()[axis],
            elementSize = scroller.getElementSize()[axis],
            baseLength = me.length,
            minLength = me.getMinLength(),
            length = baseLength,
            maxPosition = elementSize - baseLength - me.sizeAdjust,
            round = Math.round,
            max = Math.max,
            position;
        if (value < 0) {
            length = round(max(baseLength + (baseLength * value / elementSize), minLength));
            position = 0;
        } else if (value > maxScrollPosition) {
            length = round(max(baseLength - (baseLength * (value - maxScrollPosition) / elementSize), minLength));
            position = maxPosition + baseLength - length;
        } else {
            position = round(value / maxScrollPosition * maxPosition);
        }
        me[names.translate](position);
        el[names.setLength](length);
    },
    
    show: function() {
        var me = this,
            el = me.element,
            anim = el.getActiveAnimation();
        if (anim) {
            anim.end();
        }
        if (!me._inDom) {
            
            me.getScroller().getElement().appendChild(el);
            me._inDom = true;
            if (!me.size) {
                me.cacheStyles();
            }
        }
        me.refreshLength();
        clearTimeout(me._hideTimer);
        el.setStyle('opacity', '');
    },
    privates: {
        
        cacheStyles: function() {
            var me = this,
                el = me.element,
                axis = me.getAxis(),
                names = me.names;
            
            me.size = el[names.getSize]();
            
            me.margin = el.getMargin(names.side);
        },
        doHide: function() {
            var animation = this.getHideAnimation(),
                el = this.element;
            if (animation) {
                el.animate(animation);
            } else {
                el.setStyle('opacity', 0);
            }
        },
        
        hasOpposite: function() {
            return this.getScroller().isAxisEnabled(this.oppositeAxis[this.getAxis()]);
        },
        onHideAnimationEnd: function() {
            
            
            this.element.setStyle('opacity', '0');
        },
        refreshLength: function() {
            var me = this,
                names = me.names,
                axis = me.getAxis(),
                scroller = me.getScroller(),
                scrollSize = scroller.getSize()[axis],
                elementSize = scroller.getElementSize()[axis],
                ratio = elementSize / scrollSize,
                baseSizeAdjust = me.margin * 2,
                sizeAdjust = me.hasOpposite() ? (baseSizeAdjust + me.size) : baseSizeAdjust,
                length = Math.max(Math.round((elementSize - sizeAdjust) * ratio), me.getMinLength());
            me.sizeAdjust = sizeAdjust;
            
            me.length = length;
            me.element[names.setLength](length);
        },
        translateX: function(value) {
            this.element.translate(value);
        },
        translateY: function(value) {
            this.element.translate(0, value);
        }
    }
});


Ext.define('Ext.scroll.TouchScroller', {
    extend: 'Ext.scroll.Scroller',
    alias: 'scroller.touch',
    requires: [
        'Ext.fx.easing.BoundMomentum',
        'Ext.fx.easing.EaseOut',
        'Ext.util.Translatable',
        'Ext.scroll.Indicator',
        'Ext.GlobalEvents'
    ],
    isTouchScroller: true,
    config: {
        
        autoRefresh: true,
        
        bounceEasing: {
            duration: 400
        },
        
        elementSize: undefined,
        indicators: true,
        
        fps: 'auto',
        
        maxAbsoluteVelocity: 6,
        
        momentumEasing: {
            momentum: {
                acceleration: 30,
                friction: 0.5
            },
            bounce: {
                acceleration: 30,
                springTension: 0.3
            },
            minVelocity: 1
        },
        
        outOfBoundRestrictFactor: 0.5,
        
        innerElement: null,
        size: undefined,
        
        slotSnapEasing: {
            duration: 150
        },
        
        slotSnapSize: {
            x: 0,
            y: 0
        },
        
        slotSnapOffset: {
            x: 0,
            y: 0
        },
        
        startMomentumResetTime: 300,
        
        translatable: {
            translationMethod: 'auto',
            useWrapper: false
        }
    },
    cls: Ext.baseCSSPrefix + 'scroll-container',
    scrollerCls: Ext.baseCSSPrefix + 'scroll-scroller',
    dragStartTime: 0,
    dragEndTime: 0,
    isDragging: false,
    isAnimating: false,
    isMouseEvent: {
        mousedown: 1,
        mousemove: 1,
        mouseup: 1
    },
    listenerMap: {
        touchstart: 'onTouchStart',
        touchmove: 'onTouchMove',
        touchend: 'onTouchEnd',
        dragstart: 'onDragStart',
        drag: 'onDrag',
        dragend: 'onDragEnd'
    },
    refreshCounter: 0,
    constructor: function(config) {
        var me = this,
            onEvent = 'onEvent';
        me.elementListeners = {
            touchstart: onEvent,
            touchmove: onEvent,
            touchend: onEvent,
            dragstart: onEvent,
            drag: onEvent,
            dragend: onEvent,
            scope: me
        };
        me.minPosition = {
            x: 0,
            y: 0
        };
        me.startPosition = {
            x: 0,
            y: 0
        };
        me.position = {
            x: 0,
            y: 0
        };
        me.velocity = {
            x: 0,
            y: 0
        };
        me.isAxisEnabledFlags = {
            x: false,
            y: false
        };
        me.flickStartPosition = {
            x: 0,
            y: 0
        };
        me.flickStartTime = {
            x: 0,
            y: 0
        };
        me.lastDragPosition = {
            x: 0,
            y: 0
        };
        me.dragDirection = {
            x: 0,
            y: 0
        };
        Ext.GlobalEvents.on('idle', me.onIdle, me);
        me.callParent([
            config
        ]);
        me.refreshAxes();
    },
    applyBounceEasing: function(easing) {
        var defaultClass = Ext.fx.easing.EaseOut;
        return {
            x: Ext.factory(easing, defaultClass),
            y: Ext.factory(easing, defaultClass)
        };
    },
    applyElementSize: function(size) {
        var el = this.getElement(),
            dom, x, y;
        if (!el) {
            return null;
        }
        dom = el.dom;
        if (!dom) {
            return;
        }
        if (size == null) {
            
            x = dom.clientWidth;
            y = dom.clientHeight;
        } else {
            x = size.x;
            y = size.y;
        }
        return {
            x: x,
            y: y
        };
    },
    applyIndicators: function(indicators, oldIndicators) {
        var me = this,
            xIndicator, yIndicator, x, y;
        if (indicators) {
            if (indicators === true) {
                xIndicator = yIndicator = {};
            } else {
                x = indicators.x;
                y = indicators.y;
                if (x || y) {
                    
                    
                    
                    xIndicator = (x == null || x === true) ? {} : x;
                    yIndicator = (x == null || y === true) ? {} : y;
                } else {
                    
                    
                    xIndicator = yIndicator = indicators;
                }
            }
            if (oldIndicators) {
                if (xIndicator) {
                    oldIndicators.x.setConfig(xIndicator);
                } else {
                    oldIndicators.x.destroy();
                    oldIndicators.x = null;
                }
                if (yIndicator) {
                    oldIndicators.y.setConfig(yIndicator);
                } else {
                    oldIndicators.y.destroy();
                    oldIndicators.y = null;
                }
                indicators = oldIndicators;
            } else {
                indicators = {
                    x: null,
                    y: null
                };
                if (xIndicator) {
                    indicators.x = new Ext.scroll.Indicator(Ext.applyIf({
                        axis: 'x',
                        scroller: me
                    }, xIndicator));
                }
                if (yIndicator) {
                    indicators.y = new Ext.scroll.Indicator(Ext.applyIf({
                        axis: 'y',
                        scroller: me
                    }, yIndicator));
                }
            }
        } else if (oldIndicators) {
            oldIndicators.x.destroy();
            oldIndicators.y.destroy();
            oldIndicators.x = null;
            oldIndicators.y = null;
        }
        return indicators;
    },
    applyMomentumEasing: function(easing) {
        var defaultClass = Ext.fx.easing.BoundMomentum;
        return {
            x: Ext.factory(easing, defaultClass),
            y: Ext.factory(easing, defaultClass)
        };
    },
    applyInnerElement: function(innerElement) {
        if (innerElement && !innerElement.isElement) {
            innerElement = Ext.get(innerElement);
        }
        if (this.isConfiguring && !innerElement) {
            Ext.Error.raise("Cannot create Ext.scroll.TouchScroller instance with null innerElement");
        }
        return innerElement;
    },
    applySize: function(size) {
        var el, dom, scrollerDom, x, y;
        if (size == null) {
            
            el = this.getElement();
            if (!el) {
                return null;
            }
            dom = el.dom;
            scrollerDom = this.getInnerElement().dom;
            
            
            x = Math.max(scrollerDom.scrollWidth, dom.clientWidth);
            y = Math.max(scrollerDom.scrollHeight, dom.clientHeight);
        } else if (typeof size === 'number') {
            x = size;
            y = size;
        } else {
            x = size.x;
            y = size.y;
        }
        return {
            x: x,
            y: y
        };
    },
    applySlotSnapOffset: function(snapOffset) {
        if (typeof snapOffset == 'number') {
            return {
                x: snapOffset,
                y: snapOffset
            };
        }
        return snapOffset;
    },
    applySlotSnapSize: function(snapSize) {
        if (typeof snapSize == 'number') {
            return {
                x: snapSize,
                y: snapSize
            };
        }
        return snapSize;
    },
    applySlotSnapEasing: function(easing) {
        var defaultClass = Ext.fx.easing.EaseOut;
        return {
            x: Ext.factory(easing, defaultClass),
            y: Ext.factory(easing, defaultClass)
        };
    },
    applyTranslatable: function(config, translatable) {
        return Ext.factory(config, Ext.util.Translatable, translatable);
    },
    destroy: function() {
        var me = this,
            element = me.getElement(),
            innerElement = me.getInnerElement(),
            sizeMonitors = me.sizeMonitors;
        if (sizeMonitors) {
            sizeMonitors.element.destroy();
            sizeMonitors.container.destroy();
        }
        if (element && !element.isDestroyed) {
            element.removeCls(me.cls);
        }
        if (innerElement && !innerElement.isDestroyed) {
            innerElement.removeCls(me.scrollerCls);
        }
        if (me._isWrapped) {
            if (!element.isDestroyed) {
                me.unwrapContent();
            }
            innerElement.destroy();
            if (me.FixedHBoxStretching) {
                innerElement.parent().destroy();
            }
        }
        me.setElement(null);
        me.setInnerElement(null);
        Ext.GlobalEvents.un('idle', me.onIdle, me);
        Ext.destroy(me.getTranslatable());
        me.callParent(arguments);
    },
    getPosition: function() {
        return this.position;
    },
    refresh: function(immediate, 
    options) {
        ++this.refreshCounter;
        if (immediate) {
            this.doRefresh(options);
        }
    },
    updateAutoRefresh: function(autoRefresh) {
        this.toggleResizeListeners(autoRefresh);
    },
    updateBounceEasing: function(easing) {
        this.getTranslatable().setEasingX(easing.x).setEasingY(easing.y);
    },
    updateElementSize: function() {
        if (!this.isConfiguring) {
            
            
            this.refreshAxes();
        }
    },
    updateDisabled: function(disabled) {
        
        if (!this.isConfiguring) {
            if (disabled) {
                this.detachListeners();
            } else {
                this.attachListeners();
            }
        }
    },
    updateElement: function(element, oldElement) {
        var me = this,
            
            innerElement = me.getInnerElement(),
            fixedHBoxStretching = this.FixedHBoxStretching,
            listeners;
        if (!innerElement) {
            
            
            
            innerElement = element.dom.firstChild;
            if (fixedHBoxStretching && innerElement) {
                innerElement = innerElement.dom.firstChild;
            }
            if (!innerElement || innerElement.nodeType !== 1 || !Ext.fly(innerElement).hasCls(me.scrollerCls)) {
                
                innerElement = me.wrapContent(element);
            }
            me.setInnerElement(innerElement);
        }
        if (!fixedHBoxStretching) {
            element.addCls(me.cls);
        }
        if (me.isConfiguring) {
            if (!me.getTranslatable().isScrollParent) {
                
                
                
                listeners = me.elementListeners;
                listeners.mousewheel = 'onMouseWheel';
                listeners.scroll = {
                    fn: 'onElementScroll',
                    delegated: false,
                    scope: me
                };
            }
        }
        if (!me.getDisabled()) {
            me.attachListeners();
        }
        if (!me.isConfiguring) {
            
            
            if (me.getAutoRefresh()) {
                me.toggleResizeListeners(true);
            }
            
            me.setSize(null);
            me.setElementSize(null);
        }
        me.callParent([
            element,
            oldElement
        ]);
    },
    updateFps: function(fps) {
        if (fps !== 'auto') {
            this.getTranslatable().setFps(fps);
        }
    },
    updateMaxUserPosition: function() {
        this.snapToBoundary();
    },
    updateMinUserPosition: function() {
        this.snapToBoundary();
    },
    updateInnerElement: function(innerElement) {
        if (innerElement) {
            innerElement.addCls(this.scrollerCls);
        }
        this.getTranslatable().setElement(innerElement);
    },
    updateSize: function() {
        if (!this.isConfiguring) {
            
            
            this.refreshAxes();
        }
    },
    updateTranslatable: function(translatable) {
        translatable.setElement(this.getInnerElement());
        translatable.on({
            animationframe: 'onAnimationFrame',
            animationend: 'onAnimationEnd',
            scope: this
        });
    },
    updateX: function() {
        if (!this.isConfiguring) {
            
            
            this.refreshAxes();
        }
    },
    updateY: function() {
        if (!this.isConfiguring) {
            
            
            this.refreshAxes();
        }
    },
    privates: {
        attachListeners: function() {
            this.getElement().on(this.elementListeners);
        },
        constrainX: function(x) {
            var maxX = this.getMaxPosition().x;
            return Math.min(this.getMaxPosition().x, Math.max(x, 0));
        },
        constrainY: function(y) {
            var maxY = this.getMaxPosition().y;
            return Math.min(this.getMaxPosition().y, Math.max(y, 0));
        },
        
        convertEasingConfig: function(config) {
            return config;
        },
        detachListeners: function() {
            this.getElement().un(this.elementListeners);
        },
        
        doRefresh: function(options) {
            var me = this,
                size, elementSize;
            if (me.refreshCounter && me.getElement()) {
                me.stopAnimation();
                me.getTranslatable().refresh();
                if (options) {
                    
                    
                    
                    size = options.size;
                    elementSize = options.elementSize;
                    if (size) {
                        me.setSize(size);
                    }
                    if (elementSize) {
                        me.setElementSize(elementSize);
                    }
                } else {
                    
                    
                    me.setSize(null);
                    me.setElementSize(null);
                }
                me.fireEvent('refresh', me);
                me.refreshCounter = 0;
            }
        },
        doScrollTo: function(x, y, animation, 
        allowOverscroll) {
            var me = this,
                isDragging = me.isDragging,
                fireScrollCallback;
            if (me.isDestroyed || !me.getElement()) {
                return me;
            }
            
            
            allowOverscroll = allowOverscroll || me.isDragging;
            var translatable = me.getTranslatable(),
                position = me.position,
                positionChanged = false,
                translationX, translationY;
            if (!isDragging || me.isAxisEnabled('x')) {
                if (isNaN(x) || typeof x != 'number') {
                    x = position.x;
                } else {
                    if (!allowOverscroll) {
                        x = me.constrainX(x);
                    }
                    if (position.x !== x) {
                        position.x = x;
                        positionChanged = true;
                    }
                }
                translationX = me.convertX(-x);
            }
            if (!isDragging || me.isAxisEnabled('y')) {
                if (isNaN(y) || typeof y != 'number') {
                    y = position.y;
                } else {
                    if (!allowOverscroll) {
                        y = me.constrainY(y);
                    }
                    if (position.y !== y) {
                        position.y = y;
                        positionChanged = true;
                    }
                }
                translationY = -y;
            }
            if (positionChanged) {
                if (animation) {
                    
                    fireScrollCallback = function() {
                        me.onScroll();
                    };
                    
                    if (animation === true) {
                        animation = {
                            callback: fireScrollCallback
                        };
                    }
                    
                    else if (animation.callback) {
                        animation.callback = Ext.Function.createSequence(animation.callback, fireScrollCallback);
                    } else 
                    {
                        animation.callback = fireScrollCallback;
                    }
                    translatable.translateAnimated(translationX, translationY, animation);
                } else {
                    translatable.translate(translationX, translationY);
                    me.onScroll();
                }
            }
            return me;
        },
        
        getAnimationEasing: function(axis, e) {
            if (!this.isAxisEnabled(axis)) {
                return null;
            }
            var me = this,
                currentPosition = me.position[axis],
                minPosition = me.getMinUserPosition()[axis],
                maxPosition = me.getMaxUserPosition()[axis],
                maxAbsVelocity = me.getMaxAbsoluteVelocity(),
                boundValue = null,
                dragEndTime = me.dragEndTime,
                velocity = e.flick.velocity[axis],
                isX = axis === 'x',
                easingConfig, easing;
            if (currentPosition < minPosition) {
                boundValue = minPosition;
            } else if (currentPosition > maxPosition) {
                boundValue = maxPosition;
            }
            if (isX) {
                currentPosition = me.convertX(currentPosition);
                boundValue = me.convertX(boundValue);
            }
            
            if (boundValue !== null) {
                easing = me.getBounceEasing()[axis];
                easing.setConfig({
                    startTime: dragEndTime,
                    startValue: -currentPosition,
                    endValue: -boundValue
                });
                return easing;
            }
            if (velocity === 0) {
                return null;
            }
            if (velocity < -maxAbsVelocity) {
                velocity = -maxAbsVelocity;
            } else if (velocity > maxAbsVelocity) {
                velocity = maxAbsVelocity;
            }
            if (Ext.browser.is.IE) {
                velocity *= 2;
            }
            easing = me.getMomentumEasing()[axis];
            easingConfig = {
                startTime: dragEndTime,
                startValue: -currentPosition,
                startVelocity: velocity * 1.5,
                minMomentumValue: -maxPosition,
                maxMomentumValue: 0
            };
            if (isX) {
                me.convertEasingConfig(easingConfig);
            }
            easing.setConfig(easingConfig);
            return easing;
        },
        
        getSnapPosition: function(axis) {
            var me = this,
                snapSize = me.getSlotSnapSize()[axis],
                snapPosition = null,
                position, snapOffset, maxPosition, mod;
            if (snapSize !== 0 && me.isAxisEnabled(axis)) {
                position = me.position[axis];
                snapOffset = me.getSlotSnapOffset()[axis];
                maxPosition = me.getMaxUserPosition()[axis];
                mod = Math.floor((position - snapOffset) % snapSize);
                if (mod !== 0) {
                    if (position !== maxPosition) {
                        if (Math.abs(mod) > snapSize / 2) {
                            snapPosition = Math.min(maxPosition, position + ((mod > 0) ? snapSize - mod : mod - snapSize));
                        } else {
                            snapPosition = position - mod;
                        }
                    } else {
                        snapPosition = position - mod;
                    }
                }
            }
            return snapPosition;
        },
        hideIndicators: function() {
            var me = this,
                indicators = me.getIndicators(),
                xIndicator, yIndicator;
            if (indicators) {
                if (me.isAxisEnabled('x')) {
                    xIndicator = indicators.x;
                    if (xIndicator) {
                        xIndicator.hide();
                    }
                }
                if (me.isAxisEnabled('y')) {
                    yIndicator = indicators.y;
                    if (yIndicator) {
                        yIndicator.hide();
                    }
                }
            }
        },
        
        isAxisEnabled: function(axis) {
            this.getX();
            this.getY();
            return this.isAxisEnabledFlags[axis];
        },
        onAnimationEnd: function() {
            this.snapToBoundary();
            this.onScrollEnd();
        },
        onAnimationFrame: function(translatable, x, y) {
            var position = this.position;
            position.x = this.convertX(-x);
            position.y = -y;
            this.onScroll();
        },
        onAxisDrag: function(axis, delta) {
            if (!this.isAxisEnabled(axis)) {
                return;
            }
            var me = this,
                flickStartPosition = me.flickStartPosition,
                flickStartTime = me.flickStartTime,
                lastDragPosition = me.lastDragPosition,
                dragDirection = me.dragDirection,
                old = me.position[axis],
                min = me.getMinUserPosition()[axis],
                max = me.getMaxUserPosition()[axis],
                start = me.startPosition[axis],
                last = lastDragPosition[axis],
                current = start - delta,
                lastDirection = dragDirection[axis],
                restrictFactor = me.getOutOfBoundRestrictFactor(),
                startMomentumResetTime = me.getStartMomentumResetTime(),
                now = Ext.Date.now(),
                distance;
            if (current < min) {
                current *= restrictFactor;
            } else if (current > max) {
                distance = current - max;
                current = max + distance * restrictFactor;
            }
            if (current > last) {
                dragDirection[axis] = 1;
            } else if (current < last) {
                dragDirection[axis] = -1;
            }
            if ((lastDirection !== 0 && (dragDirection[axis] !== lastDirection)) || (now - flickStartTime[axis]) > startMomentumResetTime) {
                flickStartPosition[axis] = old;
                flickStartTime[axis] = now;
            }
            lastDragPosition[axis] = current;
        },
        
        
        
        onDomScroll: function() {
            var me = this,
                dom, position;
            if (me.getTranslatable().isScrollParent) {
                dom = me.getElement().dom;
                position = me.position;
                position.x = dom.scrollLeft;
                position.y = dom.scrollTop;
            }
            me.callParent();
        },
        onDrag: function(e) {
            var me = this,
                lastDragPosition = me.lastDragPosition;
            if (!me.isDragging) {
                return;
            }
            me.onAxisDrag('x', me.convertX(e.deltaX));
            me.onAxisDrag('y', e.deltaY);
            me.doScrollTo(lastDragPosition.x, lastDragPosition.y);
        },
        onDragEnd: function(e) {
            var me = this,
                easingX, easingY;
            if (!me.isDragging) {
                return;
            }
            me.dragEndTime = Ext.Date.now();
            me.onDrag(e);
            me.isDragging = false;
            easingX = me.getAnimationEasing('x', e);
            easingY = me.getAnimationEasing('y', e);
            if (easingX || easingY) {
                me.getTranslatable().animate(easingX, easingY);
            } else {
                me.onScrollEnd();
            }
        },
        onDragStart: function(e) {
            var me = this,
                direction = me.getDirection(),
                absDeltaX = e.absDeltaX,
                absDeltaY = e.absDeltaY,
                directionLock = me.getDirectionLock(),
                startPosition = me.startPosition,
                flickStartPosition = me.flickStartPosition,
                flickStartTime = me.flickStartTime,
                lastDragPosition = me.lastDragPosition,
                currentPosition = me.position,
                dragDirection = me.dragDirection,
                x = currentPosition.x,
                y = currentPosition.y,
                now = Ext.Date.now();
            me.isDragging = true;
            if (directionLock && direction !== 'both') {
                if ((direction === 'horizontal' && absDeltaX > absDeltaY) || (direction === 'vertical' && absDeltaY > absDeltaX)) {
                    e.stopPropagation();
                } else {
                    me.isDragging = false;
                    return;
                }
            }
            lastDragPosition.x = x;
            lastDragPosition.y = y;
            flickStartPosition.x = x;
            flickStartPosition.y = y;
            startPosition.x = x;
            startPosition.y = y;
            flickStartTime.x = now;
            flickStartTime.y = now;
            dragDirection.x = 0;
            dragDirection.y = 0;
            me.dragStartTime = now;
            me.isDragging = true;
            me.onScrollStart();
        },
        onElementResize: function(element, info) {
            this.refresh(true, {
                elementSize: {
                    x: info.width,
                    y: info.height
                }
            });
        },
        onElementScroll: function(event, targetEl) {
            targetEl.scrollTop = targetEl.scrollLeft = 0;
        },
        onEvent: function(e) {
            
            
            var me = this,
                browserEvent = e.browserEvent;
            if ((!me.self.isTouching || me.isTouching) && 
            
            
            
            
            
            
            ((!me.getTranslatable().isScrollParent) || (!me.isMouseEvent[browserEvent.type] && browserEvent.pointerType !== 'mouse')) && (me.getY() || me.getX())) {
                me[me.listenerMap[e.type]](e);
            }
        },
        onIdle: function() {
            this.doRefresh();
        },
        onInnerElementResize: function(element, info) {
            this.refresh(true, {
                size: {
                    x: info.width,
                    y: info.height
                }
            });
        },
        onMouseWheel: function(e) {
            var me = this,
                delta = e.getWheelDeltas(),
                deltaX = -delta.x,
                deltaY = -delta.y,
                position = me.position,
                maxPosition = me.getMaxUserPosition(),
                minPosition = me.getMinUserPosition(),
                max = Math.max,
                min = Math.min,
                positionX = max(min(position.x + deltaX, maxPosition.x), minPosition.x),
                positionY = max(min(position.y + deltaY, maxPosition.y), minPosition.y);
            deltaX = positionX - position.x;
            deltaY = positionY - position.y;
            if (!deltaX && !deltaY) {
                return;
            }
            e.stopEvent();
            me.onScrollStart();
            me.scrollBy(deltaX, deltaY);
            me.onScroll();
            me.onScrollEnd();
        },
        onPartnerScrollEnd: function() {
            this.hideIndicators();
        },
        onPartnerScrollStart: function() {
            this.showIndicators();
        },
        onScroll: function() {
            var me = this,
                position = me.position,
                x = position.x,
                y = position.y,
                indicators = me.getIndicators(),
                xIndicator, yIndicator;
            if (indicators) {
                if (me.isAxisEnabled('x')) {
                    xIndicator = indicators.x;
                    if (xIndicator) {
                        xIndicator.setValue(x);
                    }
                }
                if (me.isAxisEnabled('y')) {
                    yIndicator = indicators.y;
                    if (yIndicator) {
                        yIndicator.setValue(y);
                    }
                }
            }
            me.fireScroll(x, y);
        },
        onScrollEnd: function() {
            var me = this,
                position = me.position;
            if (!me.isTouching && !me.snapToSlot()) {
                me.hideIndicators();
                Ext.isScrolling = false;
                me.fireScrollEnd(position.x, position.y);
            }
        },
        onScrollStart: function() {
            var me = this,
                position = me.position;
            me.showIndicators();
            Ext.isScrolling = true;
            me.fireScrollStart(position.x, position.y);
        },
        onTouchEnd: function() {
            var me = this;
            me.isTouching = me.self.isTouching = false;
            if (!me.isDragging && me.snapToSlot()) {
                me.onScrollStart();
            }
        },
        onTouchMove: function(e) {
            
            
            
            e.preventDefault();
        },
        onTouchStart: function() {
            this.isTouching = this.self.isTouching = true;
            this.stopAnimation();
        },
        refreshAxes: function() {
            var me = this,
                flags = me.isAxisEnabledFlags,
                size = me.getSize(),
                elementSize = me.getElementSize(),
                indicators = me.getIndicators(),
                maxX, maxY, x, y, xIndicator, yIndicator;
            if (!size || !elementSize) {
                return;
            }
            maxX = Math.max(0, size.x - elementSize.x);
            maxY = Math.max(0, size.y - elementSize.y);
            x = me.getX();
            y = me.getY();
            me.setMaxPosition({
                x: maxX,
                y: maxY
            });
            if (x === true || x === 'auto') {
                
                
                flags.x = !!maxX;
            } else if (x === false) {
                flags.x = false;
                xIndicator = indicators && indicators.x;
                if (xIndicator) {
                    
                    
                    xIndicator.hide();
                }
            } else if (x === 'scroll') {
                flags.x = true;
            }
            if (y === true || y === 'auto') {
                
                
                flags.y = !!maxY;
            } else if (y === false) {
                flags.y = false;
                yIndicator = indicators && indicators.y;
                if (yIndicator) {
                    
                    
                    yIndicator.hide();
                }
            } else if (y === 'scroll') {
                flags.y = true;
            }
            me.setMaxUserPosition({
                x: flags.x ? maxX : 0,
                y: flags.y ? maxY : 0
            });
        },
        showIndicators: function() {
            var me = this,
                indicators = me.getIndicators(),
                xIndicator, yIndicator;
            if (indicators) {
                if (me.isAxisEnabled('x')) {
                    xIndicator = indicators.x;
                    if (xIndicator) {
                        xIndicator.show();
                    }
                }
                if (me.isAxisEnabled('y')) {
                    yIndicator = indicators.y;
                    if (yIndicator) {
                        yIndicator.show();
                    }
                }
            }
        },
        snapToBoundary: function() {
            if (this.isConfiguring) {
                return;
            }
            var me = this,
                position = me.position,
                minPosition = me.getMinUserPosition(),
                maxPosition = me.getMaxUserPosition(),
                minX = minPosition.x,
                minY = minPosition.y,
                maxX = maxPosition.x,
                maxY = maxPosition.y,
                x = Math.round(position.x),
                y = Math.round(position.y);
            if (x < minX) {
                x = minX;
            } else if (x > maxX) {
                x = maxX;
            }
            if (y < minY) {
                y = minY;
            } else if (y > maxY) {
                y = maxY;
            }
            me.doScrollTo(x, y);
        },
        
        snapToSlot: function() {
            var me = this,
                snapX = me.getSnapPosition('x'),
                snapY = me.getSnapPosition('y'),
                easing = me.getSlotSnapEasing();
            if (snapX !== null || snapY !== null) {
                me.doScrollTo(snapX, snapY, {
                    easingX: easing.x,
                    easingY: easing.y
                });
                return true;
            }
            return false;
        },
        
        stopAnimation: function() {
            this.getTranslatable().stopAnimation();
        },
        toggleResizeListeners: function(on) {
            var me = this,
                element = me.getElement(),
                method = on ? 'on' : 'un';
            if (element) {
                element[method]('resize', 'onElementResize', me);
                me.getInnerElement()[method]('resize', 'onInnerElementResize', me);
            }
        },
        unwrapContent: function() {
            var innerDom = this.getInnerElement().dom,
                dom = this.getElement().dom,
                child;
            while ((child = innerDom.firstChild)) {
                dom.insertBefore(child, innerDom);
            }
        },
        
        wrapContent: function(element) {
            var wrap = document.createElement('div'),
                dom = element.dom,
                child;
            while (child = dom.lastChild) {
                wrap.insertBefore(child, wrap.firstChild);
            }
            dom.appendChild(wrap);
            this.setInnerElement(wrap);
            
            
            
            this._isWrapped = true;
            return this.getInnerElement();
        }
    }
});


Ext.define('Ext.scroll.DomScroller', {
    extend: 'Ext.scroll.Scroller',
    alias: 'scroller.dom',
    isDomScroller: true,
    constructor: function(config) {
        var me = this;
        me.callParent([
            config
        ]);
    },
    getMaxPosition: function() {
        var element = this.getElement(),
            x = 0,
            y = 0,
            dom;
        if (element && !element.isDestroyed) {
            dom = element.dom;
            x = dom.scrollWidth - dom.clientWidth;
            y = dom.scrollHeight - dom.clientHeight;
        }
        return {
            x: x,
            y: y
        };
    },
    getMaxUserPosition: function() {
        var me = this,
            element = me.getElement(),
            x = 0,
            y = 0,
            dom;
        if (element && !element.isDestroyed) {
            dom = element.dom;
            if (me.getX()) {
                x = dom.scrollWidth - dom.clientWidth;
            }
            if (me.getY()) {
                y = dom.scrollHeight - dom.clientHeight;
            }
        }
        return {
            x: x,
            y: y
        };
    },
    getPosition: function() {
        var element = this.getElement(),
            x = 0,
            y = 0,
            position;
        if (element && !element.isDestroyed) {
            position = this.getElementScroll(element);
            x = position.left;
            y = position.top;
        }
        return {
            x: x,
            y: y
        };
    },
    getSize: function() {
        var element = this.getElement(),
            size, dom;
        if (element && !element.isDestroyed) {
            dom = element.dom;
            size = {
                x: dom.scrollWidth,
                y: dom.scrollHeight
            };
        } else {
            size = {
                x: 0,
                y: 0
            };
        }
        return size;
    },
    setSize: Ext.emptyFn,
    updateElement: function(element, oldElement) {
        this.initXStyle();
        this.initYStyle();
        this.callParent([
            element,
            oldElement
        ]);
    },
    updateX: function(x) {
        this.initXStyle();
    },
    updateY: function(y) {
        this.initYStyle();
    },
    privates: {
        doScrollTo: function(x, y, animate) {
            var me = this,
                element = me.getElement(),
                maxPosition, dom, to, xInf, yInf;
            if (element && !element.isDestroyed) {
                dom = this.getElement().dom;
                xInf = (x === Infinity);
                yInf = (y === Infinity);
                if (xInf || yInf) {
                    maxPosition = me.getMaxPosition();
                    if (xInf) {
                        x = maxPosition.x;
                    }
                    if (yInf) {
                        y = maxPosition.y;
                    }
                }
                x = me.convertX(x);
                if (animate) {
                    to = {};
                    if (y != null) {
                        to.scrollTop = y;
                    }
                    if (x != null) {
                        to.scrollLeft = x;
                    }
                    element.animate(Ext.mergeIf({
                        to: {
                            scrollTop: y,
                            scrollLeft: x
                        }
                    }, animate));
                } else {
                    if (y != null) {
                        dom.scrollTop = y;
                    }
                    if (x != null) {
                        dom.scrollLeft = x;
                    }
                }
            }
        },
        
        getElementScroll: function(element) {
            return element.getScroll();
        },
        initXStyle: function() {
            var element = this.getElement(),
                x = this.getX();
            if (!x) {
                x = 'hidden';
            } else if (x === true) {
                x = 'auto';
            }
            if (element) {
                element.setStyle('overflow-x', x);
            }
        },
        initYStyle: function() {
            var element = this.getElement(),
                y = this.getY();
            if (!y) {
                y = 'hidden';
            } else if (y === true) {
                y = 'auto';
            }
            if (element) {
                element.setStyle('overflow-y', y);
            }
        },
        stopAnimation: function() {
            var anim = this.getElement().getActiveAnimation();
            if (anim) {
                anim.end();
            }
        }
    }
});


Ext.define('Ext.util.ElementContainer', {
    mixinId: 'elementCt',
    config: {
        
        childEls: {
            $value: {},
            cached: true,
            lazy: true,
            merge: function(newValue, oldValue, target, mixinClass) {
                var childEls = oldValue ? Ext.Object.chain(oldValue) : {},
                    i, val;
                
                if (newValue instanceof Array) {
                    for (i = newValue.length; i--; ) {
                        val = newValue[i];
                        if (!mixinClass || !(val in childEls)) {
                            if (typeof val === 'string') {
                                childEls[val] = {
                                    name: val,
                                    itemId: val
                                };
                            } else {
                                childEls[val.name] = val;
                            }
                        }
                    }
                } else if (newValue) {
                    if (newValue.constructor === Object) {
                        for (i in newValue) {
                            if (!mixinClass || !(i in childEls)) {
                                val = newValue[i];
                                if (val === true) {
                                    childEls[i] = {
                                        itemId: i
                                    };
                                } else if (typeof val === 'string') {
                                    childEls[i] = {
                                        itemId: val
                                    };
                                } else {
                                    childEls[i] = val;
                                    if (!('itemId' in val)) {
                                        val.itemId = i;
                                    }
                                }
                                childEls[i].name = i;
                            }
                        }
                    } else {
                        if (!mixinClass || !(newValue in childEls)) {
                            childEls[newValue] = {
                                name: newValue,
                                itemId: newValue
                            };
                        }
                    }
                }
                return childEls;
            }
        }
    },
    destroy: function() {
        var me = this,
            childEls = me.getChildEls(),
            child, childName;
        for (childName in childEls) {
            child = me[childName];
            if (child) {
                if (child.destroy) {
                    child.destroy();
                }
                me[childName] = null;
            }
        }
    },
    privates: {
        
        afterClassMixedIn: function(targetClass) {
            
            
            var proto = targetClass.prototype,
                childEls = proto.childEls;
            if (childEls) {
                delete proto.childEls;
                targetClass.getConfigurator().add({
                    childEls: childEls
                });
            }
        },
        
        attachChildEls: function(el, owner) {
            var me = this,
                childEls = me.getChildEls(),
                comp = owner || me,
                
                baseId = comp.id + '-',
                unframed = !comp.frame,
                childName, elements, entry, k, selector, value, id;
            for (childName in childEls) {
                
                entry = childEls[childName];
                if (unframed && entry.frame) {
                    
                    continue;
                }
                selector = entry.select;
                if (selector) {
                    value = el.select(selector, true);
                }
                
                else if (!(selector = entry.selectNode)) {
                    if (!(id = entry.id)) {
                        
                        
                        
                        id = baseId + entry.itemId;
                        value = Ext.cache[id];
                    } else 
                    {
                        
                        
                        value = Ext.cache[id] || el.getById(id);
                    }
                } else {
                    value = el.selectNode(selector, false);
                }
                if (value) {
                    if (value.isElement) {
                        value.component = comp;
                    } else if (value.isComposite && !value.isLite) {
                        elements = value.elements;
                        for (k = elements.length; k--; ) {
                            elements[k].component = comp;
                        }
                    }
                }
                me[childName] = value || null;
            }
        }
    }
});


Ext.define('Ext.util.Renderable', {
    mixinId: 'renderable',
    requires: [
        'Ext.dom.Element'
    ],
    frameCls: Ext.baseCSSPrefix + 'frame',
    frameIdRegex: /[\-]frame\d+[TMB][LCR]$/,
    frameElNames: [
        'TL',
        'TC',
        'TR',
        'ML',
        'MC',
        'MR',
        'BL',
        'BC',
        'BR',
        'Table'
    ],
    frameTpl: [
        '{%this.renderDockedItems(out,values,0);%}',
        '<tpl if="top">',
        '<tpl if="left"><div id="{fgid}TL" data-ref="frameTL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>{frameElCls}" role="presentation"></tpl>',
        '<tpl if="right"><div id="{fgid}TR" data-ref="frameTR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>{frameElCls}" role="presentation"></tpl>',
        '<div id="{fgid}TC" data-ref="frameTC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>{frameElCls}" role="presentation"></div>',
        '<tpl if="right"></div></tpl>',
        '<tpl if="left"></div></tpl>',
        '</tpl>',
        '<tpl if="left"><div id="{fgid}ML" data-ref="frameML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>{frameElCls}" role="presentation"></tpl>',
        '<tpl if="right"><div id="{fgid}MR" data-ref="frameMR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>{frameElCls}" role="presentation"></tpl>',
        '<div id="{fgid}Body" data-ref="frameBody" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>{frameElCls}" role="presentation">',
        '{%this.applyRenderTpl(out, values)%}',
        '</div>',
        '<tpl if="right"></div></tpl>',
        '<tpl if="left"></div></tpl>',
        '<tpl if="bottom">',
        '<tpl if="left"><div id="{fgid}BL" data-ref="frameBL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>{frameElCls}" role="presentation"></tpl>',
        '<tpl if="right"><div id="{fgid}BR" data-ref="frameBR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>{frameElCls}" role="presentation"></tpl>',
        '<div id="{fgid}BC" data-ref="frameBC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>{frameElCls}" role="presentation"></div>',
        '<tpl if="right"></div></tpl>',
        '<tpl if="left"></div></tpl>',
        '</tpl>',
        '{%this.renderDockedItems(out,values,1);%}'
    ],
    frameTableTpl: [
        '{%this.renderDockedItems(out,values,0);%}',
        '<table id="{fgid}Table" data-ref="frameTable" class="{frameCls} ',
        Ext.baseCSSPrefix + 'table-plain" cellpadding="0" role="presentation">',
        '<tpl if="top">',
        '<tr role="presentation">',
        '<tpl if="left"><td id="{fgid}TL" data-ref="frameTL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>{frameElCls}" role="presentation"></td></tpl>',
        '<td id="{fgid}TC" data-ref="frameTC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>{frameElCls}" role="presentation"></td>',
        '<tpl if="right"><td id="{fgid}TR" data-ref="frameTR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>{frameElCls}" role="presentation"></td></tpl>',
        '</tr>',
        '</tpl>',
        '<tr role="presentation">',
        '<tpl if="left"><td id="{fgid}ML" data-ref="frameML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>{frameElCls}" role="presentation"></td></tpl>',
        '<td id="{fgid}Body" data-ref="frameBody" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>{frameElCls}" style="{mcStyle}" role="presentation">',
        '{%this.applyRenderTpl(out, values)%}',
        '</td>',
        '<tpl if="right"><td id="{fgid}MR" data-ref="frameMR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>{frameElCls}" role="presentation"></td></tpl>',
        '</tr>',
        '<tpl if="bottom">',
        '<tr role="presentation">',
        '<tpl if="left"><td id="{fgid}BL" data-ref="frameBL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>{frameElCls}" role="presentation"></td></tpl>',
        '<td id="{fgid}BC" data-ref="frameBC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>{frameElCls}" role="presentation"></td>',
        '<tpl if="right"><td id="{fgid}BR" data-ref="frameBR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>{frameElCls}" role="presentation"></td></tpl>',
        '</tr>',
        '</tpl>',
        '</table>',
        '{%this.renderDockedItems(out,values,1);%}'
    ],
    
    _renderState: 0,
    _layerCls: Ext.baseCSSPrefix + 'layer',
    _fixedLayerCls: Ext.baseCSSPrefix + 'fixed-layer',
    statics: {
        makeRenderSetter: function(cfg, renderState) {
            var name = cfg.name;
            return function(value) {
                var me = this,
                    bucket = (me.renderConfigs || (me.renderConfigs = {})),
                    pending = bucket[renderState];
                if (me._renderState >= renderState) {
                    (cfg.setter || cfg.getSetter()).call(me, value);
                } else {
                    if (!pending) {
                        bucket[renderState] = pending = {};
                    }
                    if (!(name in pending)) {
                        pending[name] = me[name];
                    }
                    me[name] = value;
                }
                return me;
            };
        },
        processRenderConfig: function(source, configName, state) {
            
            
            var proto = this.prototype,
                configurator = this.getConfigurator(),
                Renderable = Ext.util.Renderable,
                makeSetter = Renderable.makeRenderSetter,
                renderConfig = source[configName],
                cachedSetter, cfg, name, setterName;
            for (name in renderConfig) {
                cfg = Ext.Config.get(name);
                if (!proto[setterName = cfg.names.set]) {
                    cachedSetter = (cfg.renderSetter || (cfg.renderSetter = {}));
                    proto[setterName] = cachedSetter[state] || (cachedSetter[state] = makeSetter(cfg, state));
                }
            }
            delete source[configName];
            configurator.add(renderConfig);
        }
    },
    onClassMixedIn: function(targetClass) {
        var override = targetClass.override,
            processRenderConfig = this.processRenderConfig,
            processOverride = function(body) {
                if (body.beforeRenderConfig) {
                    this.processRenderConfig(body, 'beforeRenderConfig', 1);
                }
                if (body.renderConfig) {
                    this.processRenderConfig(body, 'renderConfig', 3);
                }
                override.call(this, body);
            },
            processClass = function(theClass, classBody) {
                
                theClass.override = processOverride;
                
                theClass.processRenderConfig = processRenderConfig;
                if (classBody.beforeRenderConfig) {
                    theClass.processRenderConfig(classBody, 'beforeRenderConfig', 1);
                }
                if (classBody.renderConfig) {
                    theClass.processRenderConfig(classBody, 'renderConfig', 3);
                }
            };
        
        processClass(targetClass, targetClass.prototype);
        
        targetClass.onExtended(processClass);
    },
    
    afterRender: function() {
        var me = this,
            data = {},
            protoEl = me.protoEl,
            target = me.el,
            controller, item, pre, hidden, contentEl;
        me.finishRenderChildren();
        me._renderState = 4;
        
        
        if (me.contentEl) {
            pre = Ext.baseCSSPrefix;
            hidden = pre + 'hidden-';
            contentEl = me.contentEl = Ext.get(me.contentEl);
            contentEl.component = me;
            contentEl.removeCls([
                pre + 'hidden',
                hidden + 'display',
                hidden + 'offsets'
            ]);
            me.getContentTarget().appendChild(contentEl.dom);
        }
        protoEl.writeTo(data);
        
        
        item = data.removed;
        if (item) {
            target.removeCls(item);
        }
        item = data.cls;
        if (item.length) {
            target.addCls(item);
        }
        item = data.style;
        if (data.style) {
            target.setStyle(item);
        }
        me.protoEl = null;
        
        if (!me.ownerCt) {
            me.updateLayout();
        }
        if (!(me.x && me.y) && (me.pageX || me.pageY)) {
            me.setPagePosition(me.pageX, me.pageY);
        }
        if (me.disableOnRender) {
            me.onDisable();
        } else if (me.enableOnRender) {
            me.onEnable();
        }
        if (Ext.enableAria) {
            me.ariaApplyAfterRenderAttributes();
        }
        controller = me.controller;
        if (controller && controller.afterRender) {
            controller.afterRender(me);
        }
    },
    afterFirstLayout: function(width, height) {
        var me = this,
            x = me.x,
            y = me.y,
            alignSpec = me.defaultAlign,
            alignOffset = me.alignOffset,
            controller, hasX, hasY, pos, xy;
        
        
        if (!me.ownerLayout) {
            hasX = x !== undefined;
            hasY = y !== undefined;
        }
        
        
        if (me.floating && !me.preventDefaultAlign && (!hasX || !hasY)) {
            if (me.floatParent) {
                pos = me.floatParent.getTargetEl().getViewRegion();
                xy = me.el.getAlignToXY(me.alignTarget || me.floatParent.getTargetEl(), alignSpec, alignOffset);
                pos.x = xy[0] - pos.x;
                pos.y = xy[1] - pos.y;
            } else {
                xy = me.el.getAlignToXY(me.alignTarget || me.container, alignSpec, alignOffset);
                pos = me.container.translateXY(xy[0], xy[1]);
            }
            x = hasX ? x : pos.x;
            y = hasY ? y : pos.y;
            hasX = hasY = true;
        }
        if (hasX || hasY) {
            me.setPosition(x, y);
        }
        me.onBoxReady(width, height);
        controller = me.controller;
        if (controller && controller.boxReady) {
            controller.boxReady(me);
        }
    },
    beforeRender: function() {
        var me = this,
            floating = me.floating,
            layout = me.getComponentLayout(),
            cls, controller;
        me._renderState = 1;
        controller = me.controller;
        if (controller && controller.beforeRender) {
            controller.beforeRender(me);
        }
        
        me.initBindable();
        if (me.renderConfigs) {
            me.flushRenderConfigs();
        }
        if (me.reference) {
            
            
            
            me.publishState();
        }
        if (floating) {
            me.addCls(me.fixed ? me._fixedLayerCls : me._layerCls);
            cls = floating.cls;
            if (cls) {
                me.addCls(cls);
            }
        }
        
        me.frame = me.frame || me.alwaysFramed;
        if (!layout.initialized) {
            layout.initLayout();
        }
        
        
        me.initOverflow();
        me.setUI(me.ui);
        if (me.disabled) {
            
            me.disable(true);
        }
    },
    
    doApplyRenderTpl: function(out, values) {
        
        
        var me = values.$comp,
            tpl;
        
        if (!me.rendered) {
            tpl = me.initRenderTpl();
            tpl.applyOut(values.renderData, out);
        }
    },
    getElConfig: function() {
        var me = this,
            autoEl = me.autoEl,
            frameInfo = me.getFrameInfo(),
            config = {
                tag: 'div',
                tpl: frameInfo ? me.initFramingTpl(frameInfo.table) : me.initRenderTpl()
            },
            protoEl = me.protoEl,
            frameData;
        me.initStyles(protoEl);
        protoEl.writeTo(config);
        protoEl.flush();
        if (Ext.isString(autoEl)) {
            config.tag = autoEl;
        } else {
            Ext.apply(config, autoEl);
        }
        
        if (Ext.enableAria && me.ariaRenderAttributesToElement) {
            Ext.apply(config, me.ariaGetRenderAttributes());
        }
        
        config.id = me.id;
        if (config.tpl) {
            
            if (frameInfo) {
                config.tplData = frameData = me.getFrameRenderData();
                frameData.renderData = me.initRenderData();
            } else {
                config.tplData = me.initRenderData();
            }
        }
        return config;
    },
    
    getInsertPosition: function(position) {
        
        if (position !== undefined) {
            if (Ext.isNumber(position)) {
                position = this.container.dom.childNodes[position];
            } else {
                position = Ext.getDom(position);
            }
        }
        return position;
    },
    getRenderTree: function() {
        var me = this,
            ret = null;
        if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
            me._renderState = 1;
            me.beforeRender();
            
            
            me.rendering = true;
            me._renderState = 2;
            ret = me.getElConfig();
            if (me.el) {
                
                
                
                ret.id = me.$pid = Ext.id(null, me.el.identifiablePrefix);
            }
        }
        
        return ret;
    },
    
    initRenderData: function() {
        var me = this;
        return Ext.apply({
            $comp: me,
            id: me.id,
            ui: me.ui,
            uiCls: me.uiCls,
            baseCls: me.baseCls,
            componentCls: me.componentCls,
            frame: me.frame,
            renderScroller: me.touchScroll,
            scrollerCls: me.scrollerCls,
            role: me.ariaRole,
            childElCls: ''
        }, 
        me.renderData);
    },
    
    onRender: function(parentNode, containerIdx) {
        var me = this,
            x = me.x,
            y = me.y,
            lastBox = null,
            el = me.el,
            width, height;
        me.applyRenderSelectors();
        
        
        me.rendering = null;
        me.rendered = true;
        me._renderState = 3;
        if (me.renderConfigs) {
            me.flushRenderConfigs();
        }
        
        if (x != null) {
            lastBox = {
                x: x
            };
        }
        if (y != null) {
            (lastBox = lastBox || {}).y = y;
        }
        
        
        if (!me.getFrameInfo()) {
            width = me.width;
            height = me.height;
            if (typeof width === 'number') {
                lastBox = lastBox || {};
                lastBox.width = width;
            }
            if (typeof height === 'number') {
                lastBox = lastBox || {};
                lastBox.height = height;
            }
        }
        if (me.touchScroll === 1) {
            
            
            
            me.getOverflowEl().disableTouchScroll();
        }
        me.lastBox = el.lastBox = lastBox;
    },
    
    render: function(container, position) {
        var me = this,
            el = me.el,
            ownerLayout = me.ownerLayout,
            vetoed, tree, nextSibling;
        if (el && !el.isElement) {
            me.wrapPrimaryEl(el);
            
            el = me.el;
        }
        Ext.suspendLayouts();
        container = me.initContainer(container);
        nextSibling = me.getInsertPosition(position);
        if (!el) {
            tree = me.getRenderTree();
            
            if (ownerLayout && ownerLayout.transformItemRenderTree) {
                tree = ownerLayout.transformItemRenderTree(tree);
            }
            
            if (tree) {
                if (nextSibling) {
                    el = Ext.DomHelper.insertBefore(nextSibling, tree);
                } else {
                    el = Ext.DomHelper.append(container, tree);
                }
                me.wrapPrimaryEl(el);
                
                
                me.cacheRefEls(el);
            }
        } else {
            if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
                me.beforeRender();
                
                
                me.needsRenderTpl = me.rendering = true;
                me._renderState = 2;
                
                me.initStyles(el);
                if (me.allowDomMove !== false) {
                    if (nextSibling) {
                        container.dom.insertBefore(el.dom, nextSibling);
                    } else {
                        container.dom.appendChild(el.dom);
                    }
                }
            } else {
                vetoed = true;
            }
        }
        if (el && !vetoed) {
            me.finishRender(position);
        }
        Ext.resumeLayouts(!me.hidden && !container.isDetachedBody);
    },
    
    ensureAttachedToBody: function(runLayout) {
        var comp = this,
            body;
        while (comp.ownerCt) {
            comp = comp.ownerCt;
        }
        if (comp.container.isDetachedBody) {
            comp.container = body = Ext.getBody();
            body.appendChild(comp.el.dom);
            if (runLayout) {
                comp.updateLayout();
            }
            if (typeof comp.x == 'number' || typeof comp.y == 'number') {
                comp.setPosition(comp.x, comp.y);
            }
        }
    },
    
    privates: {
        
        applyRenderSelectors: function() {
            var me = this,
                selectors = me.renderSelectors,
                el = me.el,
                query, selector;
            me.attachChildEls(el);
            
            
            
            if (selectors) {
                for (selector in selectors) {
                    query = selectors[selector];
                    if (query) {
                        me[selector] = el.selectNode(query, false);
                    }
                }
            }
        },
        
        cacheRefEls: function(el) {
            el = el || this.el;
            var cache = Ext.cache,
                El = Ext.dom.Element,
                dom = el.isElement ? el.dom : el,
                refs = dom.querySelectorAll('[data-ref]'),
                len = refs.length,
                ref, i;
            for (i = 0; i < len; i++) {
                ref = refs[i];
                if (!cache[ref.id]) {
                    new El(ref);
                }
            }
        },
        
        doAutoRender: function() {
            var me = this;
            if (!me.rendered) {
                if (me.floating) {
                    me.render(me.renderTo || document.body);
                } else {
                    me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender);
                }
            }
        },
        doRenderContent: function(out, renderData) {
            
            
            var me = renderData.$comp,
                data = me.data;
            if (me.html) {
                Ext.DomHelper.generateMarkup(me.html, out);
                delete me.html;
            }
            if (me.tpl) {
                
                if (!me.tpl.isTemplate) {
                    me.tpl = new Ext.XTemplate(me.tpl);
                }
                if (data) {
                    me.data = data = data.isEntity ? data.getData(true) : data;
                    
                    me.tpl.applyOut(data, out);
                }
            }
        },
        doRenderFramingDockedItems: function(out, renderData, after) {
            
            
            var me = renderData.$comp;
            
            
            if (!me.rendered && me.doRenderDockedItems) {
                
                
                renderData.renderData.$skipDockedItems = true;
                
                
                me.doRenderDockedItems.call(this, out, renderData, after);
            }
        },
        flushRenderConfigs: function() {
            var me = this,
                configs = me.renderConfigs,
                state = me._renderState,
                bucket, i, name, newConfigs, value;
            if (configs) {
                for (i = 0; i <= state; ++i) {
                    bucket = configs[i];
                    if (bucket) {
                        configs[i] = null;
                        for (name in bucket) {
                            value = bucket[name];
                            (newConfigs || (newConfigs = {}))[name] = me[name];
                            me[name] = value;
                        }
                    }
                }
                if (newConfigs) {
                    me.setConfig(newConfigs);
                }
            }
        },
        
        finishRender: function(containerIdx) {
            var me = this,
                cache = Ext.cache,
                
                proxy, first, id, tpl, data, dom, el;
            
            
            
            
            
            
            
            if (!me.el || me.$pid) {
                if (me.container) {
                    el = cache[me.id];
                    dom = el ? el.dom : me.container.getById(me.id, true);
                } else {
                    id = me.$pid || me.id;
                    el = cache[id];
                    dom = el ? el.dom : Ext.getDom(id);
                }
                if (!me.el) {
                    
                    me.wrapPrimaryEl(dom);
                } else {
                    
                    
                    delete me.$pid;
                    if (!me.el.dom) {
                        
                        me.wrapPrimaryEl(me.el);
                    }
                    
                    dom.parentNode.insertBefore(me.el.dom, dom);
                    
                    
                    
                    
                    proxy = dom;
                    
                    dom = me.el.dom;
                    
                    first = dom.firstChild;
                    
                    while (proxy.firstChild) {
                        dom.insertBefore(proxy.firstChild, first);
                    }
                    
                    
                    me.el.addCls(proxy.className);
                    Ext.removeNode(proxy);
                }
            }
            
            
            else if (me.needsRenderTpl) {
                
                
                
                tpl = me.initRenderTpl();
                if (tpl) {
                    data = me.initRenderData();
                    tpl.insertFirst(me.getTargetEl(), data);
                }
                
                
                me.cacheRefEls();
            }
            
            me.el.component = me;
            if (!me.container) {
                
                me.container = Ext.get(me.el.dom.parentNode);
            }
            if (me.ctCls) {
                me.container.addCls(me.ctCls);
            }
            
            me.onRender(me.container, containerIdx);
            
            if (!me.overflowInited) {
                me.initOverflow();
            }
            
            
            me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]);
            if (me.overCls) {
                me.el.hover(me.addOverCls, me.removeOverCls, me);
            }
            if (me.hasListeners.render) {
                me.fireEvent('render', me);
            }
            me.afterRender();
            
            if (me.hasListeners.afterrender) {
                me.fireEvent('afterrender', me);
            }
            me.initEvents();
            if (me.hidden) {
                
                
                
                me.el.hide();
            }
        },
        finishRenderChildren: function() {
            var layout = this.getComponentLayout();
            layout.finishRender();
        },
        getFrameRenderData: function() {
            var me = this,
                
                frameInfo = me.frameSize,
                mcStyle = '';
            if (me._syncFrameHeight && me.height) {
                
                
                mcStyle = 'height:' + (me.height - frameInfo.height) + 'px';
            }
            return {
                $comp: me,
                fgid: me.id + '-frame',
                ui: me.ui,
                uiCls: me.uiCls,
                frameCls: me.frameCls,
                baseCls: me.baseCls,
                top: !!frameInfo.top,
                left: !!frameInfo.left,
                right: !!frameInfo.right,
                bottom: !!frameInfo.bottom,
                mcStyle: mcStyle,
                
                
                frameElCls: ''
            };
        },
        
        getFrameInfo: function() {
            
            if (Ext.supports.CSS3BorderRadius || !this.frame) {
                return false;
            }
            var me = this,
                frameInfoCache = me.frameInfoCache,
                cls = me.getFramingInfoCls() + '-frameInfo',
                frameInfo = frameInfoCache[cls],
                styleEl, info, frameTop, frameRight, frameBottom, frameLeft, frameMax, borderTopWidth, borderRightWidth, borderBottomWidth, borderLeftWidth, paddingTop, paddingRight, paddingBottom, paddingLeft;
            if (frameInfo == null) {
                
                styleEl = Ext.fly(me.getStyleProxy(cls), 'frame-style-el');
                info = styleEl.getStyle('font-family');
                if (info) {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    info = info.split('-');
                    frameTop = parseInt(info[1], 10);
                    frameRight = parseInt(info[2], 10);
                    frameBottom = parseInt(info[3], 10);
                    frameLeft = parseInt(info[4], 10);
                    borderTopWidth = parseInt(info[5], 10);
                    borderRightWidth = parseInt(info[6], 10);
                    borderBottomWidth = parseInt(info[7], 10);
                    borderLeftWidth = parseInt(info[8], 10);
                    paddingTop = parseInt(info[9], 10);
                    paddingRight = parseInt(info[10], 10);
                    paddingBottom = parseInt(info[11], 10);
                    paddingLeft = parseInt(info[12], 10);
                    frameInfo = {
                        table: info[0].charAt(0) === 't',
                        vertical: info[0].charAt(1) === 'v',
                        top: frameTop,
                        right: frameRight,
                        bottom: frameBottom,
                        left: frameLeft,
                        
                        width: frameLeft + frameRight,
                        height: frameTop + frameBottom,
                        border: {
                            top: borderTopWidth,
                            right: borderRightWidth,
                            bottom: borderBottomWidth,
                            left: borderLeftWidth,
                            width: borderLeftWidth + borderRightWidth,
                            height: borderTopWidth + borderBottomWidth
                        },
                        padding: {
                            top: paddingTop,
                            right: paddingRight,
                            bottom: paddingBottom,
                            left: paddingLeft,
                            width: paddingLeft + paddingRight,
                            height: paddingTop + paddingBottom
                        }
                    };
                } else {
                    frameInfo = false;
                }
                
                
                if (me.frame === true && !frameInfo) {
                    Ext.log.error('You have set frame: true explicity on this component (' + me.getXType() + ') and it ' + 'does not have any framing defined in the CSS template. In this case IE cannot figure out ' + 'what sizes to use and thus framing on this component will be disabled.');
                }
                frameInfoCache[cls] = frameInfo;
            }
            me.frame = !!frameInfo;
            me.frameSize = frameInfo;
            return frameInfo;
        },
        getFramingInfoCls: function() {
            return this.baseCls + '-' + this.ui;
        },
        
        getStyleProxy: function(cls) {
            var result = this.styleProxyEl || (Ext.Component.prototype.styleProxyEl = Ext.getBody().createChild({
                    
                    'data-sticky': true,
                    role: 'presentation',
                    style: {
                        position: 'absolute',
                        top: '-10000px'
                    }
                }, null, true));
            result.className = cls;
            return result;
        },
        
        getFrameTpl: function(table) {
            return this.getTpl(table ? 'frameTableTpl' : 'frameTpl');
        },
        initContainer: function(container) {
            var me = this;
            
            
            
            if (!container && me.el) {
                container = me.el.dom.parentNode;
                me.allowDomMove = false;
            }
            me.container = container.dom ? container : Ext.get(container);
            return me.container;
        },
        initOverflow: function() {
            var me = this,
                
                overflowStyle = me.getOverflowStyle(),
                scrollFlags = me.scrollFlags,
                overflowEl = me.getOverflowEl(),
                hasOverflow = (scrollFlags.y || scrollFlags.x),
                touchScroll = me.touchScroll = (hasOverflow && Ext.supports.touchScroll);
            
            if (!hasOverflow || !overflowEl || !overflowEl.isElement) {
                return;
            }
            me.overflowInited = true;
            if (touchScroll === 2) {
                
                
                overflowEl.setStyle('overflow', 'hidden');
            } else {
                overflowEl.setStyle(overflowStyle);
            }
        },
        doRenderPadding: function(out, renderData) {
            
            
            
            
            
            
            
            
            
            
            var me = renderData.$comp;
            if (me.touchScroll) {
                out.push('padding:', me.unitizeBox(me.padding));
            }
        },
        
        
        initFramingTpl: function(table) {
            var tpl = this.getFrameTpl(table);
            if (tpl && !tpl.applyRenderTpl) {
                this.setupFramingTpl(tpl);
            }
            return tpl;
        },
        
        initRenderTpl: function() {
            var tpl = this.getTpl('renderTpl');
            if (tpl && !tpl.renderContent) {
                this.setupRenderTpl(tpl);
            }
            return tpl;
        },
        
        setupFramingTpl: function(frameTpl) {
            frameTpl.applyRenderTpl = this.doApplyRenderTpl;
            frameTpl.renderDockedItems = this.doRenderFramingDockedItems;
        },
        setupRenderTpl: function(renderTpl) {
            renderTpl.renderBody = renderTpl.renderContent = this.doRenderContent;
            renderTpl.renderPadding = this.doRenderPadding;
        },
        
        updateFrame: function() {
            if (Ext.supports.CSS3BorderRadius || !this.frame) {
                return;
            }
            var me = this,
                dom = me.el.dom,
                frameTable = me.frameTable,
                oldFrameBody = me.frameBody,
                oldFrameBodyDom = oldFrameBody.dom,
                frameInfo = me.getFrameInfo(),
                childEls, childElName, div, el, first, frameData, frameDom, frameTpl, i, newBody, newFrameEls;
            
            
            
            
            
            div = document.createElement('div');
            frameData = me.getFrameRenderData();
            frameTpl = me.getFrameTpl(frameInfo.table);
            frameTpl.insertFirst(div, frameData);
            
            
            newFrameEls = div.querySelectorAll('[data-ref]');
            newBody = div.querySelector('[data-ref="frameBody"]');
            
            for (first = oldFrameBodyDom; first.parentNode !== dom; ) {
                first = first.parentNode;
            }
            while (div.firstChild) {
                dom.insertBefore(div.firstChild, first);
            }
            
            newBody.parentNode.replaceChild(oldFrameBodyDom, newBody);
            oldFrameBodyDom.className = newBody.className;
            oldFrameBody.setSize();
            
            
            childEls = me.getChildEls();
            if (frameTable) {
                frameTable.destroy();
                me.frameTable = null;
            }
            for (childElName in childEls) {
                if (childEls[childElName].frame) {
                    el = me[childElName];
                    if (el && el !== oldFrameBody) {
                        el.destroy();
                        me[childElName] = null;
                    }
                }
            }
            
            for (i = newFrameEls.length; i--; ) {
                childElName = (frameDom = newFrameEls[i]).getAttribute('data-ref');
                if (childElName !== 'frameBody') {
                    me[childElName] = new Ext.dom.Element(frameDom);
                }
            }
        },
        
        frameInfoCache: {}
    }
});


Ext.define('Ext.state.Provider', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    prefix: 'ext-',
    
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);
        me.state = {};
        me.mixins.observable.constructor.call(me);
    },
    
    get: function(name, defaultValue) {
        return typeof this.state[name] == "undefined" ? defaultValue : this.state[name];
    },
    
    clear: function(name) {
        var me = this;
        delete me.state[name];
        me.fireEvent("statechange", me, name, null);
    },
    
    set: function(name, value) {
        var me = this;
        me.state[name] = value;
        me.fireEvent("statechange", me, name, value);
    },
    
    decodeValue: function(value) {
        
        
        
        
        
        
        
        var me = this,
            re = /^(a|n|d|b|s|o|e)\:(.*)$/,
            matches = re.exec(unescape(value)),
            all, type, keyValue, values, vLen, v;
        if (!matches || !matches[1]) {
            return;
        }
        
        type = matches[1];
        value = matches[2];
        switch (type) {
            case 'e':
                return null;
            case 'n':
                return parseFloat(value);
            case 'd':
                return new Date(Date.parse(value));
            case 'b':
                return (value == '1');
            case 'a':
                all = [];
                if (value != '') {
                    values = value.split('^');
                    vLen = values.length;
                    for (v = 0; v < vLen; v++) {
                        value = values[v];
                        all.push(me.decodeValue(value));
                    }
                };
                return all;
            case 'o':
                all = {};
                if (value != '') {
                    values = value.split('^');
                    vLen = values.length;
                    for (v = 0; v < vLen; v++) {
                        value = values[v];
                        keyValue = value.split('=');
                        all[keyValue[0]] = me.decodeValue(keyValue[1]);
                    }
                };
                return all;
            default:
                return value;
        }
    },
    
    encodeValue: function(value) {
        var flat = '',
            i = 0,
            enc, len, key;
        if (value == null) {
            return 'e:1';
        } else if (typeof value == 'number') {
            enc = 'n:' + value;
        } else if (typeof value == 'boolean') {
            enc = 'b:' + (value ? '1' : '0');
        } else if (Ext.isDate(value)) {
            enc = 'd:' + value.toUTCString();
        } else if (Ext.isArray(value)) {
            for (len = value.length; i < len; i++) {
                flat += this.encodeValue(value[i]);
                if (i != len - 1) {
                    flat += '^';
                }
            }
            enc = 'a:' + flat;
        } else if (typeof value == 'object') {
            for (key in value) {
                if (typeof value[key] != 'function' && value[key] !== undefined) {
                    flat += key + '=' + this.encodeValue(value[key]) + '^';
                }
            }
            enc = 'o:' + flat.substring(0, flat.length - 1);
        } else {
            enc = 's:' + value;
        }
        return escape(enc);
    }
});


Ext.define('Ext.state.Manager', {
    singleton: true,
    requires: [
        'Ext.state.Provider'
    ],
    constructor: function() {
        this.provider = new Ext.state.Provider();
    },
    
    setProvider: function(stateProvider) {
        this.provider = stateProvider;
    },
    
    get: function(key, defaultValue) {
        return this.provider.get(key, defaultValue);
    },
    
    set: function(key, value) {
        this.provider.set(key, value);
    },
    
    clear: function(key) {
        this.provider.clear(key);
    },
    
    getProvider: function() {
        return this.provider;
    }
});


Ext.define('Ext.state.Stateful', {
    mixinId: 'state',
    requires: [
        'Ext.state.Manager'
    ],
    
    stateful: false,
    
    
    
    saveDelay: 100,
    
    
    
    
    constructor: function() {
        var me = this;
        if (!me.stateEvents) {
            me.stateEvents = [];
        }
        if (me.stateful !== false) {
            me.addStateEvents(me.stateEvents);
            me.initState();
        }
    },
    
    addStateEvents: function(events) {
        var me = this,
            i, event, stateEventsByName, eventArray;
        if (me.stateful && me.getStateId()) {
            eventArray = (typeof events === 'string') ? arguments : events;
            stateEventsByName = me.stateEventsByName || (me.stateEventsByName = {});
            for (i = eventArray.length; i--; ) {
                event = eventArray[i];
                if (event && !stateEventsByName[event]) {
                    stateEventsByName[event] = 1;
                    me.on(event, me.onStateChange, me);
                }
            }
        }
    },
    
    onStateChange: function() {
        var me = this,
            delay = me.saveDelay,
            statics, runner;
        if (!me.stateful) {
            return;
        }
        if (delay) {
            if (!me.stateTask) {
                statics = Ext.state.Stateful;
                runner = statics.runner || (statics.runner = new Ext.util.TaskRunner());
                me.stateTask = runner.newTask({
                    run: me.saveState,
                    scope: me,
                    interval: delay,
                    repeat: 1,
                    fireIdleEvent: false
                });
            }
            me.stateTask.start();
        } else {
            me.saveState();
        }
    },
    
    saveState: function() {
        var me = this,
            id = me.stateful && me.getStateId(),
            hasListeners = me.hasListeners,
            plugins, plugin, i, len, state, pluginState;
        if (id) {
            state = me.getState() || {};
            
            
            plugins = me.getPlugins() || [];
            for (i = 0 , len = plugins.length; i < len; i++) {
                plugin = plugins[i];
                if (plugin && plugin.getState) {
                    pluginState = plugin.getState(state);
                    if (pluginState && !state[plugin.ptype]) {
                        
                        state[plugin.ptype] = pluginState;
                    }
                }
            }
            if (!hasListeners.beforestatesave || me.fireEvent('beforestatesave', me, state) !== false) {
                Ext.state.Manager.set(id, state);
                if (hasListeners.statesave) {
                    me.fireEvent('statesave', me, state);
                }
            }
        }
    },
    
    getState: function() {
        return null;
    },
    
    applyState: function(state) {
        if (state) {
            Ext.apply(this, state);
        }
    },
    
    getStateId: function() {
        var me = this;
        return me.stateId || (me.autoGenId ? null : me.id);
    },
    
    initState: function() {
        var me = this,
            id = me.stateful && me.getStateId(),
            hasListeners = me.hasListeners,
            state, combinedState, i, len, plugins, plugin, pluginType;
        if (id) {
            combinedState = Ext.state.Manager.get(id);
            if (combinedState) {
                state = Ext.apply({}, combinedState);
                if (!hasListeners.beforestaterestore || me.fireEvent('beforestaterestore', me, combinedState) !== false) {
                    
                    plugins = me.getPlugins() || [];
                    for (i = 0 , len = plugins.length; i < len; i++) {
                        plugin = plugins[i];
                        if (plugin) {
                            pluginType = plugin.ptype;
                            if (plugin.applyState) {
                                plugin.applyState(state[pluginType], combinedState);
                            }
                            delete state[pluginType];
                        }
                    }
                    
                    me.applyState(state);
                    if (hasListeners.staterestore) {
                        me.fireEvent('staterestore', me, combinedState);
                    }
                }
            }
        }
    },
    
    savePropToState: function(propName, state, stateName) {
        var me = this,
            value = me[propName],
            config = me.initialConfig;
        if (me.hasOwnProperty(propName)) {
            if (!config || config[propName] !== value) {
                if (state) {
                    state[stateName || propName] = value;
                }
                return true;
            }
        }
        return false;
    },
    
    savePropsToState: function(propNames, state) {
        var me = this,
            i, n;
        if (typeof propNames === 'string') {
            me.savePropToState(propNames, state);
        } else {
            for (i = 0 , n = propNames.length; i < n; ++i) {
                me.savePropToState(propNames[i], state);
            }
        }
        return state;
    },
    
    destroy: function() {
        var me = this,
            task = me.stateTask;
        if (task) {
            task.destroy();
            me.stateTask = null;
        }
        me.clearListeners();
    }
});


Ext.define('Ext.util.Floating', {
    mixinId: 'floating',
    uses: [
        'Ext.ZIndexManager'
    ],
    
    focusOnToFront: true,
    
    shadow: 'sides',
    
    animateShadow: false,
    
    constrain: false,
    
    
    
    
    
    
    config: {
        
        activeCounter: 0,
        
        alwaysOnTop: false
    },
    preventDefaultAlign: false,
    _visModeMap: {
        visibility: 1,
        display: 2,
        offsets: 3
    },
    constructor: function() {
        var me = this,
            el = me.el,
            shadow = me.shadow,
            shadowOffset, shadowConfig;
        if (shadow) {
            shadowConfig = {
                mode: (shadow === true) ? 'sides' : shadow
            };
            shadowOffset = me.shadowOffset;
            if (shadowOffset) {
                shadowConfig.offset = shadowOffset;
            }
            shadowConfig.animate = me.animateShadow;
            shadowConfig.fixed = me.fixed;
            el.enableShadow(shadowConfig, false);
        }
        if (me.shim || Ext.useShims) {
            el.enableShim({
                fixed: me.fixed
            }, false);
        }
        el.setVisibilityMode(me._visModeMap[me.hideMode]);
        
        
        if (me.modal && !(Ext.enableFocusManager)) {
            me.mon(me.el, {
                keydown: me.onKeyDown,
                scope: me
            });
        }
        
        
        me.mon(me.el, {
            mousedown: me.onMouseDown,
            scope: me,
            capture: true
        });
        
        
        
        me.registerWithOwnerCt();
        me.initHierarchyEvents();
    },
    initFloatConstrain: function() {
        var me = this,
            floatParent = me.floatParent;
        
        
        if ((me.constrain || me.constrainHeader) && !me.constrainTo) {
            me.constrainTo = floatParent ? floatParent.getTargetEl() : me.container;
        }
    },
    initHierarchyEvents: function() {
        var me = this,
            syncHidden = this.syncHidden;
        if (!me.hasHierarchyEventListeners) {
            me.mon(Ext.GlobalEvents, {
                hide: syncHidden,
                collapse: syncHidden,
                show: syncHidden,
                expand: syncHidden,
                added: syncHidden,
                scope: me
            });
            me.hasHierarchyEventListeners = true;
        }
    },
    registerWithOwnerCt: function() {
        var me = this,
            ownerCt = me.ownerCt,
            zip = me.zIndexParent;
        if (zip) {
            zip.unregisterFloatingItem(me);
        }
        
        
        zip = me.zIndexParent = me.up('[floating]');
        
        
        
        me.floatParent = ownerCt || zip;
        me.initFloatConstrain();
        delete me.ownerCt;
        if (zip) {
            zip.registerFloatingItem(me);
        } else {
            Ext.WindowManager.register(me);
        }
    },
    
    onKeyDown: function(e) {
        var me = this,
            shift, focusables, first, last;
        
        
        
        
        
        
        
        
        
        
        
        if (e.getKey() === e.TAB) {
            shift = e.shiftKey;
            focusables = me.query(':focusable');
            if (focusables.length) {
                first = focusables[0];
                last = focusables[focusables.length - 1];
                if (!shift && last.hasFocus) {
                    e.stopEvent();
                    first.focus();
                } else if (shift && first.hasFocus) {
                    e.stopEvent();
                    last.focus();
                }
            }
        }
    },
    
    
    
    onMouseDown: function(e) {
        var me = this,
            focusTask = me.focusTask,
            
            
            parentEvent = e.parentEvent,
            preventFocus = parentEvent && parentEvent.type === 'touchstart',
            target, dom, skipFronting;
        if (me.floating && 
        
        
        (!focusTask || !focusTask.id)) {
            target = e.target;
            dom = me.el.dom;
            
            
            
            
            
            while (!preventFocus && target && target !== dom) {
                if (Ext.fly(target).isFocusable()) {
                    preventFocus = true;
                }
                target = target.parentNode;
            }
            
            
            skipFronting = Ext.WindowManager.getActive() === me && (target === dom || preventFocus);
            
            
            if (!skipFronting) {
                me.toFront(preventFocus);
            }
        }
    },
    onBeforeFloatLayout: function() {
        this.el.preventSync = true;
    },
    onAfterFloatLayout: function() {
        var el = this.el;
        if (el.shadow || el.shim) {
            
            
            
            
            el.setUnderlaysVisible(true);
            el.syncUnderlays();
        }
    },
    
    syncHidden: function() {
        var me = this,
            hidden = me.hidden || !me.rendered,
            hierarchicallyHidden = me.hierarchicallyHidden = me.isHierarchicallyHidden(),
            pendingShow = me.pendingShow;
        if (hidden !== hierarchicallyHidden) {
            if (hierarchicallyHidden) {
                me.hide();
                me.pendingShow = true;
            } else if (pendingShow) {
                delete me.pendingShow;
                if (pendingShow.length) {
                    me.show.apply(me, pendingShow);
                } else {
                    me.show();
                }
            }
        }
    },
    
    
    
    
    
    setZIndex: function(index) {
        var me = this;
        me.el.setZIndex(index);
        
        index += 10;
        
        
        if (me.floatingDescendants) {
            index = Math.floor(me.floatingDescendants.setBase(index) / 100) * 100 + 10000;
        }
        return index;
    },
    
    doConstrain: function(constrainTo) {
        var me = this,
            
            
            
            xy = me.calculateConstrainedPosition(constrainTo, null, true);
        
        if (xy) {
            me.setPosition(xy);
        }
    },
    updateActiveCounter: function(activeCounter) {
        var z = this.zIndexParent;
        
        if (z && this.bringParentToFront !== false) {
            z.setActiveCounter(++Ext.ZIndexManager.activeCounter);
        }
        
        if (z = this.zIndexManager) {
            z.onComponentUpdate(this);
        }
    },
    updateAlwaysOnTop: function(alwaysOnTop) {
        var z = this.zIndexManager;
        
        if (z) {
            z.onComponentUpdate(this);
        }
    },
    
    toFront: function(preventFocus) {
        var me = this;
        
        
        if (me.zIndexManager.bringToFront(me, preventFocus || !me.focusOnToFront)) {
            if (me.hasListeners.tofront) {
                me.fireEvent('tofront', me, me.el.getZIndex());
            }
        }
        return me;
    },
    
    setActive: function(active, newActive, doFocus) {
        var me = this;
        if (active) {
            if (me.el.shadow && !me.maximized) {
                me.el.enableShadow(null, true);
            }
            
            
            if (doFocus) {
                
                if (!me.el.contains(Ext.Element.getActiveElement())) {
                    me.focus();
                }
            }
            me.fireEvent('activate', me);
        } else 
        
        
        {
            me.fireEvent('deactivate', me);
        }
    },
    
    toBack: function() {
        this.zIndexManager.sendToBack(this);
        return this;
    },
    
    center: function() {
        var me = this,
            xy;
        if (me.isVisible()) {
            xy = me.getAlignToXY(me.container, 'c-c');
            me.setPagePosition(xy);
        } else {
            me.needsCenter = true;
        }
        return me;
    },
    onFloatShow: function() {
        if (this.needsCenter) {
            this.center();
        }
        delete this.needsCenter;
        if (this.toFrontOnShow) {
            this.toFront();
        }
    },
    
    fitContainer: function(animate) {
        var me = this,
            parent = me.floatParent,
            container = parent ? parent.getTargetEl() : me.container,
            newBox = container.getViewSize(),
            newPosition = parent || (container.dom !== document.body) ? 
            [
                0,
                0
            ] : 
            container.getXY();
        newBox.x = newPosition[0];
        newBox.y = newPosition[1];
        me.setBox(newBox, animate);
    }
});


Ext.define('Ext.util.Focusable', {
    mixinId: 'focusable',
    
    focusable: false,
    
    
    focusCls: 'focus',
    
    
    
    initFocusable: Ext.emptyFn,
    
    initFocusableEvents: function() {
        
        
        this.initFocusableElement();
    },
    destroyFocusable: function() {
        this.focusListeners = Ext.destroy(this.focusListeners);
        delete this.focusTask;
    },
    enableFocusable: Ext.emptyFn,
    disableFocusable: function() {
        var me = this,
            focusCls = me.focusCls,
            focusClsEl;
        focusClsEl = me.getFocusClsEl();
        if (focusCls && focusClsEl) {
            focusClsEl.removeCls(me.removeClsWithUI(focusCls, true));
        }
    },
    
    isFocusable: function(deep) {
        var me = this,
            focusEl;
        if (!me.focusable && (!me.isContainer || !deep)) {
            return false;
        }
        focusEl = me.getFocusEl();
        if (focusEl && me.canFocus()) {
            
            
            
            return focusEl.isFocusable(deep);
        }
        return false;
    },
    canFocus: function() {
        var me = this;
        
        
        
        return (me.isContainer || me.focusable) && me.rendered && !me.destroying && !me.isDestroyed && !me.disabled && me.isVisible(true);
    },
    
    focus: function(selectText, delay, callback, scope) {
        var me = this,
            focusEl, focusElDom, containerScrollTop;
        if ((!me.focusable && !me.isContainer) || me.isDestroyed || me.destroying) {
            return;
        }
        
        if (delay) {
            me.getFocusTask().delay(Ext.isNumber(delay) ? delay : 10, me.focus, me, [
                selectText,
                false,
                callback,
                scope
            ]);
            return me;
        }
        
        me.cancelFocus();
        
        
        if (me.canFocus() && (focusEl = me.getFocusEl())) {
            
            
            if (focusEl.isComponent) {
                return focusEl.focus(selectText, delay, callback, scope);
            }
            focusElDom = focusEl.dom;
            
            if (focusElDom) {
                
                
                if (focusEl.needsTabIndex()) {
                    focusElDom.tabIndex = -1;
                }
                if (me.floating) {
                    containerScrollTop = me.container.dom.scrollTop;
                }
                
                
                
                focusEl.focus();
                if (selectText) {
                    if (Ext.isArray(selectText)) {
                        if (me.selectText) {
                            me.selectText.apply(me, selectText);
                        }
                    } else if (focusElDom.select) {
                        
                        focusElDom.select();
                    } else if (me.selectText) {
                        me.selectText();
                    }
                }
                
                Ext.callback(callback, scope);
            }
            
            
            if (me.floating) {
                
                
                if (me !== me.zIndexManager.getActive()) {
                    me.toFront(true);
                }
                if (containerScrollTop !== undefined) {
                    me.container.dom.scrollTop = containerScrollTop;
                }
            }
        }
        return me;
    },
    
    cancelFocus: function() {
        var task = this.getFocusTask();
        if (task) {
            task.cancel();
        }
    },
    
    beforeBlur: Ext.emptyFn,
    
    onBlur: function(e) {
        var me = this,
            container = me.focusableContainer,
            focusCls = me.focusCls,
            focusClsEl;
        if (!me.focusable || me.destroying) {
            return;
        }
        me.beforeBlur(e);
        if (container) {
            container.beforeFocusableChildBlur(me, e);
        }
        focusClsEl = me.getFocusClsEl();
        if (focusCls && focusClsEl) {
            focusClsEl.removeCls(me.removeClsWithUI(focusCls, true));
        }
        if (me.validateOnBlur) {
            me.validate();
        }
        me.hasFocus = false;
        me.fireEvent('blur', me, e);
        me.postBlur(e);
        if (container) {
            container.afterFocusableChildBlur(me, e);
        }
    },
    
    postBlur: Ext.emptyFn,
    
    beforeFocus: Ext.emptyFn,
    
    onFocus: function(e) {
        var me = this,
            container = me.focusableContainer,
            focusCls = me.focusCls,
            focusClsEl;
        if (!me.focusable) {
            return;
        }
        if (me.canFocus()) {
            me.beforeFocus(e);
            if (container) {
                container.beforeFocusableChildFocus(me, e);
            }
            focusClsEl = me.getFocusClsEl();
            if (focusCls && focusClsEl) {
                focusClsEl.addCls(me.addClsWithUI(focusCls, true));
            }
            if (!me.hasFocus) {
                me.hasFocus = true;
                me.fireEvent('focus', me, e);
            }
            me.postFocus(e);
            if (container) {
                container.afterFocusableChildFocus(me, e);
            }
        }
    },
    
    postFocus: Ext.emptyFn,
    
    getTabIndex: function() {
        var me = this,
            el, index;
        if (!me.focusable) {
            return;
        }
        el = me.getFocusEl();
        if (el) {
            
            if (el.isComponent) {
                index = el.getTabIndex();
            }
            
            
            else if (el.isElement) {
                index = el.getAttribute(Ext.Element.tabIndexAttributeName);
            } else 
            
            
            
            {
                return;
            }
            me.tabIndex = index;
        } else {
            index = me.tabIndex;
        }
        return index - 0;
    },
    
    
    setTabIndex: function(newTabIndex) {
        var me = this,
            el;
        if (!me.focusable) {
            return;
        }
        me.tabIndex = newTabIndex;
        el = me.getFocusEl();
        if (el) {
            
            if (el.isComponent) {
                el.setTabIndex(newTabIndex);
            }
            
            
            
            else if (el.isElement) {
                el.set({
                    tabindex: newTabIndex
                });
            }
        }
    },
    
    onFocusEnter: function(e) {
        var me = this;
        me.previousFocus = e.relatedTarget;
        me.containsFocus = true;
        me.fireEvent('focusenter', me, e);
    },
    
    onFocusLeave: function(e) {
        var me = this;
        me.previousFocus = null;
        me.containsFocus = false;
        me.fireEvent('focusleave', me, e);
    },
    privates: {
        
        initFocusableElement: function() {
            var me = this,
                tabIndex = me.tabIndex,
                focusEl = me.getFocusEl(),
                needsTabIndex;
            if (focusEl && !focusEl.isComponent) {
                needsTabIndex = focusEl.needsTabIndex();
                
                if (needsTabIndex || tabIndex != null) {
                    focusEl.dom.setAttribute('tabindex', tabIndex);
                }
                
                
                focusEl.dom.setAttribute(Ext.Component.componentIdAttribute, me.id);
            }
        },
        
        getFocusEl: function() {
            return this.element || this.el;
        },
        
        getFocusClsEl: function() {
            return this.getFocusEl();
        },
        
        getFocusTask: function() {
            if (!this.focusTask) {
                this.focusTask = Ext.focusTask;
            }
            return this.focusTask;
        },
        
        blur: function() {
            var me = this,
                focusEl;
            if (!me.focusable || !me.rendered) {
                return;
            }
            focusEl = me.getFocusEl();
            if (focusEl) {
                me.blurring = true;
                focusEl.blur();
                delete me.blurring;
            }
            return me;
        },
        disableTabbing: function() {
            var me = this,
                el = me.el,
                focusEl;
            if (!me.focusable) {
                return;
            }
            focusEl = me.getFocusEl();
            if (el) {
                el.saveChildrenTabbableState();
            }
            if (focusEl) {
                focusEl = focusEl.isComponent ? focusEl.getFocusEl() : focusEl;
                focusEl.saveTabbableState();
            }
        },
        enableTabbing: function() {
            var me = this,
                el = me.el,
                focusEl;
            if (!me.focusable) {
                return;
            }
            focusEl = me.getFocusEl();
            if (focusEl) {
                focusEl = focusEl.isComponent ? focusEl.getFocusEl() : focusEl;
                focusEl.restoreTabbableState();
            }
            if (el) {
                el.restoreChildrenTabbableState();
            }
        }
    }
}, function() {
    
    
    if (!Ext.focusTask) {
        Ext.focusTask = new Ext.util.DelayedTask();
    }
});


Ext.define('Ext.Component', {
    alternateClassName: 'Ext.AbstractComponent',
    xtype: [
        'component',
        'box'
    ],
    requires: [
        'Ext.ComponentQuery',
        'Ext.ComponentManager',
        'Ext.util.ProtoElement',
        'Ext.dom.CompositeElement',
        'Ext.scroll.Scroller',
        'Ext.scroll.TouchScroller',
        'Ext.scroll.DomScroller'
    ],
    mixins: [
        'Ext.mixin.Inheritable',
        'Ext.util.Positionable',
        'Ext.util.Observable',
        'Ext.mixin.Bindable',
        'Ext.util.Animate',
        'Ext.util.ElementContainer',
        'Ext.util.Renderable',
        'Ext.state.Stateful',
        'Ext.util.Floating',
        'Ext.util.Focusable'
    ],
    uses: [
        'Ext.overrides.*',
        'Ext.Element',
        'Ext.DomHelper',
        'Ext.XTemplate',
        'Ext.ComponentLoader',
        'Ext.layout.Context',
        'Ext.layout.Layout',
        'Ext.layout.component.Auto',
        'Ext.LoadMask',
        'Ext.ZIndexManager',
        'Ext.util.DelayedTask',
        'Ext.resizer.Resizer',
        'Ext.util.ComponentDragger'
    ],
    statics: {
        AUTO_ID: 1000,
        pendingLayouts: null,
        layoutSuspendCount: 0,
        
        DIRECTION_TOP: 'top',
        DIRECTION_RIGHT: 'right',
        DIRECTION_BOTTOM: 'bottom',
        DIRECTION_LEFT: 'left',
        VERTICAL_DIRECTION_Re: /^(?:top|bottom)$/,
        
        
        INVALID_ID_CHARS_Re: /[\.,\s]/g,
        
        componentIdAttribute: 'componentId',
        
        cancelLayout: function(comp, isDestroying) {
            var context = this.runningLayoutContext || this.pendingLayouts;
            if (context) {
                context.cancelComponent(comp, false, isDestroying);
            }
        },
        
        flushLayouts: function() {
            var me = this,
                context = me.pendingLayouts;
            if (context && context.invalidQueue.length) {
                me.pendingLayouts = null;
                me.runningLayoutContext = context;
                Ext.override(context, {
                    runComplete: function() {
                        
                        
                        
                        me.runningLayoutContext = null;
                        var result = this.callParent();
                        
                        if (Ext.GlobalEvents.hasListeners.afterlayout) {
                            Ext.GlobalEvents.fireEvent('afterlayout');
                        }
                        return result;
                    }
                });
                context.run();
            }
        },
        
        resumeLayouts: function(flush) {
            if (this.layoutSuspendCount && !--this.layoutSuspendCount) {
                if (flush) {
                    this.flushLayouts();
                }
                if (Ext.GlobalEvents.hasListeners.resumelayouts) {
                    Ext.GlobalEvents.fireEvent('resumelayouts');
                }
            }
        },
        
        suspendLayouts: function() {
            ++this.layoutSuspendCount;
        },
        
        updateLayout: function(comp, defer) {
            var me = this,
                running = me.runningLayoutContext,
                pending;
            if (running) {
                running.queueInvalidate(comp);
            } else {
                pending = me.pendingLayouts || (me.pendingLayouts = new Ext.layout.Context());
                pending.queueInvalidate(comp);
                if (!defer && !me.layoutSuspendCount && !comp.isLayoutSuspended()) {
                    me.flushLayouts();
                }
            }
        }
    },
    
    
    
    
    
    $configPrefixed: false,
    
    $configStrict: false,
    config: {
        
        data: null,
        
        
        maxHeight: null,
        
        maxWidth: null,
        
        minHeight: null,
        
        minWidth: null,
        
        scrollable: null
    },
    defaultBindProperty: 'html',
    
    alignTarget: null,
    
    
    autoRender: false,
    
    
    autoShow: false,
    
    baseCls: Ext.baseCSSPrefix + 'component',
    
    
    childEls: {
        frameTable: {
            frame: true
        },
        frameTL: {
            frame: 'tl'
        },
        frameTC: {
            frame: 'tc'
        },
        frameTR: {
            frame: 'tr'
        },
        frameML: {
            frame: 'ml'
        },
        frameBody: {
            frame: 'mc'
        },
        frameMR: {
            frame: 'mr'
        },
        frameBL: {
            frame: 'bl'
        },
        frameBC: {
            frame: 'bc'
        },
        frameBR: {
            frame: 'br'
        }
    },
    
    
    
    
    
    componentLayout: 'autocomponent',
    
    
    
    
    defaultAlign: 'c-c',
    
    disabled: false,
    
    disabledRe: /^(?:button|input|select|textarea|optgroup|option|fieldset)$/i,
    nonMaskableRe: (function() {
        var re = [
                'input',
                'select',
                'textarea',
                'optgroup',
                'option',
                'table'
            ];
        
        if (Ext.isIE9m && !(Ext.isIE9 && !Ext.isIEQuirks)) {
            
            
            re.push('p');
        }
        return new RegExp('^(?:' + re.join('|') + ')$','i');
    }()),
    
    disabledCls: Ext.baseCSSPrefix + 'item-disabled',
    
    draggable: false,
    
    floating: false,
    
    
    
    
    hidden: false,
    
    hideMode: 'display',
    
    
    
    
    
    
    maskElement: null,
    
    
    
    
    
    
    
    
    
    
    renderTpl: [
        '<tpl if="renderScroller">',
        '<div class="{scrollerCls}" style="{%this.renderPadding(out, values)%}">',
        '</tpl>',
        '{%this.renderContent(out,values)%}',
        '<tpl if="renderScroller"></div></tpl>'
    ],
    
    
    resizeHandles: 'all',
    
    shrinkWrap: 2,
    
    
    toFrontOnShow: true,
    
    
    synthetic: false,
    
    tplWriteMode: 'overwrite',
    
    ui: 'default',
    
    uiCls: [],
    
    weight: null,
    
    
    
    
    
    
    
    
    
    
    
    allowDomMove: true,
    
    autoGenId: false,
    
    borderBoxCls: Ext.baseCSSPrefix + 'border-box',
    
    componentLayoutCounter: 0,
    
    contentPaddingProperty: 'padding',
    
    deferLayouts: false,
    
    
    frameSize: null,
    
    horizontalPosProp: 'left',
    
    isComponent: true,
    
    _isLayoutRoot: false,
    
    layoutSuspendCount: 0,
    
    liquidLayout: false,
    
    maskOnDisable: true,
    
    offsetsCls: Ext.baseCSSPrefix + 'hidden-offsets',
    
    
    rendered: false,
    
    rootCls: Ext.baseCSSPrefix + 'body',
    
    scrollerCls: Ext.baseCSSPrefix + 'scroll-scroller',
    scrollerSelector: '.' + Ext.baseCSSPrefix + 'scroll-scroller',
    
    _scrollFlags: {
        auto: {
            
            auto: {
                overflowX: 'auto',
                overflowY: 'auto',
                x: true,
                y: true,
                both: true
            },
            
            'false': {
                overflowX: 'auto',
                overflowY: 'hidden',
                x: true,
                y: false,
                both: false
            },
            
            scroll: {
                overflowX: 'auto',
                overflowY: 'scroll',
                x: true,
                y: true,
                both: true
            }
        },
        'false': {
            
            auto: {
                overflowX: 'hidden',
                overflowY: 'auto',
                x: false,
                y: true,
                both: false
            },
            
            'false': {
                overflowX: 'hidden',
                overflowY: 'hidden',
                x: false,
                y: false,
                both: false
            },
            
            scroll: {
                overflowX: 'hidden',
                overflowY: 'scroll',
                x: false,
                y: true,
                both: false
            }
        },
        scroll: {
            
            auto: {
                overflowX: 'scroll',
                overflowY: 'auto',
                x: true,
                y: true,
                both: true
            },
            
            'false': {
                overflowX: 'scroll',
                overflowY: 'hidden',
                x: true,
                y: false,
                both: false
            },
            
            scroll: {
                overflowX: 'scroll',
                overflowY: 'scroll',
                x: true,
                y: true,
                both: true
            }
        },
        none: {
            overflowX: '',
            overflowY: '',
            x: false,
            y: false,
            both: false
        }
    },
    _scrollableCfg: {
        x: {
            x: true,
            y: false
        },
        y: {
            x: false,
            y: true
        },
        horizontal: {
            x: true,
            y: false
        },
        vertical: {
            x: false,
            y: true
        },
        both: {
            x: true,
            y: true
        },
        'true': {
            x: true,
            y: true
        }
    },
    validIdRe: Ext.validIdRe,
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        var me = this,
            i, len, xhooks, controller, autoScroll, overflowX, overflowY, scrollable;
        config = config || {};
        if (config.initialConfig) {
            
            if (config.isAction) {
                me.baseAction = config;
            }
            config = config.initialConfig;
        }
        
        else if (config.tagName || config.dom || Ext.isString(config)) {
            
            config = {
                applyTo: config,
                id: config.id || config
            };
        }
        
        me.initialConfig = config;
        
        me.getId();
        me.protoEl = new Ext.util.ProtoElement();
        me.$calledInitConfig = true;
        me.initConfig(config);
        delete me.$calledInitConfig;
        if (me.scrollable == null) {
            autoScroll = me.autoScroll;
            if (autoScroll) {
                scrollable = !!autoScroll;
            } else {
                overflowX = me.overflowX;
                overflowY = me.overflowY;
                if (overflowX || overflowY) {
                    scrollable = {
                        x: (overflowX && overflowX !== 'hidden') ? overflowX : false,
                        y: (overflowY && overflowY !== 'hidden') ? overflowY : false
                    };
                }
            }
            if (scrollable) {
                me.setScrollable(scrollable);
            }
        }
        xhooks = me.xhooks;
        if (xhooks) {
            delete me.xhooks;
            Ext.override(me, xhooks);
        }
        me.mixins.elementCt.constructor.call(me);
        if (!me.validIdRe.test(me.id)) {
            Ext.Error.raise('Invalid component "id": "' + me.id + '"');
        }
        if (!me.validIdRe.test(me.itemId)) {
            Ext.Error.raise('Invalid component "itemId": "' + me.itemId + '"');
        }
        me.setupProtoEl();
        
        
        if (me.cls) {
            me.initialCls = me.cls;
            me.protoEl.addCls(me.cls);
        }
        if (me.style) {
            me.initialStyle = me.style;
            me.protoEl.setStyle(me.style);
        }
        me.renderData = me.renderData || {};
        me.initComponent();
        
        if (!me.preventRegister) {
            Ext.ComponentManager.register(me);
        }
        me.mixins.state.constructor.call(me);
        me.addStateEvents('resize');
        controller = me.getController();
        if (controller) {
            controller.init(me);
        }
        
        if (me.plugins) {
            for (i = 0 , len = me.plugins.length; i < len; i++) {
                me.plugins[i] = me.initPlugin(me.plugins[i]);
            }
        }
        me.loader = me.getLoader();
        if (me.renderTo) {
            me.render(me.renderTo);
        }
        
        
        
        
        
        if (me.autoShow && !me.initOwnerCt) {
            me.show();
        }
        if (Ext.isDefined(me.disabledClass)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.Component: disabledClass has been deprecated. Please use disabledCls.');
            }
            me.disabledCls = me.disabledClass;
            delete me.disabledClass;
        }
        
        
        if (me.baseAction) {
            me.baseAction.addComponent(me);
        }
    },
    beforeInitConfig: function() {
        if (!this.$calledInitConfig) {
            Ext.Error.raise('initConfig should not be called by subclasses, it will be called by Ext.Component');
        }
        this.mixins.observable.constructor.call(this);
    },
    
    
    
    
    
    addCls: function(cls) {
        var me = this,
            el = me.rendered ? me.el : me.protoEl;
        el.addCls.apply(el, arguments);
        return me;
    },
    
    addClsWithUI: function(classes, skip) {
        var me = this,
            clsArray = [],
            i = 0,
            uiCls = me.uiCls = Ext.Array.clone(me.uiCls),
            activeUI = me.activeUI,
            length, cls;
        if (typeof classes === "string") {
            classes = (classes.indexOf(' ') < 0) ? [
                classes
            ] : Ext.String.splitWords(classes);
        }
        length = classes.length;
        for (; i < length; i++) {
            cls = classes[i];
            if (cls && !me.hasUICls(cls)) {
                uiCls.push(cls);
                
                if (activeUI) {
                    clsArray = clsArray.concat(me.addUIClsToElement(cls));
                }
            }
        }
        if (skip !== true && activeUI) {
            me.addCls(clsArray);
        }
        return clsArray;
    },
    
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var me = this;
        if (++me.componentLayoutCounter === 1) {
            me.afterFirstLayout(width, height);
        }
        if (width !== oldWidth || height !== oldHeight) {
            me.onResize(width, height, oldWidth, oldHeight);
        }
        if (this.floating) {
            this.onAfterFloatLayout();
        }
    },
    
    
    addPlugin: function(plugin) {
        var me = this;
        plugin = me.constructPlugin(plugin);
        if (me.plugins) {
            me.plugins.push(plugin);
        } else {
            me.plugins = [
                plugin
            ];
        }
        if (me.pluginsInitialized) {
            me.initPlugin(plugin);
        }
        return plugin;
    },
    
    addPropertyToState: function(state, propName, value) {
        var me = this,
            len = arguments.length;
        
        
        if (len === 3 || me.hasOwnProperty(propName)) {
            if (len < 3) {
                value = me[propName];
            }
            
            
            if (value !== me.initialConfig[propName]) {
                (state || (state = {}))[propName] = value;
            }
        }
        return state;
    },
    
    addUIClsToElement: function(uiCls) {
        var me = this,
            baseClsUI = me.baseCls + '-' + me.ui + '-' + uiCls,
            result = [
                Ext.baseCSSPrefix + uiCls,
                me.baseCls + '-' + uiCls,
                baseClsUI
            ],
            childEls, childElName, el, suffix;
        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            baseClsUI += '-';
            childEls = me.getChildEls();
            for (childElName in childEls) {
                suffix = childEls[childElName].frame;
                if (suffix && suffix !== true) {
                    el = me[childElName];
                    if (el) {
                        el.addCls(baseClsUI + suffix);
                    }
                }
            }
        }
        return result;
    },
    
    removeUIClsFromElement: function(uiCls) {
        var me = this,
            baseClsUI = me.baseCls + '-' + me.ui + '-' + uiCls,
            result = [
                Ext.baseCSSPrefix + uiCls,
                me.baseCls + '-' + uiCls,
                baseClsUI
            ],
            childEls, childElName, el, suffix;
        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            baseClsUI += '-';
            childEls = me.getChildEls();
            for (childElName in childEls) {
                suffix = childEls[childElName].frame;
                if (suffix && suffix !== true) {
                    el = me[childElName];
                    if (el) {
                        el.removeCls(baseClsUI + suffix);
                    }
                }
            }
        }
        return result;
    },
    
    adjustPosition: function(x, y) {
        var me = this,
            floatParentBox;
        
        if (me.isContainedFloater()) {
            floatParentBox = me.floatParent.getTargetEl().getViewRegion();
            x += floatParentBox.left;
            y += floatParentBox.top;
        }
        return {
            x: x,
            y: y
        };
    },
    
    afterHide: function(cb, scope) {
        var me = this,
            container = me.focusableContainer;
        me.hiddenByLayout = null;
        
        if (this.ownerLayout) {
            this.updateLayout({
                isRoot: false
            });
        }
        Ext.callback(cb, scope || me);
        
        
        
        
        me.fireHierarchyEvent('hide');
        me.fireEvent('hide', me);
        if (container) {
            container.onFocusableChildHide(me);
        }
    },
    
    afterSetPosition: function(x, y) {
        var me = this;
        me.onPosition(x, y);
        if (me.hasListeners.move) {
            me.fireEvent('move', me, x, y);
        }
    },
    
    afterShow: function(animateTarget, cb, scope) {
        var me = this,
            myEl = me.el,
            fromBox, toBox, ghostPanel;
        
        animateTarget = me.getAnimateTarget(animateTarget);
        
        if (!me.ghost) {
            animateTarget = null;
        }
        
        if (animateTarget) {
            toBox = {
                x: myEl.getX(),
                y: myEl.getY(),
                width: myEl.dom.offsetWidth,
                height: myEl.dom.offsetHeight
            };
            fromBox = {
                x: animateTarget.getX(),
                y: animateTarget.getY(),
                width: animateTarget.dom.offsetWidth,
                height: animateTarget.dom.offsetHeight
            };
            myEl.addCls(me.offsetsCls);
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();
            
            ghostPanel.setX(-10000);
            me.ghostBox = toBox;
            ghostPanel.el.animate({
                from: fromBox,
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        me.unghost();
                        delete me.ghostBox;
                        myEl.removeCls(me.offsetsCls);
                        me.onShowComplete(cb, scope);
                    }
                }
            });
        } else {
            me.onShowComplete(cb, scope);
        }
        me.fireHierarchyEvent('show');
    },
    animate: function(animObj) {
        var me = this,
            hasToWidth, hasToHeight, toHeight, toWidth, to, clearWidth, clearHeight, curWidth, w, curHeight, h, isExpanding, wasConstrained, wasConstrainedHeader, passedCallback, oldOverflow;
        animObj = animObj || {};
        to = animObj.to || {};
        if (Ext.fx.Manager.hasFxBlock(me.id)) {
            return me;
        }
        hasToWidth = Ext.isDefined(to.width);
        if (hasToWidth) {
            toWidth = Ext.Number.constrain(to.width, me.minWidth, me.maxWidth);
        }
        hasToHeight = Ext.isDefined(to.height);
        if (hasToHeight) {
            toHeight = Ext.Number.constrain(to.height, me.minHeight, me.maxHeight);
        }
        
        if (!animObj.dynamic && (hasToWidth || hasToHeight)) {
            curWidth = (animObj.from ? animObj.from.width : undefined) || me.getWidth();
            w = curWidth;
            curHeight = (animObj.from ? animObj.from.height : undefined) || me.getHeight();
            h = curHeight;
            isExpanding = false;
            if (hasToHeight && toHeight > curHeight) {
                h = toHeight;
                isExpanding = true;
            }
            if (hasToWidth && toWidth > curWidth) {
                w = toWidth;
                isExpanding = true;
            }
            
            if (hasToHeight || hasToWidth) {
                oldOverflow = me.el.getStyle('overflow');
                if (oldOverflow !== 'hidden') {
                    me.el.setStyle('overflow', 'hidden');
                }
            }
            
            
            
            if (isExpanding) {
                clearWidth = !Ext.isNumber(me.width);
                clearHeight = !Ext.isNumber(me.height);
                
                
                
                me.setSize(w, h);
                me.el.setSize(curWidth, curHeight);
                if (clearWidth) {
                    delete me.width;
                }
                if (clearHeight) {
                    delete me.height;
                }
            }
            if (hasToWidth) {
                to.width = toWidth;
            }
            if (hasToHeight) {
                to.height = toHeight;
            }
        }
        
        
        wasConstrained = me.constrain;
        wasConstrainedHeader = me.constrainHeader;
        if (wasConstrained || wasConstrainedHeader) {
            me.constrain = me.constrainHeader = false;
            passedCallback = animObj.callback;
            animObj.callback = function() {
                me.constrain = wasConstrained;
                me.constrainHeader = wasConstrainedHeader;
                
                if (passedCallback) {
                    passedCallback.call(animObj.scope || me, arguments);
                }
                if (oldOverflow !== 'hidden') {
                    me.el.setStyle('overflow', oldOverflow);
                }
            };
        }
        return me.mixins.animate.animate.apply(me, arguments);
    },
    applyScrollable: function(scrollable, oldScrollable) {
        var me = this,
            rendered = me.rendered,
            scrollableCfg, innerEl;
        if (scrollable) {
            if (scrollable === true || typeof scrollable === 'string') {
                scrollableCfg = me._scrollableCfg[scrollable];
                if (!scrollableCfg) {
                    Ext.Error.raise("'" + scrollable + "' is not a valid value for 'scrollable'");
                }
                scrollable = scrollableCfg;
            }
            if (oldScrollable) {
                oldScrollable.setConfig(scrollable);
                scrollable = oldScrollable;
            } else {
                scrollable = Ext.Object.chain(scrollable);
                
                if (rendered) {
                    
                    
                    
                    
                    
                    scrollable.element = me.getOverflowEl();
                    innerEl = me.getScrollerEl();
                    if (innerEl) {
                        scrollable.innerElement = innerEl;
                    }
                }
                
                
                scrollable.autoRefresh = false;
                if (Ext.supports.touchScroll === 1) {
                    
                    
                    scrollable.translatable = {
                        translationMethod: 'scrollparent'
                    };
                    
                    scrollable.indicators = false;
                }
                scrollable = Ext.scroll.Scroller.create(scrollable);
                scrollable.component = me;
            }
        } else if (oldScrollable) {
            oldScrollable.setConfig({
                x: false,
                y: false
            });
            oldScrollable.destroy();
        }
        if (me.rendered) {
            me.getOverflowStyle();
            
            me.updateLayout();
        }
        return scrollable;
    },
    
    beforeComponentLayout: function() {
        return true;
    },
    
    beforeDestroy: Ext.emptyFn,
    
    beforeLayout: function() {
        if (this.floating) {
            this.onBeforeFloatLayout();
        }
    },
    
    beforeSetPosition: function(x, y, animate) {
        var me = this,
            pos = null,
            x0, hasX, hasY, adj;
        
        
        if (x) {
            
            if (Ext.isNumber(x0 = x[0])) {
                animate = y;
                y = x[1];
                x = x0;
            }
            
            else if ((x0 = x.x) !== undefined) {
                animate = y;
                y = x.y;
                x = x0;
            }
        }
        if (me.constrain || me.constrainHeader) {
            pos = me.calculateConstrainedPosition(null, [
                x,
                y
            ], true);
            if (pos) {
                x = pos[0];
                y = pos[1];
            }
        }
        hasX = (x !== undefined);
        hasY = (y !== undefined);
        if (hasX || hasY) {
            
            
            me.x = x;
            me.y = y;
            adj = me.adjustPosition(x, y);
            
            pos = {
                x: adj.x,
                y: adj.y,
                anim: animate,
                hasX: hasX,
                hasY: hasY
            };
        }
        return pos;
    },
    
    beforeShow: Ext.emptyFn,
    
    bubble: function(fn, scope, args) {
        var p = this;
        while (p) {
            if (fn.apply(scope || p, args || [
                p
            ]) === false) {
                break;
            }
            p = p.getBubbleTarget();
        }
        return this;
    },
    
    cloneConfig: function(overrides) {
        overrides = overrides || {};
        var id = overrides.id || Ext.id(),
            cfg = Ext.applyIf(overrides, this.initialConfig),
            self;
        cfg.id = id;
        self = Ext.getClass(this);
        
        return new self(cfg);
    },
    
    destroy: function() {
        var me = this,
            selectors = me.renderSelectors,
            viewModel = me.getConfig('viewModel', true),
            session = me.getConfig('session', true),
            selector, ownerCt, el;
        if (!me.isDestroyed) {
            if (!me.hasListeners.beforedestroy || me.fireEvent('beforedestroy', me) !== false) {
                me.destroying = true;
                ownerCt = me.floatParent || me.ownerCt;
                if (me.floating) {
                    delete me.floatParent;
                    
                    
                    if (me.zIndexManager) {
                        me.zIndexManager.unregister(me);
                        me.zIndexManager = null;
                    }
                }
                me.removeBindings();
                
                me.beforeDestroy();
                if (viewModel && viewModel.isViewModel) {
                    viewModel.destroy();
                    me.viewModel = null;
                }
                if (session && session.isSession) {
                    if (session.getAutoDestroy()) {
                        session.destroy();
                    }
                    me.session = null;
                }
                if (ownerCt && ownerCt.remove) {
                    ownerCt.remove(me, false);
                }
                me.stopAnimation();
                me.onDestroy();
                
                Ext.destroy(me.plugins);
                me.componentLayout = null;
                if (me.hasListeners.destroy) {
                    me.fireEvent('destroy', me);
                }
                if (!me.preventRegister) {
                    Ext.ComponentManager.unregister(me);
                }
                me.mixins.state.destroy.call(me);
                me.clearListeners();
                
                if (me.rendered) {
                    if (!me.preserveElOnDestroy) {
                        me.el.destroy();
                    }
                    me.mixins.elementCt.destroy.call(me);
                    
                    if (selectors) {
                        for (selector in selectors) {
                            if (selectors.hasOwnProperty(selector)) {
                                el = me[selector];
                                if (el) {
                                    
                                    delete me[selector];
                                    el.destroy();
                                }
                            }
                        }
                    }
                    me.data = me.el = me.frameBody = me.rendered = null;
                }
                me.destroying = false;
                me.isDestroyed = true;
            }
        }
    },
    
    disable: function(silent) {
        var me = this,
            container = me.focusableContainer;
        me.enableOnRender = false;
        me.addCls(me.disabledCls);
        if (me.rendered) {
            me.onDisable();
        } else {
            me.disableOnRender = true;
        }
        me.disabled = true;
        if (silent !== true) {
            delete me.resetDisable;
            me.fireEvent('disable', me);
        }
        if (container) {
            container.onFocusableChildDisable(me);
        }
        return me;
    },
    
    enable: function(silent) {
        var me = this,
            container = me.focusableContainer;
        me.disableOnRender = false;
        me.removeCls(me.disabledCls);
        if (me.rendered) {
            me.onEnable();
        } else {
            me.enableOnRender = true;
        }
        me.disabled = false;
        delete me.resetDisable;
        if (silent !== true) {
            me.fireEvent('enable', me);
        }
        if (container) {
            container.onFocusableChildEnable(me);
        }
        return me;
    },
    
    findParentBy: function(fn) {
        var p;
        
        for (p = this.getRefOwner(); p && !fn(p, this); p = p.getRefOwner()) {}
        
        return p || null;
    },
    
    findParentByType: function(xtype) {
        return Ext.isFunction(xtype) ? this.findParentBy(function(p) {
            return p.constructor === xtype;
        }) : this.up(xtype);
    },
    
    findPlugin: function(ptype) {
        var i,
            plugins = this.plugins,
            ln = plugins && plugins.length;
        for (i = 0; i < ln; i++) {
            if (plugins[i].ptype === ptype) {
                return plugins[i];
            }
        }
    },
    getAnimateTarget: function(target) {
        target = target || this.animateTarget;
        if (target) {
            target = target.isComponent ? target.getEl() : Ext.get(target);
        }
        return target || null;
    },
    
    getBubbleTarget: function() {
        return this.getRefOwner();
    },
    getComponentLayout: function() {
        var me = this;
        if (!me.componentLayout || !me.componentLayout.isLayout) {
            me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent'));
        }
        return me.componentLayout;
    },
    
    getEl: function() {
        return this.el;
    },
    
    getHeight: function() {
        return this.el.getHeight();
    },
    
    initInheritedState: function(inheritedState) {
        var me = this,
            layout = me.componentLayout;
        if (me.hidden) {
            inheritedState.hidden = true;
        }
        if (me.collapseImmune) {
            inheritedState.collapseImmune = true;
        }
        if (me.modelValidation !== undefined) {
            inheritedState.modelValidation = me.modelValidation;
        }
        me.mixins.bindable.initInheritedState.call(me, inheritedState);
        if (layout && layout.initInheritedState) {
            layout.initInheritedState(inheritedState);
        }
    },
    
    getId: function() {
        var me = this,
            xtype;
        
        
        if (!(me.id || (me.id = me.initialConfig.id))) {
            xtype = me.getXType();
            if (xtype) {
                xtype = xtype.replace(Ext.Component.INVALID_ID_CHARS_Re, '-');
            } else {
                xtype = Ext.name.toLowerCase() + '-comp';
            }
            me.id = xtype + '-' + me.getAutoId();
        }
        return me.id;
    },
    
    getItemId: function() {
        return this.itemId || this.id;
    },
    
    getLoader: function() {
        var me = this,
            loader = me.loader;
        if (loader) {
            if (!loader.isLoader) {
                me.loader = new Ext.ComponentLoader(Ext.apply({
                    target: me
                }, loader));
            } else {
                loader.setTarget(me);
            }
            return me.loader;
        }
        return null;
    },
    
    getMaskTarget: function() {
        return this.maskElement ? this[this.maskElement] : null;
    },
    
    getPlugin: function(pluginId) {
        var i,
            plugins = this.plugins,
            ln = plugins && plugins.length;
        for (i = 0; i < ln; i++) {
            if (plugins[i].pluginId === pluginId) {
                return plugins[i];
            }
        }
        return null;
    },
    
    getPosition: function(local) {
        var me = this,
            xy,
            isContainedFloater = me.isContainedFloater(),
            floatParentBox;
        
        if ((local === true) && !isContainedFloater) {
            return [
                me.getLocalX(),
                me.getLocalY()
            ];
        }
        xy = me.getXY();
        
        if ((local === true) && isContainedFloater) {
            floatParentBox = me.floatParent.getTargetEl().getViewRegion();
            xy[0] -= floatParentBox.left;
            xy[1] -= floatParentBox.top;
        }
        return xy;
    },
    
    getScrollX: function() {
        var scroller = this.getScrollable();
        return scroller ? scroller.getPosition().x : 0;
    },
    
    getScrollY: function() {
        var scroller = this.getScrollable();
        return scroller ? scroller.getPosition().y : 0;
    },
    
    getSize: function(contentSize) {
        return this.el.getSize(contentSize);
    },
    
    getSizeModel: function(ownerCtSizeModel) {
        var me = this,
            models = Ext.layout.SizeModel,
            ownerContext = me.componentLayout.ownerContext,
            width = me.width,
            height = me.height,
            typeofWidth, typeofHeight, hasPixelWidth, hasPixelHeight, heightModel, ownerLayout, policy, shrinkWrap, topLevel, widthModel,
            
            isFloating = me.floating || me.floated;
        if (ownerContext) {
            
            
            
            
            widthModel = ownerContext.widthModel;
            heightModel = ownerContext.heightModel;
        }
        if (!widthModel || !heightModel) {
            hasPixelWidth = ((typeofWidth = typeof width) == 'number');
            hasPixelHeight = ((typeofHeight = typeof height) == 'number');
            topLevel = isFloating || !(ownerLayout = me.ownerLayout);
            
            if (topLevel) {
                policy = Ext.layout.Layout.prototype.autoSizePolicy;
                shrinkWrap = isFloating ? 3 : me.shrinkWrap;
                if (hasPixelWidth) {
                    widthModel = models.configured;
                }
                if (hasPixelHeight) {
                    heightModel = models.configured;
                }
            } else {
                policy = ownerLayout.getItemSizePolicy(me, ownerCtSizeModel);
                shrinkWrap = ownerLayout.isItemShrinkWrap(me);
            }
            if (ownerContext) {
                ownerContext.ownerSizePolicy = policy;
            }
            shrinkWrap = (shrinkWrap === true) ? 3 : (shrinkWrap || 0);
            
            
            
            
            
            if (topLevel && shrinkWrap) {
                if (width && typeofWidth == 'string') {
                    shrinkWrap &= 2;
                }
                
                if (height && typeofHeight == 'string') {
                    shrinkWrap &= 1;
                }
            }
            
            if (shrinkWrap !== 3) {
                if (!ownerCtSizeModel) {
                    ownerCtSizeModel = me.ownerCt && me.ownerCt.getSizeModel();
                }
                if (ownerCtSizeModel) {
                    shrinkWrap |= (ownerCtSizeModel.width.shrinkWrap ? 1 : 0) | (ownerCtSizeModel.height.shrinkWrap ? 2 : 0);
                }
            }
            if (!widthModel) {
                if (!policy.setsWidth) {
                    if (hasPixelWidth) {
                        widthModel = models.configured;
                    } else {
                        widthModel = (shrinkWrap & 1) ? models.shrinkWrap : models.natural;
                    }
                } else if (policy.readsWidth) {
                    if (hasPixelWidth) {
                        widthModel = models.calculatedFromConfigured;
                    } else {
                        widthModel = (shrinkWrap & 1) ? models.calculatedFromShrinkWrap : models.calculatedFromNatural;
                    }
                } else {
                    widthModel = models.calculated;
                }
            }
            if (!heightModel) {
                if (!policy.setsHeight) {
                    if (hasPixelHeight) {
                        heightModel = models.configured;
                    } else {
                        heightModel = (shrinkWrap & 2) ? models.shrinkWrap : models.natural;
                    }
                } else if (policy.readsHeight) {
                    if (hasPixelHeight) {
                        heightModel = models.calculatedFromConfigured;
                    } else {
                        heightModel = (shrinkWrap & 2) ? models.calculatedFromShrinkWrap : models.calculatedFromNatural;
                    }
                } else {
                    heightModel = models.calculated;
                }
            }
        }
        
        
        return widthModel.pairsByHeightOrdinal[heightModel.ordinal];
    },
    
    getState: function() {
        var me = this,
            state = null,
            sizeModel = me.getSizeModel();
        if (sizeModel.width.configured) {
            state = me.addPropertyToState(state, 'width');
        }
        if (sizeModel.height.configured) {
            state = me.addPropertyToState(state, 'height');
        }
        return state;
    },
    
    getWidth: function() {
        return this.el.getWidth();
    },
    
    getXType: function() {
        return this.self.xtype;
    },
    
    getXTypes: function() {
        var self = this.self,
            xtypes, parentPrototype, parentXtypes;
        if (!self.xtypes) {
            xtypes = [];
            parentPrototype = this;
            while (parentPrototype) {
                parentXtypes = parentPrototype.xtypes;
                if (parentXtypes !== undefined) {
                    xtypes.unshift.apply(xtypes, parentXtypes);
                }
                parentPrototype = parentPrototype.superclass;
            }
            self.xtypeChain = xtypes;
            self.xtypes = xtypes.join('/');
        }
        return self.xtypes;
    },
    
    hasCls: function(cls) {
        var el = this.rendered ? this.el : this.protoEl;
        return el.hasCls.apply(el, arguments);
    },
    
    hasUICls: function(cls) {
        var me = this,
            uiCls = me.uiCls || [];
        return Ext.Array.contains(uiCls, cls);
    },
    
    hide: function(animateTarget, cb, scope) {
        var me = this,
            continueHide;
        if (me.pendingShow) {
            
            
            delete me.pendingShow;
        }
        if (!(me.rendered && !me.isVisible())) {
            continueHide = (me.fireEvent('beforehide', me) !== false);
            if (me.hierarchicallyHidden || continueHide) {
                me.hidden = true;
                me.getInherited().hidden = true;
                if (me.rendered) {
                    
                    
                    if (me.floating) {
                        me.setActive(false);
                    }
                    me.onHide.apply(me, arguments);
                }
            }
        }
        return me;
    },
    
    initComponent: function() {
        var me = this,
            width = me.width,
            height = me.height;
        
        
        
        if (me.plugins && !me.plugins.processed) {
            me.plugins = me.constructPlugins();
        }
        me.pluginsInitialized = true;
        
        
        if (width != null || height != null) {
            me.setSize(width, height);
        }
        if (me.listeners) {
            me.on(me.listeners);
            me.listeners = null;
        }
        
        if (me.focusable) {
            me.initFocusable();
        }
    },
    
    initEvents: function() {
        var me = this,
            afterRenderEvents = me.afterRenderEvents,
            afterRenderEvent, el, property, index, len;
        if (afterRenderEvents) {
            for (property in afterRenderEvents) {
                el = me[property];
                if (el && el.on) {
                    afterRenderEvent = afterRenderEvents[property];
                    for (index = 0 , len = afterRenderEvent.length; index < len; ++index) {
                        me.mon(el, afterRenderEvent[index]);
                    }
                }
            }
        }
        if (me.focusable) {
            me.initFocusableEvents();
        }
    },
    
    is: function(selector) {
        return Ext.ComponentQuery.is(this, selector);
    },
    
    isDescendantOf: function(ancestor) {
        var p;
        
        for (p = this.getRefOwner(); p && p !== ancestor; p = p.getRefOwner()) {}
        
        return p || null;
    },
    
    isAncestor: function(possibleDescendant) {
        while (possibleDescendant) {
            if (possibleDescendant.getRefOwner() === this) {
                return true;
            }
            possibleDescendant = possibleDescendant.getRefOwner();
        }
    },
    
    isDisabled: function() {
        return this.disabled;
    },
    
    isDraggable: function() {
        return !!this.draggable;
    },
    
    isDroppable: function() {
        return !!this.droppable;
    },
    
    isFloating: function() {
        return this.floating;
    },
    
    isHidden: function() {
        return this.hidden;
    },
    isHierarchicallyHidden: function() {
        var child = this,
            hidden = false,
            parent, parentInheritedState;
        
        
        
        
        for (; (parent = child.ownerCt || child.floatParent); child = parent) {
            parentInheritedState = parent.getInherited();
            if (parentInheritedState.hidden) {
                hidden = true;
                break;
            }
            if (child.getInherited().collapseImmune) {
                
                if (parent.collapsed && !child.collapseImmune) {
                    
                    
                    
                    hidden = true;
                    break;
                }
            } else {
                
                
                
                hidden = !!parentInheritedState.collapsed;
                break;
            }
        }
        return hidden;
    },
    
    isLayoutChild: function(ownerCandidate) {
        return !this.floating && !!this.up(ownerCandidate);
    },
    
    isLayoutRoot: function() {
        var me = this,
            ownerLayout = me.ownerLayout;
        
        
        
        
        if (!ownerLayout || me._isLayoutRoot || me.floating) {
            return true;
        }
        return ownerLayout.isItemLayoutRoot(me);
    },
    
    isLayoutSuspended: function() {
        var comp = this,
            ownerLayout;
        while (comp) {
            if (comp.layoutSuspendCount || comp.suspendLayout) {
                return true;
            }
            ownerLayout = comp.ownerLayout;
            if (!ownerLayout) {
                break;
            }
            
            
            
            
            comp = ownerLayout.owner;
        }
        return false;
    },
    
    isVisible: function(deep) {
        var me = this,
            hidden;
        if (me.hidden || !me.rendered || me.isDestroyed) {
            hidden = true;
        } else if (deep) {
            hidden = me.isHierarchicallyHidden();
        }
        return !hidden;
    },
    
    isXType: function(xtype, shallow) {
        return shallow ? (Ext.Array.indexOf(this.xtypes, xtype) !== -1) : !!this.xtypesMap[xtype];
    },
    
    isMasked: function(deep) {
        var me = this;
        return me.masked || (me.loadMask && me.loadMask.isVisible()) || (deep && !!me.getInherited().masked);
    },
    
    setMasked: function(isMasked) {
        var me = this,
            container = me.focusableContainer;
        if (isMasked) {
            me.masked = true;
            me.getInherited().masked = isMasked;
        } else {
            me.masked = false;
            delete me.getInherited().masked;
        }
        if (container) {
            container.onFocusableChildMasked(me, isMasked);
        }
        return me;
    },
    
    mask: function(msg, msgCls, elHeight) {
        var box = this.lastBox,
            
            
            
            target = this.getMaskTarget() || this.el;
        
        if (box) {
            elHeight = box.height;
        }
        target.mask(msg, msgCls, elHeight);
        this.setMasked(true);
    },
    
    nextNode: function(selector, 
    includeSelf) {
        var node = this,
            ownerCt = node.ownerCt,
            result, it, len, i, sib;
        
        if (includeSelf && node.is(selector)) {
            return node;
        }
        if (ownerCt) {
            for (it = ownerCt.items.items , i = Ext.Array.indexOf(it, node) + 1 , len = it.length; i < len; i++) {
                sib = it[i];
                if (sib.is(selector)) {
                    return sib;
                }
                if (sib.down) {
                    result = sib.down(selector);
                    if (result) {
                        return result;
                    }
                }
            }
            return ownerCt.nextNode(selector);
        }
        return null;
    },
    
    nextSibling: function(selector) {
        var o = this.ownerCt,
            it, last, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this) + 1;
            if (idx) {
                if (selector) {
                    for (last = it.getCount(); idx < last; idx++) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx < it.getCount()) {
                        return it.getAt(idx);
                    }
                }
            }
        }
        return null;
    },
    
    onAdded: function(container, pos, instanced) {
        var me = this,
            inheritedState = me.inheritedState;
        me.ownerCt = container;
        
        
        
        if (inheritedState && instanced) {
            me.invalidateInheritedState();
        }
        if (me.reference) {
            me.fixReference();
        }
        if (me.hasListeners && me.hasListeners.added) {
            me.fireEvent('added', me, container, pos);
        }
        if (Ext.GlobalEvents.hasListeners.added) {
            me.fireHierarchyEvent('added');
        }
    },
    
    onRemoved: function(destroying) {
        var me = this,
            refHolder;
        if (Ext.GlobalEvents.hasListeners.removed) {
            me.fireHierarchyEvent('removed');
        }
        if (me.hasListeners.removed) {
            me.fireEvent('removed', me, me.ownerCt);
        }
        if (me.reference) {
            refHolder = me.lookupReferenceHolder();
            if (refHolder) {
                refHolder.clearReference(me);
            }
        }
        if (!destroying) {
            me.removeBindings();
        }
        if (me.inheritedState && !destroying) {
            me.invalidateInheritedState();
        }
        me.ownerCt = me.ownerLayout = null;
    },
    
    onBoxReady: function(width, height) {
        var me = this,
            scroller = me.scrollable;
        if (me.resizable) {
            me.initResizable(me.resizable);
        }
        
        
        if (me.draggable) {
            me.initDraggable();
        }
        if (scroller) {
            if (me.touchScroll) {
                scroller.setInnerElement(me.getScrollerEl());
            }
            scroller.setElement(me.getOverflowEl());
        }
        if (me.hasListeners.boxready) {
            me.fireEvent('boxready', me, width, height);
        }
    },
    
    onDestroy: function() {
        var me = this,
            controller = me.controller,
            container = me.focusableContainer;
        if (me.bind) {
            me.removeBindings();
        }
        if (controller) {
            controller.destroy();
        }
        me.controller = null;
        
        if (me.rendered) {
            Ext.destroy(me.dd, me.resizer, me.proxy, me.proxyWrap, me.resizerComponent, me.scrollable, me.contentEl);
        }
        if (container) {
            container.onFocusableChildDestroy(me);
        }
        if (me.focusable) {
            me.destroyFocusable();
        }
        
        Ext.destroy(me.componentLayout, me.loadMask, me.floatingDescendants);
    },
    
    onDisable: function() {
        var me = this,
            dom, nodeName;
        if (me.focusable) {
            me.disableFocusable();
        }
        if (me.maskOnDisable) {
            dom = me.el.dom;
            nodeName = dom.nodeName;
            if (me.disabledRe.test(nodeName)) {
                dom.disabled = true;
            }
            if (!me.nonMaskableRe.test(nodeName)) {
                me.mask();
            }
        }
    },
    
    onEnable: function() {
        var me = this,
            dom, nodeName;
        if (me.focusable) {
            me.enableFocusable();
        }
        if (me.maskOnDisable) {
            dom = me.el.dom;
            nodeName = dom.nodeName;
            if (me.disabledRe.test(nodeName)) {
                dom.disabled = false;
            }
            if (!me.nonMaskableRe.test(nodeName)) {
                me.unmask();
            }
        }
    },
    
    onHide: function(animateTarget, cb, scope) {
        var me = this,
            ghostPanel, fromSize, toBox,
            focusTarget = me.previousFocus;
        me.previousFocus = null;
        
        
        
        
        if (me.floating && focusTarget && me.containsFocus) {
            
            if (!focusTarget.isComponent) {
                focusTarget = Ext.fly(focusTarget);
            }
            if (Ext.isIE8 || focusTarget.isFocusable()) {
                focusTarget.focus();
            }
        }
        
        animateTarget = me.getAnimateTarget(animateTarget);
        
        if (!me.ghost) {
            animateTarget = null;
        }
        
        if (animateTarget) {
            toBox = {
                x: animateTarget.getX(),
                y: animateTarget.getY(),
                width: animateTarget.dom.offsetWidth,
                height: animateTarget.dom.offsetHeight
            };
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();
            fromSize = me.getSize();
            ghostPanel.el.animate({
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        ghostPanel.el.hide();
                        ghostPanel.setHiddenState(true);
                        ghostPanel.el.setSize(fromSize);
                        me.afterHide(cb, scope);
                    }
                }
            });
        }
        me.el.hide();
        if (!animateTarget) {
            me.afterHide(cb, scope);
        }
    },
    
    onPosition: Ext.emptyFn,
    
    onResize: function(width, height, oldWidth, oldHeight) {
        var me = this;
        
        if (me.floating && me.constrain) {
            me.doConstrain();
        }
        
        
        
        if (oldWidth) {
            me.refreshScroll();
        }
        if (me.hasListeners.resize) {
            me.fireEvent('resize', me, width, height, oldWidth, oldHeight);
        }
    },
    
    
    
    
    onShow: function() {
        var me = this;
        me.el.show();
        me.updateLayout({
            isRoot: false
        });
        
        if (me.floating) {
            if (me.maximized) {
                me.fitContainer();
            } else if (me.constrain) {
                me.doConstrain();
            }
        }
    },
    
    onShowComplete: function(cb, scope) {
        var me = this,
            container = me.focusableContainer;
        if (me.floating) {
            me.onFloatShow();
        }
        Ext.callback(cb, scope || me);
        me.fireEvent('show', me);
        if (container) {
            container.onFocusableChildShow(me);
        }
        delete me.hiddenByLayout;
    },
    onShowVeto: Ext.emptyFn,
    
    previousNode: function(selector, 
    includeSelf) {
        var node = this,
            ownerCt = node.ownerCt,
            result, it, i, sib;
        
        if (includeSelf && node.is(selector)) {
            return node;
        }
        if (ownerCt) {
            for (it = ownerCt.items.items , i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) {
                sib = it[i];
                if (sib.query) {
                    result = sib.query(selector);
                    result = result[result.length - 1];
                    if (result) {
                        return result;
                    }
                }
                if (sib.is(selector)) {
                    return sib;
                }
            }
            return ownerCt.previousNode(selector, true);
        }
        return null;
    },
    
    previousSibling: function(selector) {
        var o = this.ownerCt,
            it, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this);
            if (idx != -1) {
                if (selector) {
                    for (--idx; idx >= 0; idx--) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx) {
                        return it.getAt(--idx);
                    }
                }
            }
        }
        return null;
    },
    
    registerFloatingItem: function(cmp) {
        var me = this;
        if (!me.floatingDescendants) {
            me.floatingDescendants = new Ext.ZIndexManager(me);
        }
        me.floatingDescendants.register(cmp);
    },
    
    removeCls: function(cls) {
        var me = this,
            el = me.rendered ? me.el : me.protoEl;
        el.removeCls.apply(el, arguments);
        return me;
    },
    
    removeClsWithUI: function(classes, skip) {
        var me = this,
            clsArray = [],
            i = 0,
            extArray = Ext.Array,
            remove = extArray.remove,
            uiCls = me.uiCls = extArray.clone(me.uiCls),
            activeUI = me.activeUI,
            length, cls;
        if (typeof classes === "string") {
            classes = (classes.indexOf(' ') < 0) ? [
                classes
            ] : Ext.String.splitWords(classes);
        }
        length = classes.length;
        for (i = 0; i < length; i++) {
            cls = classes[i];
            if (cls && me.hasUICls(cls)) {
                remove(uiCls, cls);
                
                if (activeUI) {
                    clsArray = clsArray.concat(me.removeUIClsFromElement(cls));
                }
            }
        }
        if (skip !== true && activeUI) {
            me.removeCls(clsArray);
        }
        return clsArray;
    },
    resumeLayouts: function(flushOptions) {
        var me = this;
        if (!me.rendered) {
            return;
        }
        if (!me.layoutSuspendCount) {
            Ext.log.warn('Mismatched call to resumeLayouts - layouts are currently not suspended.');
        }
        if (me.layoutSuspendCount && !--me.layoutSuspendCount) {
            me.suspendLayout = false;
            if (flushOptions && !me.isLayoutSuspended()) {
                me.updateLayout(flushOptions);
            }
        }
    },
    
    scrollBy: function(deltaX, deltaY, animate) {
        var scroller = this.getScrollable();
        if (scroller) {
            scroller.scrollBy(deltaX, deltaY, animate);
        }
    },
    
    scrollTo: function(x, y, animate) {
        var scroller = this.getScrollable();
        if (scroller) {
            scroller.scrollTo(x, y, animate);
        }
    },
    
    setAutoScroll: function(scroll) {
        this.setScrollable(!!scroll);
        return this;
    },
    
    setBorder: function(border, 
    targetEl) {
        var me = this,
            initial = !!targetEl;
        if (me.rendered || initial) {
            if (!initial) {
                targetEl = me.el;
            }
            if (!border) {
                border = 0;
            } else if (border === true) {
                border = '1px';
            } else {
                border = this.unitizeBox(border);
            }
            targetEl.setStyle('border-width', border);
            if (!initial) {
                me.updateLayout();
            }
        }
        me.border = border;
    },
    
    setDock: function(dock) {
        var me = this,
            ownerCt = me.ownerCt;
        if (dock !== me.dock) {
            if (ownerCt && ownerCt.moveDocked) {
                ownerCt.moveDocked(me, dock);
            } else {
                me.dock = dock;
            }
        }
        return me;
    },
    
    setDisabled: function(disabled) {
        return this[disabled ? 'disable' : 'enable']();
    },
    
    setFlex: function(flex) {
        this.flex = flex;
    },
    
    setHeight: function(height) {
        return this.setSize(undefined, height);
    },
    
    setLoading: function(load, 
    targetEl) {
        var me = this,
            config = {
                target: me
            };
        if (me.rendered) {
            
            if (load !== false) {
                if (Ext.isString(load)) {
                    config.msg = load;
                } else {
                    Ext.apply(config, load);
                }
                
                if (!me.loadMask || !me.loadMask.isLoadMask) {
                    
                    
                    if (targetEl && config.useTargetEl == null) {
                        config.useTargetEl = true;
                    }
                    me.loadMask = new Ext.LoadMask(config);
                } else 
                {
                    Ext.apply(me.loadMask, config);
                }
                
                if (me.loadMask.isVisible()) {
                    me.loadMask.afterShow();
                } else 
                {
                    me.loadMask.show();
                }
            } else 
            {
                if (me.loadMask && me.loadMask.isLoadMask) {
                    me.loadMask.hide();
                }
            }
        }
        return me.loadMask;
    },
    
    setMargin: function(margin, 
    preventLayout) {
        var me = this;
        if (me.rendered) {
            if (!margin && margin !== 0) {
                margin = '';
            } else {
                if (margin === true) {
                    margin = 5;
                }
                margin = this.unitizeBox(margin);
            }
            me.margin = margin;
            
            me.margin$ = null;
            me.getEl().setStyle('margin', margin);
            if (!preventLayout) {
                
                
                me.updateLayout(me._notAsLayoutRoot);
            }
        } else {
            me.margin = margin;
        }
    },
    
    setOverflowXY: function(overflowX, overflowY) {
        this.setScrollable({
            x: (overflowX && overflowX !== 'hidden') ? overflowX : false,
            y: (overflowY && overflowY !== 'hidden') ? overflowY : false
        });
        return this;
    },
    
    setPagePosition: function(x, y, animate) {
        var me = this,
            p, floatParentBox;
        if (Ext.isArray(x)) {
            y = x[1];
            x = x[0];
        }
        me.pageX = x;
        me.pageY = y;
        if (me.floating) {
            
            if (me.isContainedFloater()) {
                floatParentBox = me.floatParent.getTargetEl().getViewRegion();
                if (Ext.isNumber(x) && Ext.isNumber(floatParentBox.left)) {
                    x -= floatParentBox.left;
                }
                if (Ext.isNumber(y) && Ext.isNumber(floatParentBox.top)) {
                    y -= floatParentBox.top;
                }
            } else {
                p = me.el.translateXY(x, y);
                x = p.x;
                y = p.y;
            }
            me.setPosition(x, y, animate);
        } else {
            p = me.el.translateXY(x, y);
            me.setPosition(p.x, p.y, animate);
        }
        return me;
    },
    
    setPosition: function(x, y, animate) {
        var me = this,
            pos = me.beforeSetPosition.apply(me, arguments);
        if (pos && me.rendered) {
            x = pos.x;
            y = pos.y;
            if (animate) {
                
                
                
                if (x !== me.getLocalX() || y !== me.getLocalY()) {
                    me.stopAnimation();
                    me.animate(Ext.apply({
                        duration: 1000,
                        listeners: {
                            afteranimate: Ext.Function.bind(me.afterSetPosition, me, [
                                x,
                                y
                            ])
                        },
                        to: {
                            
                            
                            
                            
                            left: x,
                            top: y
                        }
                    }, animate));
                }
            } else {
                me.setLocalXY(x, y);
                me.afterSetPosition(x, y);
            }
        }
        return me;
    },
    
    setScrollX: function(x, animate) {
        var scroller = this.getScrollable();
        if (scroller) {
            scroller.scrollTo(x, null, animate);
        }
    },
    
    setScrollY: function(y, animate) {
        var scroller = this.getScrollable();
        if (scroller) {
            scroller.scrollTo(null, y, animate);
        }
    },
    
    setSize: function(width, height) {
        var me = this;
        
        if (width && typeof width == 'object') {
            height = width.height;
            width = width.width;
        }
        
        if (typeof width == 'number') {
            me.width = Ext.Number.constrain(width, me.minWidth, me.maxWidth);
        } else if (width === null) {
            delete me.width;
        }
        if (typeof height == 'number') {
            me.height = Ext.Number.constrain(height, me.minHeight, me.maxHeight);
        } else if (height === null) {
            delete me.height;
        }
        
        
        if (me.rendered && me.isVisible()) {
            if (me.liquidLayout) {
                
                
                
                me.el.setSize(me.width, me.height);
            }
            
            me.updateLayout(me._notAsLayoutRoot);
        }
        return me;
    },
    
    setStyle: function(prop, value) {
        var el = this.el || this.protoEl;
        el.setStyle(prop, value);
        return this;
    },
    
    setUI: function(ui) {
        var me = this,
            uiCls = me.uiCls,
            activeUI = me.activeUI,
            classes;
        if (ui === activeUI) {
            
            return;
        }
        
        if (activeUI) {
            classes = me.removeClsWithUI(uiCls, true);
            if (classes.length) {
                me.removeCls(classes);
            }
            
            me.removeUIFromElement();
        } else {
            
            me.uiCls = [];
        }
        
        me.ui = ui;
        
        
        me.activeUI = ui;
        
        me.addUIToElement();
        classes = me.addClsWithUI(uiCls, true);
        if (classes.length) {
            me.addCls(classes);
        }
        
        
        
        if (me.rendered) {
            me.updateLayout();
        }
    },
    
    setVisible: function(visible) {
        return this[visible ? 'show' : 'hide']();
    },
    
    setHidden: function(hidden) {
        return this.setVisible(!hidden);
    },
    
    setWidth: function(width) {
        return this.setSize(width);
    },
    
    show: function(animateTarget, cb, scope) {
        var me = this,
            rendered = me.rendered;
        if (me.hierarchicallyHidden || (me.floating && !rendered && me.isHierarchicallyHidden())) {
            
            
            
            if (!rendered) {
                
                
                
                
                
                
                
                
                
                me.initHierarchyEvents();
            }
            
            if (arguments.length > 1) {
                arguments[0] = null;
                me.pendingShow = arguments;
            } else {
                me.pendingShow = true;
            }
        } else if (rendered && me.isVisible()) {
            if (me.floating) {
                me.onFloatShow();
            }
        } else {
            if (me.fireEvent('beforeshow', me) !== false) {
                me.hidden = false;
                delete this.getInherited().hidden;
                
                
                
                
                
                
                
                
                Ext.suspendLayouts();
                if (!rendered && (me.autoRender || me.floating)) {
                    me.doAutoRender();
                    rendered = me.rendered;
                }
                if (rendered) {
                    me.beforeShow();
                    Ext.resumeLayouts();
                    me.onShow.apply(me, arguments);
                    me.afterShow.apply(me, arguments);
                } else {
                    Ext.resumeLayouts(true);
                }
            } else {
                me.onShowVeto();
            }
        }
        return me;
    },
    
    showAt: function(x, y, animate) {
        var me = this;
        
        
        if (!me.rendered && (me.autoRender || me.floating)) {
            me.x = x;
            me.y = y;
            return me.show();
        }
        if (me.floating) {
            me.setPosition(x, y, animate);
        } else {
            me.setPagePosition(x, y, animate);
        }
        me.show();
    },
    
    showBy: function(cmp, pos, off) {
        var me = this;
        if (!me.floating) {
            Ext.log.warn('Using showBy on a non-floating component');
        }
        if (me.floating && cmp) {
            me.alignTarget = cmp;
            if (pos) {
                me.defaultAlign = pos;
            }
            if (off) {
                me.alignOffset = off;
            }
            me.show();
            
            if (!me.hidden) {
                me.alignTo(cmp, pos || me.defaultAlign, off || me.alignOffset);
            }
        }
        return me;
    },
    suspendLayouts: function() {
        var me = this;
        if (!me.rendered) {
            return;
        }
        if (++me.layoutSuspendCount === 1) {
            me.suspendLayout = true;
        }
    },
    unitizeBox: function(box) {
        return Ext.Element.unitizeBox(box);
    },
    
    unmask: function() {
        (this.getMaskTarget() || this.el).unmask();
        this.setMasked(false);
    },
    unregisterFloatingItem: function(cmp) {
        var me = this;
        if (me.floatingDescendants) {
            me.floatingDescendants.unregister(cmp);
        }
    },
    
    up: function(selector, limit) {
        var result = this.getRefOwner(),
            limitSelector = typeof limit === 'string',
            limitCount = typeof limit === 'number',
            limitComponent = limit && limit.isComponent,
            steps = 0;
        if (selector) {
            for (; result; result = result.getRefOwner()) {
                steps++;
                if (selector.isComponent) {
                    if (result === selector) {
                        return result;
                    }
                } else {
                    if (Ext.ComponentQuery.is(result, selector)) {
                        return result;
                    }
                }
                
                if (limitSelector && result.is(limit)) {
                    return;
                }
                if (limitCount && steps === limit) {
                    return;
                }
                if (limitComponent && result === limit) {
                    return;
                }
            }
        }
        return result;
    },
    
    update: function(htmlOrData, loadScripts, callback) {
        var me = this,
            isData = (me.tpl && !Ext.isString(htmlOrData)),
            scroller = me.getScrollable(),
            container = me.focusableContainer,
            sizeModel, doLayout, el;
        if (isData) {
            me.data = (htmlOrData && htmlOrData.isEntity) ? htmlOrData.getData(true) : htmlOrData;
        } else {
            me.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
        }
        if (me.rendered) {
            sizeModel = me.getSizeModel();
            doLayout = sizeModel.width.shrinkWrap || sizeModel.height.shrinkWrap;
            if (me.isContainer) {
                el = me.layout.getRenderTarget();
                
                doLayout = doLayout || me.items.items.length > 0;
            } else {
                el = me.touchScroll ? me.getScrollerEl() : me.getTargetEl();
            }
            if (isData) {
                me.tpl[me.tplWriteMode](el, me.data || {});
            } else {
                el.setHtml(me.html, loadScripts, callback);
            }
            if (doLayout) {
                me.updateLayout();
            }
            if (scroller) {
                scroller.refresh(true);
            }
            if (container) {
                container.onFocusableChildUpdate(me);
            }
        }
    },
    setHtml: function(html) {
        this.update(html);
    },
    applyData: function(data) {
        
        this.update(data);
    },
    
    updateBox: function(box) {
        this.setSize(box.width, box.height);
        this.setPagePosition(box.x, box.y);
        return this;
    },
    _asLayoutRoot: {
        isRoot: true
    },
    _notAsLayoutRoot: {
        isRoot: false
    },
    
    updateLayout: function(options) {
        var me = this,
            defer,
            lastBox = me.lastBox,
            isRoot = options && options.isRoot;
        if (lastBox) {
            
            
            lastBox.invalid = true;
        }
        if (!me.rendered || me.layoutSuspendCount || me.suspendLayout) {
            return;
        }
        if (me.hidden) {
            Ext.Component.cancelLayout(me);
        } else if (typeof isRoot != 'boolean') {
            isRoot = me.isLayoutRoot();
        }
        
        if (isRoot || !me.ownerLayout || !me.ownerLayout.onContentChange(me)) {
            
            if (!me.isLayoutSuspended()) {
                
                defer = (options && options.hasOwnProperty('defer')) ? options.defer : me.deferLayouts;
                Ext.Component.updateLayout(me, defer);
            }
        }
    },
    updateMaxHeight: function(maxHeight, oldMaxHeight) {
        this.changeConstraint(maxHeight, oldMaxHeight, 'min', 'max-height', 'height');
    },
    updateMaxWidth: function(maxWidth, oldMaxWidth) {
        this.changeConstraint(maxWidth, oldMaxWidth, 'min', 'max-width', 'width');
    },
    updateMinHeight: function(minHeight, oldMinHeight) {
        this.changeConstraint(minHeight, oldMinHeight, 'max', 'min-height', 'height');
    },
    updateMinWidth: function(minWidth, oldMinWidth) {
        this.changeConstraint(minWidth, oldMinWidth, 'max', 'min-width', 'width');
    },
    
    
    
    
    
    
    
    
    getAnchorToXY: function(el, anchor, local, mySize) {
        return el.getAnchorXY(anchor, local, mySize);
    },
    getBorderPadding: function() {
        return this.el.getBorderPadding();
    },
    getLocalX: function() {
        return this.el.getLocalX();
    },
    getLocalXY: function() {
        return this.el.getLocalXY();
    },
    getLocalY: function() {
        return this.el.getLocalY();
    },
    getX: function() {
        return this.el.getX();
    },
    getXY: function() {
        return this.el.getXY();
    },
    getY: function() {
        return this.el.getY();
    },
    setLocalX: function(x) {
        this.el.setLocalX(x);
    },
    setLocalXY: function(x, y) {
        this.el.setLocalXY(x, y);
    },
    setLocalY: function(y) {
        this.el.setLocalY(y);
    },
    setX: function(x, animate) {
        this.el.setX(x, animate);
    },
    setXY: function(xy, animate) {
        this.el.setXY(xy, animate);
    },
    setY: function(y, animate) {
        this.el.setY(y, animate);
    },
    
    
    
    
    privates: {
        addOverCls: function() {
            var me = this;
            if (!me.disabled) {
                me.el.addCls(me.overCls);
            }
        },
        
        addUIToElement: function() {
            var me = this,
                baseClsUI = me.baseCls + '-' + me.ui,
                childEls, childElName, el, suffix;
            me.addCls(baseClsUI);
            if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
                
                baseClsUI += '-';
                childEls = me.getChildEls();
                for (childElName in childEls) {
                    suffix = childEls[childElName].frame;
                    if (suffix && suffix !== true) {
                        el = me[childElName];
                        if (el) {
                            el.addCls(baseClsUI + suffix);
                        }
                    }
                }
            }
        },
        
        changeConstraint: function(newValue, oldValue, constrainMethod, styleName, sizeName) {
            var me = this,
                size = me[sizeName];
            if (newValue != null && typeof size === 'number') {
                me[sizeName] = Math[constrainMethod](size, newValue);
            }
            if (me.liquidLayout) {
                
                if (newValue != null) {
                    me.setStyle(styleName, newValue + 'px');
                } else if (oldValue) {
                    me.setStyle(styleName, '');
                }
            }
            if (me.rendered) {
                me.updateLayout();
            }
        },
        
        constructPlugin: function(plugin) {
            var me = this;
            
            if (typeof plugin === 'string') {
                plugin = Ext.PluginManager.create({}, plugin, me);
            } else 
            {
                plugin = Ext.PluginManager.create(plugin, null, me);
            }
            return plugin;
        },
        
        constructPlugins: function() {
            var me = this,
                plugins = me.plugins,
                result, i, len;
            if (plugins) {
                result = [];
                
                
                
                result.processed = true;
                if (!Ext.isArray(plugins)) {
                    plugins = [
                        plugins
                    ];
                }
                for (i = 0 , len = plugins.length; i < len; i++) {
                    
                    result[i] = me.constructPlugin(plugins[i]);
                }
            }
            me.pluginsInitialized = true;
            return result;
        },
        doAddListener: function(ename, fn, scope, options, order, caller, manager) {
            var me = this,
                listeners, option, eventOptions, elementName, element;
            if (Ext.isObject(fn) || (options && options.element)) {
                if (options.element) {
                    elementName = options.element;
                    listeners = {};
                    listeners[ename] = fn;
                    if (scope) {
                        listeners.scope = scope;
                    }
                    eventOptions = me.$elementEventOptions;
                    for (option in options) {
                        if (eventOptions[option]) {
                            listeners[option] = options[option];
                        }
                    }
                } else {
                    listeners = fn;
                    elementName = ename;
                }
                element = me[elementName];
                if (element && element.isObservable) {
                    
                    me.mon(element, listeners);
                } else {
                    me.afterRenderEvents = me.afterRenderEvents || {};
                    if (!me.afterRenderEvents[elementName]) {
                        me.afterRenderEvents[elementName] = [];
                    }
                    me.afterRenderEvents[elementName].push(listeners);
                }
                return;
            }
            me.mixins.observable.doAddListener.call(me, ename, fn, scope, options, order, caller, manager);
        },
        
        fireHierarchyEvent: function(eventName) {
            var globalEvents = Ext.GlobalEvents;
            if (globalEvents.hasListeners[eventName]) {
                globalEvents.fireEvent(eventName, this);
            }
        },
        getActionEl: function() {
            return this.el;
        },
        
        getAutoId: function() {
            this.autoGenId = true;
            return ++Ext.Component.AUTO_ID;
        },
        
        getContentTarget: function() {
            return this.el;
        },
        getDragEl: function() {
            return this.el;
        },
        
        getOverflowEl: function() {
            return this.getTargetEl();
        },
        
        getOverflowStyle: function() {
            var me = this,
                scroller = me.getScrollable(),
                flags = me._scrollFlags,
                x, y, scrollFlags;
            if (scroller) {
                x = scroller.getX();
                if (x === true) {
                    x = 'auto';
                }
                y = scroller.getY();
                if (y === true) {
                    y = 'auto';
                }
                scrollFlags = flags[x][y];
            } else {
                scrollFlags = flags.none;
            }
            me.scrollFlags = scrollFlags;
            return {
                overflowX: scrollFlags.overflowX,
                overflowY: scrollFlags.overflowY
            };
        },
        
        getPlugins: function() {
            var plugins = this.plugins;
            plugins = (plugins && plugins.processed) ? plugins : this.constructPlugins();
            return plugins || null;
        },
        getProxy: function() {
            var me = this,
                target;
            if (!me.proxy) {
                target = Ext.getBody();
                me.proxy = me.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', target, true);
            }
            return me.proxy;
        },
        getScrollerEl: function() {
            var me = this;
            return me.scrollerEl || (me.scrollerEl = me.componentLayout.getScrollerEl() || me.getOverflowEl().child(me.scrollerSelector));
        },
        
        getTargetEl: function() {
            return this.frameBody || this.el;
        },
        
        getTdCls: function() {
            return Ext.baseCSSPrefix + this.getTdType() + '-' + this.ui + '-cell';
        },
        
        getTdType: function() {
            return this.xtype;
        },
        
        getTpl: function(name) {
            return Ext.XTemplate.getTpl(this, name);
        },
        initCls: function() {
            var me = this,
                cls = [
                    me.baseCls
                ],
                targetCls = me.getComponentLayout().targetCls;
            if (targetCls) {
                cls.push(targetCls);
            }
            if (Ext.isDefined(me.cmpCls)) {
                if (Ext.isDefined(Ext.global.console)) {
                    Ext.global.console.warn('Ext.Component: cmpCls has been deprecated. Please use componentCls.');
                }
                me.componentCls = me.cmpCls;
                delete me.cmpCls;
            }
            if (me.componentCls) {
                cls.push(me.componentCls);
            } else {
                me.componentCls = me.baseCls;
            }
            return cls;
        },
        initDraggable: function() {
            var me = this,
                
                
                
                dragTarget = (me.resizer && me.resizer.el !== me.el) ? me.resizerComponent = new Ext.Component({
                    ariaRole: 'presentation',
                    el: me.resizer.el,
                    rendered: true,
                    container: me.container
                }) : me,
                ddConfig = Ext.applyIf({
                    el: dragTarget.getDragEl(),
                    constrainTo: (me.constrain || me.draggable.constrain) ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.container)) : undefined
                }, me.draggable);
            
            if (me.constrain || me.constrainDelegate) {
                ddConfig.constrain = me.constrain;
                ddConfig.constrainDelegate = me.constrainDelegate;
            }
            me.dd = new Ext.util.ComponentDragger(dragTarget,ddConfig);
        },
        
        initPadding: function(targetEl) {
            var me = this,
                padding = me.padding;
            if (padding != null) {
                if (me.touchScroll || (me.layout && me.layout.managePadding && me.contentPaddingProperty === 'padding')) {
                    
                    
                    
                    
                    
                    
                    
                    targetEl.setStyle('padding', 0);
                } else {
                    
                    
                    targetEl.setStyle('padding', this.unitizeBox((padding === true) ? 5 : padding));
                }
            }
        },
        
        initPlugin: function(plugin) {
            plugin.init(this);
            return plugin;
        },
        initResizable: function(resizable) {
            var me = this;
            resizable = Ext.apply({
                target: me,
                dynamic: false,
                constrainTo: (me.constrain || (resizable && resizable.constrain)) ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.container)) : undefined,
                handles: me.resizeHandles
            }, resizable);
            resizable.target = me;
            me.resizer = new Ext.resizer.Resizer(resizable);
        },
        
        initStyles: function(targetEl) {
            var me = this,
                margin = me.margin,
                border = me.border,
                cls = me.cls,
                style = me.style,
                x = me.x,
                y = me.y,
                liquidLayout = me.liquidLayout,
                width, height;
            me.initPadding(targetEl);
            if (margin != null) {
                targetEl.setStyle('margin', this.unitizeBox((margin === true) ? 5 : margin));
            }
            if (border != null) {
                me.setBorder(border, targetEl);
            }
            
            
            if (cls && cls != me.initialCls) {
                targetEl.addCls(cls);
                me.cls = me.initialCls = null;
            }
            if (style && style != me.initialStyle) {
                targetEl.setStyle(style);
                me.style = me.initialStyle = null;
            }
            if (x != null) {
                targetEl.setStyle(me.horizontalPosProp, (typeof x == 'number') ? (x + 'px') : x);
            }
            if (y != null) {
                targetEl.setStyle('top', (typeof y == 'number') ? (y + 'px') : y);
            }
            if (!me.ownerCt || me.floating) {
                if (Ext.scopeCss) {
                    targetEl.addCls(me.rootCls);
                }
                targetEl.addCls(me.borderBoxCls);
            }
            
            
            
            if (liquidLayout || !me.getFrameInfo()) {
                width = me.width;
                height = me.height;
                
                if (width != null) {
                    if (typeof width === 'number') {
                        targetEl.setStyle('width', width + 'px');
                    } else {
                        targetEl.setStyle('width', width);
                    }
                }
                if (height != null) {
                    if (typeof height === 'number') {
                        targetEl.setStyle('height', height + 'px');
                    } else {
                        targetEl.setStyle('height', height);
                    }
                }
            }
        },
        
        
        isContainedFloater: function() {
            return (this.floating && this.floatParent);
        },
        isDescendant: function(ancestor) {
            if (ancestor.isContainer) {
                for (var c = this.ownerCt; c; c = c.ownerCt) {
                    if (c === ancestor) {
                        return true;
                    }
                }
            }
            return false;
        },
        
        owns: function(element) {
            var result = false,
                cmp;
            if (element.isEvent) {
                element = element.target;
            } else if (element.isElement) {
                element = element.dom;
            }
            cmp = Ext.ComponentManager.byElement(element);
            if (cmp) {
                result = (cmp === this) || (!!cmp.up(this));
            }
            return result;
        },
        parseBox: function(box) {
            return Ext.Element.parseBox(box);
        },
        
        refreshScroll: function() {
            var scroller = this.getScrollable();
            if (scroller) {
                scroller.refresh();
            }
        },
        removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope) {
            var me = this,
                element = managedListener.options ? managedListener.options.element : null;
            if (element) {
                element = me[element];
                if (element && element.un) {
                    if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
                        element.un(managedListener.ename, managedListener.fn, managedListener.scope);
                        if (!isClear) {
                            Ext.Array.remove(me.managedListeners, managedListener);
                        }
                    }
                }
            } else {
                return me.mixins.observable.removeManagedListenerItem.apply(me, arguments);
            }
        },
        removeOverCls: function() {
            this.el.removeCls(this.overCls);
        },
        removePlugin: function(plugin) {
            Ext.Array.remove(this.plugins, plugin);
            plugin.destroy();
        },
        
        removeUIFromElement: function() {
            var me = this,
                baseClsUI = me.baseCls + '-' + me.ui,
                childEls, childElName, el, suffix;
            me.removeCls(baseClsUI);
            if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
                
                baseClsUI += '-';
                childEls = me.getChildEls();
                for (childElName in childEls) {
                    suffix = childEls[childElName].frame;
                    if (suffix && suffix !== true) {
                        el = me[childElName];
                        if (el) {
                            el.removeCls(baseClsUI + suffix);
                        }
                    }
                }
            }
        },
        
        setComponentLayout: function(layout) {
            var currentLayout = this.componentLayout;
            if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
                currentLayout.setOwner(null);
            }
            this.componentLayout = layout;
            layout.setOwner(this);
        },
        setHiddenState: function(hidden) {
            var me = this,
                inheritedState = me.getInherited(),
                zIndexManager = me.zIndexManager;
            me.hidden = hidden;
            if (hidden) {
                inheritedState.hidden = true;
            } else {
                delete inheritedState.hidden;
            }
            
            if (zIndexManager) {
                zIndexManager.onComponentShowHide(me);
            }
        },
        setupProtoEl: function() {
            var cls = this.initCls();
            this.protoEl.addCls(cls);
        },
        wrapPrimaryEl: function(dom) {
            var me = this,
                el = me.el;
            if (!el || !el.isElement) {
                
                me.el = Ext.get(dom);
            }
            if (me.floating) {
                this.mixins.floating.constructor.call(this);
            }
        }
    },
    
    deprecated: {
        5: {
            methods: {
                
                addClass: 'addCls',
                
                doComponentLayout: function() {
                    this.updateLayout();
                    return this;
                },
                
                removeClass: 'removeCls',
                
                forceComponentLayout: 'updateLayout',
                
                setDocked: 'setDock'
            }
        }
    }
}, function(Component) {
    
    
    (Component.prototype.$elementEventOptions = Ext.Object.chain(Ext.Element.prototype.$eventOptions)).element = 1;
    Component.createAlias({
        on: 'addListener',
        prev: 'previousSibling',
        next: 'nextSibling'
    });
    
    Ext.resumeLayouts = function(flush) {
        Component.resumeLayouts(flush);
    };
    
    Ext.suspendLayouts = function() {
        Component.suspendLayouts();
    };
    
    Ext.batchLayouts = function(fn, scope) {
        Component.suspendLayouts();
        
        fn.call(scope);
        Component.resumeLayouts(true);
    };
    
    Ext.setGlyphFontFamily = function(fontFamily) {
        Ext._glyphFontFamily = fontFamily;
    };
    
    Component.hierarchyEventSource = Component.prototype.hierarchyEventSource = Ext.GlobalEvents;
});


Ext.define('Ext.layout.container.border.Region', {
    override: 'Ext.Component',
    
    initBorderRegion: function() {
        var me = this;
        if (!me._borderRegionInited) {
            me._borderRegionInited = true;
            
            
            me.addStateEvents([
                'changeregion',
                'changeweight'
            ]);
            
            
            
            
            Ext.override(me, {
                getState: function() {
                    var state = me.callParent();
                    
                    
                    state = me.addPropertyToState(state, 'region');
                    state = me.addPropertyToState(state, 'weight');
                    return state;
                }
            });
        }
    },
    
    getOwningBorderContainer: function() {
        var layout = this.getOwningBorderLayout();
        return layout && layout.owner;
    },
    
    getOwningBorderLayout: function() {
        
        var layout = this.ownerLayout;
        return (layout && layout.isBorderLayout) ? layout : null;
    },
    
    setRegion: function(region) {
        var me = this,
            borderLayout,
            old = me.region;
        if (typeof region !== 'string') {
            
            
            Ext.Error.raise('Use setBox to set the size or position of a component.');
        }
        if (region !== old) {
            borderLayout = me.getOwningBorderLayout();
            if (borderLayout) {
                var regionFlags = borderLayout.regionFlags[region],
                    placeholder = me.placeholder,
                    splitter = me.splitter,
                    owner = borderLayout.owner,
                    regionMeta = borderLayout.regionMeta,
                    collapsed = me.collapsed || me.floated,
                    delta, items, index;
                if (me.fireEventArgs('beforechangeregion', [
                    me,
                    region
                ]) === false) {
                    return old;
                }
                Ext.suspendLayouts();
                me.region = region;
                Ext.apply(me, regionFlags);
                if (me.updateCollapseTool) {
                    me.updateCollapseTool();
                }
                if (splitter) {
                    
                    Ext.apply(splitter, regionFlags);
                    splitter.updateOrientation();
                    items = owner.items;
                    index = items.indexOf(me);
                    if (index >= 0) {
                        delta = regionMeta[region].splitterDelta;
                        if (items.getAt(index + delta) !== splitter) {
                            
                            items.remove(splitter);
                            index = items.indexOf(me);
                            
                            if (delta > 0) {
                                ++index;
                            }
                            
                            items.insert(index, splitter);
                        }
                    }
                }
                
                
                
                if (placeholder) {
                    
                    
                    
                    
                    if (collapsed) {
                        me.expand(false);
                    }
                    owner.remove(placeholder);
                    me.placeholder = null;
                    
                    if (collapsed) {
                        me.collapse(null, false);
                    }
                }
                owner.updateLayout();
                Ext.resumeLayouts(true);
                me.fireEventArgs('changeregion', [
                    me,
                    old
                ]);
            } else {
                me.region = region;
            }
        }
        
        return old;
    },
    
    setWeight: function(weight) {
        var me = this,
            ownerCt = me.getOwningBorderContainer(),
            placeholder = me.placeholder,
            old = me.weight;
        if (weight !== old) {
            if (me.fireEventArgs('beforechangeweight', [
                me,
                weight
            ]) !== false) {
                me.weight = weight;
                if (placeholder) {
                    placeholder.weight = weight;
                }
                if (ownerCt) {
                    ownerCt.updateLayout();
                }
                me.fireEventArgs('changeweight', [
                    me,
                    old
                ]);
            }
        }
        return old;
    }
}, function(Component) {
    var proto = Component.prototype;
    
    proto.setBorderRegion = proto.setRegion;
    proto.setRegionWeight = proto.setWeight;
});

Ext.define('Ext.overrides.app.domain.Component', {
    override: 'Ext.app.domain.Component',
    requires: [
        'Ext.Component'
    ]
}, function(ComponentDomain) {
    
    
    ComponentDomain.monitor(Ext.Component);
});


Ext.define('Ext.app.EventBus', {
    singleton: true,
    requires: [
        'Ext.app.domain.Component'
    ],
    constructor: function() {
        var me = this,
            domains = Ext.app.EventDomain.instances;
        me.callParent();
        me.domains = domains;
        me.bus = domains.component.bus;
    },
    
    
    control: function(selectors, controller) {
        return this.domains.component.listen(selectors, controller);
    },
    
    listen: function(to, controller) {
        var domains = this.domains,
            domain;
        for (domain in to) {
            if (to.hasOwnProperty(domain)) {
                domains[domain].listen(to[domain], controller);
            }
        }
    },
    
    unlisten: function(controllerId) {
        var domains = Ext.app.EventDomain.instances,
            domain;
        for (domain in domains) {
            domains[domain].unlisten(controllerId);
        }
    }
});


Ext.define('Ext.app.domain.Global', {
    extend: 'Ext.app.EventDomain',
    requires: [
        'Ext.GlobalEvents'
    ],
    singleton: true,
    type: 'global',
    constructor: function() {
        var me = this;
        me.callParent();
        me.monitor(Ext.GlobalEvents);
    },
    
    listen: function(listeners, controller) {
        this.callParent([
            {
                global: listeners
            },
            controller
        ]);
    },
    match: Ext.returnTrue
});


Ext.define('Ext.app.BaseController', {
    requires: [
        'Ext.app.EventBus',
        'Ext.app.domain.Global'
    ],
    uses: [
        'Ext.app.domain.Controller'
    ],
    mixins: [
        'Ext.mixin.Observable'
    ],
    isController: true,
    config: {
        
        id: null,
        
        control: null,
        
        listen: null,
        
        routes: null,
        before: null
    },
    
    constructor: function(config) {
        var me = this;
        
        
        
        
        
        Ext.apply(me, config);
        
        
        delete me.control;
        delete me.listen;
        me.eventbus = Ext.app.EventBus;
        
        me.mixins.observable.constructor.call(me, config);
        
        me.ensureId();
    },
    applyListen: function(listen) {
        if (Ext.isObject(listen)) {
            listen = Ext.clone(listen);
        }
        return listen;
    },
    applyControl: function(control) {
        if (Ext.isObject(control)) {
            control = Ext.clone(control);
        }
        return control;
    },
    
    updateControl: function(control) {
        this.ensureId();
        if (control) {
            this.control(control);
        }
    },
    
    updateListen: function(listen) {
        this.ensureId();
        if (listen) {
            this.listen(listen);
        }
    },
    
    updateRoutes: function(routes) {
        if (routes) {
            var me = this,
                befores = me.getBefore() || {},
                Router = Ext.app.route.Router,
                url, config, method;
            for (url in routes) {
                config = routes[url];
                if (Ext.isString(config)) {
                    config = {
                        action: config
                    };
                }
                method = config.action;
                if (!config.before) {
                    config.before = befores[method];
                } else if (befores[method]) {
                    Ext.log.warn('You have a before method configured on a route ("' + url + '") and in the before object property also in the "' + me.self.getName() + '" controller. Will use the before method in the route and disregard the one in the before property.');
                }
                
                Router.connect(url, config, me);
            }
        }
    },
    isActive: function() {
        return true;
    },
    
    control: function(selectors, listeners, controller) {
        var me = this,
            ctrl = controller,
            obj;
        if (Ext.isString(selectors)) {
            obj = {};
            obj[selectors] = listeners;
        } else {
            obj = selectors;
            ctrl = listeners;
        }
        me.eventbus.control(obj, ctrl || me);
    },
    
    listen: function(to, controller) {
        this.eventbus.listen(to, controller || this);
    },
    destroy: function() {
        var me = this,
            bus = me.eventbus;
        Ext.app.route.Router.disconnectAll(me);
        if (bus) {
            bus.unlisten(me);
            me.eventbus = null;
        }
        me.clearListeners();
        me.callParent();
    },
    
    redirectTo: function(token, force) {
        if (token.isModel) {
            token = token.toUrl();
        }
        if (!force) {
            var currentToken = Ext.util.History.getToken();
            if (currentToken === token) {
                return false;
            }
        } else {
            Ext.app.route.Router.onStateChange(token);
        }
        Ext.util.History.add(token);
        return true;
    }
});


Ext.define('Ext.app.Util', {}, function() {
    Ext.apply(Ext.app, {
        namespaces: {
            Ext: {}
        },
        
        addNamespaces: function(namespace) {
            var namespaces = Ext.app.namespaces,
                i, l;
            if (!Ext.isArray(namespace)) {
                namespace = [
                    namespace
                ];
            }
            for (i = 0 , l = namespace.length; i < l; i++) {
                namespaces[namespace[i]] = true;
            }
        },
        
        clearNamespaces: function() {
            Ext.app.namespaces = {};
        },
        
        getNamespace: function(className) {
            var namespaces = Ext.apply({}, Ext.ClassManager.paths, Ext.app.namespaces),
                deepestPrefix = '',
                prefix;
            for (prefix in namespaces) {
                if (namespaces.hasOwnProperty(prefix) && prefix.length > deepestPrefix.length && (prefix + '.' === className.substring(0, prefix.length + 1))) {
                    deepestPrefix = prefix;
                }
            }
            return deepestPrefix === '' ? undefined : deepestPrefix;
        }
    });
    
    Ext.getNamespace = Ext.app.getNamespace;
});


Ext.define('Ext.util.CollectionKey', {
    mixins: [
        'Ext.mixin.Identifiable'
    ],
    isCollectionKey: true,
    observerPriority: -200,
    config: {
        collection: null,
        
        keyFn: null,
        
        property: null,
        
        rootProperty: null,
        unique: true
    },
    
    generation: 0,
    
    map: null,
    
    mapRebuilds: 0,
    
    constructor: function(config) {
        this.initConfig(config);
        if (!Ext.isFunction(this.getKey)) {
            Ext.Error.raise('CollectionKey requires a keyFn or property config');
        }
    },
    
    get: function(key) {
        var map = this.map || this.getMap();
        return map[key] || null;
    },
    
    clear: function() {
        this.map = null;
    },
    getRootProperty: function() {
        var me = this,
            root = this.callParent();
        return root !== null ? root : me.getCollection().getRootProperty();
    },
    
    indexOf: function(key, startAt) {
        var map = this.map || this.getMap(),
            item = map[key],
            collection = this.getCollection(),
            length = collection.length,
            i, index, items, n;
        if (!item) {
            return -1;
        }
        if (startAt === undefined) {
            startAt = -1;
        }
        if (item instanceof Array) {
            items = item;
            index = length;
            
            for (n = items.length; n-- > 0; ) {
                i = collection.indexOf(items[n]);
                if (i < index && i > startAt) {
                    index = i;
                }
            }
            if (index === length) {
                return -1;
            }
        } else {
            index = collection.indexOf(item);
        }
        return (index > startAt) ? index : -1;
    },
    
    updateKey: function(item, oldKey) {
        var me = this,
            map = me.map,
            bucket, index;
        if (map) {
            bucket = map[oldKey];
            if (bucket instanceof Array) {
                index = Ext.Array.indexOf(bucket, item);
                if (index >= 0) {
                    if (bucket.length > 2) {
                        bucket.splice(index, 1);
                    } else {
                        
                        
                        
                        map[oldKey] = bucket[1 - index];
                    }
                }
            }
            
            else if (bucket) {
                if (me.getUnique() && bucket !== item) {
                    Ext.Error.raise('Incorrect oldKey "' + oldKey + '" for item with newKey "' + me.getKey(item) + '"');
                }
                delete map[oldKey];
            }
            me.add([
                item
            ]);
        }
    },
    
    
    onCollectionAdd: function(collection, add) {
        if (this.map) {
            this.add(add.items);
        }
    },
    onCollectionItemChange: function(collection, details) {
        this.map = null;
    },
    onCollectionRefresh: function() {
        this.map = null;
    },
    onCollectionRemove: function(collection, remove) {
        var me = this,
            map = me.map,
            items = remove.items,
            length = items.length,
            i, item, key;
        if (map) {
            if (me.getUnique() && length < collection.length / 2) {
                for (i = 0; i < length; ++i) {
                    key = me.getKey(item = items[i]);
                    delete map[key];
                }
            } else {
                me.map = null;
            }
        }
    },
    
    
    add: function(items) {
        var me = this,
            map = me.map,
            bucket, i, item, key, length, unique;
        length = items.length;
        unique = me.getUnique();
        for (i = 0; i < length; ++i) {
            key = me.getKey(item = items[i]);
            if (unique || !(key in map)) {
                map[key] = item;
            } else {
                if (!((bucket = map[key]) instanceof Array)) {
                    map[key] = bucket = [
                        bucket
                    ];
                }
                bucket.push(item);
            }
        }
    },
    applyKeyFn: function(keyFn) {
        if (Ext.isString(keyFn)) {
            this.getKey = function(item) {
                return item[keyFn]();
            };
        } else {
            this.getKey = keyFn;
        }
    },
    applyProperty: function(property) {
        var root = this.getRootProperty();
        this.getKey = function(item) {
            return (root ? item[root] : item)[property];
        };
    },
    getMap: function() {
        var me = this,
            map = me.map;
        if (!map) {
            me.map = map = {};
            me.keysByItemKey = {};
            ++me.mapRebuilds;
            me.add(me.getCollection().items);
        }
        return map;
    },
    updateCollection: function(collection) {
        collection.addObserver(this);
    }
});


Ext.define('Ext.util.Grouper', {
    extend: 'Ext.util.Sorter',
    isGrouper: true,
    config: {
        
        groupFn: null,
        
        
        sortProperty: null
    },
    constructor: function(config) {
        if (config) {
            if (config.getGroupString) {
                Ext.Error.raise("Cannot set getGroupString - use groupFn instead");
            }
        }
        this.callParent(arguments);
    },
    
    getGroupString: function(item) {
        var group = this._groupFn(item);
        return (group != null) ? String(group) : '';
    },
    sortFn: function(item1, item2) {
        var me = this,
            lhs = me._groupFn(item1),
            rhs = me._groupFn(item2),
            property = me._sortProperty,
            
            root = me._root,
            sorterFn = me._sorterFn,
            transform = me._transform;
        
        
        if (lhs === rhs) {
            return 0;
        }
        if (property || sorterFn) {
            if (sorterFn) {
                return sorterFn.call(this, item1, item2);
            }
            if (root) {
                item1 = item1[root];
                item2 = item2[root];
            }
            lhs = item1[property];
            rhs = item2[property];
            if (transform) {
                lhs = transform(lhs);
                rhs = transform(rhs);
            }
        }
        return (lhs > rhs) ? 1 : (lhs < rhs ? -1 : 0);
    },
    standardGroupFn: function(item) {
        var root = this._root;
        return (root ? item[root] : item)[this._property];
    },
    updateSorterFn: function() {},
    
    updateProperty: function() {
        
        if (!this.getGroupFn()) {
            this.setGroupFn(this.standardGroupFn);
        }
    }
});


Ext.define('Ext.util.Collection', {
    mixins: [
        'Ext.mixin.Observable'
    ],
    requires: [
        'Ext.util.CollectionKey',
        'Ext.util.Filter',
        'Ext.util.Sorter',
        'Ext.util.Grouper'
    ],
    uses: [
        'Ext.util.SorterCollection',
        'Ext.util.FilterCollection',
        'Ext.util.GroupCollection'
    ],
    
    isCollection: true,
    config: {
        autoFilter: true,
        autoSort: true,
        
        decoder: null,
        
        extraKeys: null,
        
        filters: null,
        
        grouper: null,
        
        groups: null,
        
        rootProperty: null,
        
        sorters: null,
        
        multiSortLimit: 3,
        
        defaultSortDirection: 'ASC',
        
        source: null
    },
    
    generation: 0,
    
    indices: null,
    
    indexRebuilds: 0,
    
    updating: 0,
    
    grouped: false,
    
    sorted: false,
    
    filtered: false,
    
    
    
    $endUpdatePriority: 1001,
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        var me = this;
        
        me.items = [];
        
        me.map = {};
        
        me.length = 0;
        
        if (config && config.keyFn) {
            me.getKey = config.keyFn;
        }
        me.mixins.observable.constructor.call(me, config);
    },
    
    destroy: function() {
        var me = this,
            filters = me._filters,
            sorters = me._sorters,
            groups = me._groups;
        if (filters) {
            filters.destroy();
            me._filters = null;
        }
        if (sorters) {
            sorters.destroy();
            me._sorters = null;
        }
        if (groups) {
            groups.destroy();
            me._groups = null;
        }
        me.setSource(null);
        me.observers = me.items = me.map = null;
    },
    
    add: function(item) {
        var me = this,
            items = me.decodeItems(arguments, 0),
            ret = items;
        if (items.length) {
            me.requestedIndex = me.length;
            me.splice(me.length, 0, items);
            delete me.requestedIndex;
            ret = (items.length === 1) ? items[0] : items;
        }
        return ret;
    },
    
    replaceAll: function() {
        var me = this,
            len = me.length,
            ret, items;
        if (len === 0) {
            return me.add.apply(me, arguments);
        }
        items = me.decodeItems(arguments, 0);
        ret = items;
        if (items.length) {
            me.splice(0, me.length, items);
            ret = (items.length === 1) ? items[0] : items;
        } else {
            me.removeAll();
        }
        return ret;
    },
    
    aggregate: function(property, operation, begin, end, scope) {
        var me = this,
            args = Ext.Array.slice(arguments);
        args.unshift(me.items);
        return me.aggregateItems.apply(me, args);
    },
    
    aggregateByGroup: function(property, operation, scope) {
        var groups = this.getGroups();
        return this.aggregateGroups(groups, property, operation, scope);
    },
    
    aggregateItems: function(items, property, operation, begin, end, scope) {
        var me = this,
            range = Ext.Number.clipIndices(items.length, [
                begin,
                end
            ]),
            
            subsetRequested = (begin !== 0 && end !== items.length),
            i, j, rangeLen, root, value, values, valueItems;
        begin = range[0];
        end = range[1];
        if (!Ext.isFunction(operation)) {
            operation = me._aggregators[operation];
            return operation.call(me, items, begin, end, property, me.getRootProperty());
        }
        root = me.getRootProperty();
        
        
        values = new Array(rangeLen);
        valueItems = subsetRequested ? new Array(rangeLen) : items;
        
        for (i = begin , j = 0; i < end; ++i , j++) {
            if (subsetRequested) {
                valueItems[j] = value = items[i];
            }
            values[j] = (root ? value[root] : value)[property];
        }
        return operation.call(scope || me, items, values, 0);
    },
    
    aggregateGroups: function(groups, property, operation, scope) {
        var items = groups.items,
            len = items.length,
            callDirect = !Ext.isFunction(operation),
            out = {},
            i, group, result;
        for (i = 0; i < len; ++i) {
            group = items[i];
            if (!callDirect) {
                result = this.aggregateItems(group.items, property, operation, null, null, scope);
            } else {
                result = group[operation](property);
            }
            out[group.getGroupKey()] = result;
        }
        return out;
    },
    
    beginUpdate: function() {
        if (!this.updating++) {
            this.notify('beginupdate');
        }
    },
    
    clear: function() {
        var me = this,
            generation = me.generation,
            ret = generation ? me.items : [],
            extraKeys, indexName;
        if (generation) {
            me.items = [];
            me.length = 0;
            me.map = {};
            me.indices = {};
            me.generation++;
            
            extraKeys = me.getExtraKeys();
            if (extraKeys) {
                for (indexName in extraKeys) {
                    extraKeys[indexName].clear();
                }
            }
        }
        return ret;
    },
    
    clone: function() {
        var me = this,
            copy = new me.self(me.initialConfig);
        copy.add(me.items);
        return copy;
    },
    
    collect: function(property, root, allowNull) {
        var items = this.items,
            length = items.length,
            map = {},
            ret = [],
            i, strValue, value;
        for (i = 0; i < length; ++i) {
            value = items[i];
            value = (root ? value[root] : value)[property];
            strValue = String(value);
            if ((allowNull || !Ext.isEmpty(value)) && !map[strValue]) {
                map[strValue] = 1;
                ret.push(value);
            }
        }
        return ret;
    },
    
    contains: function(item) {
        var ret = false,
            key;
        if (item != null) {
            key = this.getKey(item);
            ret = this.map[key] === item;
        }
        return ret;
    },
    
    containsKey: function(key) {
        return key in this.map;
    },
    
    createFiltered: function(property, value, anyMatch, caseSensitive, exactMatch) {
        var me = this,
            ret = new me.self(me.initialConfig),
            root = me.getRootProperty(),
            items = me.items,
            length, i, filters, fn, scope;
        if (Ext.isFunction(property)) {
            fn = property;
            scope = value;
        } else {
            
            if (Ext.isString(property)) {
                filters = [
                    new Ext.util.Filter({
                        property: property,
                        value: value,
                        root: root,
                        anyMatch: anyMatch,
                        caseSensitive: caseSensitive,
                        exactMatch: exactMatch
                    })
                ];
            } else if (property instanceof Ext.util.Filter) {
                filters = [
                    property
                ];
                property.setRoot(root);
            } else if (Ext.isArray(property)) {
                filters = property.slice(0);
                for (i = 0 , length = filters.length; i < length; ++i) {
                    filters[i].setRoot(root);
                }
            }
            
            
            
            fn = Ext.util.Filter.createFilterFn(filters);
        }
        scope = scope || me;
        for (i = 0 , length = items.length; i < length; i++) {
            if (fn.call(scope, items[i])) {
                ret.add(items[i]);
            }
        }
        return ret;
    },
    
    filterBy: function(fn, scope) {
        return this.createFiltered(fn, scope);
    },
    
    each: function(fn, scope) {
        var items = this.items,
            len = items.length,
            i, ret;
        if (len) {
            scope = scope || this;
            items = items.slice(0);
            
            for (i = 0; i < len; i++) {
                ret = fn.call(scope, items[i], i, len);
                if (ret === false) {
                    break;
                }
            }
        }
        return ret;
    },
    
    eachKey: function(fn, scope) {
        var me = this,
            items = me.items,
            len = items.length,
            i, item, key, ret;
        if (len) {
            scope = scope || me;
            items = items.slice(0);
            
            for (i = 0; i < len; i++) {
                key = me.getKey(item = items[i]);
                ret = fn.call(scope, key, item, i, len);
                if (ret === false) {
                    break;
                }
            }
        }
        return ret;
    },
    
    endUpdate: function() {
        if (!--this.updating) {
            this.notify('endupdate');
        }
    },
    
    find: function(property, value, start, startsWith, endsWith, ignoreCase) {
        if (Ext.isEmpty(value, false)) {
            return null;
        }
        var regex = Ext.String.createRegex(value, startsWith, endsWith, ignoreCase),
            root = this.getRootProperty();
        return this.findBy(function(item) {
            return item && regex.test((root ? item[root] : item)[property]);
        }, null, start);
    },
    
    findBy: function(fn, scope, start) {
        var me = this,
            items = me.items,
            len = items.length,
            i, item, key;
        scope = scope || me;
        for (i = start || 0; i < len; i++) {
            key = me.getKey(item = items[i]);
            if (fn.call(scope, item, key)) {
                return items[i];
            }
        }
        return null;
    },
    
    findIndex: function(property, value, start, startsWith, endsWith, ignoreCase) {
        var item = this.find(property, value, start, startsWith, endsWith, ignoreCase);
        return item ? this.indexOf(item) : -1;
    },
    
    findIndexBy: function(fn, scope, start) {
        var item = this.findBy(fn, scope, start);
        return item ? this.indexOf(item) : -1;
    },
    
    first: function(grouped) {
        var groups = grouped ? this.getGroups() : undefined;
        return groups ? this.aggregateGroups(groups, null, 'first') : this.items[0];
    },
    
    last: function(grouped) {
        var groups = grouped ? this.getGroups() : undefined;
        return groups ? this.aggregateGroups(groups, null, 'last') : this.items[this.length - 1];
    },
    
    get: function(key) {
        return this.map[key];
    },
    
    getAt: function(index) {
        return this.items[index];
    },
    
    getByKey: function(key) {
        return this.map[key];
    },
    
    getCount: function() {
        return this.length;
    },
    
    getKey: function(item) {
        var id = item.id;
        return (id === 0 || id) ? id : ((id = item._id) === 0 || id) ? id : item.getId();
    },
    
    getRange: function(begin, end) {
        var items = this.items,
            length = items.length,
            range;
        if (begin > end) {
            Ext.Error.raise('Inverted range passed to Collection.getRange: [' + begin + ',' + end + ']');
        }
        if (!length) {
            range = [];
        } else {
            range = Ext.Number.clipIndices(length, [
                begin,
                end
            ]);
            range = items.slice(range[0], range[1]);
        }
        return range;
    },
    
    getValues: function(property, root, start, end) {
        var items = this.items,
            range = Ext.Number.clipIndices(items.length, [
                start,
                end
            ]),
            ret = [],
            i, value;
        for (i = range[0] , end = range[1]; i < end; ++i) {
            value = items[i];
            value = (root ? value[root] : value)[property];
            ret.push(value);
        }
        return ret;
    },
    
    indexOf: function(item) {
        if (!item) {
            return -1;
        }
        var key = this.getKey(item);
        return this.indexOfKey(key);
    },
    
    indexOfKey: function(key) {
        var me = this,
            indices = me.indices;
        if (key in me.map) {
            if (!indices) {
                indices = me.getIndices();
            }
            return indices[key];
        }
        return -1;
    },
    
    insert: function(index, item) {
        var me = this,
            items = me.decodeItems(arguments, 1),
            ret = items;
        if (items.length) {
            me.requestedIndex = index;
            me.splice(index, 0, items);
            delete me.requestedIndex;
            ret = (items.length === 1) ? items[0] : items;
        }
        return ret;
    },
    
    itemChanged: function(item, modified, oldKey, 
    meta) {
        var me = this,
            keyChanged = oldKey === 0 || !!oldKey,
            filtered = me.filtered && me.getAutoFilter(),
            filterChanged = false,
            itemMovement = 0,
            items = me.items,
            last = me.length - 1,
            sorted = me.sorted && last > 0,
            
            
            
            
            source = me.getSource(),
            toAdd,
            toRemove = 0,
            index,
            itemFiltered = false,
            newIndex,
            wasFiltered = false,
            details, newKey, sortFn;
        
        if (source && !source.updating) {
            source.itemChanged(item, modified, oldKey, meta);
        } else 
        
        {
            newKey = me.getKey(item);
            if (filtered) {
                index = me.indexOfKey(keyChanged ? oldKey : newKey);
                wasFiltered = (index < 0);
                itemFiltered = me.isItemFiltered(item);
                filterChanged = (wasFiltered !== itemFiltered);
            }
            if (filterChanged) {
                if (itemFiltered) {
                    toRemove = [
                        item
                    ];
                    newIndex = -1;
                } else {
                    toAdd = [
                        item
                    ];
                    newIndex = me.length;
                }
            }
            
            
            
            else if (sorted && !itemFiltered) {
                
                
                if (!filtered) {
                    
                    
                    index = me.indexOfKey(keyChanged ? oldKey : newKey);
                }
                sortFn = me.getSortFn();
                if (index && sortFn(items[index - 1], items[index]) > 0) {
                    
                    
                    
                    itemMovement = -1;
                    
                    
                    newIndex = Ext.Array.binarySearch(items, item, 0, index, sortFn);
                } else if (index < last && sortFn(items[index], items[index + 1]) > 0) {
                    
                    
                    
                    itemMovement = 1;
                    
                    
                    newIndex = Ext.Array.binarySearch(items, item, index + 1, sortFn);
                }
                if (itemMovement) {
                    toAdd = [
                        item
                    ];
                }
            }
            
            
            
            
            
            
            details = {
                item: item,
                key: newKey,
                index: newIndex,
                filterChanged: filterChanged,
                keyChanged: keyChanged,
                indexChanged: !!itemMovement,
                filtered: itemFiltered,
                oldIndex: index,
                newIndex: newIndex,
                wasFiltered: wasFiltered,
                meta: meta
            };
            if (keyChanged) {
                details.oldKey = oldKey;
            }
            if (modified) {
                details.modified = modified;
            }
            me.beginUpdate();
            me.notify('beforeitemchange', [
                details
            ]);
            if (keyChanged) {
                me.updateKey(item, oldKey);
            }
            if (toAdd || toRemove) {
                
                
                
                
                
                me.splice(newIndex, toRemove, toAdd);
            }
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (itemMovement > 0) {
                details.newIndex--;
            } else if (itemMovement < 0) {
                details.oldIndex++;
            }
            
            
            
            me.notify(itemFiltered ? 'filtereditemchange' : 'itemchange', [
                details
            ]);
            me.endUpdate();
        }
    },
    
    remove: function(item) {
        var me = this,
            items = me.decodeRemoveItems(arguments, 0),
            length = me.length;
        me.splice(0, items);
        return length - me.length;
    },
    
    removeAll: function() {
        var me = this,
            length = me.length;
        if (me.generation && length) {
            me.splice(0, length);
        }
        return me;
    },
    
    removeAt: function(index, count) {
        var me = this,
            length = me.length,
            Num = Ext.Number,
            range = Num.clipIndices(length, [
                index,
                (count === undefined) ? 1 : count
            ], Num.Clip.COUNT),
            n = range[0],
            removeCount = range[1] - n,
            item = (removeCount === 1) && me.getAt(n),
            removed;
        me.splice(n, removeCount);
        removed = me.length - length;
        return (item && removed) ? item : removed;
    },
    
    removeByKey: function(key) {
        var item = this.getByKey(key);
        if (!item || !this.remove(item)) {
            return false;
        }
        return item;
    },
    
    replace: function(item) {
        var index = this.indexOf(item);
        if (index === -1) {
            this.add(item);
        } else {
            this.insert(index, item);
        }
    },
    
    splice: function(index, toRemove, toAdd) {
        var me = this,
            autoSort = me.sorted && me.getAutoSort(),
            map = me.map,
            items = me.items,
            length = me.length,
            removeItems = (toRemove instanceof Array) ? me.decodeRemoveItems(toRemove) : null,
            isRemoveCount = !removeItems,
            Num = Ext.Number,
            range = Num.clipIndices(length, [
                index,
                isRemoveCount ? toRemove : 0
            ], Num.Clip.COUNT),
            begin = range[0],
            end = range[1],
            
            removeCount = end - begin,
            newItems = me.decodeItems(arguments, 2),
            newCount = newItems ? newItems.length : 0,
            addItems, newItemsMap, removeMap,
            insertAt = begin,
            indices = me.indices || ((newCount || removeItems) ? me.getIndices() : null),
            adds = null,
            removes = removeCount ? [
                begin
            ] : null,
            newKeys = null,
            source = me.getSource(),
            chunk, chunkItems, chunks, i, item, itemIndex, k, key, keys, n, duplicates, sorters;
        if (source && !source.updating) {
            
            
            
            
            if (isRemoveCount) {
                removeItems = [];
                for (i = 0; i < removeCount; ++i) {
                    removeItems.push(items[begin + i]);
                }
            }
            if (begin < length) {
                
                
                i = source.indexOf(items[begin]);
            } else {
                
                i = source.length;
            }
            source.splice(i, removeItems, newItems);
            return me;
        }
        
        
        
        
        if (newCount) {
            addItems = newItems;
            newKeys = [];
            newItemsMap = {};
            
            
            
            
            if (autoSort) {
                
                sorters = me.getSorters();
                if (newCount > 1) {
                    if (!addItems.$cloned) {
                        newItems = addItems = addItems.slice(0);
                    }
                    me.sortData(addItems);
                }
            }
            for (i = 0; i < newCount; ++i) {
                key = me.getKey(item = newItems[i]);
                if ((k = newItemsMap[key]) !== undefined) {
                    
                    
                    
                    (duplicates || (duplicates = {}))[k] = 1;
                } else {
                    
                    
                    
                    itemIndex = indices[key];
                    if (itemIndex < begin || end <= itemIndex) {
                        (removes || (removes = [])).push(itemIndex);
                    }
                }
                
                newItemsMap[key] = i;
                
                newKeys.push(key);
            }
            
            if (duplicates) {
                keys = newKeys;
                addItems = [];
                newKeys = [];
                addItems.$cloned = true;
                for (i = 0; i < newCount; ++i) {
                    if (!duplicates[i]) {
                        item = newItems[i];
                        addItems.push(item);
                        newKeys.push(keys[i]);
                    }
                }
                newCount = addItems.length;
            }
            adds = {
                
                
                
                items: addItems,
                keys: newKeys
            };
        }
        
        for (i = removeItems ? removeItems.length : 0; i-- > 0; ) {
            key = me.getKey(removeItems[i]);
            if ((itemIndex = indices[key]) !== undefined) {
                
                (removes || (removes = [])).push(itemIndex);
            }
        }
        
        if (!adds && !removes) {
            return me;
        }
        me.beginUpdate();
        
        
        
        if (removes) {
            chunk = null;
            chunks = [];
            removeMap = {};
            if (removes.length > 1) {
                removes.sort(Ext.Array.numericSortFn);
            }
            
            
            for (i = 0 , n = removes.length; i < n; ++i) {
                key = me.getKey(item = items[itemIndex = removes[i]]);
                if (!(key in map)) {
                    
                    continue;
                }
                
                
                delete map[key];
                
                
                
                
                
                
                
                
                
                if (!chunk || itemIndex > (chunk.at + chunkItems.length)) {
                    chunks.push(chunk = {
                        at: itemIndex,
                        items: (chunkItems = []),
                        keys: (keys = []),
                        map: removeMap,
                        next: chunk,
                        replacement: adds
                    });
                    
                    if (adds) {
                        adds.replaced = chunk;
                    }
                }
                chunkItems.push(removeMap[key] = item);
                keys.push(key);
                if (itemIndex < insertAt) {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    --insertAt;
                }
                if (removeCount > 1 && itemIndex === begin) {
                    
                    
                    
                    
                    
                    
                    --removeCount;
                    
                    removes[i--] = ++begin;
                }
            }
            
            
            if (adds) {
                adds.at = insertAt;
            }
            
            
            
            for (k = chunks.length; k-- > 0; ) {
                chunk = chunks[k];
                i = chunk.at;
                n = chunk.items.length;
                if (i + n < length) {
                    
                    
                    
                    me.indices = indices = null;
                }
                me.length = length -= n;
                
                
                
                
                items.splice(i, n);
                if (indices) {
                    keys = chunk.keys;
                    for (i = 0; i < n; ++i) {
                        delete indices[keys[i]];
                    }
                }
                ++me.generation;
                me.notify('remove', [
                    chunk
                ]);
            }
        }
        
        if (adds) {
            if (autoSort && newCount > 1 && length) {
                me.spliceMerge(addItems, newKeys);
            } else {
                if (autoSort) {
                    if (newCount > 1) {
                        
                        insertAt = 0;
                        me.indices = indices = null;
                    } else {
                        
                        
                        insertAt = sorters.findInsertionIndex(adds.items[0], items, me.getSortFn());
                    }
                }
                if (insertAt === length) {
                    
                    items.push.apply(items, addItems);
                    
                    
                    indices = me.indices;
                    if (indices) {
                        for (i = 0; i < newCount; ++i) {
                            indices[newKeys[i]] = insertAt + i;
                        }
                    }
                } else {
                    
                    me.indices = null;
                    Ext.Array.insert(items, insertAt, addItems);
                }
                for (i = 0; i < newCount; ++i) {
                    map[newKeys[i]] = addItems[i];
                }
                me.length += newCount;
                adds.at = insertAt;
                adds.atItem = insertAt === 0 ? null : items[insertAt - 1];
                ++me.generation;
                me.notify('add', [
                    adds
                ]);
            }
        }
        
        me.endUpdate();
        return me;
    },
    
    update: function(fn, scope) {
        var me = this;
        me.beginUpdate();
        try {
            return fn.call(scope || me, me);
        } catch (e) {
            Ext.log.error(this.$className + ': Unhandled Exception: ', e.description || e.message);
            throw e;
        } finally {
            me.endUpdate();
        }
    },
    
    updateKey: function(item, oldKey) {
        var me = this,
            map = me.map,
            indices = me.indices,
            source = me.getSource(),
            newKey;
        if (source && !source.updating) {
            
            
            source.updateKey(item, oldKey);
        } else if ((newKey = me.getKey(item)) !== oldKey) {
            if (oldKey in map || map[newKey] !== item) {
                if (oldKey in map) {
                    if (map[oldKey] !== item) {
                        Ext.Error.raise('Incorrect oldKey "' + oldKey + '" for item with newKey "' + newKey + '"');
                    }
                    delete map[oldKey];
                }
                
                
                
                me.updating++;
                me.generation++;
                map[newKey] = item;
                if (indices) {
                    indices[newKey] = indices[oldKey];
                    delete indices[oldKey];
                }
                me.notify('updatekey', [
                    {
                        item: item,
                        newKey: newKey,
                        oldKey: oldKey
                    }
                ]);
                me.updating--;
            }
        }
    },
    findInsertIndex: function(item) {
        var source = this.getSource(),
            sourceItems = source.items,
            i = source.indexOf(item) - 1,
            sourceItem, index;
        while (i > -1) {
            sourceItem = sourceItems[i];
            index = this.indexOf(sourceItem);
            if (index > -1) {
                return index + 1;
            }
            --i;
        }
        
        
        return 0;
    },
    
    
    
    onCollectionAdd: function(source, details) {
        var me = this,
            atItem = details.atItem,
            items = details.items,
            requestedIndex = me.requestedIndex,
            filtered, index, copy, i, item, n;
        
        if (!me.sorted) {
            
            
            if (requestedIndex !== undefined) {
                index = requestedIndex;
            } else if (atItem) {
                index = me.indexOf(atItem);
                if (index === -1) {
                    
                    
                    
                    index = me.findInsertIndex(items[0]);
                } else {
                    
                    ++index;
                }
            } else {
                
                index = 0;
            }
        }
        if (me.getAutoFilter() && me.filtered) {
            for (i = 0 , n = items.length; i < n; ++i) {
                item = items[i];
                if (me.isItemFiltered(item)) {
                    
                    
                    if (!copy) {
                        copy = items.slice(0, i);
                    }
                    if (!filtered) {
                        filtered = [];
                    }
                    filtered.push(item);
                } else if (copy) {
                    
                    
                    copy.push(item);
                }
            }
        }
        me.splice((index < 0) ? me.length : index, 0, copy || items);
        if (filtered) {
            
            
            me.notify('filteradd', [
                filtered
            ]);
        }
    },
    
    onCollectionBeforeItemChange: function(source, details) {
        
        this.onCollectionUpdateKey = null;
    },
    
    onCollectionBeginUpdate: function() {
        this.beginUpdate();
    },
    
    onCollectionEndUpdate: function() {
        this.endUpdate();
    },
    
    onCollectionItemChange: function(source, details) {
        
        delete this.onCollectionUpdateKey;
        this.itemChanged(details.item, details.modified, details.oldKey, details.meta);
    },
    
    
    onCollectionFilteredItemChange: null,
    
    onCollectionRefresh: function(source) {
        var me = this,
            map = {},
            indices = {},
            i, item, items, key, length;
        items = source.items;
        items = me.filtered && me.getAutoFilter() ? Ext.Array.filter(items, me.getFilterFn()) : items.slice(0);
        if (me.sorted) {
            me.sortData(items);
        }
        me.items = items;
        me.length = length = items.length;
        me.map = map;
        me.indices = indices;
        for (i = 0; i < length; ++i) {
            key = me.getKey(item = items[i]);
            map[key] = item;
            indices[key] = i;
        }
        me.notify('refresh');
    },
    
    onCollectionRemove: function(source, details) {
        this.splice(0, details.items);
    },
    
    
    
    
    
    onCollectionUpdateKey: function(source, details) {
        this.updateKey(details.item, details.oldKey);
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    _aggregators: {
        average: function(items, begin, end, property, root) {
            var n = end - begin;
            return n && this._aggregators.sum.call(this, items, begin, end, property, root) / n;
        },
        bounds: function(items, begin, end, property, root) {
            for (var value, max, min,
                i = begin; i < end; ++i) {
                value = items[i];
                value = (root ? value[root] : value)[property];
                
                
                
                if (!(value < max)) {
                    max = value;
                }
                if (!(value > min)) {
                    min = value;
                }
            }
            return [
                min,
                max
            ];
        },
        count: function(items) {
            return items.length;
        },
        extremes: function(items, begin, end, property, root) {
            var most = null,
                least = null,
                i, item, max, min, value;
            for (i = begin; i < end; ++i) {
                item = items[i];
                value = (root ? item[root] : item)[property];
                
                if (!(value < max)) {
                    max = value;
                    most = item;
                }
                if (!(value > min)) {
                    min = value;
                    least = item;
                }
            }
            return [
                least,
                most
            ];
        },
        max: function(items, begin, end, property, root) {
            var b = this._aggregators.bounds.call(this, items, begin, end, property, root);
            return b[1];
        },
        maxItem: function(items, begin, end, property, root) {
            var b = this._aggregators.extremes.call(this, items, begin, end, property, root);
            return b[1];
        },
        min: function(items, begin, end, property, root) {
            var b = this._aggregators.bounds.call(this, items, begin, end, property, root);
            return b[0];
        },
        minItem: function(items, begin, end, property, root) {
            var b = this._aggregators.extremes.call(this, items, begin, end, property, root);
            return b[0];
        },
        sum: function(items, begin, end, property, root) {
            for (var value,
                sum = 0,
                i = begin; i < end; ++i) {
                value = items[i];
                value = (root ? value[root] : value)[property];
                sum += value;
            }
            return sum;
        }
    },
    _eventToMethodMap: {
        add: 'onCollectionAdd',
        beforeitemchange: 'onCollectionBeforeItemChange',
        beginupdate: 'onCollectionBeginUpdate',
        endupdate: 'onCollectionEndUpdate',
        itemchange: 'onCollectionItemChange',
        filtereditemchange: 'onCollectionFilteredItemChange',
        refresh: 'onCollectionRefresh',
        remove: 'onCollectionRemove',
        beforesort: 'beforeCollectionSort',
        sort: 'onCollectionSort',
        filter: 'onCollectionFilter',
        filteradd: 'onCollectionFilterAdd',
        updatekey: 'onCollectionUpdateKey'
    },
    
    addObserver: function(observer) {
        var observers = this.observers,
            observerMap,
            observerId = observer.getId();
        if (!observers) {
            observers = this.observers = [];
            this.observerMap = {};
        }
        observerMap = this.observerMap;
        if (!observerMap[observerId]) {
            observers.push(observer);
            observerMap[observerId] = observer;
            if (observers.length > 1) {
                
                
                Ext.Array.sort(observers, this.prioritySortFn);
            }
        }
    },
    prioritySortFn: function(o1, o2) {
        var a = o1.observerPriority || 0,
            b = o2.observerPriority || 0;
        return a - b;
    },
    applyExtraKeys: function(extraKeys, oldExtraKeys) {
        var me = this,
            ret = oldExtraKeys || {},
            config, name, value;
        for (name in extraKeys) {
            value = extraKeys[name];
            if (!value.isCollectionKey) {
                config = {
                    collection: me
                };
                if (Ext.isString(value)) {
                    config.property = value;
                } else {
                    Ext.apply(config, value);
                }
                value = new Ext.util.CollectionKey(config);
            }
            ret[name] = me[name] = value;
            value.name = name;
        }
        return ret;
    },
    applyGrouper: function(grouper) {
        if (grouper) {
            grouper = this.getSorters().decodeSorter(grouper, 'Ext.util.Grouper');
        }
        return grouper;
    },
    
    decodeItems: function(args, index) {
        var me = this,
            ret = (index === undefined) ? args : args[index],
            cloned, decoder, i;
        if (!ret || !ret.$cloned) {
            cloned = args.length > index + 1 || !Ext.isIterable(ret);
            if (cloned) {
                ret = Ext.Array.slice(args, index);
                if (ret.length === 1 && ret[0] === undefined) {
                    ret.length = 0;
                }
            }
            decoder = me.getDecoder();
            if (decoder) {
                if (!cloned) {
                    ret = ret.slice(0);
                    cloned = true;
                }
                for (i = ret.length; i-- > 0; ) {
                    if ((ret[i] = decoder.call(me, ret[i])) === false) {
                        ret.splice(i, 1);
                    }
                }
            }
            if (cloned) {
                ret.$cloned = true;
            }
        }
        return ret;
    },
    
    getIndices: function() {
        var me = this,
            indices = me.indices,
            items = me.items,
            n = items.length,
            i, key;
        if (!indices) {
            me.indices = indices = {};
            ++me.indexRebuilds;
            for (i = 0; i < n; ++i) {
                key = me.getKey(items[i]);
                indices[key] = i;
            }
        }
        return indices;
    },
    
    notify: function(eventName, args) {
        var me = this,
            observers = me.observers,
            methodName = me._eventToMethodMap[eventName],
            added = 0,
            index, length, method, observer;
        args = args || [];
        if (observers && methodName) {
            for (index = 0 , length = observers.length; index < length; ++index) {
                method = (observer = observers[index])[methodName];
                if (method) {
                    if (!added++) {
                        args.unshift(me);
                    }
                    
                    method.apply(observer, args);
                }
            }
        }
        
        if (!me.hasListeners) {
            return;
        }
        if (me.hasListeners[eventName]) {
            if (!added) {
                args.unshift(me);
            }
            
            me.fireEventArgs(eventName, args);
        }
    },
    
    getFilterFn: function() {
        return this.getFilters().getFilterFn();
    },
    
    getFilters: function(autoCreate) {
        var ret = this._filters;
        if (!ret && autoCreate !== false) {
            ret = new Ext.util.FilterCollection();
            this.setFilters(ret);
        }
        return ret;
    },
    
    isItemFiltered: function(item) {
        return !this.getFilters().filterFn(item);
    },
    
    onFilterChange: function(filters) {
        var me = this,
            source = me.getSource();
        if (!source) {
            
            
            
            source = new Ext.util.Collection({
                keyFn: me.getKey,
                extraKeys: me.getInitialConfig().extraKeys,
                rootProperty: me.getRootProperty()
            });
            if (me.length) {
                source.add(me.items);
            }
            me.setSource(source);
            me.autoSource = source;
        } else if (source.length || me.length) {
            
            me.onCollectionRefresh(source);
        }
        me.notify('filter');
    },
    
    
    applyFilters: function(filters, collection) {
        if (filters == null || (filters && filters.isFilterCollection)) {
            return filters;
        }
        if (filters) {
            if (!collection) {
                collection = this.getFilters();
            }
            collection.splice(0, collection.length, filters);
        }
        return collection;
    },
    updateFilters: function(newFilters, oldFilters) {
        var me = this;
        if (oldFilters) {
            
            
            
            
            oldFilters.un('endupdate', 'onEndUpdateFilters', me);
        }
        if (newFilters) {
            newFilters.on({
                endupdate: 'onEndUpdateFilters',
                scope: me,
                priority: me.$endUpdatePriority
            });
            newFilters.$filterable = me;
        }
        me.onEndUpdateFilters(newFilters);
    },
    onEndUpdateFilters: function(filters) {
        var me = this,
            was = me.filtered,
            is = !!filters && (filters.length > 0);
        
        if (was || is) {
            me.filtered = is;
            me.onFilterChange(filters);
        }
    },
    
    getSortFn: function() {
        return this._sortFn || this.createSortFn();
    },
    
    getSorters: function(autoCreate) {
        var ret = this._sorters;
        if (!ret && autoCreate !== false) {
            ret = new Ext.util.SorterCollection();
            this.setSorters(ret);
        }
        return ret;
    },
    
    onSortChange: function() {
        if (this.sorted) {
            this.sortItems();
        }
    },
    
    sort: function(property, direction, mode) {
        var sorters = this.getSorters();
        sorters.addSort.apply(sorters, arguments);
        return this;
    },
    
    sortData: function(data) {
        Ext.Array.sort(data, this.getSortFn());
        return data;
    },
    
    sortItems: function(sortFn) {
        var me = this;
        if (me.sorted) {
            if (sortFn) {
                Ext.Error.raise('Collections with sorters cannot resorted');
            }
            sortFn = me.getSortFn();
        }
        me.indices = null;
        me.notify('beforesort', [
            me.getSorters()
        ]);
        if (me.length) {
            Ext.Array.sort(me.items, sortFn);
        }
        
        
        me.notify('sort');
    },
    
    sortBy: function(sortFn) {
        return this.sortItems(sortFn);
    },
    
    
    
    
    findInsertionIndex: function(item, items, comparatorFn) {
        if (!items) {
            items = this.items;
        }
        if (!comparatorFn) {
            comparatorFn = this.getSortFn();
        }
        return Ext.Array.binarySearch(items, item, comparatorFn);
    },
    applySorters: function(sorters, collection) {
        if (sorters == null || (sorters && sorters.isSorterCollection)) {
            return sorters;
        }
        if (sorters) {
            if (!collection) {
                collection = this.getSorters();
            }
            collection.splice(0, collection.length, sorters);
        }
        return collection;
    },
    createSortFn: function() {
        var me = this,
            grouper = me.getGrouper(),
            sorterFn = me.getSorters().getSortFn();
        if (!grouper) {
            return sorterFn;
        }
        return function(lhs, rhs) {
            return grouper.sort(lhs, rhs) || sorterFn(lhs, rhs);
        };
    },
    updateGrouper: function(grouper) {
        var me = this,
            groups = me.getGroups(),
            sorters = me.getSorters(),
            populate;
        me.onSorterChange();
        me.grouped = !!grouper;
        if (grouper) {
            if (!groups) {
                groups = new Ext.util.GroupCollection({
                    itemRoot: me.getRootProperty()
                });
                me.addObserver(groups);
                me.setGroups(groups);
            }
            groups.setGrouper(grouper);
            populate = true;
        } else {
            if (groups) {
                me.removeObserver(groups);
                groups.destroy();
            }
            me.setGroups(null);
        }
        if (!sorters.updating) {
            me.onEndUpdateSorters(sorters);
        }
        if (populate) {
            groups.onCollectionRefresh(me);
        }
    },
    updateSorters: function(newSorters, oldSorters) {
        var me = this;
        if (oldSorters) {
            
            
            
            
            oldSorters.un('endupdate', 'onEndUpdateSorters', me);
        }
        if (newSorters) {
            newSorters.on({
                endupdate: 'onEndUpdateSorters',
                scope: me,
                priority: me.$endUpdatePriority
            });
            newSorters.$sortable = me;
        }
        me.onSorterChange();
        me.onEndUpdateSorters(newSorters);
    },
    onSorterChange: function() {
        this._sortFn = null;
    },
    onEndUpdateSorters: function(sorters) {
        var me = this,
            was = me.sorted,
            is = me.grouped || (sorters && (sorters.length > 0));
        if (was || is) {
            
            
            me.sorted = !!is;
            me.onSortChange(sorters);
        }
    },
    
    removeObserver: function(observer) {
        var observers = this.observers;
        if (observers) {
            Ext.Array.remove(observers, observer);
            delete this.observerMap[observer.getId()];
        }
    },
    
    spliceMerge: function(newItems, newKeys) {
        var me = this,
            map = me.map,
            newLength = newItems.length,
            oldIndex = 0,
            oldItems = me.items,
            oldLength = oldItems.length,
            adds = [],
            count = 0,
            items = [],
            sorters = me.getSorters(),
            addItems, end, i, newItem, oldItem, newIndex;
        me.items = items;
        
        
        
        
        
        
        
        
        
        
        
        
        
        for (newIndex = 0; newIndex < newLength; newIndex = end) {
            newItem = newItems[newIndex];
            
            for (; oldIndex < oldLength; ++oldIndex) {
                
                
                
                
                if (sorters.sortFn(newItem, oldItem = oldItems[oldIndex]) < 0) {
                    break;
                }
                items.push(oldItem);
            }
            if (oldIndex === oldLength) {
                
                
                
                
                adds[count++] = {
                    at: items.length,
                    itemAt: items[items.length - 1],
                    items: (addItems = [])
                };
                if (count > 1) {
                    adds[count - 2].next = adds[count - 1];
                }
                for (; newIndex < newLength; ++newIndex) {
                    addItems.push(newItem = newItems[newIndex]);
                    items.push(newItem);
                }
                break;
            }
            
            
            
            
            adds[count++] = {
                at: items.length,
                itemAt: items[items.length - 1],
                items: (addItems = [
                    newItem
                ])
            };
            if (count > 1) {
                adds[count - 2].next = adds[count - 1];
            }
            items.push(newItem);
            for (end = newIndex + 1; end < newLength; ++end) {
                
                
                
                if (sorters.sortFn(newItem = newItems[end], oldItem) >= 0) {
                    break;
                }
                items.push(newItem);
                addItems.push(newItem);
            }
        }
        
        
        for (; oldIndex < oldLength; ++oldIndex) {
            
            
            items.push(oldItems[oldIndex]);
        }
        for (i = 0; i < newLength; ++i) {
            map[newKeys[i]] = newItems[i];
        }
        me.length = items.length;
        ++me.generation;
        me.indices = null;
        
        for (i = 0; i < count; ++i) {
            me.notify('add', [
                adds[i]
            ]);
        }
    },
    getGroups: function() {
        return this.callParent() || null;
    },
    updateGroups: function(newGroups, oldGroups) {
        if (oldGroups) {
            this.removeObserver(oldGroups);
        }
        if (newGroups) {
            this.addObserver(newGroups);
        }
    },
    updateSource: function(newSource, oldSource) {
        var auto = this.autoSource;
        if (oldSource) {
            oldSource.removeObserver(this);
            if (oldSource === auto) {
                auto.destroy();
                this.autoSource = null;
            }
        }
        if (newSource) {
            newSource.addObserver(this);
            if (newSource.length || this.length) {
                this.onCollectionRefresh(newSource);
            }
        }
    }
}, function() {
    var prototype = this.prototype;
    
    prototype.removeAtKey = prototype.removeByKey;
    
    prototype.decodeRemoveItems = prototype.decodeItems;
    Ext.Object.each(prototype._aggregators, function(name) {
        prototype[name] = function(property, begin, end) {
            return this.aggregate(property, name, begin, end);
        };
        prototype[name + 'ByGroup'] = function(property) {
            return this.aggregateByGroup(property, name);
        };
    });
});


Ext.define('Ext.util.ObjectTemplate', {
    requires: [
        'Ext.XTemplate'
    ],
    isObjectTemplate: true,
    excludeProperties: {},
    valueRe: /^[{][a-z\.]+[}]$/i,
    statics: {
        
        create: function(template, options) {
            if (!Ext.isObject(template)) {
                Ext.Error.raise('The template is not an Object');
            }
            return template.isObjectTemplate ? template : new Ext.util.ObjectTemplate(template,options);
        }
    },
    
    constructor: function(template, options) {
        Ext.apply(this, options);
        this.template = template;
    },
    
    apply: function(context) {
        var me = this;
        delete me.apply;
        me.apply = me.compile(me.template);
        return me.apply(context);
    },
    privates: {
        
        compile: function(template) {
            var me = this,
                exclude = me.excludeProperties,
                compiled, i, len, fn;
            
            if (Ext.isString(template)) {
                if (template.indexOf('{') < 0) {
                    fn = function() {
                        return template;
                    };
                } else if (me.valueRe.test(template)) {
                    template = template.substring(1, template.length - 1).split('.');
                    fn = function(context) {
                        for (var v = context,
                            i = 0; v && i < template.length; ++i) {
                            v = v[template[i]];
                        }
                        return v;
                    };
                } else {
                    template = new Ext.XTemplate(template);
                    fn = function(context) {
                        return template.apply(context);
                    };
                }
            } else if (Ext.isPrimitive(template) || Ext.isFunction(template)) {
                fn = function() {
                    return template;
                };
            } else if (template instanceof Array) {
                compiled = [];
                for (i = 0 , len = template.length; i < len; ++i) {
                    compiled[i] = me.compile(template[i]);
                }
                fn = function(context) {
                    var ret = [],
                        i;
                    for (i = 0; i < len; ++i) {
                        ret[i] = compiled[i](context);
                    }
                    return ret;
                };
            } else {
                compiled = {};
                for (i in template) {
                    if (!exclude[i]) {
                        compiled[i] = me.compile(template[i]);
                    }
                }
                fn = function(context) {
                    var ret = {},
                        i, v;
                    for (i in template) {
                        v = exclude[i] ? template[i] : compiled[i](context);
                        if (v !== undefined) {
                            ret[i] = v;
                        }
                    }
                    return ret;
                };
            }
            return fn;
        }
    }
});


Ext.define('Ext.data.schema.Role', {
    
    isRole: true,
    
    left: true,
    
    owner: false,
    
    side: 'left',
    
    isMany: false,
    
    
    
    
    defaultReaderType: 'json',
    constructor: function(association, config) {
        var me = this,
            extra = config.extra;
        Ext.apply(me, config);
        if (extra) {
            delete extra.type;
            Ext.apply(me, extra);
            delete me.extra;
        }
        me.association = association;
        
        
        if (association.owner === me.side) {
            association.owner = me;
            me.owner = true;
        }
    },
    processUpdate: function() {
        Ext.Error.raise('Only the "many" for an association may be processed. "' + this.role + '" is not valid.');
    },
    
    processLoad: function(store, associatedEntity, records, session) {
        return records;
    },
    
    checkMembership: Ext.emptyFn,
    
    adoptAssociated: function(record, session) {
        var other = this.getAssociatedItem(record);
        if (other) {
            session.adopt(other);
        }
    },
    createAssociationStore: function(session, from, records, isComplete) {
        var me = this,
            association = me.association,
            foreignKeyName = association.getFieldName(),
            isMany = association.isManyToMany,
            storeConfig = me.storeConfig,
            id = from.getId(),
            config = {
                model: me.cls,
                role: me,
                session: session,
                associatedEntity: from,
                disableMetaChangeEvent: true,
                pageSize: null,
                remoteFilter: true,
                trackRemoved: !session
            },
            store;
        if (isMany) {
            
            config.filters = [
                {
                    property: me.inverse.field,
                    
                    value: id,
                    exactMatch: true
                }
            ];
        } else if (foreignKeyName) {
            config.filters = [
                {
                    property: foreignKeyName,
                    
                    value: id,
                    exactMatch: true
                }
            ];
            config.foreignKeyName = foreignKeyName;
        }
        if (storeConfig) {
            Ext.apply(config, storeConfig);
        }
        store = Ext.Factory.store(config);
        me.onStoreCreate(store, session, id);
        if (foreignKeyName || (isMany && session)) {
            store.on({
                scope: me,
                add: 'onAddToMany',
                remove: 'onRemoveFromMany',
                clear: 'onRemoveFromMany'
            });
        }
        if (records) {
            store.loadData(records);
            store.complete = !!isComplete;
        }
        return store;
    },
    onStoreCreate: Ext.emptyFn,
    getAssociatedStore: function(inverseRecord, options, scope, records, isComplete) {
        
        
        
        
        var me = this,
            storeName = me.getStoreName(),
            store = inverseRecord[storeName],
            load = options && options.reload,
            source = inverseRecord.$source,
            session = inverseRecord.session,
            args, i, len, raw, rec, sourceStore;
        if (!store) {
            
            
            
            if (!records && source) {
                source = source[storeName];
                if (source && !source.isLoading()) {
                    sourceStore = source;
                    records = [];
                    raw = source.getData().items;
                    for (i = 0 , len = raw.length; i < len; ++i) {
                        rec = raw[i];
                        records.push(session.getRecord(rec.self, rec.id));
                    }
                    isComplete = true;
                }
            }
            store = me.createAssociationStore(session, inverseRecord, records, isComplete);
            store.$source = sourceStore;
            if (!records && (me.autoLoad || options)) {
                load = true;
            }
            inverseRecord[storeName] = store;
        }
        if (options) {
            
            
            if (load || store.isLoading()) {
                store.on('load', function(store, records, success, operation) {
                    args = [
                        store,
                        operation
                    ];
                    scope = scope || options.scope || inverseRecord;
                    if (success) {
                        Ext.callback(options.success, scope, args);
                    } else {
                        Ext.callback(options.failure, scope, args);
                    }
                    args.push(success);
                    Ext.callback(options, scope, args);
                    Ext.callback(options.callback, scope, args);
                }, null, {
                    single: true
                });
            } else {
                
                args = [
                    store,
                    null
                ];
                scope = scope || options.scope || inverseRecord;
                Ext.callback(options.success, scope, args);
                args.push(true);
                Ext.callback(options, scope, args);
                Ext.callback(options.callback, scope, args);
            }
        }
        if (load && !store.isLoading()) {
            store.load();
        }
        return store;
    },
    
    getAssociatedItem: function(rec) {
        var key = this.isMany ? this.getStoreName() : this.getInstanceName();
        return rec[key] || null;
    },
    onDrop: Ext.emptyFn,
    getReaderRoot: function() {
        var me = this;
        return me.associationKey || (me.associationKey = me.association.schema.getNamer().readerRoot(me.role));
    },
    getReader: function() {
        var me = this,
            reader = me.reader,
            Model = me.cls,
            useSimpleAccessors = !me.associationKey,
            root = this.getReaderRoot();
        if (reader && !reader.isReader) {
            if (Ext.isString(reader)) {
                reader = {
                    type: reader
                };
            }
            Ext.applyIf(reader, {
                model: Model,
                rootProperty: root,
                useSimpleAccessors: useSimpleAccessors,
                type: me.defaultReaderType
            });
            reader = me.reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        return reader;
    },
    getInstanceName: function() {
        var me = this;
        return me.instanceName || (me.instanceName = me.association.schema.getNamer().instanceName(me.role));
    },
    getStoreName: function() {
        var me = this;
        return me.storeName || (me.storeName = me.association.schema.getNamer().storeName(me.role));
    },
    constructReader: function(fromReader) {
        var me = this,
            reader = me.getReader(),
            Model = me.cls,
            useSimpleAccessors = !me.associationKey,
            root = me.getReaderRoot(),
            proxyReader, proxy;
        
        if (!reader) {
            proxy = Model.getProxy();
            
            if (proxy) {
                proxyReader = proxy.getReader();
                reader = new proxyReader.self();
                reader.copyFrom(proxyReader);
                reader.setRootProperty(root);
            } else {
                reader = new fromReader.self({
                    model: Model,
                    useSimpleAccessors: useSimpleAccessors,
                    rootProperty: root
                });
            }
            me.reader = reader;
        }
        return reader;
    },
    read: function(record, data, fromReader, readOptions) {
        var me = this,
            reader = this.constructReader(fromReader);
        return reader.read(data, readOptions);
    },
    getCallbackOptions: function(options, scope, defaultScope) {
        if (typeof options === 'function') {
            options = {
                callback: options,
                scope: scope || defaultScope
            };
        } else if (options) {
            options = Ext.apply({}, options);
            options.scope = scope || options.scope || defaultScope;
        }
        return options;
    },
    doGetFK: function(leftRecord, options, scope) {
        
        
        
        
        
        var me = this,
            
            cls = me.cls,
            
            foreignKey = me.association.getFieldName(),
            
            instanceName = me.getInstanceName(),
            
            rightRecord = leftRecord[instanceName],
            
            reload = options && options.reload,
            done = rightRecord !== undefined && !reload,
            session = leftRecord.session,
            foreignKeyId, args;
        if (!done) {
            
            if (session) {
                foreignKeyId = leftRecord.get(foreignKey);
                if (foreignKeyId || foreignKeyId === 0) {
                    done = session.peekRecord(cls, foreignKeyId, true) && !reload;
                    rightRecord = session.getRecord(cls, foreignKeyId, false);
                } else {
                    done = true;
                    leftRecord[instanceName] = rightRecord = null;
                }
            } else if (foreignKey) {
                
                
                foreignKeyId = leftRecord.get(foreignKey);
                if (!foreignKeyId && foreignKeyId !== 0) {
                    
                    
                    done = true;
                    leftRecord[instanceName] = rightRecord = null;
                } else {
                    
                    
                    
                    if (!rightRecord) {
                        
                        rightRecord = cls.createWithId(foreignKeyId);
                    }
                }
            } else 
            {
                
                
                done = true;
            }
        } else if (rightRecord) {
            
            done = !rightRecord.isLoading();
        }
        if (done) {
            if (options) {
                args = [
                    rightRecord,
                    null
                ];
                scope = scope || options.scope || leftRecord;
                Ext.callback(options.success, scope, args);
                args.push(true);
                Ext.callback(options, scope, args);
                Ext.callback(options.callback, scope, args);
            }
        } else {
            leftRecord[instanceName] = rightRecord;
            options = me.getCallbackOptions(options, scope, leftRecord);
            rightRecord.load(options);
        }
        return rightRecord;
    },
    doSetFK: function(leftRecord, rightRecord, options, scope) {
        
        
        
        
        
        var me = this,
            foreignKey = me.association.getFieldName(),
            
            instanceName = me.getInstanceName(),
            
            ret = leftRecord[instanceName],
            inverse = me.inverse,
            inverseSetter = inverse.setterName,
            
            session = leftRecord.session,
            modified;
        if (rightRecord && rightRecord.isEntity) {
            if (ret !== rightRecord) {
                if (foreignKey) {
                    leftRecord.set(foreignKey, rightRecord.getId());
                }
                leftRecord[instanceName] = rightRecord;
                if (inverseSetter) {
                    
                    
                    
                    rightRecord[inverseSetter](leftRecord);
                }
            }
        } else {
            
            
            if (!foreignKey) {
                Ext.Error.raise('No foreignKey specified for "' + me.association.left.role + '" by ' + leftRecord.$className);
            }
            modified = (leftRecord.changingKey && !inverse.isMany) || leftRecord.set(foreignKey, rightRecord);
            
            if (modified && ret && ret.isEntity && !ret.isEqual(ret.getId(), rightRecord)) {
                
                
                leftRecord[instanceName] = undefined;
                if (!inverse.isMany) {
                    ret[inverse.getInstanceName()] = undefined;
                }
            }
        }
        if (options) {
            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope || leftRecord
                };
            }
            return leftRecord.save(options);
        }
    }
});


Ext.define('Ext.data.schema.Association', {
    requires: [
        'Ext.data.schema.Role'
    ],
    isOneToOne: false,
    isManyToOne: false,
    isManyToMany: false,
    
    
    owner: null,
    
    
    field: null,
    
    
    
    
    constructor: function(config) {
        var me = this,
            left, right;
        Ext.apply(me, config);
        me.left = left = new me.Left(me,me.left);
        me.right = right = new me.Right(me,me.right);
        left.inverse = right;
        right.inverse = left;
    },
    hasField: function() {
        return !!this.field;
    },
    getFieldName: function() {
        var field = this.field;
        return field ? field.name : '';
    }
});


Ext.define('Ext.data.schema.OneToOne', {
    extend: 'Ext.data.schema.Association',
    isOneToOne: true,
    isToOne: true,
    kind: 'one-to-one',
    Left: Ext.define(null, {
        extend: 'Ext.data.schema.Role',
        onDrop: function(rightRecord, session) {
            var leftRecord = this.getAssociatedItem(rightRecord);
            rightRecord[this.getInstanceName()] = null;
            if (leftRecord) {
                leftRecord[this.inverse.getInstanceName()] = null;
            }
        },
        createGetter: function() {
            var me = this;
            return function() {
                
                return me.doGet(this);
            };
        },
        createSetter: function() {
            var me = this;
            return function(value) {
                
                return me.doSet(this, value);
            };
        },
        doGet: function(rightRecord) {
            
            
            
            
            
            
            var instanceName = this.getInstanceName(),
                
                ret = rightRecord[instanceName],
                session = rightRecord.session;
            if (!ret && session) {}
            
            
            return ret || null;
        },
        doSet: function(rightRecord, leftRecord) {
            
            
            
            var instanceName = this.getInstanceName(),
                
                ret = rightRecord[instanceName],
                inverseSetter = this.inverse.setterName;
            
            if (ret !== leftRecord) {
                rightRecord[instanceName] = leftRecord;
                if (inverseSetter) {
                    
                    
                    
                    leftRecord[inverseSetter](rightRecord);
                }
            }
            return ret;
        },
        read: function(rightRecord, node, fromReader, readOptions) {
            var me = this,
                result = me.callParent([
                    rightRecord,
                    node,
                    fromReader,
                    readOptions
                ]),
                leftRecord = result.getRecords()[0];
            if (leftRecord) {
                leftRecord[me.inverse.getInstanceName()] = rightRecord;
                rightRecord[me.getInstanceName()] = leftRecord;
                
                delete rightRecord.data[me.role];
            }
        }
    }),
    Right: Ext.define(null, {
        extend: 'Ext.data.schema.Role',
        left: false,
        side: 'right',
        createGetter: function() {
            
            
            var me = this;
            return function(options, scope) {
                
                return me.doGetFK(this, options, scope);
            };
        },
        createSetter: function() {
            var me = this;
            return function(value, options, scope) {
                
                return me.doSetFK(this, value, options, scope);
            };
        },
        onDrop: function(leftRecord, session) {
            var me = this,
                field = me.association.field,
                rightRecord = me.getAssociatedItem(leftRecord),
                id;
            if (me.inverse.owner) {
                if (session) {
                    id = leftRecord.get(field.name);
                    if (id || id === 0) {
                        rightRecord = session.getEntry(me.cls, id).record;
                        if (rightRecord) {
                            rightRecord.drop();
                        }
                    }
                } else {
                    if (rightRecord) {
                        rightRecord.drop();
                    }
                }
            }
            if (field) {
                leftRecord.set(field.name, null);
            }
            leftRecord[me.getInstanceName()] = null;
            if (rightRecord) {
                rightRecord[me.inverse.getInstanceName()] = null;
            }
        },
        onValueChange: function(leftRecord, session, newValue) {
            
            var me = this,
                rightRecord = me.getAssociatedItem(leftRecord),
                hasNewValue = newValue || newValue === 0,
                instanceName = me.getInstanceName(),
                cls = me.cls;
            leftRecord.changingKey = true;
            me.doSetFK(leftRecord, newValue);
            if (!hasNewValue) {
                leftRecord[instanceName] = null;
            } else if (session && cls) {
                
                leftRecord[instanceName] = session.peekRecord(cls, newValue) || undefined;
            }
            if (me.inverse.owner && rightRecord) {
                me.association.schema.queueKeyCheck(rightRecord, me);
            }
            leftRecord.changingKey = false;
        },
        checkKeyForDrop: function(rightRecord) {
            var leftRecord = this.inverse.getAssociatedItem(rightRecord);
            if (!leftRecord) {
                
                rightRecord.drop();
            }
        },
        read: function(leftRecord, node, fromReader, readOptions) {
            var me = this,
                result = me.callParent([
                    leftRecord,
                    node,
                    fromReader,
                    readOptions
                ]),
                rightRecord = result.getRecords()[0],
                field = this.association.field,
                session = leftRecord.session,
                oldId;
            if (rightRecord) {
                rightRecord[me.inverse.getInstanceName()] = leftRecord;
                leftRecord[me.getInstanceName()] = rightRecord;
                
                delete leftRecord.data[me.role];
                
                
                if (field) {
                    oldId = leftRecord.data[field.name];
                    if (oldId !== rightRecord.id) {
                        leftRecord.data[field.name] = rightRecord.id;
                        if (session) {
                            session.updateReference(leftRecord, field, rightRecord.id, oldId);
                        }
                    }
                }
            }
        }
    })
});


Ext.define('Ext.data.schema.ManyToOne', {
    extend: 'Ext.data.schema.Association',
    isManyToOne: true,
    isToOne: true,
    kind: 'many-to-one',
    Left: Ext.define(null, {
        extend: 'Ext.data.schema.Role',
        isMany: true,
        onDrop: function(rightRecord, session) {
            var me = this,
                store = me.getAssociatedItem(rightRecord),
                leftRecords, len, i, refs, id;
            if (store) {
                
                leftRecords = store.removeAll();
                if (leftRecords && me.inverse.owner) {
                    
                    for (i = 0 , len = leftRecords.length; i < len; ++i) {
                        leftRecords[i].drop();
                    }
                }
                store.destroy();
                rightRecord[me.getStoreName()] = null;
            } else if (session) {
                leftRecords = session.getRefs(rightRecord, me);
                if (leftRecords) {
                    for (id in leftRecords) {
                        leftRecords[id].drop();
                    }
                }
            }
        },
        processUpdate: function(session, associationData) {
            var me = this,
                entityType = me.inverse.cls,
                items = associationData.R,
                id, rightRecord, store, leftRecords;
            if (items) {
                for (id in items) {
                    rightRecord = session.peekRecord(entityType, id);
                    if (rightRecord) {
                        leftRecords = session.getEntityList(me.cls, items[id]);
                        store = me.getAssociatedItem(rightRecord);
                        if (store) {
                            store.loadData(leftRecords);
                            store.complete = true;
                        } else {
                            
                            rightRecord[me.getterName](null, null, leftRecords);
                        }
                    } else {
                        session.onInvalidAssociationEntity(entityType, id);
                    }
                }
            }
        },
        findRecords: function(session, rightRecord, leftRecords) {
            var ret = leftRecords,
                refs = session.getRefs(rightRecord, this, true),
                leftRecord, id, i, len, seen;
            if (!rightRecord.phantom) {
                ret = [];
                if (refs) {
                    if (leftRecords) {
                        seen = {};
                        
                        
                        for (i = 0 , len = leftRecords.length; i < len; ++i) {
                            leftRecord = leftRecords[i];
                            id = leftRecord.id;
                            if (refs[id]) {
                                ret.push(leftRecord);
                            }
                            seen[id] = true;
                        }
                    }
                    
                    for (id in refs) {
                        if (!seen || !seen[id]) {
                            ret.push(refs[id]);
                        }
                    }
                }
            }
            return ret;
        },
        processLoad: function(store, rightRecord, leftRecords, session) {
            var ret = leftRecords;
            if (session) {
                ret = this.findRecords(session, rightRecord, leftRecords);
            }
            this.onLoadMany(rightRecord, ret, session);
            return ret;
        },
        adoptAssociated: function(rightRecord, session) {
            var store = this.getAssociatedItem(rightRecord),
                leftRecords, i, len;
            if (store) {
                store.setSession(session);
                leftRecords = store.getData().items;
                for (i = 0 , len = leftRecords.length; i < len; ++i) {
                    session.adopt(leftRecords[i]);
                }
            }
        },
        createGetter: function() {
            var me = this;
            return function(options, scope, leftRecords) {
                
                var session = this.session,
                    hadRecords = !!leftRecords;
                if (session) {
                    leftRecords = me.findRecords(session, this, leftRecords);
                    if (!hadRecords && (!leftRecords || !leftRecords.length)) {
                        leftRecords = null;
                    }
                }
                return me.getAssociatedStore(this, options, scope, leftRecords, hadRecords);
            };
        },
        createSetter: null,
        
        onAddToMany: function(store, leftRecords) {
            this.syncFK(leftRecords, store.getAssociatedEntity(), false);
        },
        onLoadMany: function(rightRecord, leftRecords, session) {
            var instanceName = this.inverse.getInstanceName(),
                id = rightRecord.getId(),
                field = this.association.field,
                i, len, leftRecord, oldId, data, name;
            if (field) {
                for (i = 0 , len = leftRecords.length; i < len; ++i) {
                    leftRecord = leftRecords[i];
                    leftRecord[instanceName] = rightRecord;
                    if (field) {
                        name = field.name;
                        data = leftRecord.data;
                        oldId = data[name];
                        if (oldId !== id) {
                            data[name] = id;
                            if (session) {
                                session.updateReference(leftRecord, field, id, oldId);
                            }
                        }
                    }
                }
            }
        },
        onRemoveFromMany: function(store, leftRecords) {
            this.syncFK(leftRecords, store.getAssociatedEntity(), true);
        },
        read: function(rightRecord, node, fromReader, readOptions) {
            var me = this,
                
                
                instanceName = me.inverse.getInstanceName(),
                result = me.callParent([
                    rightRecord,
                    node,
                    fromReader,
                    readOptions
                ]),
                store, leftRecords, len, i;
            
            if (result.getReadRoot()) {
                
                store = rightRecord[me.getterName](null, null, result.getRecords());
                
                delete rightRecord.data[me.role];
                leftRecords = store.getData().items;
                for (i = 0 , len = leftRecords.length; i < len; ++i) {
                    leftRecords[i][instanceName] = rightRecord;
                }
            }
        },
        syncFK: function(leftRecords, rightRecord, clearing) {
            
            
            
            
            var foreignKeyName = this.association.getFieldName(),
                setter = this.inverse.setterName,
                
                i = leftRecords.length,
                id = rightRecord.getId(),
                different, leftRecord;
            while (i-- > 0) {
                leftRecord = leftRecords[i];
                different = !leftRecord.isEqual(id, leftRecord.get(foreignKeyName));
                if (different !== clearing) {
                    
                    
                    
                    
                    
                    
                    
                    
                    leftRecord.changingKey = true;
                    if (setter) {
                        leftRecord[setter](clearing ? null : rightRecord);
                    } else {
                        leftRecord.set(foreignKeyName, clearing ? null : id);
                    }
                    leftRecord.changingKey = false;
                }
            }
        }
    }),
    Right: Ext.define(null, {
        extend: 'Ext.data.schema.Role',
        left: false,
        side: 'right',
        onDrop: function(leftRecord, session) {
            
            
            
            
            var field = this.association.field;
            if (field) {
                leftRecord.set(field.name, null);
            }
            leftRecord[this.getInstanceName()] = null;
        },
        createGetter: function() {
            
            
            var me = this;
            return function(options, scope) {
                
                return me.doGetFK(this, options, scope);
            };
        },
        createSetter: function() {
            var me = this;
            return function(rightRecord, options, scope) {
                
                return me.doSetFK(this, rightRecord, options, scope);
            };
        },
        checkMembership: function(session, leftRecord) {
            var field = this.association.field,
                store;
            store = this.getSessionStore(session, leftRecord.get(field.name));
            
            if (store && !store.contains(leftRecord)) {
                store.add(leftRecord);
            }
        },
        onValueChange: function(leftRecord, session, newValue, oldValue) {
            
            
            var me = this,
                instanceName = me.getInstanceName(),
                cls = me.cls,
                hasNewValue, joined, store, i, len, associated, rightRecord;
            if (!leftRecord.changingKey) {
                hasNewValue = newValue || newValue === 0;
                if (!hasNewValue) {
                    leftRecord[instanceName] = null;
                }
                if (session) {
                    
                    store = me.getSessionStore(session, oldValue);
                    if (store) {
                        store.remove(leftRecord);
                    }
                    
                    if (hasNewValue) {
                        store = me.getSessionStore(session, newValue);
                        if (store && !store.isLoading()) {
                            store.add(leftRecord);
                        }
                        if (cls) {
                            rightRecord = session.peekRecord(cls, newValue);
                        }
                        
                        leftRecord[instanceName] = rightRecord || undefined;
                    }
                } else {
                    joined = leftRecord.joined;
                    if (joined) {
                        for (i = 0 , len = joined.length; i < len; ++i) {
                            store = joined[i];
                            if (store.isStore) {
                                associated = store.getAssociatedEntity();
                                if (associated && associated.self === me.cls && associated.getId() === oldValue) {
                                    store.remove(leftRecord);
                                }
                            }
                        }
                    }
                }
            }
            if (me.owner && newValue === null) {
                me.association.schema.queueKeyCheck(leftRecord, me);
            }
        },
        checkKeyForDrop: function(leftRecord) {
            var field = this.association.field;
            if (leftRecord.get(field.name) === null) {
                leftRecord.drop();
            }
        },
        getSessionStore: function(session, value) {
            
            var cls = this.cls,
                rec;
            if (cls) {
                rec = session.peekRecord(cls, value);
                if (rec) {
                    return this.inverse.getAssociatedItem(rec);
                }
            }
        },
        read: function(leftRecord, node, fromReader, readOptions) {
            var result = this.callParent([
                    leftRecord,
                    node,
                    fromReader,
                    readOptions
                ]),
                rightRecord = result.getRecords()[0];
            if (rightRecord) {
                leftRecord[this.getInstanceName()] = rightRecord;
                delete leftRecord.data[this.role];
            }
        }
    })
});


Ext.define('Ext.data.schema.ManyToMany', {
    extend: 'Ext.data.schema.Association',
    isManyToMany: true,
    isToMany: true,
    kind: 'many-to-many',
    Left: Ext.define(null, {
        extend: 'Ext.data.schema.Role',
        isMany: true,
        digitRe: /^\d+$/,
        findRecords: function(session, rightRecord, leftRecords) {
            var slice = session.getMatrixSlice(this.inverse, rightRecord.id),
                members = slice.members,
                ret = [],
                cls = this.cls,
                seen, i, len, id, member, leftRecord;
            if (leftRecords) {
                seen = {};
                
                
                for (i = 0 , len = leftRecords.length; i < len; ++i) {
                    leftRecord = leftRecords[i];
                    id = leftRecord.id;
                    member = members[id];
                    if (!(member && member[2] === -1)) {
                        ret.push(leftRecord);
                    }
                    seen[id] = true;
                }
            }
            
            for (id in members) {
                member = members[id];
                if (!seen || !seen[id] && (member && member[2] !== -1)) {
                    leftRecord = session.peekRecord(cls, id);
                    if (leftRecord) {
                        ret.push(leftRecord);
                    }
                }
            }
            return ret;
        },
        processLoad: function(store, rightRecord, leftRecords, session) {
            var ret = leftRecords;
            if (session) {
                ret = this.findRecords(session, rightRecord, leftRecords);
                this.onAddToMany(store, ret, true);
            }
            return ret;
        },
        processUpdate: function(session, associationData) {
            var me = this,
                entityType = me.inverse.cls,
                items = associationData.R,
                id, rightRecord, store, leftRecords;
            if (items) {
                for (id in items) {
                    rightRecord = session.peekRecord(entityType, id);
                    if (rightRecord) {
                        leftRecords = session.getEntityList(me.cls, items[id]);
                        store = me.getAssociatedItem(rightRecord);
                        if (store) {
                            store.loadData(leftRecords);
                            store.complete = true;
                        } else {
                            
                            rightRecord[me.getterName](null, null, leftRecords);
                        }
                    } else {
                        session.onInvalidAssociationEntity(entityType, id);
                    }
                }
            }
            me.processMatrixBlock(session, associationData.C, 1);
            me.processMatrixBlock(session, associationData.D, -1);
        },
        checkMembership: function(session, rightRecord) {
            var matrix = session.getMatrix(this.association, true),
                side, entityType, inverse, slice, slices, id, members, member, leftRecord, store;
            if (!matrix) {
                return;
            }
            side = this.left ? matrix.right : matrix.left;
            entityType = side.inverse.role.cls;
            inverse = this.inverse;
            slices = side.slices;
            if (slices) {
                slice = slices[rightRecord.id];
                if (slice) {
                    members = slice.members;
                    for (id in members) {
                        member = members[id];
                        if (member[2] !== -1) {
                            
                            leftRecord = session.peekRecord(entityType, id);
                            if (leftRecord) {
                                store = inverse.getAssociatedItem(leftRecord);
                                if (store) {
                                    store.matrixUpdate = 1;
                                    store.add(rightRecord);
                                    store.matrixUpdate = 0;
                                }
                            }
                        }
                    }
                }
            }
        },
        onStoreCreate: function(store, session, id) {
            var me = this,
                matrix;
            if (session) {
                
                
                
                matrix = session.getMatrixSlice(me.inverse, id);
                matrix.attach(store);
                matrix.notify = me.onMatrixUpdate;
                matrix.scope = me;
            }
        },
        processMatrixBlock: function(session, leftKeys, state) {
            var inverse = this.inverse,
                digitRe = this.digitRe,
                slice, id;
            if (leftKeys) {
                for (id in leftKeys) {
                    
                    
                    if (digitRe.test(id)) {
                        id = parseInt(id, 10);
                    }
                    slice = session.getMatrixSlice(inverse, id);
                    slice.update(leftKeys[id], state);
                }
            }
        },
        createGetter: function() {
            var me = this;
            return function(options, scope, leftRecords) {
                
                var session = this.session,
                    hadRecords;
                if (session) {
                    hadRecords = !!leftRecords;
                    leftRecords = me.findRecords(session, this, leftRecords);
                    if (!hadRecords && !leftRecords.length) {
                        leftRecords = null;
                    }
                }
                return me.getAssociatedStore(this, options, scope, leftRecords, hadRecords);
            };
        },
        
        onAddToMany: function(store, leftRecords, load) {
            if (!store.matrixUpdate) {
                store.matrixUpdate = 1;
                
                
                store.matrix.update(leftRecords, load === true ? 0 : 1);
                store.matrixUpdate = 0;
            }
        },
        
        onRemoveFromMany: function(store, records) {
            if (!store.matrixUpdate) {
                store.matrixUpdate = 1;
                store.matrix.update(records, -1);
                store.matrixUpdate = 0;
            }
        },
        read: function(rightRecord, node, fromReader, readOptions) {
            var me = this,
                result = me.callParent([
                    rightRecord,
                    node,
                    fromReader,
                    readOptions
                ]);
            
            if (result.getReadRoot()) {
                
                rightRecord[me.getterName](null, null, result.getRecords());
                
                delete rightRecord.data[me.role];
            }
        },
        onMatrixUpdate: function(matrixSlice, id, state) {
            var store = matrixSlice.store,
                index, leftRecord, entry;
            if (store && !store.loading && !store.matrixUpdate) {
                store.matrixUpdate = 1;
                index = store.indexOfId(id);
                if (state < 0) {
                    if (index >= 0) {
                        store.remove([
                            index
                        ]);
                    }
                } else if (index < 0) {
                    entry = store.getSession().getEntry(this.type, id);
                    leftRecord = entry && entry.record;
                    if (leftRecord) {
                        store.add(leftRecord);
                    }
                }
                store.matrixUpdate = 0;
            }
        },
        adoptAssociated: function(record, session) {
            var store = this.getAssociatedItem(record),
                records, i, len;
            if (store) {
                store.setSession(session);
                this.onStoreCreate(store, session, record.getId());
                records = store.getData().items;
                for (i = 0 , len = records.length; i < len; ++i) {
                    session.adopt(records[i]);
                }
            }
        }
    }, function() {
        var Left = this;
        
        Ext.ClassManager.onCreated(function() {
            Ext.data.schema.ManyToMany.prototype.Right = Ext.define(null, {
                extend: Left,
                left: false,
                side: 'right'
            });
        }, null, 'Ext.data.schema.ManyToMany');
    })
});


Ext.define('Ext.util.Inflector', {
    
    singleton: true,
    
    
    plurals: [
        [
            (/(quiz)$/i),
            "$1zes"
        ],
        [
            (/^(ox)$/i),
            "$1en"
        ],
        [
            (/([m|l])ouse$/i),
            "$1ice"
        ],
        [
            (/(matr|vert|ind)ix|ex$/i),
            "$1ices"
        ],
        [
            (/(x|ch|ss|sh)$/i),
            "$1es"
        ],
        [
            (/([^aeiouy]|qu)y$/i),
            "$1ies"
        ],
        [
            (/(hive)$/i),
            "$1s"
        ],
        [
            (/(?:([^f])fe|([lr])f)$/i),
            "$1$2ves"
        ],
        [
            (/sis$/i),
            "ses"
        ],
        [
            (/([ti])um$/i),
            "$1a"
        ],
        [
            (/(buffal|tomat|potat)o$/i),
            "$1oes"
        ],
        [
            (/(bu)s$/i),
            "$1ses"
        ],
        [
            (/(alias|status|sex)$/i),
            "$1es"
        ],
        [
            (/(octop|vir)us$/i),
            "$1i"
        ],
        [
            (/(ax|test)is$/i),
            "$1es"
        ],
        [
            (/^(p)erson$/i),
            "$1eople"
        ],
        [
            (/^(m)an$/i),
            "$1en"
        ],
        [
            (/(.*)(child)(ren)?$/i),
            "$1$2ren"
        ],
        [
            (/s$/i),
            "s"
        ],
        [
            (/$/),
            "s"
        ]
    ],
    
    singulars: [
        [
            (/(address)$/i),
            "$1"
        ],
        [
            (/(quiz)zes$/i),
            "$1"
        ],
        [
            (/(matr)ices$/i),
            "$1ix"
        ],
        [
            (/(vert|ind)ices$/i),
            "$1ex"
        ],
        [
            (/^(ox)en/i),
            "$1"
        ],
        [
            (/(alias|status)es$/i),
            "$1"
        ],
        [
            (/(octop|vir)i$/i),
            "$1us"
        ],
        [
            (/(cris|ax|test)es$/i),
            "$1is"
        ],
        [
            (/(shoe)s$/i),
            "$1"
        ],
        [
            (/(o)es$/i),
            "$1"
        ],
        [
            (/(bus)es$/i),
            "$1"
        ],
        [
            (/([m|l])ice$/i),
            "$1ouse"
        ],
        [
            (/(x|ch|ss|sh)es$/i),
            "$1"
        ],
        [
            (/(m)ovies$/i),
            "$1ovie"
        ],
        [
            (/(s)eries$/i),
            "$1eries"
        ],
        [
            (/([^aeiouy]|qu)ies$/i),
            "$1y"
        ],
        [
            (/([lr])ves$/i),
            "$1f"
        ],
        [
            (/(tive)s$/i),
            "$1"
        ],
        [
            (/(hive)s$/i),
            "$1"
        ],
        [
            (/([^f])ves$/i),
            "$1fe"
        ],
        [
            (/(^analy)ses$/i),
            "$1sis"
        ],
        [
            (/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i),
            "$1$2sis"
        ],
        [
            (/([ti])a$/i),
            "$1um"
        ],
        [
            (/(n)ews$/i),
            "$1ews"
        ],
        [
            (/(p)eople$/i),
            "$1erson"
        ],
        [
            (/s$/i),
            ""
        ]
    ],
    
    uncountable: [
        "sheep",
        "fish",
        "series",
        "species",
        "money",
        "rice",
        "information",
        "equipment",
        "grass",
        "mud",
        "offspring",
        "deer",
        "means"
    ],
    
    singular: function(matcher, replacer) {
        this.singulars.unshift([
            matcher,
            replacer
        ]);
    },
    
    plural: function(matcher, replacer) {
        this.plurals.unshift([
            matcher,
            replacer
        ]);
    },
    
    clearSingulars: function() {
        this.singulars = [];
    },
    
    clearPlurals: function() {
        this.plurals = [];
    },
    
    isTransnumeral: function(word) {
        return Ext.Array.indexOf(this.uncountable, word) != -1;
    },
    
    pluralize: function(word) {
        if (this.isTransnumeral(word)) {
            return word;
        }
        var plurals = this.plurals,
            length = plurals.length,
            tuple, regex, i;
        for (i = 0; i < length; i++) {
            tuple = plurals[i];
            regex = tuple[0];
            if (regex == word || (regex.test && regex.test(word))) {
                return word.replace(regex, tuple[1]);
            }
        }
        return word;
    },
    
    singularize: function(word) {
        if (this.isTransnumeral(word)) {
            return word;
        }
        var singulars = this.singulars,
            length = singulars.length,
            tuple, regex, i;
        for (i = 0; i < length; i++) {
            tuple = singulars[i];
            regex = tuple[0];
            if (regex == word || (regex.test && regex.test(word))) {
                return word.replace(regex, tuple[1]);
            }
        }
        return word;
    },
    
    classify: function(word) {
        return Ext.String.capitalize(this.singularize(word));
    },
    
    ordinalize: function(number) {
        var parsed = parseInt(number, 10),
            mod10 = parsed % 10,
            mod100 = parsed % 100;
        
        if (11 <= mod100 && mod100 <= 13) {
            return number + "th";
        } else {
            switch (mod10) {
                case 1:
                    return number + "st";
                case 2:
                    return number + "nd";
                case 3:
                    return number + "rd";
                default:
                    return number + "th";
            }
        }
    }
}, function() {
    
    var irregulars = {
            alumnus: 'alumni',
            cactus: 'cacti',
            focus: 'foci',
            nucleus: 'nuclei',
            radius: 'radii',
            stimulus: 'stimuli',
            ellipsis: 'ellipses',
            paralysis: 'paralyses',
            oasis: 'oases',
            appendix: 'appendices',
            index: 'indexes',
            beau: 'beaux',
            bureau: 'bureaux',
            tableau: 'tableaux',
            woman: 'women',
            child: 'children',
            man: 'men',
            corpus: 'corpora',
            criterion: 'criteria',
            curriculum: 'curricula',
            genus: 'genera',
            memorandum: 'memoranda',
            phenomenon: 'phenomena',
            foot: 'feet',
            goose: 'geese',
            tooth: 'teeth',
            antenna: 'antennae',
            formula: 'formulae',
            nebula: 'nebulae',
            vertebra: 'vertebrae',
            vita: 'vitae'
        },
        singular;
    for (singular in irregulars) {
        if (irregulars.hasOwnProperty(singular)) {
            this.plural(singular, irregulars[singular]);
            this.singular(irregulars[singular], singular);
        }
    }
});


Ext.define('Ext.data.schema.Namer', {
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    requires: [
        'Ext.util.Inflector'
    ],
    alias: 'namer.default',
    
    isNamer: true,
    
    
    capitalize: function(name) {
        return Ext.String.capitalize(name);
    },
    
    fieldRole: function(name) {
        var match = name.match(this.endsWithIdRe, '');
        if (match) {
            name = name.substr(0, name.length - (match[1] || match[2]).length);
        }
        return this.apply('uncapitalize', name);
    },
    idField: function(name) {
        
        return this.apply('uncapitalize,singularize', name) + 'Id';
    },
    instanceName: function(roleName) {
        return this.apply('underscore', roleName);
    },
    multiRole: function(name) {
        return this.apply('undotted,uncapitalize,pluralize', name);
    },
    pluralize: function(name) {
        return Ext.util.Inflector.pluralize(name);
    },
    readerRoot: function(roleName) {
        return this.apply('uncapitalize', roleName);
    },
    singularize: function(name) {
        return Ext.util.Inflector.singularize(name);
    },
    storeName: function(roleName) {
        return this.apply('underscore', roleName);
    },
    uncapitalize: function(name) {
        return Ext.String.uncapitalize(name);
    },
    underscore: function(name) {
        return '_' + name;
    },
    uniRole: function(name) {
        return this.apply('undotted,uncapitalize,singularize', name);
    },
    undotted: function(name) {
        if (name.indexOf('.') < 0) {
            return name;
        }
        var parts = name.split('.'),
            index = parts.length;
        while (index-- > 1) {
            parts[index] = this.apply('capitalize', parts[index]);
        }
        return parts.join('');
    },
    
    
    getterName: function(role) {
        var name = role.role;
        if (role && role.isMany) {
            
            return name;
        }
        
        return 'get' + this.apply('capitalize', name);
    },
    inverseFieldRole: function(leftType, unique, rightRole, rightType) {
        
        
        
        
        var me = this,
            leftRole = me.apply(unique ? 'uniRole' : 'multiRole', leftType),
            s1 = me.apply('pluralize', rightRole),
            s2 = me.apply('undotted,pluralize', rightType);
        if (s1.toLowerCase() !== s2.toLowerCase()) {
            
            
            
            leftRole = rightRole + me.apply('capitalize', leftRole);
        }
        return leftRole;
    },
    manyToMany: function(relation, leftType, rightType) {
        var me = this,
            
            ret = me.apply('undotted,capitalize,singularize', leftType) + me.apply('undotted,capitalize,pluralize', rightType);
        if (relation) {
            ret = me.apply('capitalize', relation + ret);
        }
        return ret;
    },
    
    manyToOne: function(leftType, leftRole, rightType, rightRole) {
        
        
        return this.apply('capitalize,singularize', rightType) + this.apply('capitalize', leftRole);
    },
    matrixRole: function(relation, entityType) {
        var ret = this.apply(relation ? 'multiRole,capitalize' : 'multiRole', entityType);
        return relation ? relation + ret : ret;
    },
    oneToOne: function(leftType, leftRole, rightType, rightRole) {
        return this.apply('undotted,capitalize,singularize', rightType) + this.apply('capitalize', leftRole);
    },
    setterName: function(role) {
        return 'set' + this.apply('capitalize', role.role);
    },
    
    
    endsWithIdRe: /(?:(_id)|[^A-Z](Id))$/,
    cache: {},
    apply: function(operation, name) {
        var me = this,
            cache = me.cache,
            entry = cache[name] || (cache[name] = {}),
            ret = entry[operation],
            i, length, operations;
        if (!ret) {
            if (operation.indexOf(',') < 0) {
                ret = me[operation](name);
            } else {
                length = (operations = operation.split(',')).length;
                ret = name;
                for (i = 0; i < length; ++i) {
                    ret = me.apply(operations[i], ret);
                }
            }
            entry[operation] = ret;
        }
        return ret;
    }
});


Ext.define('Ext.data.schema.Schema', {
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    requires: [
        'Ext.util.ObjectTemplate',
        'Ext.data.schema.OneToOne',
        'Ext.data.schema.ManyToOne',
        'Ext.data.schema.ManyToMany',
        'Ext.data.schema.Namer'
    ],
    alias: 'schema.default',
    
    aliasPrefix: 'schema.',
    isSchema: true,
    
    type: 'default',
    statics: {
        
        instances: {},
        
        get: function(config) {
            var Schema = this,
                cache = Schema.instances,
                id = 'default',
                isString = config && Ext.isString(config),
                instance, newConfig;
            if (config) {
                if (config.isSchema) {
                    return config;
                }
                id = isString ? config : (config.id || id);
            }
            if (!(instance = cache[id])) {
                cache[id] = instance = Schema.create(config);
                instance.id = id;
            } else if (config && !isString) {
                if (id !== 'default') {
                    Ext.Error.raise('Only the default Schema instance can be reconfigured');
                }
                
                
                
                
                
                
                newConfig = Ext.merge({}, instance.config);
                Ext.merge(newConfig, config);
                instance.setConfig(newConfig);
                instance.config = newConfig;
                instance.setConfig = function() {
                    Ext.Error.raise('The schema can only be reconfigured once');
                };
            }
            return instance;
        },
        lookupEntity: function(entity) {
            var ret = null,
                instances = this.instances,
                match, name, schema;
            if (entity) {
                if (entity.isEntity) {
                    ret = entity.self;
                }
                
                else if (Ext.isFunction(entity)) {
                    
                    ret = entity;
                } else if (Ext.isString(entity)) {
                    ret = Ext.ClassManager.get(entity);
                    
                    if (ret && (!ret.prototype || !ret.prototype.isEntity)) {
                        ret = null;
                    }
                    if (!ret) {
                        for (name in instances) {
                            schema = instances[name];
                            match = schema.getEntity(entity);
                            if (match) {
                                if (ret) {
                                    Ext.Error.raise('Ambiguous entity name "' + entity + '". Defined by schema "' + ret.schema.type + '" and "' + name + '"');
                                }
                                ret = match;
                            }
                        }
                    }
                    if (!ret) {
                        Ext.Error.raise('No such Entity "' + entity + '".');
                    }
                }
            }
            return ret;
        }
    },
    
    assocCount: 0,
    
    entityCount: 0,
    config: {
        
        defaultIdentifier: null,
        
        keyCheckDelay: 10,
        
        namer: 'default',
        
        namespace: null,
        
        proxy: {
            type: 'ajax',
            url: '{prefix}/{entityName}'
        },
        
        urlPrefix: ''
    },
    onClassExtended: function(cls, data) {
        var alias = data.alias;
        if (alias && !data.type) {
            if (!Ext.isString(alias)) {
                alias = alias[0];
            }
            cls.prototype.type = alias.substring(this.prototype.aliasPrefix.length);
        }
    },
    constructor: function(config) {
        this.initConfig(config);
        this.clear();
    },
    
    
    
    applyDefaultIdentifier: function(identifier) {
        return identifier && Ext.Factory.dataIdentifier(identifier);
    },
    applyNamer: function(namer) {
        var ret = Ext.data.schema.Namer.create(namer);
        ret.schema = this;
        return ret;
    },
    applyNamespace: function(namespace) {
        if (namespace) {
            var end = namespace.length - 1;
            if (namespace.charAt(end) !== '.') {
                namespace += '.';
            }
        }
        return namespace;
    },
    applyProxy: function(proxy) {
        return Ext.util.ObjectTemplate.create(proxy);
    },
    
    
    
    eachAssociation: function(fn, scope) {
        var associations = this.associations,
            name;
        for (name in associations) {
            if (associations.hasOwnProperty(name)) {
                if (fn.call(scope, name, associations[name]) === false) {
                    break;
                }
            }
        }
    },
    eachEntity: function(fn, scope) {
        var entities = this.entities,
            name;
        for (name in entities) {
            if (entities.hasOwnProperty(name)) {
                if (fn.call(scope, name, entities[name].cls) === false) {
                    break;
                }
            }
        }
    },
    
    getAssociation: function(name) {
        var entry = this.associations[name];
        return entry || null;
    },
    
    getEntity: function(name) {
        var entry = this.entityClasses[name] || this.entities[name];
        return (entry && entry.cls) || null;
    },
    
    getEntityName: function(cls) {
        var ns = this.getNamespace(),
            index, name;
        if (typeof cls === 'string') {
            name = cls;
        } else {
            name = cls.$className || null;
        }
        if (name) {
            
            if (ns) {
                index = ns.length;
                if (name.substring(0, index) !== ns) {
                    return name;
                }
            }
            if (index) {
                name = name.substring(index);
            }
        }
        return name;
    },
    
    hasAssociations: function(name) {
        name = name.entityName || name;
        return !!this.associationEntityMap[name];
    },
    
    hasEntity: function(entity) {
        var name = this.getEntityName(entity);
        return !!(this.entities[name] || this.entityClasses[name]);
    },
    
    
    
    addMatrix: function(entityType, matrixName, relation, left, right) {
        var me = this,
            namer = me.getNamer(),
            associations = me.associations,
            entities = me.entities,
            leftType = left.type,
            rightType = right.type,
            leftField = left.field || namer.apply('idField', leftType),
            rightField = right.field || namer.apply('idField', rightType),
            leftRole = left.role || namer.matrixRole(relation, leftType),
            rightRole = right.role || namer.matrixRole(relation, rightType),
            matrix, leftEntry, rightEntry;
        leftEntry = entities[leftType] || (entities[leftType] = {
            cls: null,
            name: leftType,
            associations: {}
        });
        rightEntry = entities[rightType] || (entities[rightType] = {
            cls: null,
            name: rightType,
            associations: {}
        });
        ++me.assocCount;
        associations[matrixName] = matrix = new Ext.data.schema.ManyToMany({
            name: matrixName,
            schema: me,
            definedBy: entityType,
            left: {
                cls: leftEntry.cls,
                type: leftType,
                role: leftRole,
                field: leftField,
                associationKey: left.associationKey
            },
            right: {
                cls: rightEntry.cls,
                type: rightType,
                role: rightRole,
                field: rightField,
                associationKey: right.associationKey
            }
        });
        leftEntry.associations[matrix.right.role] = matrix.right;
        rightEntry.associations[matrix.left.role] = matrix.left;
        if (leftEntry.cls) {
            me.associationEntityMap[leftEntry.cls.entityName] = true;
        }
        if (rightEntry.cls) {
            me.associationEntityMap[rightEntry.cls.entityName] = true;
        }
        me.decorateModel(matrix);
    },
    
    addReference: function(entityType, referenceField, descr, unique) {
        var me = this,
            namer = me.getNamer(),
            entities = me.entities,
            associations = me.associations,
            entityName = entityType.entityName,
            association = descr.association,
            legacy = !!descr.legacy,
            child = descr.child,
            parent = descr.parent,
            rightRole = descr.role,
            
            rightType = descr.type || parent || child,
            leftVal = descr.inverse,
            left = Ext.isString(leftVal) ? {
                role: leftVal
            } : leftVal,
            leftRole = left && left.role,
            entry, T;
        if (!rightRole) {
            
            
            
            if (legacy) {
                rightRole = namer.apply('uncapitalize', rightType);
            } else {
                rightRole = namer.apply('fieldRole', referenceField.name);
            }
        }
        if (!leftRole) {
            leftRole = namer.inverseFieldRole(entityName, unique, rightRole, rightType);
        }
        if (!association) {
            if (unique) {
                association = namer.oneToOne(entityType, leftRole, rightType, rightRole);
            } else {
                association = namer.manyToOne(entityType, leftRole, rightType, rightRole);
            }
        }
        if (association in associations) {
            Ext.Error.raise('Duplicate association: "' + association + '" declared by ' + entityName + (referenceField ? ('.' + referenceField.name) : '') + ' (collides with ' + associations[association].definedBy.entityName + ')');
        }
        if (referenceField && referenceField.definedBy === entities[rightType]) {
            Ext.Error.raise('ForeignKey reference should not be owned by the target model');
        }
        
        
        entry = entities[rightType] || (entities[rightType] = {
            cls: null,
            name: rightType,
            associations: {}
        });
        
        T = unique ? Ext.data.schema.OneToOne : Ext.data.schema.ManyToOne;
        association = new T({
            name: association,
            
            owner: child ? 'left' : (parent ? 'right' : null),
            definedBy: entityType,
            schema: me,
            field: referenceField,
            nullable: referenceField ? !!referenceField.allowBlank : true,
            legacy: descr.legacy,
            left: {
                cls: entityType,
                type: entityName,
                role: leftRole,
                extra: left
            },
            right: {
                cls: entry.cls,
                type: rightType,
                role: rightRole,
                extra: descr
            }
        });
        
        
        
        entityType.associations[rightRole] = association.right;
        entry.associations[leftRole] = association.left;
        if (referenceField) {
            
            
            referenceField.reference = association.right;
            entityType.references.push(referenceField);
        }
        ++me.assocCount;
        me.associationEntityMap[entityName] = true;
        if (entry.cls) {
            me.associationEntityMap[entry.cls.entityName] = true;
        }
        associations[association.name] = association;
        if (association.right.cls) {
            me.decorateModel(association);
        }
    },
    
    
    privates: {
        
        addEntity: function(entityType) {
            var me = this,
                entities = me.entities,
                entityName = entityType.entityName,
                entry = entities[entityName],
                fields = entityType.fields,
                associations, field, i, length, name;
            if (!entry) {
                entities[entityName] = entry = {
                    name: entityName,
                    associations: {}
                };
            } else if (entry.cls) {
                Ext.Error.raise('Duplicate entity name "' + entityName + '": ' + entry.cls.$className + ' and ' + entityType.$className);
            } else {
                associations = entry.associations;
                for (name in associations) {
                    
                    
                    associations[name].inverse.cls = entityType;
                    me.associationEntityMap[entityName] = true;
                    
                    
                    me.decorateModel(associations[name].association);
                }
            }
            entry.cls = entityType;
            entityType.prototype.associations = entityType.associations = entry.associations;
            me.entityClasses[entityType.$className] = entry;
            ++me.entityCount;
            for (i = 0 , length = fields.length; i < length; ++i) {
                field = fields[i];
                if (field.reference) {
                    me.addReferenceDescr(entityType, field);
                }
            }
        },
        
        addMatrices: function(entityType, matrices) {
            var me = this,
                i, length, matrixName;
            if (Ext.isString(matrices)) {
                me.addMatrixDescr(entityType, null, matrices);
            } else if (matrices[0]) {
                
                for (i = 0 , length = matrices.length; i < length; ++i) {
                    me.addMatrixDescr(entityType, null, matrices[i]);
                }
            } else {
                for (matrixName in matrices) {
                    me.addMatrixDescr(entityType, matrixName, matrices[matrixName]);
                }
            }
        },
        
        addMatrixDescr: function(entityType, matrixName, matrixDef) {
            var me = this,
                entityName = entityType.entityName,
                associations = me.associations,
                namer = me.getNamer(),
                left = matrixDef.left,
                right = matrixDef.right,
                last, relation;
            if (Ext.isString(matrixDef)) {
                if (matrixDef.charAt(0) === '#') {
                    
                    
                    left = {
                        type: entityName
                    };
                    
                    right = {
                        type: matrixDef.substring(1)
                    };
                }
                
                else if (matrixDef.charAt(last = matrixDef.length - 1) === '#') {
                    
                    
                    left = {
                        type: matrixDef.substring(0, last)
                    };
                    
                    right = {
                        type: entityName
                    };
                }
                
                else if (namer.apply('multiRole', entityName) < namer.apply('multiRole', matrixDef)) {
                    
                    left = {
                        type: entityName
                    };
                    
                    right = {
                        type: matrixDef
                    };
                } else 
                {
                    
                    left = {
                        type: matrixDef
                    };
                    
                    right = {
                        type: entityName
                    };
                }
            } else 
            {
                Ext.Assert.isString(matrixDef.type, 'No "type" for manyToMany in ' + entityName);
                relation = matrixDef.relation;
                if (left || (!right && namer.apply('multiRole', entityName) < namer.apply('multiRole', matrixDef.type))) {
                    if (!left || left === true) {
                        
                        left = {
                            type: entityName
                        };
                    } else 
                    {
                        
                        left = Ext.apply({
                            type: entityName
                        }, left);
                    }
                    
                    right = matrixDef;
                } else 
                {
                    if (!right || right === true) {
                        
                        right = {
                            type: entityName
                        };
                    } else 
                    {
                        
                        right = Ext.apply({
                            type: entityName
                        }, right);
                    }
                    
                    left = matrixDef;
                }
            }
            
            if (!matrixName) {
                matrixName = namer.manyToMany(relation, left.type, right.type);
            }
            if (!(matrixName in associations)) {
                me.addMatrix(entityType, matrixName, relation, left, right);
            } else 
            
            
            
            
            {
                var entry = associations[matrixName],
                    before = [
                        entry.kind,
                        entry.left.type,
                        entry.left.role,
                        entry.left.field,
                        entry.right.type,
                        entry.right.role,
                        entry.right.field
                    ].join('|');
                
                delete associations[matrixName];
                me.addMatrix(entityType, matrixName, relation, left, right);
                var after = associations[matrixName];
                
                associations[matrixName] = entry;
                entry.left.cls.associations[entry.right.role] = entry.right;
                entry.right.cls.associations[entry.left.role] = entry.left;
                --me.assocCount;
                
                after = [
                    after.kind,
                    after.left.type,
                    after.left.role,
                    after.left.field,
                    after.right.type,
                    after.right.role,
                    after.right.field
                ].join('|');
                if (before != after) {
                    Ext.log.warn(matrixName + '(' + entry.definedBy.entityName + '): ' + before);
                    Ext.log.warn(matrixName + '(' + entityName + '): ' + after);
                    Ext.Error.raise('Conflicting association: "' + matrixName + '" declared by ' + entityName + ' was previously declared by ' + entry.definedBy.entityName);
                }
            }
        },
        
        addReferenceDescr: function(entityType, referenceField) {
            var me = this,
                descr = referenceField.$reference;
            if (Ext.isString(descr)) {
                descr = {
                    type: descr
                };
            } else {
                descr = Ext.apply({}, descr);
            }
            if (descr.legacy) {
                if (descr.single) {
                    me.addLegacySingle(entityType, descr);
                } else {
                    me.addLegacyHasMany(entityType, descr);
                }
            } else {
                me.addReference(entityType, referenceField, descr, referenceField.unique);
            }
        },
        
        
        
        addPending: function(name, entityType, assoc, type) {
            var pending = this.pending;
            if (!pending[name]) {
                pending[name] = [];
            }
            pending[name].push([
                entityType,
                assoc,
                type
            ]);
        },
        addLegacyBelongsTo: function(entityType, assoc) {
            this.addLegacySingle(entityType, assoc);
        },
        addLegacyHasOne: function(entityType, assoc) {
            this.addLegacySingle(entityType, assoc);
        },
        addLegacySingle: function(entityType, assoc) {
            var foreignKey, name, referenceField;
            assoc = this.constructLegacyAssociation(entityType, assoc);
            assoc.single = true;
            name = assoc.type;
            foreignKey = assoc.foreignKey || (name.toLowerCase() + '_id');
            referenceField = entityType.getField(foreignKey);
            if (referenceField) {
                referenceField.$reference = assoc;
            }
            this.addReference(entityType, referenceField, assoc, true);
        },
        addLegacyHasMany: function(entityType, assoc) {
            var me = this,
                entities = me.entities,
                pending = me.pending,
                associationKey = assoc.associationKey,
                cls, name, referenceField, target, foreignKey, assocName;
            assoc = this.constructLegacyAssociation(entityType, assoc);
            name = assoc.type;
            target = entities[name];
            if (target && target.cls) {
                assoc.type = entityType.entityName;
                foreignKey = assoc.foreignKey || (assoc.type.toLowerCase() + '_id');
                cls = target.cls;
                referenceField = cls.getField(foreignKey);
                assoc.inverse = assoc || {};
                assocName = assoc.name;
                if (assocName || associationKey) {
                    if (assocName) {
                        assoc.inverse.role = assocName;
                    }
                    if (associationKey) {
                        assoc.inverse.associationKey = associationKey;
                    }
                }
                if (referenceField) {
                    referenceField.$reference = assoc;
                }
                
                me.addReference(cls, referenceField, assoc, false);
            } else {
                
                if (!pending[name]) {
                    pending[name] = [];
                }
                pending[name].push([
                    entityType,
                    assoc
                ]);
            }
        },
        constructLegacyAssociation: function(entityType, assoc) {
            if (Ext.isString(assoc)) {
                assoc = {
                    model: assoc
                };
            }
            assoc.legacy = true;
            assoc.type = this.getEntityName(assoc.model);
            var name = assoc.associatedName || assoc.name;
            if (name) {
                assoc.role = name;
            }
            return assoc;
        },
        afterLegacyAssociations: function(cls) {
            var pending = this.pending,
                name = cls.entityName,
                mine = pending[name],
                i, len;
            if (mine) {
                for (i = 0 , len = mine.length; i < len; ++i) {
                    this.addLegacyHasMany.apply(this, mine[i]);
                }
                delete pending[name];
            }
        },
        clear: function(clearNamespace) {
            
            var me = this,
                timer = me.timer;
            delete me.setConfig;
            if (timer) {
                window.clearTimeout(timer);
                me.timer = null;
            }
            me.associations = {};
            me.associationEntityMap = {};
            me.entities = {};
            me.entityClasses = {};
            me.pending = {};
            me.assocCount = me.entityCount = 0;
            if (clearNamespace) {
                me.setNamespace(null);
            }
        },
        constructProxy: function(Model) {
            var me = this,
                data = Ext.Object.chain(Model),
                proxy = me.getProxy();
            data.schema = me;
            data.prefix = me.getUrlPrefix();
            return proxy.apply(data);
        },
        applyDecoration: function(role) {
            var me = this,
                
                
                
                
                cls = role.inverse.cls,
                namer = me.getNamer(),
                getterName, setterName, proto;
            
            
            if (cls && !role.decorated) {
                role.decorated = true;
                proto = cls.prototype;
                if (!(getterName = role.getterName)) {
                    role.getterName = getterName = namer.getterName(role);
                }
                proto[getterName] = role.createGetter();
                
                if (role.createSetter) {
                    if (!(setterName = role.setterName)) {
                        role.setterName = setterName = namer.setterName(role);
                    }
                    proto[setterName] = role.createSetter();
                }
            }
        },
        decorateModel: function(association) {
            this.applyDecoration(association.left);
            this.applyDecoration(association.right);
        },
        processKeyChecks: function(processAll) {
            var me = this,
                keyCheckQueue = me.keyCheckQueue,
                timer = me.timer,
                len, i, item;
            if (timer) {
                window.clearTimeout(timer);
                me.timer = null;
            }
            if (!keyCheckQueue) {
                return;
            }
            
            
            
            
            do {
                keyCheckQueue = me.keyCheckQueue;
                me.keyCheckQueue = [];
                for (i = 0 , len = keyCheckQueue.length; i < len; ++i) {
                    item = keyCheckQueue[i];
                    item.role.checkKeyForDrop(item.record);
                }
            } while (processAll && me.keyCheckQueue.length);
        },
        queueKeyCheck: function(record, role) {
            var me = this,
                keyCheckQueue = me.keyCheckQueue,
                timer = me.timer;
            if (!keyCheckQueue) {
                me.keyCheckQueue = keyCheckQueue = [];
            }
            keyCheckQueue.push({
                record: record,
                role: role
            });
            if (!timer) {
                me.timer = timer = Ext.Function.defer(me.processKeyChecks, me.getKeyCheckDelay(), me);
            }
        },
        rankEntities: function() {
            var me = this,
                entities = me.entities,
                entityNames = Ext.Object.getKeys(entities),
                length = entityNames.length,
                entityType, i;
            me.nextRank = 1;
            
            entityNames.sort();
            for (i = 0; i < length; ++i) {
                entityType = entities[entityNames[i]].cls;
                if (!entityType.rank) {
                    me.rankEntity(entityType);
                }
            }
            me.topoStack = null;
        },
        
        rankEntity: function(entityType) {
            var associations = entityType.associations,
                associatedType, role, roleName;
            var topoStack = this.topoStack || (this.topoStack = []),
                entityName = entityType.entityName;
            topoStack.push(entityName);
            if (entityType.rank === 0) {
                Ext.Error.raise(entityName + " has circular foreign-key references: " + topoStack.join(" --> "));
            }
            entityType.rank = 0;
            
            for (roleName in associations) {
                role = associations[roleName];
                
                
                
                
                
                if (!role.left && role.association.field) {
                    
                    
                    associatedType = role.cls;
                    if (!associatedType.rank) {
                        this.rankEntity(associatedType);
                    }
                }
            }
            entityType.rank = this.nextRank++;
            topoStack.pop();
        }
    }
});


Ext.define('Ext.data.AbstractStore', {
    mixins: [
        'Ext.mixin.Observable',
        'Ext.mixin.Factoryable'
    ],
    requires: [
        'Ext.util.Collection',
        'Ext.data.schema.Schema',
        'Ext.util.Filter'
    ],
    factoryConfig: {
        defaultType: 'store',
        type: 'store'
    },
    $configPrefixed: false,
    $configStrict: false,
    config: {
        autoFilter: true,
        autoSort: true,
        
        filters: undefined,
        
        autoDestroy: undefined,
        
        storeId: null,
        
        statefulFilters: false,
        
        sorters: undefined,
        
        remoteSort: false,
        
        remoteFilter: false,
        
        groupField: undefined,
        
        groupDir: 'ASC',
        
        grouper: null,
        
        pageSize: 25
    },
    
    currentPage: 1,
    
    loading: false,
    
    isDestroyed: false,
    
    isStore: true,
    
    updating: 0,
    
    constructor: function(config) {
        var me = this,
            storeId;
        
        
        
        
        
        
        
        
        
        
        me.isInitializing = true;
        me.mixins.observable.constructor.call(me, config);
        me.isInitializing = false;
        storeId = me.getStoreId();
        if (!storeId && (config && config.id)) {
            me.setStoreId(storeId = config.id);
        }
        if (storeId) {
            Ext.data.StoreManager.register(me);
        }
    },
    
    getCount: function() {
        return this.getData().getCount();
    },
    
    rangeCached: function(start, end) {
        return this.getData().getCount() >= Math.max(start, end);
    },
    
    
    find: function(property, value, startIndex, anyMatch, caseSensitive, exactMatch) {
        
        
        
        
        
        var startsWith = !anyMatch,
            endsWith = !!(startsWith && exactMatch);
        return this.getData().findIndex(property, value, startIndex, startsWith, endsWith, !caseSensitive);
    },
    
    findRecord: function() {
        var me = this,
            index = me.find.apply(me, arguments);
        return index !== -1 ? me.getAt(index) : null;
    },
    
    findExact: function(property, value, start) {
        return this.getData().findIndexBy(function(rec) {
            return rec.isEqual(rec.get(property), value);
        }, this, start);
    },
    
    findBy: function(fn, scope, start) {
        return this.getData().findIndexBy(fn, scope, start);
    },
    
    getAt: function(index) {
        return this.getData().getAt(index) || null;
    },
    
    getRange: function(start, end, 
    options) {
        
        var result = this.getData().getRange(start, Ext.isNumber(end) ? end + 1 : end);
        
        
        if (options && options.callback) {
            options.callback.call(options.scope || this, result, start, end, options);
        }
        return result;
    },
    applyFilters: function(filters, filtersCollection) {
        if (!filtersCollection) {
            filtersCollection = this.createFiltersCollection();
        }
        filtersCollection.add(filters);
        return filtersCollection;
    },
    applySorters: function(sorters, sortersCollection) {
        if (!sortersCollection) {
            sortersCollection = this.createSortersCollection();
        }
        sortersCollection.add(sorters);
        return sortersCollection;
    },
    updateAutoFilter: function(autoFilter) {
        var data = this.getData();
        if (data.setAutoFilter) {
            
            
            data.setAutoFilter(autoFilter);
        }
    },
    updateAutoSort: function(autoSort) {
        var data = this.getData();
        if (data.setAutoSort) {
            
            
            data.setAutoSort(autoSort);
        }
    },
    filter: function(filters, value, supressEvent) {
        if (Ext.isString(filters)) {
            filters = {
                property: filters,
                value: value
            };
        }
        this.suppressNextFilter = !!supressEvent;
        this.getFilters().add(filters);
        this.suppressNextFilter = false;
    },
    
    removeFilter: function(filter, supressEvent) {
        var me = this,
            filters = me.getFilters();
        me.suppressNextFilter = !!supressEvent;
        if (filter instanceof Ext.util.Filter) {
            filters.remove(filter);
        } else {
            filters.removeByKey(filter);
        }
        me.suppressNextFilter = false;
    },
    updateRemoteSort: function(remote) {
        var me = this,
            sorters = me.getSorters(),
            
            data = me.getData();
        
        
        if (remote) {
            sorters.on('endupdate', me.onSorterEndUpdate, me);
            data.un('beforesort', me.onBeforeCollectionSort, me);
        } else 
        
        {
            sorters.un('endupdate', me.onSorterEndUpdate, me);
            data.on('beforesort', me.onBeforeCollectionSort, me);
        }
    },
    updateRemoteFilter: function(remote) {
        var me = this,
            filters = me.getFilters();
        
        if (remote) {
            filters.on('endupdate', me.onFilterEndUpdate, me);
        } else {
            filters.un('endupdate', me.onFilterEndUpdate, me);
        }
    },
    
    addFilter: function(filters, supressEvent) {
        this.suppressNextFilter = !!supressEvent;
        this.getFilters().add(filters);
        this.suppressNextFilter = false;
    },
    
    filterBy: function(fn, scope) {
        this.getFilters().add({
            filterFn: fn,
            scope: scope || this
        });
    },
    
    clearFilter: function(supressEvent) {
        var me = this,
            filters = me.getFilters(false);
        if (!filters || filters.getCount() === 0) {
            return;
        }
        me.suppressNextFilter = !!supressEvent;
        filters.removeAll();
        me.suppressNextFilter = false;
    },
    
    isFiltered: function() {
        return this.getFilters().getCount() > 0;
    },
    
    isSorted: function() {
        return this.getSorters().getCount() > 0 || this.isGrouped();
    },
    addFieldTransform: function(sorter) {
        
        if (sorter.getTransform()) {
            return;
        }
        var fieldName = sorter.getProperty(),
            Model = this.getModel(),
            field, sortType;
        if (Model) {
            field = Model.getField(fieldName);
            sortType = field ? field.getSortType() : null;
        }
        if (sortType && sortType !== Ext.identityFn) {
            sorter.setTransform(sortType);
        }
    },
    
    beginUpdate: function() {
        if (!this.updating++) {
            this.fireEvent('beginupdate');
        }
    },
    
    endUpdate: function() {
        if (this.updating && !--this.updating) {
            this.fireEvent('endupdate');
            this.onEndUpdate();
        }
    },
    
    getState: function() {
        var me = this,
            sorters = [],
            filters = me.getFilters(),
            grouper = me.getGrouper(),
            filterState, hasState, storeFilters, result;
        
        me.getSorters().each(function(s) {
            sorters[sorters.length] = s.getState();
            hasState = true;
        });
        
        
        if (me.statefulFilters && me.saveStatefulFilters) {
            
            
            
            hasState = true;
            filterState = [];
            filters.each(function(f) {
                filterState[filterState.length] = f.getState();
            });
        }
        if (grouper) {
            hasState = true;
        }
        
        if (hasState) {
            result = {};
            if (sorters.length) {
                result.sorters = sorters;
            }
            if (filterState) {
                result.filters = filterState;
            }
            if (grouper) {
                result.grouper = grouper.getState();
            }
        }
        return result;
    },
    
    applyState: function(state) {
        var me = this,
            sorters = me.getSorters(),
            filters = me.getFilters(),
            stateSorters = state.sorters,
            stateFilters = state.filters,
            stateGrouper = state.grouper;
        if (stateSorters) {
            sorters.replaceAll(stateSorters);
        }
        if (stateFilters) {
            
            me.saveStatefulFilters = true;
            filters.replaceAll(stateFilters);
        }
        if (stateGrouper) {
            this.setGrouper(stateGrouper);
        }
    },
    
    
    hasPendingLoad: Ext.emptyFn,
    
    isLoaded: Ext.emptyFn,
    
    isLoading: Ext.emptyFn,
    destroy: function() {
        var me = this;
        me.clearListeners();
        if (me.getStoreId()) {
            Ext.data.StoreManager.unregister(me);
        }
        me.onDestroy();
        me.callParent();
    },
    
    sort: function(field, direction, mode) {
        var me = this;
        if (arguments.length === 0) {
            if (me.getRemoteSort()) {
                me.attemptLoad();
            } else {
                me.forceLocalSort();
            }
        } else {
            me.getSorters().addSort(field, direction, mode);
        }
    },
    
    
    onBeforeCollectionSort: function(store, sorters) {
        this.fireEvent('beforesort', this, sorters.getRange());
    },
    onSorterEndUpdate: function() {
        var me = this,
            sorters;
        
        if (me.settingGroups) {
            return;
        }
        sorters = me.getSorters().getRange();
        
        if (sorters.length) {
            if (me.getRemoteSort()) {
                me.attemptLoad({
                    callback: function() {
                        me.fireEvent('sort', me, sorters);
                    }
                });
            } else {
                
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
                me.fireEvent('sort', me, sorters);
            }
        } else {
            
            me.fireEvent('sort', me, sorters);
        }
    },
    onFilterEndUpdate: function() {
        var me = this,
            suppressNext = me.suppressNextFilter;
        if (me.getAutoFilter()) {
            if (me.getRemoteFilter()) {
                me.getFilters().each(function(filter) {
                    if (filter.getInitialConfig().filterFn) {
                        Ext.Error.raise('Unable to use a filtering function in conjunction with remote filtering.');
                    }
                });
                me.currentPage = 1;
                if (!suppressNext) {
                    me.attemptLoad();
                }
            } else {
                if (!suppressNext) {
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                }
            }
        }
        if (me.trackStateChanges) {
            
            me.saveStatefulFilters = true;
        }
        
        me.fireEvent('filterchange', me, me.getFilters().getRange());
    },
    updateGroupField: function(field) {
        if (field) {
            this.setGrouper({
                property: field,
                direction: this.getGroupDir()
            });
        } else {
            this.setGrouper(null);
        }
    },
    getGrouper: function() {
        return this.getData().getGrouper();
    },
    
    group: function(grouper, direction) {
        var me = this,
            change = grouper || me.getSorters().getCount() > 0;
        if (grouper && typeof grouper === 'string') {
            grouper = {
                property: grouper,
                direction: direction || me.getGroupDir()
            };
        }
        me.settingGroups = true;
        me.getData().setGrouper(grouper);
        delete me.settingGroups;
        if (me.isLoadBlocked()) {
            return;
        }
        if (change) {
            if (me.getRemoteSort()) {
                me.attemptLoad({
                    scope: me,
                    callback: me.fireGroupChange
                });
            } else {
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
                me.fireGroupChange();
            }
        } else 
        
        {
            me.fireGroupChange();
        }
    },
    fireGroupChange: function() {
        this.fireEvent('groupchange', this, this.getGrouper());
    },
    
    clearGrouping: function() {
        this.group(null);
    },
    getGroupField: function() {
        var grouper = this.getGrouper(),
            group = '';
        if (grouper) {
            group = grouper.getProperty();
        }
        return group;
    },
    
    isGrouped: function() {
        return !!this.getGrouper();
    },
    applyGrouper: function(grouper) {
        this.group(grouper);
        return this.getData().getGrouper();
    },
    
    getGroups: function() {
        return this.getData().getGroups();
    },
    onEndUpdate: Ext.emptyFn,
    privates: {
        loadsSynchronously: Ext.privateFn
    },
    deprecated: {
        5: {
            methods: {
                destroyStore: function() {
                    this.destroy();
                }
            }
        }
    }
});


Ext.define('Ext.data.Error', {
    isError: true,
    $configPrefixed: false,
    
    config: {
        
        field: null,
        
        message: ''
    },
    constructor: function(config) {
        this.initConfig(config);
        this.msg = this.message;
    }
});


Ext.define('Ext.data.ErrorCollection', {
    extend: 'Ext.util.MixedCollection',
    
    alternateClassName: 'Ext.data.Errors',
    requires: [
        'Ext.data.Error'
    ],
    init: function(record) {
        var me = this,
            fields = record.fields,
            data = record.data,
            before, field, item, i, len, msg, val, name;
        for (i = 0 , len = fields.length; i < len; ++i) {
            field = fields[i];
            name = field.name;
            val = data[name];
            if (field.validate && !field.validate.$nullFn) {
                before = me.length;
                msg = field.validate(val, null, me);
                if (before === me.length && msg !== true) {
                    me.add(name, msg);
                }
            }
        }
        return me;
    },
    add: function(key, value) {
        var me = this,
            defaultMessage = Ext.data.field.Field.defaultInvalidMessage,
            obj = key,
            
            current;
        if (Ext.isString(key)) {
            obj = new Ext.data.Error({
                field: key,
                message: value || defaultMessage
            });
        } else {
            if (!(obj.isError)) {
                obj = new Ext.data.Error({
                    field: obj.field || obj.name,
                    message: obj.error || obj.message || obj.msg || defaultMessage
                });
            }
            key = obj.field;
        }
        current = me.get(key);
        if (current) {
            if (Ext.isArray(current)) {
                current.push(obj);
                return current;
            }
            me.removeAtKey(key);
            obj = [
                current,
                obj
            ];
            obj.field = key;
            
            
            obj = [
                obj
            ];
        }
        return me.callParent([
            obj
        ]);
    },
    getKey: function(item) {
        return item.field;
    },
    
    isValid: function() {
        return this.length === 0;
    },
    
    getByField: function(fieldName) {
        var values = this.get(fieldName);
        if (values && !Ext.isArray(values)) {
            values = [
                values
            ];
        }
        return values || [];
    }
});


Ext.define('Ext.data.operation.Operation', {
    alternateClassName: 'Ext.data.Operation',
    isOperation: true,
    config: {
        
        synchronous: false,
        
        url: '',
        
        params: undefined,
        
        callback: undefined,
        
        scope: undefined,
        
        resultSet: null,
        
        response: null,
        
        request: null,
        
        records: null,
        
        id: undefined,
        
        proxy: null,
        
        batch: null,
        
        recordCreator: null,
        
        
        
        
        internalCallback: null,
        
        internalScope: null
    },
    
    order: 0,
    
    foreignKeyDirection: 1,
    
    started: false,
    
    running: false,
    
    complete: false,
    
    success: undefined,
    
    exception: false,
    
    error: undefined,
    
    constructor: function(config) {
        
        
        
        
        
        
        var scope = config && config.scope;
        this.initConfig(config);
        if (config) {
            config.scope = scope;
        }
        if (scope) {
            this.setScope(scope);
            this.initialConfig.scope = scope;
        }
    },
    getAction: function() {
        return this.action;
    },
    
    execute: function() {
        var me = this;
        delete me.error;
        delete me.success;
        me.complete = me.exception = false;
        me.setStarted();
        return me.request = me.doExecute();
    },
    doExecute: Ext.emptyFn,
    
    abort: function() {
        var me = this,
            request = me.request;
        if (me.running && request) {
            me.getProxy().abort(request);
            me.request = null;
        }
    },
    process: function(resultSet, request, response, autoComplete) {
        var me = this;
        autoComplete = autoComplete !== false;
        me.setResponse(response);
        me.setResultSet(resultSet);
        if (resultSet.getSuccess()) {
            me.doProcess(resultSet, request, response);
            me.setSuccessful(autoComplete);
        } else if (autoComplete) {
            me.setException(resultSet.getMessage());
        }
    },
    
    
    
    _commitSetOptions: {
        convert: true,
        commit: true
    },
    
    doProcess: function(resultSet, request, response) {
        var me = this,
            commitSetOptions = me._commitSetOptions,
            clientRecords = me.getRecords(),
            clientLen = clientRecords.length,
            clientIdProperty = clientRecords[0].clientIdProperty,
            serverRecords = resultSet.getRecords(),
            
            serverLen = serverRecords ? serverRecords.length : 0,
            clientMap, serverRecord, clientRecord, i;
        if (serverLen && clientIdProperty) {
            
            clientMap = Ext.Array.toValueMap(clientRecords, 'id');
            
            
            for (i = 0; i < serverLen; ++i) {
                serverRecord = serverRecords[i];
                clientRecord = clientMap[serverRecord[clientIdProperty]];
                if (clientRecord) {
                    
                    delete clientMap[clientRecord.id];
                    
                    delete serverRecord[clientIdProperty];
                    clientRecord.set(serverRecord, commitSetOptions);
                } else 
                {
                    Ext.log.warn('Ignoring server record: ' + Ext.encode(serverRecord));
                }
            }
            
            for (i in clientMap) {
                clientMap[i].commit();
            }
        } else {
            
            
            
            for (i = 0; i < clientLen; ++i) {
                clientRecord = clientRecords[i];
                if (serverLen === 0 || !(serverRecord = serverRecords[i])) {
                    
                    clientRecord.commit();
                } else {
                    clientRecord.set(serverRecord, commitSetOptions);
                }
            }
        }
    },
    
    setStarted: function() {
        this.started = this.running = true;
    },
    
    setCompleted: function() {
        this.complete = true;
        this.running = false;
        this.triggerCallbacks();
    },
    
    setSuccessful: function(complete) {
        this.success = true;
        if (complete) {
            this.setCompleted();
        }
    },
    
    setException: function(error) {
        var me = this;
        me.exception = true;
        me.success = me.running = false;
        me.error = error;
        me.setCompleted();
    },
    triggerCallbacks: function() {
        var me = this,
            callback = me.getInternalCallback();
        
        if (callback) {
            callback.call(me.getInternalScope() || me, me);
            me.setInternalCallback(null);
            me.setInternalScope(null);
        }
        
        if (callback = me.getCallback()) {
            
            callback.call(me.getScope() || me, me.getRecords(), me, me.wasSuccessful());
            me.setCallback(null);
            me.setScope(null);
        }
    },
    
    hasException: function() {
        return this.exception;
    },
    
    getError: function() {
        return this.error;
    },
    
    getRecords: function() {
        var resultSet;
        return this._records || ((resultSet = this.getResultSet()) ? resultSet.getRecords() : null);
    },
    
    isStarted: function() {
        return this.started;
    },
    
    isRunning: function() {
        return this.running;
    },
    
    isComplete: function() {
        return this.complete;
    },
    
    wasSuccessful: function() {
        return this.isComplete() && this.success === true;
    },
    
    
    allowWrite: function() {
        return true;
    }
});


Ext.define('Ext.data.operation.Create', {
    extend: 'Ext.data.operation.Operation',
    alias: 'data.operation.create',
    action: 'create',
    isCreateOperation: true,
    order: 10,
    config: {
        recordCreator: Ext.identityFn
    },
    doExecute: function() {
        return this.getProxy().create(this);
    }
});


Ext.define('Ext.data.operation.Destroy', {
    extend: 'Ext.data.operation.Operation',
    alias: 'data.operation.destroy',
    action: 'destroy',
    isDestroyOperation: true,
    order: 30,
    foreignKeyDirection: -1,
    doProcess: function() 
    {
        var clientRecords = this.getRecords(),
            clientLen = clientRecords.length,
            i;
        for (i = 0; i < clientLen; ++i) {
            clientRecords[i].setErased();
        }
    },
    doExecute: function() {
        return this.getProxy().erase(this);
    },
    getRecordData: function(record, operation) {
        var data = {},
            idField = record.idField,
            nameProperty = this.getNameProperty() || 'name';
        data[idField[nameProperty]] = record.id;
        return data;
    }
});


Ext.define('Ext.data.operation.Read', {
    extend: 'Ext.data.operation.Operation',
    alias: 'data.operation.read',
    action: 'read',
    isReadOperation: true,
    config: {
        
        filters: undefined,
        
        sorters: undefined,
        
        grouper: undefined,
        
        start: undefined,
        
        limit: undefined,
        
        page: undefined,
        
        addRecords: false
    },
    doExecute: function() {
        return this.getProxy().read(this);
    },
    doProcess: Ext.emptyFn,
    allowWrite: function() {
        return false;
    }
});


Ext.define('Ext.data.operation.Update', {
    extend: 'Ext.data.operation.Operation',
    alias: 'data.operation.update',
    action: 'update',
    isUpdateOperation: true,
    order: 20,
    config: {
        recordCreator: Ext.identityFn
    },
    doExecute: function() {
        return this.getProxy().update(this);
    }
});


Ext.define('Ext.data.SortTypes', {
    singleton: true,
    
    none: Ext.identityFn,
    
    stripCommasRe: /,/g,
    
    stripTagsRE: /<\/?[^>]+>/gi,
    
    asText: function(s) {
        
        return (s != null) ? String(s).replace(this.stripTagsRe, '') : '\x00';
    },
    
    asUCText: function(s) {
        
        return (s != null) ? String(s).toUpperCase().replace(this.stripTagsRe, '') : '\x00';
    },
    
    asUCString: function(s) {
        
        return (s != null) ? String(s).toUpperCase() : '\x00';
    },
    
    asDate: function(s) {
        if (!s) {
            return 0;
        }
        if (Ext.isDate(s)) {
            return s.getTime();
        }
        return Date.parse(String(s));
    },
    
    asFloat: function(s) {
        var val = parseFloat(String(s).replace(this.stripCommasRe, ''));
        return isNaN(val) ? 0 : val;
    },
    
    asInt: function(s) {
        var val = parseInt(String(s).replace(this.stripCommasRe, ''), 10);
        return isNaN(val) ? 0 : val;
    }
});


Ext.define('Ext.data.validator.Validator', {
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    alias: 'data.validator.base',
    
    isValidator: true,
    
    type: 'base',
    statics: {
        all: {},
        register: function(name, cls) {
            var all = this.all;
            all[name.toUpperCase()] = all[name.toLowerCase()] = all[name] = cls.prototype;
        }
    },
    onClassExtended: function(cls, data) {
        if (data.type) {
            Ext.data.validator.Validator.register(data.type, cls);
        }
    },
    
    constructor: function(config) {
        if (typeof config === 'function') {
            this.fnOnly = true;
            this.validate = config;
        } else {
            this.initConfig(config);
        }
    },
    
    validate: function() {
        return true;
    },
    
    clone: function() {
        var me = this;
        if (me.fnOnly) {
            return new Ext.data.validator.Validator(me.validate);
        }
        return new me.self(me.getCurrentConfig());
    }
}, function() {
    this.register(this.prototype.type, this);
});


Ext.define('Ext.data.field.Field', {
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    requires: [
        'Ext.data.SortTypes',
        'Ext.data.validator.Validator'
    ],
    alternateClassName: 'Ext.data.Field',
    alias: 'data.field.auto',
    
    aliasPrefix: 'data.field.',
    type: 'auto',
    factoryConfig: {
        defaultProperty: 'name'
    },
    isDataField: true,
    isField: true,
    
    
    
    
    
    
    
    allowBlank: true,
    
    allowNull: false,
    
    
    
    critical: false,
    
    defaultInvalidMessage: 'This field is invalid',
    
    defaultValue: undefined,
    
    definedBy: null,
    
    depends: null,
    
    dependents: null,
    
    mapping: null,
    
    name: null,
    
    ordinal: undefined,
    
    persist: null,
    
    reference: null,
    
    
    
    unique: false,
    
    
    rank: null,
    
    stripRe: /[\$,%]/g,
    
    calculated: false,
    
    evil: false,
    
    identifier: false,
    onClassExtended: function(cls, data) {
        var sortType = data.sortType,
            proto = cls.prototype,
            superValidators = proto.validators,
            validators = data.validators;
        if (sortType && Ext.isString(sortType)) {
            proto.sortType = Ext.data.SortTypes[sortType];
        }
        if (validators) {
            
            if (!Ext.isArray(validators)) {
                validators = [
                    validators
                ];
            }
            delete data.validators;
            
            if (superValidators) {
                validators = superValidators.concat(validators);
            }
            proto.validators = validators;
        }
    },
    argumentNamesRe: /^function\s+\(\s*([^,\)\s]+)/,
    calculateRe: /[^\.a-z0-9_]([a-z_][a-z_0-9]*)\.([a-z_][a-z_0-9]*)/gi,
    constructor: function(config) {
        var me = this,
            calculateRe = me.calculateRe,
            calculate, calculated, defaultValue, sortType, depends, map, match, dataProp, str, fld, validators;
        
        
        if (config) {
            if (Ext.isString(config)) {
                me.name = config;
            } else {
                validators = config.validators;
                if (validators) {
                    delete config.validators;
                    me.instanceValidators = validators;
                }
                Ext.apply(me, config);
            }
        }
        if (!me.allowNull) {
            me.allowNull = !!me.reference;
        }
        calculate = me.calculate;
        depends = me.depends;
        if (calculate) {
            me.convert = me.doCalculate;
            if (!depends) {
                if (!(depends = calculate.$depends)) {
                    map = {};
                    str = calculate.toString();
                    calculate.$depends = depends = [];
                    match = me.argumentNamesRe.exec(str);
                    dataProp = match ? match[1] : 'data';
                    while ((match = calculateRe.exec(str))) {
                        if (dataProp === match[1] && !map[fld = match[2]]) {
                            map[fld] = 1;
                            depends.push(fld);
                        }
                    }
                }
                me.depends = depends;
            }
        }
        defaultValue = me.defaultValue;
        if (me.convert) {
            if (defaultValue !== undefined) {
                
                
                me.defaultValue = defaultValue = me.convert(defaultValue);
            }
            me.calculated = calculated = me.convert.length > 1;
            me.evil = calculated && !depends;
        }
        if (me.persist === null) {
            me.persist = !calculated;
        }
        sortType = me.sortType;
        if (!me.sortType) {
            me.sortType = Ext.data.SortTypes.none;
        } else if (Ext.isString(sortType)) {
            me.sortType = Ext.data.SortTypes[sortType];
        }
        if (depends && typeof depends === 'string') {
            me.depends = [
                depends
            ];
        }
        me.cloneDefaultValue = defaultValue !== undefined && (Ext.isDate(defaultValue) || Ext.isArray(defaultValue) || Ext.isObject(defaultValue));
    },
    setModelValidators: function(modelValidators) {
        this._validators = null;
        this.modelValidators = modelValidators;
    },
    compileValidators: function() {
        var me = this;
        me._validators = [];
        me.constructValidators(me.validators);
        me.constructValidators(me.modelValidators);
        me.constructValidators(me.instanceValidators);
    },
    constructValidators: function(validators) {
        if (validators) {
            if (!(validators instanceof Array)) {
                validators = [
                    validators
                ];
            }
            var length = validators.length,
                all = this._validators,
                i, item;
            for (i = 0; i < length; ++i) {
                item = validators[i];
                if (item.fn) {
                    item = item.fn;
                }
                all.push(Ext.Factory.dataValidator(item));
            }
        }
    },
    
    collate: function(value1, value2) {
        var me = this,
            lhs = value1,
            rhs = value2;
        if (me.sortType) {
            lhs = me.sortType(lhs);
            rhs = me.sortType(rhs);
        }
        return (lhs === rhs) ? 0 : ((lhs < rhs) ? -1 : 1);
    },
    
    compare: function(value1, value2) {
        return (value1 === value2) ? 0 : ((value1 < value2) ? -1 : 1);
    },
    
    isEqual: function(value1, value2) {
        return this.compare(value1, value2) === 0;
    },
    
    convert: null,
    
    serialize: null,
    
    validate: function(value, separator, errors) {
        var me = this,
            ret = '',
            result, validator, validators, length, i;
        if (!me._validators) {
            me.compileValidators();
        }
        validators = me._validators;
        for (i = 0 , length = validators.length; i < length; ++i) {
            validator = validators[i];
            result = validator.validate(value);
            
            if (result !== true) {
                result = result || me.defaultInvalidMessage;
                if (errors) {
                    errors.add(me.name, result);
                    ret = ret || result;
                } else if (separator) {
                    if (ret) {
                        ret += separator;
                    }
                    ret += result;
                } else {
                    ret = result;
                    break;
                }
            }
        }
        return ret || true;
    },
    doCalculate: function(v, rec) {
        return rec ? this.calculate(rec.data) : v;
    },
    
    getName: function() {
        return this.name;
    },
    
    getAllowBlank: function() {
        return this.allowBlank;
    },
    
    getAllowNull: function() {
        return this.allowNull;
    },
    
    getConvert: function() {
        return this.convert;
    },
    
    getDefaultValue: function() {
        return this.defaultValue;
    },
    
    getDepends: function() {
        return this.depends;
    },
    
    getMapping: function() {
        return this.mapping;
    },
    
    hasMapping: function() {
        var map = this.mapping;
        return !!(map || map === 0);
    },
    
    getPersist: function() {
        return this.persist;
    },
    
    getSortType: function() {
        return this.sortType;
    },
    
    getType: function() {
        return 'auto';
    },
    deprecated: {
        5.1: {
            methods: {
                
                getSortDir: function() {
                    return this.sortDir;
                }
            }
        }
    }
});


Ext.define('Ext.data.field.Boolean', {
    extend: 'Ext.data.field.Field',
    alias: [
        'data.field.bool',
        'data.field.boolean'
    ],
    isBooleanField: true,
    
    trueRe: /^\s*(?:true|yes|on|1)\s*$/i,
    convert: function(v) {
        if (typeof v === 'boolean') {
            return v;
        }
        if (this.allowNull && (v === undefined || v === null || v === '')) {
            return null;
        }
        return this.trueRe.test(String(v));
    },
    getType: function() {
        return 'bool';
    }
});


Ext.define('Ext.data.field.Date', {
    extend: 'Ext.data.field.Field',
    alias: 'data.field.date',
    sortType: 'asDate',
    isDateField: true,
    
    dateFormat: null,
    
    dateReadFormat: null,
    
    dateWriteFormat: null,
    compare: function(lhs, rhs) {
        var lhsIsDate = lhs instanceof Date,
            rhsIsDate = rhs instanceof Date,
            result;
        if (rhsIsDate && lhsIsDate) {
            result = lhs.getTime() - rhs.getTime();
            if (result === 0) {
                result = 0;
            } else {
                result = result < 0 ? -1 : 1;
            }
        } else if (lhsIsDate === rhsIsDate) {
            result = 0;
        } else {
            result = lhsIsDate ? 1 : -1;
        }
        return result;
    },
    convert: function(v) {
        if (!v) {
            return null;
        }
        
        
        if (v instanceof Date) {
            return v;
        }
        var dateFormat = this.dateReadFormat || this.dateFormat,
            parsed;
        if (dateFormat) {
            return Ext.Date.parse(v, dateFormat);
        }
        parsed = Date.parse(v);
        return parsed ? new Date(parsed) : null;
    },
    serialize: function(value) {
        var result = null,
            format;
        if (Ext.isDate(value)) {
            format = this.getDateWriteFormat();
            result = format ? Ext.Date.format(value, format) : value;
        }
        return result;
    },
    
    getDateFormat: function() {
        return this.dateFormat;
    },
    
    getDateReadFormat: function() {
        return this.dateReadFormat;
    },
    
    getDateWriteFormat: function() {
        var me = this;
        if (me.hasOwnProperty('dateWriteFormat')) {
            return me.dateWriteFormat;
        }
        if (me.hasOwnProperty('dateFormat')) {
            return me.dateFormat;
        }
        return me.dateWriteFormat || me.dateFormat || 'timestamp';
    },
    getType: function() {
        return 'date';
    }
});


Ext.define('Ext.data.field.Integer', {
    extend: 'Ext.data.field.Field',
    alias: [
        'data.field.int',
        'data.field.integer'
    ],
    isNumeric: true,
    isIntegerField: true,
    numericType: 'int',
    convert: function(v) {
        
        
        
        if (typeof v === 'number') {
            return this.getNumber(v);
        }
        var empty = v === undefined || v === null || v === '',
            allowNull = this.allowNull,
            out;
        if (empty) {
            out = allowNull ? null : 0;
        } else {
            out = this.parse(v);
            if (allowNull && isNaN(out)) {
                out = null;
            }
        }
        return out;
    },
    getNumber: function(v) {
        return parseInt(v, 10);
    },
    getType: function() {
        return this.numericType;
    },
    parse: function(v) {
        return parseInt(String(v).replace(this.stripRe, ''), 10);
    },
    sortType: function(s) {
        
        if (s == null) {
            s = Infinity;
        }
        return s;
    }
});


Ext.define('Ext.data.field.Number', {
    extend: 'Ext.data.field.Integer',
    alias: [
        'data.field.float',
        'data.field.number'
    ],
    isIntegerField: false,
    isNumberField: true,
    numericType: 'float',
    getNumber: Ext.identityFn,
    parse: function(v) {
        return parseFloat(String(v).replace(this.stripRe, ''));
    }
});


Ext.define('Ext.data.field.String', {
    extend: 'Ext.data.field.Field',
    alias: 'data.field.string',
    sortType: 'asUCString',
    isStringField: true,
    convert: function(v) {
        var defaultValue = this.allowNull ? null : '';
        return (v === undefined || v === null) ? defaultValue : String(v);
    },
    getType: function() {
        return 'string';
    }
});


Ext.define('Ext.data.identifier.Generator', {
    'abstract': true,
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    alias: 'data.identifier.default',
    
    factoryConfig: {
        defaultType: 'sequential'
    },
    
    
    isGenerator: true,
    config: {
        
        id: null
    },
    
    constructor: function(config) {
        var me = this,
            cache, id;
        me.initConfig(config);
        id = me.getId();
        if (id) {
            cache = (config && config.cache) || Ext.data.identifier.Generator.all;
            cache[id] = me;
        }
    },
    
    privates: {
        
        clone: function(config) {
            var cfg = this.getInitialConfig();
            cfg = config ? Ext.apply({}, config, cfg) : cfg;
            return new this.self(cfg);
        },
        statics: {
            
            all: {}
        }
    }
}, function() {
    var Generator = this,
        Factory = Ext.Factory,
        factory = Factory.dataIdentifier;
    
    
    
    Factory.dataIdentifier = function(config) {
        var id = Ext.isString(config) ? config : (config && config.id),
            existing = id && ((config && config.cache) || Generator.all)[id];
        return existing || factory(config);
    };
});


Ext.define('Ext.data.identifier.Sequential', {
    extend: 'Ext.data.identifier.Generator',
    alias: 'data.identifier.sequential',
    config: {
        
        increment: 1,
        
        prefix: null,
        
        seed: 1
    },
    
    generate: function() {
        var me = this,
            seed = me._seed,
            prefix = me._prefix;
        me._seed += me._increment;
        return (prefix !== null) ? prefix + seed : seed;
    }
});


Ext.define('Ext.data.Model', {
    alternateClassName: 'Ext.data.Record',
    requires: [
        'Ext.data.ErrorCollection',
        'Ext.data.operation.*',
        'Ext.data.field.*',
        'Ext.data.validator.Validator',
        'Ext.data.schema.Schema',
        'Ext.data.identifier.Generator',
        'Ext.data.identifier.Sequential'
    ],
    uses: [
        'Ext.data.Validation'
    ],
    
    isEntity: true,
    
    isModel: true,
    
    validIdRe: null,
    observableType: 'record',
    constructor: function(data, session) {
        var me = this,
            cls = me.self,
            identifier = cls.identifier,
            Model = Ext.data.Model,
            modelIdentifier = Model.identifier,
            idProperty = me.idField.name,
            array, id, initializeFn, internalId, len, i, fields;
        me.data = data || (data = {});
        me.session = session || null;
        me.internalId = internalId = modelIdentifier.generate();
        var dataId = data[idProperty];
        if (session && !session.isSession) {
            Ext.Error.raise('Bad Model constructor argument 2 - "session" is not a Session');
        }
        if ((array = data) instanceof Array) {
            me.data = data = {};
            fields = me.getFields();
            len = Math.min(fields.length, array.length);
            for (i = 0; i < len; ++i) {
                data[fields[i].name] = array[i];
            }
        }
        if (!(initializeFn = cls.initializeFn)) {
            cls.initializeFn = initializeFn = Model.makeInitializeFn(cls);
        }
        if (!initializeFn.$nullFn) {
            cls.initializeFn(me);
        }
        
        if (!(me.id = id = data[idProperty]) && id !== 0) {
            if (dataId) {
                Ext.Error.raise('The model ID configured in data ("' + dataId + '") has been rejected by the ' + me.fieldsMap[idProperty].type + ' field converter for the ' + idProperty + ' field');
            }
            if (session) {
                identifier = session.getIdentifier(cls);
                id = identifier.generate();
            } else if (modelIdentifier === identifier) {
                id = internalId;
            } else {
                id = identifier.generate();
            }
            data[idProperty] = me.id = id;
            me.phantom = true;
        }
        if (session) {
            session.add(me);
        }
        if (me.init && Ext.isFunction(me.init)) {
            me.init();
        }
    },
    
    
    editing: false,
    
    dirty: false,
    
    session: null,
    
    dropped: false,
    
    erased: false,
    
    clientIdProperty: null,
    evented: false,
    
    phantom: false,
    
    idProperty: 'id',
    
    identifier: null,
    
    
    
    
    
    
    previousValues: undefined,
    
    
    
    proxy: undefined,
    
    
    schema: 'default',
    
    versionProperty: null,
    
    generation: 1,
    
    
    validationSeparator: null,
    
    convertOnSet: true,
    
    
    
    
    
    beginEdit: function() {
        var me = this,
            modified = me.modified,
            previousValues = me.previousValues;
        if (!me.editing) {
            me.editing = true;
            me.editMemento = {
                dirty: me.dirty,
                data: Ext.apply({}, me.data),
                generation: me.generation,
                modified: modified && Ext.apply({}, modified),
                previousValues: previousValues && Ext.apply({}, previousValues)
            };
        }
    },
    
    cancelEdit: function() {
        var me = this,
            editMemento = me.editMemento;
        if (editMemento) {
            me.editing = false;
            
            Ext.apply(me, editMemento);
            me.editMemento = null;
        }
    },
    
    endEdit: function(silent, modifiedFieldNames) {
        var me = this,
            editMemento = me.editMemento;
        if (editMemento) {
            me.editing = false;
            me.editMemento = null;
            
            
            me.previousValues = editMemento.previousValues;
            if (!silent) {
                if (!modifiedFieldNames) {
                    modifiedFieldNames = me.getModifiedFieldNames(editMemento.data);
                }
                if (me.dirty || (modifiedFieldNames && modifiedFieldNames.length)) {
                    me.callJoined('afterEdit', [
                        modifiedFieldNames
                    ]);
                }
            }
        }
    },
    getField: function(name) {
        return this.self.getField(name);
    },
    
    getFields: function() {
        return this.self.getFields();
    },
    getFieldsMap: function() {
        return this.fieldsMap;
    },
    
    getIdProperty: function() {
        return this.idProperty;
    },
    
    getId: function() {
        return this.id;
    },
    
    getObservableId: function() {
        return this.internalId;
    },
    
    setId: function(id) {
        this.set(this.idProperty, id);
    },
    
    getPrevious: function(fieldName) {
        var previousValues = this.previousValues;
        return previousValues && previousValues[fieldName];
    },
    
    isModified: function(fieldName) {
        var modified = this.modified;
        return !!(modified && modified.hasOwnProperty(fieldName));
    },
    
    getModified: function(fieldName) {
        var out;
        if (this.isModified(fieldName)) {
            out = this.modified[fieldName];
        }
        return out;
    },
    
    get: function(fieldName) {
        return this.data[fieldName];
    },
    
    
    
    _singleProp: {},
    _rejectOptions: {
        convert: false,
        commit: true,
        silent: true
    },
    
    set: function(fieldName, newValue, options) {
        var me = this,
            cls = me.self,
            data = me.data,
            modified = me.modified,
            prevVals = me.previousValues,
            session = me.session,
            single = Ext.isString(fieldName),
            opt = (single ? options : newValue),
            convertOnSet = opt ? opt.convert !== false : me.convertOnSet,
            fieldsMap = me.fieldsMap,
            silent = opt && opt.silent,
            commit = opt && opt.commit,
            updateRefs = !(opt && opt.refs === false) && session,
            
            
            dirty = !(opt && opt.dirty === false && !commit),
            modifiedFieldNames = null,
            currentValue, field, idChanged, key, name, oldId, comparator, dep, dependents, i,
            dirtyRank = 0,
            numFields, newId, rankedFields, reference, value, values;
        if (single) {
            values = me._singleProp;
            values[fieldName] = newValue;
        } else {
            values = fieldName;
        }
        if (!(rankedFields = cls.rankedFields)) {
            
            
            rankedFields = cls.rankFields();
        }
        numFields = rankedFields.length;
        do {
            for (name in values) {
                value = values[name];
                currentValue = data[name];
                comparator = me;
                field = fieldsMap[name];
                if (field) {
                    if (convertOnSet && field.convert) {
                        value = field.convert(value, me);
                    }
                    comparator = field;
                    reference = field.reference;
                } else {
                    reference = null;
                }
                if (comparator.isEqual(currentValue, value)) {
                    
                    continue;
                }
                
                data[name] = value;
                (modifiedFieldNames || (modifiedFieldNames = [])).push(name);
                (prevVals || (me.previousValues = prevVals = {}))[name] = currentValue;
                
                
                if (reference && reference.cls) {
                    if (updateRefs) {
                        session.updateReference(me, field, value, currentValue);
                    }
                    reference.onValueChange(me, session, value, currentValue);
                }
                i = (dependents = field && field.dependents) && dependents.length;
                while (i-- > 0) {
                    
                    
                    
                    
                    (dep = dependents[i]).dirty = true;
                    dirtyRank = dirtyRank ? Math.min(dirtyRank, dep.rank) : dep.rank;
                }
                if (!field || field.persist) {
                    if (modified && modified.hasOwnProperty(name)) {
                        if (!dirty || comparator.isEqual(modified[name], value)) {
                            
                            
                            delete modified[name];
                            me.dirty = -1;
                        }
                    }
                    
                    else if (dirty) {
                        if (!modified) {
                            me.modified = modified = {};
                        }
                        
                        me.dirty = true;
                        modified[name] = currentValue;
                    }
                }
                if (name === me.idField.name) {
                    idChanged = true;
                    oldId = currentValue;
                    newId = value;
                }
            }
            if (!dirtyRank) {
                
                
                
                break;
            }
            
            
            
            
            
            
            
            field = rankedFields[dirtyRank - 1];
            
            field.dirty = false;
            
            if (single) {
                delete values[fieldName];
            } else 
            {
                values = me._singleProp;
                
                single = true;
            }
            fieldName = field.name;
            values[fieldName] = data[fieldName];
            
            
            convertOnSet = true;
            
            
            
            
            for (; dirtyRank < numFields; ++dirtyRank) {
                if (rankedFields[dirtyRank].dirty) {
                    break;
                }
            }
            if (dirtyRank < numFields) {
                
                
                ++dirtyRank;
            } else {
                
                
                dirtyRank = 0;
            }
        } while (1);
        if (me.dirty < 0) {
            
            
            me.dirty = false;
            for (key in modified) {
                if (modified.hasOwnProperty(key)) {
                    me.dirty = true;
                    break;
                }
            }
        }
        if (single) {
            
            
            delete values[fieldName];
        }
        ++me.generation;
        if (idChanged) {
            me.id = newId;
            me.callJoined('onIdChanged', [
                oldId,
                newId
            ]);
        }
        if (commit) {
            me.commit(silent, modifiedFieldNames);
        } else if (!silent && !me.editing && modifiedFieldNames) {
            me.callJoined('afterEdit', [
                modifiedFieldNames
            ]);
        }
        return modifiedFieldNames;
    },
    
    reject: function(silent) {
        var me = this,
            modified = me.modified;
        me.rejecting = true;
        if (modified) {
            me.set(modified, me._rejectOptions);
        }
        delete me.rejecting;
        me.clearState();
        if (!silent) {
            me.callJoined('afterReject');
        }
    },
    
    commit: function(silent, modifiedFieldNames) {
        var me = this,
            versionProperty = me.versionProperty,
            data = me.data,
            erased;
        me.clearState();
        if (!me.rejecting) {
            if (versionProperty && !me.phantom && !isNaN(data[versionProperty])) {
                ++data[versionProperty];
            }
            me.phantom = false;
        }
        if (me.dropped) {
            me.erased = erased = true;
        }
        if (!silent) {
            if (erased) {
                me.callJoined('afterErase');
            } else {
                me.callJoined('afterCommit', [
                    modifiedFieldNames
                ]);
            }
        }
    },
    clearState: function() {
        var me = this;
        me.dirty = me.editing = false;
        me.editMemento = me.modified = null;
    },
    
    drop: function(cascade) {
        var me = this,
            associations = me.associations,
            session = me.session,
            roleName;
        if (me.erased || me.dropped) {
            return;
        }
        me.dropped = true;
        if (associations && cascade !== false) {
            for (roleName in associations) {
                associations[roleName].onDrop(me, session);
            }
        }
        me.callJoined('afterDrop');
        if (me.phantom) {
            me.setErased();
        }
    },
    
    join: function(item) {
        var me = this,
            joined = me.joined;
        
        if (!joined) {
            joined = me.joined = [
                item
            ];
        } else if (!joined.length) {
            joined[0] = item;
        } else {
            
            Ext.Array.include(joined, item);
        }
        if (item.isStore) {
            
            me.store = me.store || item;
        }
    },
    
    unjoin: function(item) {
        var me = this,
            joined = me.joined,
            
            
            len = joined && joined.length,
            store = me.store,
            i;
        if (len === 1 && joined[0] === item) {
            joined.length = 0;
        } else if (len) {
            Ext.Array.remove(joined, item);
        }
        if (store === item) {
            store = null;
            if (joined) {
                for (i = 0 , len = joined.length; i < len; ++i) {
                    item = joined[i];
                    if (item.isStore) {
                        store = item;
                        break;
                    }
                }
            }
            me.store = store;
        }
    },
    
    clone: function(session) {
        var me = this,
            modified = me.modified,
            ret = me.copy(me.id, session);
        if (modified) {
            
            ret.modified = Ext.apply({}, modified);
        }
        ret.dirty = me.dirty;
        ret.dropped = me.dropped;
        ret.phantom = me.phantom;
        return ret;
    },
    
    copy: function(newId, session) {
        var me = this,
            data = Ext.apply({}, me.data),
            idProperty = me.idProperty,
            T = me.self;
        if (newId || newId === 0) {
            data[idProperty] = newId;
        } else if (newId === null) {
            delete data[idProperty];
        }
        return new T(data,session);
    },
    
    getProxy: function() {
        return this.self.getProxy();
    },
    
    getValidation: function(refresh) {
        var me = this,
            ret = me.validation;
        if (!ret) {
            me.validation = ret = new Ext.data.Validation();
            ret.attach(me);
        }
        if (refresh === true || (refresh !== false && ret.syncGeneration !== me.generation)) {
            ret.refresh(refresh);
        }
        return ret;
    },
    
    validate: function() {
        return new Ext.data.ErrorCollection().init(this);
    },
    
    isValid: function() {
        var val = this.getValidation();
        return !val.dirty;
    },
    
    toUrl: function() {
        var pieces = this.$className.split('.'),
            name = pieces[pieces.length - 1].toLowerCase();
        return name + '/' + this.getId();
    },
    
    erase: function(options) {
        var me = this;
        me.erasing = true;
        
        
        
        me.drop();
        delete me.erasing;
        return me.save(options);
    },
    setErased: function() {
        this.erased = true;
        this.callJoined('afterErase');
    },
    
    getChanges: function() {
        return this.getData(this._getChangesOptions);
    },
    
    getCriticalFields: function() {
        var cls = this.self,
            ret = cls.criticalFields;
        if (!ret) {
            cls.rankFields();
            ret = cls.criticalFields;
        }
        return ret;
    },
    
    
    getAssociatedData: function(result) {
        var me = this,
            associations = me.associations,
            deep, i, item, items, itemData, length, record, role, roleName;
        result = result || {};
        me.$gathering = 1;
        for (roleName in associations) {
            role = associations[roleName];
            item = role.getAssociatedItem(me);
            if (!item || item.$gathering) {
                
                continue;
            }
            if (item.isStore) {
                item.$gathering = 1;
                items = item.getData().items;
                
                length = items.length;
                itemData = [];
                for (i = 0; i < length; ++i) {
                    
                    
                    
                    
                    record = items[i];
                    deep = !record.$gathering;
                    record.$gathering = 1;
                    itemData.push(record.getData(deep));
                    delete record.$gathering;
                }
                delete item.$gathering;
            } else {
                itemData = item.getData(true);
            }
            result[roleName] = itemData;
        }
        delete me.$gathering;
        return result;
    },
    
    getData: function(options) {
        var me = this,
            ret = {},
            opts = (options === true) ? me._getAssociatedOptions : (options || ret),
            
            data = me.data,
            associated = opts.associated,
            changes = opts.changes,
            critical = changes && opts.critical,
            content = changes ? me.modified : data,
            fieldsMap = me.fieldsMap,
            persist = opts.persist,
            serialize = opts.serialize,
            criticalFields, field, n, name, value;
        
        
        
        
        if (content) {
            
            for (name in content) {
                value = data[name];
                field = fieldsMap[name];
                if (field) {
                    if (persist && !field.persist) {
                        
                        continue;
                    }
                    if (serialize && field.serialize) {
                        value = field.serialize(value, me);
                    }
                }
                ret[name] = value;
            }
        }
        if (critical) {
            criticalFields = me.self.criticalFields || me.getCriticalFields();
            for (n = criticalFields.length; n-- > 0; ) {
                name = (field = criticalFields[n]).name;
                if (!(name in ret)) {
                    value = data[name];
                    if (serialize && field.serialize) {
                        value = field.serialize(value, me);
                    }
                    ret[name] = value;
                }
            }
        }
        if (associated) {
            me.getAssociatedData(ret);
        }
        
        return ret;
    },
    
    getTransientFields: function() {
        var cls = this.self,
            ret = cls.transientFields;
        if (!ret) {
            cls.rankFields();
            
            ret = cls.transientFields;
        }
        return ret;
    },
    
    isLoading: function() {
        return !!this.loadOperation;
    },
    
    abort: function() {
        var operation = this.loadOperation;
        if (operation) {
            operation.abort();
        }
    },
    
    load: function(options) {
        options = Ext.apply({}, options);
        var me = this,
            scope = options.scope || me,
            proxy = me.getProxy(),
            callback = options.callback,
            operation = me.loadOperation,
            id = me.getId(),
            extras;
        if (operation) {
            
            extras = operation.extraCalls;
            if (!extras) {
                extras = operation.extraCalls = [];
            }
            extras.push(options);
            return operation;
        }
        var doIdCheck = true;
        if (me.phantom) {
            doIdCheck = false;
        }
        options.id = id;
        
        
        options.recordCreator = function(data, type, readOptions) {
            
            
            
            var session = me.session;
            if (readOptions) {
                readOptions.recordCreator = session ? session.recordCreator : null;
            }
            me.set(data, me._commitOptions);
            
            if (doIdCheck && me.getId() !== id) {
                Ext.Error.raise('Invalid record id returned for ' + id + '@' + me.entityName);
            }
            return me;
        };
        options.internalCallback = function(operation) {
            var success = operation.wasSuccessful() && operation.getRecords().length > 0,
                op = me.loadOperation,
                extras = op.extraCalls,
                successFailArgs = [
                    me,
                    operation
                ],
                callbackArgs = [
                    me,
                    operation,
                    success
                ],
                i, len;
            me.loadOperation = null;
            if (success) {
                Ext.callback(options.success, scope, successFailArgs);
            } else {
                Ext.callback(options.failure, scope, successFailArgs);
            }
            Ext.callback(callback, scope, callbackArgs);
            
            
            
            if (extras) {
                for (i = 0 , len = extras.length; i < len; ++i) {
                    options = extras[i];
                    if (success) {
                        Ext.callback(options.success, scope, successFailArgs);
                    } else {
                        Ext.callback(options.failure, scope, successFailArgs);
                    }
                    Ext.callback(options.callback, scope, callbackArgs);
                }
            }
            me.callJoined('afterLoad');
        };
        delete options.callback;
        me.loadOperation = operation = proxy.createOperation('read', options);
        operation.execute();
        return operation;
    },
    
    save: function(options) {
        options = Ext.apply({}, options);
        var me = this,
            phantom = me.phantom,
            dropped = me.dropped,
            action = dropped ? 'destroy' : (phantom ? 'create' : 'update'),
            scope = options.scope || me,
            callback = options.callback,
            proxy = me.getProxy(),
            operation;
        options.records = [
            me
        ];
        options.internalCallback = function(operation) {
            var args = [
                    me,
                    operation
                ],
                success = operation.wasSuccessful();
            if (success) {
                Ext.callback(options.success, scope, args);
            } else {
                Ext.callback(options.failure, scope, args);
            }
            args.push(success);
            Ext.callback(callback, scope, args);
        };
        delete options.callback;
        operation = proxy.createOperation(action, options);
        
        
        if (dropped && phantom) {
            
            operation.setResultSet(Ext.data.reader.Reader.prototype.nullResultSet);
            me.setErased();
            operation.setSuccessful(true);
        } else {
            operation.execute();
        }
        return operation;
    },
    
    
    inheritableStatics: {
        
        addFields: function(newFields) {
            this.replaceFields(newFields);
        },
        
        replaceFields: function(newFields, removeFields) {
            var me = this,
                proto = me.prototype,
                Field = Ext.data.field.Field,
                fields = me.fields,
                fieldsMap = me.fieldsMap,
                ordinals = me.fieldOrdinals,
                field, i, idField, len, name, ordinal;
            if (removeFields === true) {
                fields.length = 0;
                me.fieldsMap = fieldsMap = {};
                me.fieldOrdinals = ordinals = {};
            } else if (removeFields) {
                for (i = removeFields.length; i-- > 0; ) {
                    name = removeFields[i];
                    if (name in ordinals) {
                        delete ordinals[name];
                        delete fieldsMap[name];
                    }
                }
                for (i = 0 , len = fields.length; i < len; ++i) {
                    name = (field = fields[i]).name;
                    if (name in ordinals) {
                        ordinals[name] = i;
                    } else {
                        
                        fields.splice(i, 1);
                        --i;
                        --len;
                    }
                }
            }
            
            
            for (i = 0 , len = newFields ? newFields.length : 0; i < len; i++) {
                name = (field = newFields[i]).name;
                if (!(name in ordinals)) {
                    ordinals[name] = ordinal = fields.length;
                    
                    fields.push(field = Field.create(field));
                    fieldsMap[name] = field;
                    field.ordinal = ordinal;
                    field.definedBy = field.owner = this;
                }
            }
            
            
            me.idField = proto.idField = idField = fieldsMap[proto.idProperty];
            idField.allowNull = idField.critical = idField.identifier = true;
            idField.defaultValue = null;
            
            
            me.initializeFn = me.rankedFields = me.transientFields = me.criticalFields = null;
        },
        
        removeFields: function(removeFields) {
            this.replaceFields(null, removeFields);
        },
        getIdFromData: function(data) {
            var T = this,
                idField = T.idField,
                id = idField.calculated ? (new T(data)).id : data[idField.name];
            return id;
        },
        createWithId: function(id, data, session) {
            var d = data,
                T = this;
            if (id || id === 0) {
                d = {};
                if (data) {
                    Ext.apply(d, data);
                }
                d[T.idField.name] = id;
            }
            return new T(d,session);
        },
        getFields: function() {
            return this.fields;
        },
        getFieldsMap: function() {
            return this.fieldsMap;
        },
        getField: function(name) {
            return this.fieldsMap[name] || null;
        },
        
        getProxy: function() {
            var me = this,
                proxy = me.proxy,
                defaults;
            if (!proxy) {
                
                proxy = me.proxyConfig;
                if (!proxy || !proxy.isProxy) {
                    if (typeof proxy === 'string') {
                        proxy = {
                            type: proxy
                        };
                    }
                    
                    
                    defaults = me.schema.constructProxy(me);
                    proxy = proxy ? Ext.merge(defaults, proxy) : defaults;
                }
                proxy = me.setProxy(proxy);
            }
            return proxy;
        },
        
        setProxy: function(proxy) {
            var me = this,
                model;
            if (proxy) {
                if (!proxy.isProxy) {
                    proxy = Ext.Factory.proxy(proxy);
                } else {
                    model = proxy.getModel();
                    if (model && model !== me) {
                        proxy = proxy.clone();
                    }
                }
                proxy.setModel(me);
            }
            return (me.prototype.proxy = me.proxy = proxy);
        },
        
        load: function(id, options, session) {
            var data = {},
                rec;
            data[this.prototype.idProperty] = id;
            rec = new this(data,session);
            rec.load(options);
            return rec;
        }
    },
    deprecated: {
        5: {
            methods: {
                hasId: null,
                markDirty: null,
                setDirty: null,
                eachStore: function(callback, scope) {
                    var me = this,
                        stores = me.stores,
                        len = stores.length,
                        i;
                    for (i = 0; i < len; ++i) {
                        callback.call(scope, stores[i]);
                    }
                },
                join: function(item) {
                    var me = this,
                        stores = me.stores,
                        joined = me.joined;
                    if (!joined) {
                        joined = me.joined = [
                            item
                        ];
                    } else {
                        joined.push(item);
                    }
                    if (item.isStore) {
                        me.store = me.store || item;
                        if (!stores) {
                            stores = me.stores = [];
                        }
                        stores.push(item);
                    }
                },
                unjoin: function(item) {
                    var me = this,
                        stores = me.stores,
                        joined = me.joined;
                    if (joined.length === 1) {
                        joined.length = 0;
                    } else {
                        Ext.Array.remove(joined, item);
                    }
                    if (item.isStore) {
                        Ext.Array.remove(stores, item);
                        me.store = stores[0] || null;
                    }
                }
            },
            properties: {
                persistenceProperty: null
            },
            inheritableStatics: {
                methods: {
                    setFields: null
                }
            }
        }
    },
    
    privates: {
        _commitOptions: {
            commit: true
        },
        _getChangesOptions: {
            changes: true
        },
        _getAssociatedOptions: {
            associated: true
        },
        
        copyFrom: function(sourceRecord) {
            var me = this,
                fields = me.fields,
                fieldCount = fields.length,
                modifiedFieldNames = [],
                field,
                i = 0,
                myData, sourceData,
                idProperty = me.idProperty,
                name, value;
            if (sourceRecord) {
                myData = me.data;
                sourceData = sourceRecord.data;
                for (; i < fieldCount; i++) {
                    field = fields[i];
                    name = field.name;
                    
                    
                    
                    
                    
                    
                    if (name !== idProperty) {
                        value = sourceData[name];
                        
                        
                        if (value !== undefined && !me.isEqual(myData[name], value)) {
                            myData[name] = value;
                            modifiedFieldNames.push(name);
                        }
                    }
                }
                
                if (me.phantom && !sourceRecord.phantom) {
                    
                    
                    me.beginEdit();
                    me.setId(sourceRecord.getId());
                    me.endEdit(true);
                    me.commit(true);
                }
            }
            return modifiedFieldNames;
        },
        
        callJoined: function(funcName, args) {
            var me = this,
                joined = me.joined,
                session = me.session,
                i, len, fn, item;
            if (!joined && !session) {
                return;
            }
            if (args) {
                args.unshift(me);
            } else {
                args = [
                    me
                ];
            }
            if (joined) {
                for (i = 0 , len = joined.length; i < len; ++i) {
                    item = joined[i];
                    if (item && (fn = item[funcName])) {
                        fn.apply(item, args);
                    }
                }
            }
            fn = session && session[funcName];
            if (fn) {
                fn.apply(session, args);
            }
        },
        
        setSession: function(session) {
            if (session) {
                if (this.session) {
                    Ext.Error.raise('This model already belongs to a session.');
                }
                if (!this.id) {
                    Ext.Error.raise('The model must have an id to participate in a session.');
                }
            }
            this.session = session;
            if (session) {
                session.add(this);
            }
        },
        
        getModifiedFieldNames: function(old) {
            var me = this,
                data = me.data,
                modified = [],
                oldData = old || me.editMemento.data,
                key;
            for (key in data) {
                if (data.hasOwnProperty(key)) {
                    if (!me.isEqual(data[key], oldData[key], key)) {
                        modified.push(key);
                    }
                }
            }
            return modified;
        },
        
        isEqual: function(lhs, rhs, field) {
            var f;
            if (field) {
                f = field.isField ? field : this.fieldsMap[field];
                if (f) {
                    return f.compare(lhs, rhs) === 0;
                }
            }
            
            
            if (lhs instanceof Date && rhs instanceof Date) {
                return lhs.getTime() === rhs.getTime();
            }
            return lhs === rhs;
        },
        statics: {
            
            EDIT: 'edit',
            
            REJECT: 'reject',
            
            COMMIT: 'commit',
            rankFields: function() {
                var cls = this,
                    prototype = cls.prototype,
                    fields = cls.fields,
                    length = fields.length,
                    rankedFields = [],
                    criticalFields = [],
                    transientFields = [],
                    evilFields, field, i;
                cls.rankedFields = prototype.rankedFields = rankedFields;
                cls.criticalFields = prototype.criticalFields = criticalFields;
                cls.transientFields = prototype.transientFields = transientFields;
                
                
                
                
                
                for (i = 0; i < length; ++i) {
                    field = fields[i];
                    if (field.critical) {
                        criticalFields.push(field);
                    }
                    if (!field.persist) {
                        transientFields.push(field);
                    }
                    if (field.evil) {
                        (evilFields || (evilFields = [])).push(field);
                    } else if (!field.depends) {
                        rankedFields.push(field);
                        field.rank = rankedFields.length;
                    }
                }
                
                for (i = 0; i < length; ++i) {
                    if (!(field = fields[i]).rank && !field.evil) {
                        cls.topoAdd(field);
                    }
                }
                if (evilFields) {
                    for (i = 0 , length = evilFields.length; i < length; ++i) {
                        rankedFields.push(field = evilFields[i]);
                        field.rank = rankedFields.length;
                    }
                }
                
                cls.topoStack = null;
                
                return rankedFields;
            },
            topoAdd: function(field) {
                var cls = this,
                    dep = field.depends,
                    dependsLength = dep ? dep.length : 0,
                    rankedFields = cls.rankedFields,
                    i, targetField;
                var topoStack = cls.topoStack || (cls.topoStack = []);
                topoStack.push(field.name);
                if (field.rank === 0) {
                    
                    Ext.Error.raise(cls.$className + " has circular field dependencies: " + topoStack.join(" --> "));
                }
                if (topoStack.length && field.evil) {
                    Ext.Error.raise(cls.$className + ": Field " + topoStack[topoStack.length - 1] + " cannot depend on depends-less field " + field.name);
                }
                field.rank = 0;
                
                for (i = 0; i < dependsLength; ++i) {
                    
                    
                    targetField = cls.fieldsMap[dep[i]];
                    if (!targetField) {
                        Ext.Error.raise(cls.$className + ": Field " + field.name + " depends on undefined field " + dep[i]);
                    }
                    (targetField.dependents || (targetField.dependents = [])).push(field);
                    if (!targetField.rank) {
                        
                        cls.topoAdd(targetField);
                    }
                }
                rankedFields.push(field);
                field.rank = rankedFields.length;
                
                topoStack.pop();
            },
            initFields: function(data, cls, proto) {
                var Field = Ext.data.field.Field,
                    fieldDefs = data.fields,
                    
                    fields = [],
                    fieldOrdinals = {},
                    fieldsMap = {},
                    references = [],
                    superFields = proto.fields,
                    versionProperty = data.versionProperty || proto.versionProperty,
                    idProperty = cls.idProperty,
                    idField, field, i, length, name, ordinal, reference, superIdField, idDeclared;
                
                
                cls.fields = proto.fields = fields;
                cls.fieldOrdinals = proto.fieldOrdinals = fieldOrdinals;
                cls.fieldsMap = proto.fieldsMap = fieldsMap;
                cls.references = proto.references = references;
                if (superFields) {
                    
                    for (i = 0 , length = superFields.length; i < length; ++i) {
                        fields[i] = field = Ext.Object.chain(superFields[i]);
                        field.dependents = null;
                        
                        field.owner = cls;
                        fieldOrdinals[name = field.name] = i;
                        fieldsMap[name] = field;
                        
                        
                        field.rank = null;
                        if (field.generated) {
                            superIdField = field;
                        }
                    }
                }
                
                if (fieldDefs) {
                    delete data.fields;
                    for (i = 0 , length = fieldDefs.length; i < length; ++i) {
                        field = fieldDefs[i];
                        reference = field.reference;
                        
                        
                        if (reference && typeof reference !== 'string') {
                            
                            reference = Ext.merge({}, reference);
                        }
                        field.$reference = reference;
                        field = Field.create(fieldDefs[i]);
                        name = field.name;
                        ordinal = fieldOrdinals[name];
                        if (ordinal === undefined) {
                            
                            fieldOrdinals[name] = ordinal = fields.length;
                        }
                        
                        fieldsMap[name] = field;
                        fields[ordinal] = field;
                        field.definedBy = field.owner = cls;
                        field.ordinal = ordinal;
                        if (name === idProperty) {
                            idDeclared = true;
                        }
                    }
                }
                
                
                idField = fieldsMap[idProperty];
                if (!idField) {
                    if (superIdField && superIdField.generated) {
                        ordinal = superIdField.ordinal;
                    } else {
                        ordinal = fields.length;
                    }
                    idField = new Field(idProperty);
                    fields[ordinal] = idField;
                    fieldOrdinals[idProperty] = ordinal;
                    fieldsMap[idProperty] = idField;
                    idField.definedBy = cls;
                    idField.ordinal = ordinal;
                    idField.generated = true;
                } else if (idDeclared && superIdField && superIdField.generated) {
                    
                    
                    
                    
                    Ext.Array.remove(fields, superIdField);
                    delete fieldsMap[superIdField.name];
                    delete fieldOrdinals[superIdField.name];
                    for (i = 0 , length = fields.length; i < length; ++i) {
                        field = fields[i];
                        fields.ordinal = i;
                        fieldOrdinals[field.name] = i;
                    }
                }
                idField.allowNull = idField.critical = idField.identifier = true;
                idField.defaultValue = null;
                cls.idField = proto.idField = idField;
                if (versionProperty) {
                    field = fieldsMap[versionProperty];
                    if (!field) {
                        ordinal = fields.length;
                        field = new Field({
                            name: versionProperty,
                            type: 'int'
                        });
                        fields[ordinal] = field;
                        fieldOrdinals[versionProperty] = ordinal;
                        fieldsMap[versionProperty] = field;
                        field.definedBy = cls;
                        field.ordinal = ordinal;
                        field.generated = true;
                    }
                    field.defaultValue = 1;
                    field.critical = true;
                }
            },
            
            
            initValidators: function(data, cls, proto) {
                var superValidators = proto.validators,
                    validators, field, copy, validatorDefs, i, length, fieldValidator, name, validator, item;
                if (superValidators) {
                    validators = {};
                    for (field in superValidators) {
                        validators[field] = Ext.Array.clone(superValidators[field]);
                    }
                }
                validatorDefs = data.validators || data.validations;
                if (data.validations) {
                    delete data.validations;
                    Ext.log.warn((cls.$className || 'Ext.data.Model') + ': validations has been deprecated. Please use validators instead.');
                }
                if (validatorDefs) {
                    delete data.validators;
                    validators = validators || {};
                    
                    if (Ext.isArray(validatorDefs)) {
                        copy = {};
                        for (i = 0 , length = validatorDefs.length; i < length; ++i) {
                            item = validatorDefs[i];
                            name = item.field;
                            if (!copy[name]) {
                                copy[name] = [];
                            }
                            
                            item = item.fn || item;
                            copy[name].push(item);
                        }
                        validatorDefs = copy;
                    }
                    for (name in validatorDefs) {
                        fieldValidator = validatorDefs[name];
                        if (!Ext.isArray(fieldValidator)) {
                            fieldValidator = [
                                fieldValidator
                            ];
                        }
                        validator = validators[name];
                        if (validators[name]) {
                            
                            Ext.Array.push(validator, fieldValidator);
                        } else {
                            validators[name] = fieldValidator;
                        }
                    }
                }
                if (validators) {
                    for (name in validators) {
                        field = cls.getField(name);
                        if (field) {
                            field.setModelValidators(validators[name]);
                        }
                    }
                }
                cls.validators = proto.validators = validators;
            },
            initAssociations: function(schema, data, cls) {
                
                var associations = data.associations,
                    belongsTo = data.belongsTo,
                    hasMany = data.hasMany,
                    hasOne = data.hasOne,
                    
                    matrices = data.manyToMany,
                    i, length, assoc;
                if (data.belongsTo) {
                    Ext.log.warn('Use of "belongsTo" is obsolete' + (cls.$className ? ' in ' + cls.$className : ''));
                    delete data.belongsTo;
                }
                delete data.manyToMany;
                if (matrices) {
                    schema.addMatrices(cls, matrices);
                }
                
                delete data.associations;
                delete data.belongsTo;
                delete data.hasMany;
                delete data.hasOne;
                if (associations) {
                    associations = Ext.isArray(associations) ? associations : [
                        associations
                    ];
                    for (i = 0 , length = associations.length; i < length; ++i) {
                        assoc = associations[i];
                        switch (assoc.type) {
                            case 'belongsTo':
                                schema.addLegacyBelongsTo(cls, assoc);
                                break;
                            case 'hasMany':
                                schema.addLegacyHasMany(cls, assoc);
                                break;
                            case 'hasOne':
                                schema.addLegacyHasOne(cls, assoc);
                                break;
                            default:
                                Ext.Error.raise('Invalid association type: "' + assoc.type + '"');
                        }
                    }
                }
                if (belongsTo) {
                    belongsTo = Ext.isArray(belongsTo) ? belongsTo : [
                        belongsTo
                    ];
                    for (i = 0 , length = belongsTo.length; i < length; ++i) {
                        schema.addLegacyBelongsTo(cls, belongsTo[i]);
                    }
                }
                if (hasMany) {
                    hasMany = Ext.isArray(hasMany) ? hasMany : [
                        hasMany
                    ];
                    for (i = 0 , length = hasMany.length; i < length; ++i) {
                        schema.addLegacyHasMany(cls, hasMany[i]);
                    }
                }
                if (hasOne) {
                    hasOne = Ext.isArray(hasOne) ? hasOne : [
                        hasOne
                    ];
                    for (i = 0 , length = hasOne.length; i < length; ++i) {
                        schema.addLegacyHasOne(cls, hasOne[i]);
                    }
                }
                schema.afterLegacyAssociations(cls);
            },
            initIdentifier: function(data, cls, proto) {
                var identifier = data.identifier || data.idgen,
                    superIdent = proto.identifier || cls.schema._defaultIdentifier,
                    generatorPrefix;
                if (data.idgen) {
                    Ext.log.warn('Ext.data.Model: idgen has been deprecated. Please use identifier instead.');
                }
                if (identifier) {
                    delete data.identifier;
                    delete data.idgen;
                    
                    identifier = Ext.Factory.dataIdentifier(identifier);
                } else if (superIdent) {
                    
                    
                    
                    if (superIdent.clone && !superIdent.getId()) {
                        identifier = superIdent.clone();
                    } else if (superIdent.isGenerator) {
                        identifier = superIdent;
                    } else {
                        identifier = Ext.Factory.dataIdentifier(superIdent);
                    }
                }
                cls.identifier = proto.identifier = identifier;
                if (!identifier) {
                    
                    
                    
                    
                    generatorPrefix = cls.entityName;
                    if (!generatorPrefix) {
                        generatorPrefix = Ext.id(null, 'extModel');
                    }
                    cls.identifier = Ext.Factory.dataIdentifier({
                        type: 'sequential',
                        prefix: generatorPrefix + '-'
                    });
                }
            },
            findValidator: function(validators, name, cfg) {
                var type = cfg.type || cfg,
                    field = validators[name],
                    len, i, item;
                if (field) {
                    for (i = 0 , len = field.length; i < len; ++i) {
                        item = field[i];
                        if (item.type === type) {
                            return item;
                        }
                    }
                }
                return null;
            },
            
            makeInitializeFn: function(cls) {
                var code = [
                        'var '
                    ],
                    body = [
                        '\nreturn function (e) {\n    var data = e.data, v;\n'
                    ],
                    fieldVars = [],
                    work = 0,
                    bc, ec, 
                    convert, expr, factory, field, fields, fs, hasDefValue, i, length;
                if (!(fields = cls.rankedFields)) {
                    
                    
                    fields = cls.rankFields();
                }
                for (i = 0 , length = fields.length; i < length; ++i) {
                    
                    
                    
                    field = fields[i];
                    fieldVars[i] = fs = 'f' + i;
                    convert = field.convert;
                    if (i) {
                        code.push(',  \n    ');
                    }
                    code.push(fs, ' = $fields[' + i + ']');
                    
                    code.push('  /*  ', field.name, '  */');
                    
                    
                    
                    
                    if ((hasDefValue = (field.defaultValue !== undefined)) || convert) {
                        
                        
                        
                        
                        expr = 'data["' + field.name + '"]';
                        ++work;
                        bc = ec = '';
                        if (field.cloneDefaultValue) {
                            bc = 'Ext.clone(';
                            ec = ')';
                        }
                        body.push('\n');
                        if (convert && hasDefValue) {
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            body.push('    v = ', expr, ';\n' + '    if (v !== undefined) {\n' + '        v = ', fs, '.convert(v, e);\n' + '    }\n' + '    if (v === undefined) {\n' + '        v = ', bc, fs, '.defaultValue', ec, ';\n' + '    }\n' + '    ', expr, ' = v;');
                        } else if (convert) {
                            
                            
                            
                            
                            
                            
                            body.push('    v = ', fs, '.convert(', expr, ',e);\n' + '    if (v !== undefined) {\n' + '        ', expr, ' = v;\n' + '    }\n');
                        } else if (hasDefValue) {
                            
                            
                            
                            
                            
                            
                            
                            body.push('    if (', expr, ' === undefined) {\n' + '        ', expr, ' = ', bc, fs, '.defaultValue', ec, ';\n' + '    }\n');
                        }
                    }
                }
                if (!work) {
                    
                    return Ext.emptyFn;
                }
                code.push(';\n');
                code.push.apply(code, body);
                code.push('}');
                code = code.join('');
                factory = new Function('$fields',code);
                return factory(fields);
            }
        }
    }
}, 

function() {
    var Model = this,
        proto = Model.prototype,
        Schema = Ext.data.schema.Schema,
        defaultSchema;
    Model.proxyConfig = proto.proxy;
    delete proto.proxy;
    
    Model.fields = [];
    
    Model.fieldsMap = proto.fieldsMap = {};
    Model.schema = proto.schema = Schema.get(proto.schema);
    proto.idField = new Ext.data.field.Field(proto.idProperty);
    Model.identifier = new Ext.data.identifier.Sequential();
    Model.onExtended(function(cls, data) {
        var proto = cls.prototype,
            schemaName = data.schema,
            superCls = proto.superclass.self,
            schema, entityName, proxy;
        cls.idProperty = data.idProperty || proto.idProperty;
        if (schemaName) {
            delete data.schema;
            schema = Schema.get(schemaName);
        } else if (!(schema = proto.schema)) {
            schema = defaultSchema || (defaultSchema = Schema.get('default'));
        }
        
        cls.rankFields = Model.rankFields;
        cls.topoAdd = Model.topoAdd;
        
        
        proto.schema = cls.schema = schema;
        
        
        if (!(entityName = data.entityName)) {
            proto.entityName = entityName = schema.getEntityName(cls);
            if (!entityName) {
                if (data.associations) {
                    Ext.Error.raise('Anonymous entities cannot specify "associations"');
                }
                if (data.belongsTo) {
                    Ext.Error.raise('Anonymous entities cannot specify "belongsTo"');
                }
                if (data.hasMany) {
                    Ext.Error.raise('Anonymous entities cannot specify "hasMany"');
                }
                if (data.hasOne) {
                    Ext.Error.raise('Anonymous entities cannot specify "hasOne"');
                }
                if (data.matrices) {
                    Ext.Error.raise('Anonymous entities cannot specify "manyToMany"');
                }
            }
        }
        cls.entityName = entityName;
        cls.fieldExtractors = {};
        Model.initIdentifier(data, cls, proto);
        Model.initFields(data, cls, proto);
        Model.initValidators(data, cls, proto);
        
        
        cls.fields.items = cls.fields;
        if (entityName) {
            schema.addEntity(cls);
            Model.initAssociations(schema, data, cls);
        }
        proxy = data.proxy;
        if (proxy) {
            delete data.proxy;
        } else if (superCls !== Model) {
            proxy = superCls.proxyConfig || superCls.proxy;
        }
        cls.proxyConfig = proxy;
    });
});


Ext.define('Ext.data.ResultSet', {
    
    isResultSet: true,
    $configPrefixed: false,
    config: {
        
        loaded: true,
        
        count: null,
        
        total: null,
        
        success: false,
        
        records: null,
        
        message: null,
        
        readRoot: false
    },
    
    constructor: function(config) {
        this.initConfig(config);
    },
    applyCount: function(count) {
        if (!count && count !== 0) {
            return this.getRecords().length;
        }
        return count;
    },
    
    updateRecords: function(records) {
        this.setCount(records.length);
    }
});


Ext.define('Ext.data.reader.Reader', {
    alternateClassName: [
        'Ext.data.Reader',
        'Ext.data.DataReader'
    ],
    requires: [
        'Ext.data.ResultSet',
        'Ext.XTemplate'
    ],
    mixins: [
        'Ext.mixin.Observable',
        'Ext.mixin.Factoryable'
    ],
    alias: 'reader.base',
    factoryConfig: {
        defaultType: null
    },
    config: {
        
        totalProperty: 'total',
        
        successProperty: 'success',
        
        rootProperty: '',
        
        messageProperty: '',
        
        typeProperty: '',
        
        implicitIncludes: true,
        
        readRecordsOnFailure: true,
        
        model: null,
        
        proxy: null,
        
        transform: null
    },
    
    
    isReader: true,
    
    
    constructor: function(config) {
        if (config && config.hasOwnProperty('root')) {
            config = Ext.apply({}, config);
            config.rootProperty = config.root;
            delete config.root;
            Ext.log.error('Ext.data.reader.Reader: Using the deprecated "root" configuration. Use "rootProperty" instead.');
        }
        var me = this;
        me.duringInit = 1;
        
        me.mixins.observable.constructor.call(me, config);
        --me.duringInit;
        me.buildExtractors();
    },
    applyModel: function(model) {
        return Ext.data.schema.Schema.lookupEntity(model);
    },
    applyTransform: function(transform) {
        if (transform) {
            if (Ext.isFunction(transform)) {
                transform = {
                    fn: transform
                };
            }
            return transform.fn.bind(transform.scope || this);
        }
        return transform;
    },
    forceBuildExtractors: function() {
        if (!this.duringInit) {
            this.buildExtractors(true);
        }
    },
    updateTotalProperty: function() {
        this.forceBuildExtractors();
    },
    updateMessageProperty: function() {
        this.forceBuildExtractors();
    },
    updateSuccessProperty: function() {
        this.forceBuildExtractors();
    },
    
    read: function(response, readOptions) {
        var data, result;
        if (response) {
            if (response.responseText) {
                result = this.getResponseData(response);
                if (result && result.__$isError) {
                    return new Ext.data.ResultSet({
                        total: 0,
                        count: 0,
                        records: [],
                        success: false,
                        message: result.msg
                    });
                } else {
                    data = this.readRecords(result, readOptions);
                }
            } else {
                data = this.readRecords(response, readOptions);
            }
        }
        return data || this.nullResultSet;
    },
    
    getNullResultSet: function() {
        return this.nullResultSet;
    },
    
    createReadError: function(msg) {
        return {
            __$isError: true,
            msg: msg
        };
    },
    
    readRecords: function(data, readOptions) {
        var me = this,
            success, recordCount, records, root, total, value, message, transform;
        transform = this.getTransform();
        if (transform) {
            data = transform(data);
        }
        me.buildExtractors();
        
        me.rawData = data;
        data = me.getData(data);
        success = true;
        recordCount = 0;
        records = [];
        if (me.getSuccessProperty()) {
            value = me.getSuccess(data);
            if (value === false || value === 'false') {
                success = false;
            }
        }
        if (me.getMessageProperty()) {
            message = me.getMessage(data);
        }
        
        if (me.getReadRecordsOnFailure() || success) {
            
            
            root = Ext.isArray(data) ? data : me.getRoot(data);
            if (root) {
                total = root.length;
            }
            if (me.getTotalProperty()) {
                value = parseInt(me.getTotal(data), 10);
                if (!isNaN(value)) {
                    total = value;
                }
            }
            if (root) {
                records = me.extractData(root, readOptions);
                recordCount = records.length;
            }
        }
        return new Ext.data.ResultSet({
            total: total || recordCount,
            count: recordCount,
            records: records,
            success: success,
            message: message,
            readRoot: !!root
        });
    },
    
    extractData: function(root, readOptions) {
        var me = this,
            entityType = readOptions && readOptions.model ? Ext.data.schema.Schema.lookupEntity(readOptions.model) : me.getModel(),
            schema = entityType.schema,
            includes = schema.hasAssociations(entityType) && me.getImplicitIncludes(),
            fieldExtractorInfo = me.getFieldExtractorInfo(entityType.fieldExtractors),
            length = root.length,
            records = new Array(length),
            typeProperty = me.getTypeProperty(),
            reader, node, nodeType, record, i;
        if (!length && Ext.isObject(root)) {
            root = [
                root
            ];
            length = 1;
        }
        for (i = 0; i < length; i++) {
            record = root[i];
            if (!record.isModel) {
                
                
                node = record;
                
                
                
                
                
                
                if (typeProperty && (nodeType = me.getChildType(schema, node, typeProperty))) {
                    reader = nodeType.getProxy().getReader();
                    record = reader.extractRecord(node, readOptions, nodeType, schema.hasAssociations(nodeType) && reader.getImplicitIncludes(), reader.getFieldExtractorInfo(nodeType.fieldExtractors));
                } else {
                    record = me.extractRecord(node, readOptions, entityType, includes, fieldExtractorInfo);
                }
                if (record.isModel) {
                    
                    record.raw = node;
                }
            }
            if (record.onLoad) {
                record.onLoad();
            }
            records[i] = record;
        }
        return records;
    },
    
    getChildType: function(schema, rawNode, typeProperty) {
        var namespace;
        switch (typeof typeProperty) {
            case 'string':
                return schema.getEntity(rawNode[typeProperty]);
            case 'object':
                namespace = typeProperty.namespace;
                return schema.getEntity((namespace ? namespace + '.' : '') + rawNode[typeProperty.name]);
            case 'function':
                return schema.getEntity(typeProperty(rawNode));
        }
    },
    extractRecordData: function(node, readOptions) {
        var entityType = readOptions && readOptions.model ? Ext.data.schema.Schema.lookupEntity(readOptions.model) : this.getModel(),
            fieldExtractorInfo = this.getFieldExtractorInfo(entityType.fieldExtractors);
        return this.extractRecord(node, readOptions, entityType, false, fieldExtractorInfo);
    },
    extractRecord: function(node, readOptions, entityType, includes, fieldExtractorInfo) {
        var me = this,
            creatorFn = (readOptions && readOptions.recordCreator) || me.defaultRecordCreator,
            modelData, record;
        
        
        
        modelData = me.extractModelData(node, fieldExtractorInfo);
        record = creatorFn.call(me, modelData, entityType || me.getModel(), readOptions);
        if (includes && record.isModel) {
            me.readAssociated(record, node, readOptions);
        }
        return record;
    },
    getFieldExtractorInfo: function(extractors) {
        
        
        if (!extractors) {
            return;
        }
        var type = this.$className,
            extractor = extractors[type];
        
        
        if (extractor === undefined) {
            extractors[type] = extractor = this.buildFieldExtractors();
        }
        return extractor;
    },
    buildFieldExtractors: function() {
        var fields = this.getFields(),
            len = fields.length,
            buffer = [],
            extractors = [],
            out = null,
            cnt = 0,
            field, name, i, extractor;
        for (i = 0; i < len; ++i) {
            field = fields[i];
            extractor = this.createFieldAccessor(field);
            if (extractor) {
                name = field.name;
                
                buffer.push('val = extractors[' + cnt + '](raw); if (val !== undefined) { data[\'' + name + '\'] = val; }');
                extractors.push(extractor);
                ++cnt;
            }
        }
        if (buffer.length) {
            out = {
                extractors: extractors,
                fn: new Function('raw','data','extractors','var val;' + buffer.join(''))
            };
        }
        return out;
    },
    defaultRecordCreator: function(data, Model) {
        var record = new Model(data);
        
        
        record.phantom = false;
        return record;
    },
    getModelData: function(raw) {
        return {};
    },
    extractModelData: function(raw, fieldExtractorInfo) {
        var data = this.getModelData(raw),
            fn;
        
        if (fieldExtractorInfo) {
            fn = fieldExtractorInfo.fn;
            fn(raw, data, fieldExtractorInfo.extractors);
        }
        return data;
    },
    
    readAssociated: function(record, data, readOptions) {
        var roles = record.associations,
            key, role;
        for (key in roles) {
            if (roles.hasOwnProperty(key)) {
                role = roles[key];
                
                if (role.cls) {
                    role.read(record, data, this, readOptions);
                }
            }
        }
    },
    getFields: function() {
        return this.getModel().fields;
    },
    
    getData: Ext.identityFn,
    
    getRoot: Ext.identityFn,
    
    getResponseData: function(response) {
        Ext.Error.raise("getResponseData must be implemented in the Ext.data.reader.Reader subclass");
    },
    
    onMetaChange: function(meta) {
        var me = this,
            fields = meta.fields,
            model, newModel, clientIdProperty, proxy;
        
        me.metaData = meta;
        
        if (meta.root) {
            me.setRootProperty(meta.root);
        }
        if (meta.totalProperty) {
            me.setTotalProperty(meta.totalProperty);
        }
        if (meta.successProperty) {
            me.setSuccessProperty(meta.successProperty);
        }
        if (meta.messageProperty) {
            me.setMessageProperty(meta.messageProperty);
        }
        clientIdProperty = meta.clientIdProperty;
        if (fields) {
            newModel = Ext.define(null, {
                extend: 'Ext.data.Model',
                fields: fields,
                clientIdProperty: clientIdProperty
            });
            me.setModel(newModel);
            proxy = me.getProxy();
            if (proxy) {
                proxy.setModel(newModel);
            }
        } else if (clientIdProperty) {
            model = me.getModel();
            if (model) {
                model.self.prototype.clientIdProperty = clientIdProperty;
            }
        }
    },
    
    buildExtractors: function(force) {
        var me = this,
            totalProp = me.getTotalProperty(),
            successProp = me.getSuccessProperty(),
            messageProp = me.getMessageProperty();
        if (force || !me.hasExtractors) {
            
            if (totalProp) {
                me.getTotal = me.createAccessor(totalProp);
            }
            if (successProp) {
                me.getSuccess = me.createAccessor(successProp);
            }
            if (messageProp) {
                me.getMessage = me.createAccessor(messageProp);
            }
            me.hasExtractors = true;
            return true;
        }
    },
    createAccessor: Ext.emptyFn,
    createFieldAccessor: Ext.emptyFn,
    destroy: function() {
        var me = this;
        delete me.model;
        delete me.getTotal;
        delete me.getSuccess;
        delete me.getMessage;
    },
    privates: {
        copyFrom: function(reader) {
            var me = this;
            reader.buildExtractors();
            me.setModel(reader.getModel());
            me.getTotal = reader.getTotal;
            me.getSuccess = reader.getSuccess;
            me.getMessage = reader.getMessage;
            ++me.duringInit;
            me.setTotalProperty(reader.getTotalProperty());
            me.setSuccessProperty(reader.getSuccessProperty());
            me.setMessageProperty(reader.getMessageProperty());
            --me.duringInit;
        }
    }
}, function() {
    var proto = this.prototype;
    Ext.apply(proto, {
        
        nullResultSet: new Ext.data.ResultSet({
            total: 0,
            count: 0,
            records: [],
            success: true,
            message: ''
        })
    });
});


Ext.define('Ext.data.writer.Writer', {
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    alias: 'writer.base',
    factoryConfig: {
        defaultType: null
    },
    alternateClassName: [
        'Ext.data.DataWriter',
        'Ext.data.Writer'
    ],
    config: {
        
        clientIdProperty: null,
        
        allDataOptions: {
            persist: true
        },
        
        partialDataOptions: {
            changes: true,
            critical: true
        },
        
        writeAllFields: false,
        
        dateFormat: null,
        
        nameProperty: 'name',
        
        writeRecordId: true,
        
        transform: null
    },
    
    isWriter: true,
    
    constructor: function(config) {
        this.initConfig(config);
    },
    applyTransform: function(transform) {
        if (transform) {
            if (Ext.isFunction(transform)) {
                transform = {
                    fn: transform
                };
            }
            return transform.fn.bind(transform.scope || this);
        }
        return transform;
    },
    
    write: function(request) {
        var operation = request.getOperation(),
            records = operation.getRecords() || [],
            len = records.length,
            data = [],
            i;
        for (i = 0; i < len; i++) {
            data.push(this.getRecordData(records[i], operation));
        }
        return this.writeRecords(request, data);
    },
    
    writeRecords: Ext.emptyFn,
    
    getRecordData: function(record, operation) {
        var me = this,
            nameProperty = me.getNameProperty(),
            mapping = nameProperty !== 'name',
            idField = record.self.idField,
            key = idField[nameProperty] || idField.name,
            
            value = record.id,
            writeAll = me.getWriteAllFields(),
            ret, dateFormat, phantom, options, clientIdProperty, fieldsMap, data, field;
        if (idField.serialize) {
            value = idField.serialize(value);
        }
        if (!writeAll && operation && operation.isDestroyOperation) {
            ret = {};
            ret[key] = value;
        } else {
            dateFormat = me.getDateFormat();
            phantom = record.phantom;
            options = (phantom || writeAll) ? me.getAllDataOptions() : me.getPartialDataOptions();
            clientIdProperty = phantom && me.getClientIdProperty();
            fieldsMap = record.getFieldsMap();
            options.serialize = false;
            
            data = record.getData(options);
            
            
            ret = mapping ? {} : data;
            if (clientIdProperty) {
                
                ret[clientIdProperty] = value;
                
                delete data[key];
            }
            
            else if (!me.getWriteRecordId()) {
                delete data[key];
            }
            for (key in data) {
                value = data[key];
                if (!(field = fieldsMap[key])) {
                    
                    
                    
                    if (mapping) {
                        ret[key] = value;
                    }
                } else {
                    
                    
                    
                    if (field.isDateField && dateFormat && Ext.isDate(value)) {
                        value = Ext.Date.format(value, dateFormat);
                    } else if (field.serialize) {
                        value = field.serialize(value, record);
                    }
                    if (mapping) {
                        key = field[nameProperty] || key;
                    }
                    ret[key] = value;
                }
            }
        }
        return ret;
    }
});


Ext.define('Ext.data.proxy.Proxy', {
    mixins: [
        'Ext.mixin.Factoryable',
        'Ext.mixin.Observable'
    ],
    $configPrefixed: false,
    alias: 'proxy.proxy',
    
    alternateClassName: [
        'Ext.data.DataProxy',
        'Ext.data.Proxy'
    ],
    requires: [
        'Ext.data.schema.Schema',
        'Ext.data.reader.Reader',
        'Ext.data.writer.Writer'
    ],
    uses: [
        'Ext.data.Batch',
        'Ext.data.operation.*',
        'Ext.data.Model'
    ],
    config: {
        
        batchOrder: 'create,update,destroy',
        
        batchActions: true,
        
        model: undefined,
        
        
        reader: {
            type: 'json'
        },
        
        
        writer: {
            type: 'json'
        }
    },
    
    isProxy: true,
    
    isSynchronous: false,
    
    
    constructor: function(config) {
        
        this.mixins.observable.constructor.call(this, config);
    },
    applyModel: function(model) {
        return Ext.data.schema.Schema.lookupEntity(model);
    },
    updateModel: function(model) {
        if (model) {
            var reader = this.getReader();
            if (reader && !reader.getModel()) {
                reader.setModel(model);
            }
        }
    },
    applyReader: function(reader) {
        return Ext.Factory.reader(reader);
    },
    updateReader: function(reader) {
        if (reader) {
            var me = this,
                model = me.getModel();
            if (!model) {
                model = reader.getModel();
                if (model) {
                    me.setModel(model);
                }
            } else {
                reader.setModel(model);
            }
            
            if (reader.onMetaChange) {
                reader.onMetaChange = Ext.Function.createSequence(reader.onMetaChange, me.onMetaChange, me);
            }
        }
    },
    applyWriter: function(writer) {
        var reader = this.getReader();
        writer = Ext.Factory.writer(writer);
        
        
        if (writer.getRecord && !writer.getRecord() && reader && reader.getRecord) {
            reader = reader.getRecord();
            if (reader) {
                writer.setRecord(reader);
            }
        }
        return writer;
    },
    abort: Ext.emptyFn,
    
    onMetaChange: function(meta) {
        this.fireEvent('metachange', this, meta);
    },
    
    create: Ext.emptyFn,
    
    read: Ext.emptyFn,
    
    update: Ext.emptyFn,
    
    erase: Ext.emptyFn,
    
    batch: function(options, 
    listeners) {
        var me = this,
            useBatch = me.getBatchActions(),
            batch, records, actions, aLen, action, a, r, rLen, record;
        if (options.operations === undefined) {
            
            
            options = {
                operations: options,
                listeners: listeners
            };
        }
        if (options.batch) {
            if (Ext.isDefined(options.batch.runOperation)) {
                batch = Ext.applyIf(options.batch, {
                    proxy: me,
                    listeners: {}
                });
            }
        } else {
            options.batch = {
                proxy: me,
                listeners: options.listeners || {}
            };
        }
        if (!batch) {
            batch = new Ext.data.Batch(options.batch);
        }
        batch.on('complete', Ext.bind(me.onBatchComplete, me, [
            options
        ], 0));
        actions = me.getBatchOrder().split(',');
        aLen = actions.length;
        for (a = 0; a < aLen; a++) {
            action = actions[a];
            records = options.operations[action];
            if (records) {
                if (useBatch) {
                    batch.add(me.createOperation(action, {
                        records: records,
                        
                        params: options.params
                    }));
                } else {
                    rLen = records.length;
                    for (r = 0; r < rLen; r++) {
                        record = records[r];
                        batch.add(me.createOperation(action, {
                            records: [
                                record
                            ],
                            
                            params: options.params
                        }));
                    }
                }
            }
        }
        batch.start();
        return batch;
    },
    
    onBatchComplete: function(batchOptions, batch) {
        var scope = batchOptions.scope || this;
        if (batch.hasException()) {
            if (Ext.isFunction(batchOptions.failure)) {
                Ext.callback(batchOptions.failure, scope, [
                    batch,
                    batchOptions
                ]);
            }
        } else if (Ext.isFunction(batchOptions.success)) {
            Ext.callback(batchOptions.success, scope, [
                batch,
                batchOptions
            ]);
        }
        if (Ext.isFunction(batchOptions.callback)) {
            Ext.callback(batchOptions.callback, scope, [
                batch,
                batchOptions
            ]);
        }
    },
    createOperation: function(action, config) {
        var operation = Ext.createByAlias('data.operation.' + action, config);
        operation.setProxy(this);
        return operation;
    },
    clone: function() {
        return new this.self(this.getInitialConfig());
    }
});


Ext.define('Ext.data.proxy.Client', {
    extend: 'Ext.data.proxy.Proxy',
    alternateClassName: 'Ext.data.ClientProxy',
    
    isSynchronous: true,
    
    clear: function() {
        Ext.Error.raise("The Ext.data.proxy.Client subclass that you are using has not defined a 'clear' function. See src/data/ClientProxy.js for details.");
    }
});


Ext.define('Ext.data.proxy.Memory', {
    extend: 'Ext.data.proxy.Client',
    alias: 'proxy.memory',
    alternateClassName: 'Ext.data.MemoryProxy',
    isMemoryProxy: true,
    config: {
        
        enablePaging: false,
        
        data: {
            $value: null,
            
            merge: function(newValue, currentValue, target, mixinClass) {
                if (Ext.isArray(newValue)) {
                    return Ext.Array.clone(newValue);
                } else {
                    return Ext.clone(newValue);
                }
            }
        }
    },
    
    finishOperation: function(operation) {
        var i = 0,
            recs = operation.getRecords(),
            len = recs.length;
        for (i; i < len; i++) {
            recs[i].commit();
        }
        operation.setSuccessful(true);
    },
    
    create: function(operation) {
        this.finishOperation(operation);
    },
    
    update: function(operation) {
        this.finishOperation(operation);
    },
    
    erase: function(operation) {
        this.finishOperation(operation);
    },
    
    read: function(operation) {
        var me = this,
            resultSet = me.getReader().read(me.getData()),
            records = resultSet.getRecords(),
            sorters = operation.getSorters(),
            grouper = operation.getGrouper(),
            filters = operation.getFilters(),
            start = operation.getStart(),
            limit = operation.getLimit();
        
        if (operation.process(resultSet, null, null, false) !== false) {
            
            if (filters && filters.length) {
                
                resultSet.setRecords(records = Ext.Array.filter(records, Ext.util.Filter.createFilterFn(filters)));
                resultSet.setTotal(records.length);
            }
            
            if (grouper) {
                
                sorters = sorters ? sorters.concat(grouper) : sorters;
            }
            
            if (sorters && sorters.length) {
                resultSet.setRecords(records = Ext.Array.sort(records, Ext.util.Sortable.createComparator(sorters)));
            }
            
            
            if (me.getEnablePaging() && start !== undefined && limit !== undefined) {
                
                if (start >= resultSet.getTotal()) {
                    resultSet.setConfig({
                        success: false,
                        records: [],
                        total: 0
                    });
                } else 
                {
                    resultSet.setRecords(Ext.Array.slice(records, start, start + limit));
                }
            }
            operation.setCompleted();
        }
    },
    clear: Ext.emptyFn
});


Ext.define('Ext.data.ProxyStore', {
    extend: 'Ext.data.AbstractStore',
    requires: [
        'Ext.data.Model',
        'Ext.data.proxy.Proxy',
        'Ext.data.proxy.Memory',
        'Ext.data.operation.*'
    ],
    config: {
        
        
        model: undefined,
        
        fields: null,
        
        
        proxy: undefined,
        
        autoLoad: undefined,
        
        autoSync: false,
        
        batchUpdateMode: 'operation',
        
        sortOnLoad: true,
        
        trackRemoved: true,
        
        autoLoadDelay: 1
    },
    onClassExtended: function(cls, data, hooks) {
        var model = data.model,
            onBeforeClassCreated;
        if (typeof model === 'string') {
            onBeforeClassCreated = hooks.onBeforeCreated;
            hooks.onBeforeCreated = function() {
                var me = this,
                    args = arguments;
                Ext.require(model, function() {
                    onBeforeClassCreated.apply(me, args);
                });
            };
        }
    },
    
    implicitModel: false,
    blockLoadCounter: 0,
    loadsWhileBlocked: 0,
    
    
    autoSyncSuspended: 0,
    
    constructor: function(config) {
        var me = this;
        var configModel = me.model;
        
        
        
        
        
        
        me.removed = [];
        me.blockLoad();
        me.callParent(arguments);
        me.unblockLoad();
        if (!me.getModel() && me.useModelWarning !== false && me.getStoreId() !== 'ext-empty-store') {
            
            var logMsg = [
                    Ext.getClassName(me) || 'Store',
                    ' created with no model.'
                ];
            if (typeof configModel === 'string') {
                logMsg.push(" The name '", configModel, "'", ' does not correspond to a valid model.');
            }
            Ext.log.warn(logMsg.join(''));
        }
    },
    updateAutoLoad: function(autoLoad) {
        var me = this,
            task;
        
        me.getData();
        if (autoLoad) {
            task = me.loadTask || (me.loadTask = new Ext.util.DelayedTask(null,null,null,null,false));
            
            task.delay(me.autoLoadDelay, me.attemptLoad, me, Ext.isObject(autoLoad) ? [
                autoLoad
            ] : undefined);
        }
    },
    
    getTotalCount: function() {
        return this.totalCount || 0;
    },
    applyFields: function(fields) {
        var me = this,
            model, proxy;
        
        if (fields) {
            me.implicitModel = true;
            me.setModel(model = Ext.define(null, {
                extend: 'Ext.data.Model',
                fields: fields,
                proxy: (proxy = me.getProxy())
            }));
            
            if (proxy && !proxy.getModel()) {
                proxy.setModel(model);
            }
        }
    },
    applyModel: function(model) {
        if (model) {
            model = Ext.data.schema.Schema.lookupEntity(model);
        } else 
        {
            this.getFields();
            model = this.getModel();
        }
        return model;
    },
    applyProxy: function(proxy) {
        var model = this.getModel();
        if (proxy !== null) {
            if (proxy) {
                if (proxy.isProxy) {
                    proxy.setModel(model);
                } else {
                    if (Ext.isString(proxy)) {
                        proxy = {
                            type: proxy,
                            model: model
                        };
                    } else if (!proxy.model) {
                        proxy = Ext.apply({
                            model: model
                        }, proxy);
                    }
                    proxy = Ext.createByAlias('proxy.' + proxy.type, proxy);
                    proxy.autoCreated = true;
                }
            } else if (model) {
                proxy = model.getProxy();
            }
            if (!proxy) {
                proxy = Ext.createByAlias('proxy.memory');
                proxy.autoCreated = true;
            }
        }
        return proxy;
    },
    updateProxy: function(proxy, oldProxy) {
        var me = this,
            listeners = {
                scope: me,
                beginprocessresponse: me.beginUpdate,
                endprocessresponse: me.endUpdate
            };
        if (!me.disableMetaChangeEvent) {
            listeners.metachange = me.onMetaChange;
        }
        if (oldProxy) {
            oldProxy.un(listeners);
        }
        
        if (proxy) {
            proxy.on(listeners);
        }
    },
    updateTrackRemoved: function(track) {
        this.removed = track ? [] : null;
    },
    
    onMetaChange: function(proxy, meta) {
        this.fireEvent('metachange', this, meta);
    },
    
    create: function(data, options) {
        var me = this,
            instance = new me.model(data),
            proxy = me.getProxy(),
            operation;
        options = Ext.apply({}, options);
        if (!options.records) {
            options.records = [
                instance
            ];
        }
        options.internalScope = me;
        options.internalCallback = me.onProxyWrite;
        operation = proxy.createOperation('create', options);
        return operation.execute();
    },
    read: function() {
        return this.load.apply(this, arguments);
    },
    update: function(options) {
        var me = this,
            proxy = me.getProxy(),
            operation;
        options = Ext.apply({}, options);
        if (!options.records) {
            options.records = me.getUpdatedRecords();
        }
        options.internalScope = me;
        options.internalCallback = me.onProxyWrite;
        operation = proxy.createOperation('update', options);
        return operation.execute();
    },
    
    onProxyWrite: function(operation) {
        var me = this,
            success = operation.wasSuccessful(),
            records = operation.getRecords();
        switch (operation.getAction()) {
            case 'create':
                me.onCreateRecords(records, operation, success);
                break;
            case 'update':
                me.onUpdateRecords(records, operation, success);
                break;
            case 'destroy':
                me.onDestroyRecords(records, operation, success);
                break;
        }
        if (success) {
            me.fireEvent('write', me, operation);
            me.fireEvent('datachanged', me);
        }
    },
    
    onCreateRecords: Ext.emptyFn,
    
    onUpdateRecords: Ext.emptyFn,
    
    onDestroyRecords: function(records, operation, success) {
        if (success) {
            this.removed.length = 0;
        }
    },
    
    
    erase: function(options) {
        var me = this,
            proxy = me.getProxy(),
            operation;
        options = Ext.apply({}, options);
        if (!options.records) {
            options.records = me.getRemovedRecords();
        }
        options.internalScope = me;
        options.internalCallback = me.onProxyWrite;
        operation = proxy.createOperation('destroy', options);
        return operation.execute();
    },
    
    onBatchOperationComplete: function(batch, operation) {
        return this.onProxyWrite(operation);
    },
    
    onBatchComplete: function(batch, operation) {
        var me = this,
            operations = batch.operations,
            length = operations.length,
            i;
        if (me.batchUpdateMode !== 'operation') {
            me.suspendEvents();
            for (i = 0; i < length; i++) {
                me.onProxyWrite(operations[i]);
            }
            me.resumeEvents();
        }
        me.isSyncing = false;
        me.fireEvent('datachanged', me);
    },
    
    onBatchException: function(batch, operation) {},
    
    
    
    
    
    
    filterNew: function(item) {
        
        return item.phantom === true && item.isValid();
    },
    
    getNewRecords: function() {
        return [];
    },
    
    getUpdatedRecords: function() {
        return [];
    },
    
    getModifiedRecords: function() {
        return [].concat(this.getNewRecords(), this.getUpdatedRecords());
    },
    
    filterUpdated: function(item) {
        
        return item.dirty === true && item.phantom !== true && item.isValid();
    },
    
    getRemovedRecords: function() {
        return this.removed;
    },
    
    sync: function(options) {
        var me = this,
            operations = {},
            toCreate = me.getNewRecords(),
            toUpdate = me.getUpdatedRecords(),
            toDestroy = me.getRemovedRecords(),
            needsSync = false;
        if (me.isSyncing) {
            Ext.log.warn('Sync called while a sync operation is in progress. Consider configuring autoSync as false.');
        }
        me.needsSync = false;
        if (toCreate.length > 0) {
            operations.create = toCreate;
            needsSync = true;
        }
        if (toUpdate.length > 0) {
            operations.update = toUpdate;
            needsSync = true;
        }
        if (toDestroy.length > 0) {
            operations.destroy = toDestroy;
            needsSync = true;
        }
        if (needsSync && me.fireEvent('beforesync', operations) !== false) {
            me.isSyncing = true;
            options = options || {};
            me.proxy.batch(Ext.apply(options, {
                operations: operations,
                listeners: me.getBatchListeners()
            }));
        }
        return me;
    },
    
    getBatchListeners: function() {
        var me = this,
            listeners = {
                scope: me,
                exception: me.onBatchException,
                complete: me.onBatchComplete
            };
        if (me.batchUpdateMode === 'operation') {
            listeners.operationcomplete = me.onBatchOperationComplete;
        }
        return listeners;
    },
    
    save: function() {
        return this.sync.apply(this, arguments);
    },
    
    load: function(options) {
        
        if (this.isLoadBlocked()) {
            return;
        }
        var me = this,
            proxy = me.getProxy(),
            operation = {
                internalScope: me,
                internalCallback: me.onProxyLoad
            },
            filters, sorters;
        
        if (me.getRemoteFilter()) {
            filters = me.getFilters();
            if (filters.getCount()) {
                operation.filters = filters.getRange();
            }
        }
        if (me.getRemoteSort()) {
            sorters = me.getSorters();
            if (sorters.getCount()) {
                operation.sorters = sorters.getRange();
            }
            me.fireEvent('beforesort', me, operation.sorters);
        }
        Ext.apply(operation, options);
        operation.scope = operation.scope || me;
        me.lastOptions = operation;
        operation = proxy.createOperation('read', operation);
        if (me.fireEvent('beforeload', me, operation) !== false) {
            me.onBeforeLoad(operation);
            me.loading = true;
            me.clearLoadTask();
            operation.execute();
        }
        return me;
    },
    
    reload: function(options) {
        var o = Ext.apply({}, options, this.lastOptions);
        return this.load(o);
    },
    onEndUpdate: function() {
        var me = this;
        if (me.needsSync && me.autoSync && !me.autoSyncSuspended) {
            me.sync();
        }
    },
    
    afterReject: function(record) {
        var me = this;
        
        
        
        
        
        if (me.contains(record)) {
            me.onUpdate(record, Ext.data.Model.REJECT, null);
            me.fireEvent('update', me, record, Ext.data.Model.REJECT, null);
        }
    },
    
    afterCommit: function(record, modifiedFieldNames) {
        var me = this;
        if (!modifiedFieldNames) {
            modifiedFieldNames = null;
        }
        if (me.contains(record)) {
            me.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames);
            me.fireEvent('update', me, record, Ext.data.Model.COMMIT, modifiedFieldNames);
        }
    },
    afterErase: function(record) {
        this.onErase(record);
    },
    onErase: Ext.emptyFn,
    onUpdate: Ext.emptyFn,
    
    onDestroy: function() {
        var me = this,
            proxy = me.getProxy();
        me.blockLoad();
        me.clearData();
        me.setProxy(null);
        if (proxy.autoCreated) {
            proxy.destroy();
        }
        me.setModel(null);
    },
    
    hasPendingLoad: function() {
        return !!this.loadTask || this.isLoading();
    },
    
    isLoading: function() {
        return !!this.loading;
    },
    
    isLoaded: function() {
        return this.loadCount > 0;
    },
    
    suspendAutoSync: function() {
        ++this.autoSyncSuspended;
    },
    
    resumeAutoSync: function(syncNow) {
        var me = this;
        if (!me.autoSyncSuspended) {
            Ext.log.warn('Mismatched call to resumeAutoSync - auto synchronization is currently not suspended.');
        }
        if (me.autoSyncSuspended && !--me.autoSyncSuspended) {
            if (syncNow) {
                me.sync();
            }
        }
    },
    
    removeAll: Ext.emptyFn,
    
    
    
    clearData: Ext.emptyFn,
    privates: {
        attemptLoad: function(options) {
            if (this.isLoadBlocked()) {
                ++this.loadsWhileBlocked;
                return;
            }
            this.load(options);
        },
        blockLoad: function(value) {
            ++this.blockLoadCounter;
        },
        clearLoadTask: function() {
            var loadTask = this.loadTask;
            if (loadTask) {
                loadTask.cancel();
                this.loadTask = null;
            }
        },
        isLoadBlocked: function() {
            return !!this.blockLoadCounter;
        },
        loadsSynchronously: function() {
            return this.getProxy().isSynchronous;
        },
        onBeforeLoad: Ext.privateFn,
        unblockLoad: function(doLoad) {
            var me = this,
                loadsWhileBlocked = me.loadsWhileBlocked;
            --me.blockLoadCounter;
            if (!me.blockLoadCounter) {
                me.loadsWhileBlocked = 0;
                if (doLoad && loadsWhileBlocked) {
                    me.load();
                }
            }
        }
    }
});


Ext.define('Ext.data.LocalStore', {
    extend: 'Ext.Mixin',
    mixinConfig: {
        id: 'localstore'
    },
    config: {
        extraKeys: null
    },
    applyExtraKeys: function(extraKeys) {
        var indexName,
            data = this.getData();
        
        data.setExtraKeys(extraKeys);
        
        extraKeys = data.getExtraKeys();
        for (indexName in extraKeys) {
            this[indexName] = extraKeys[indexName];
        }
    },
    
    add: function(arg) {
        return this.insert(this.getCount(), arguments.length === 1 ? arg : arguments);
    },
    constructDataCollection: function() {
        var data = new Ext.util.Collection({
                rootProperty: 'data',
                extraKeys: {
                    byInternalId: {
                        property: 'internalId',
                        rootProperty: ''
                    }
                }
            }),
            sorters;
        this.byInternalId = data.byInternalId;
        sorters = data.getSorters();
        sorters.setSorterConfigure(this.addFieldTransform, this);
        return data;
    },
    
    createModel: function(record) {
        var session = this.getSession();
        if (!record.isModel) {
            var Model = this.getModel();
            record = new Model(record,session);
        }
        return record;
    },
    createFiltersCollection: function() {
        return this.getData().getFilters();
    },
    createSortersCollection: function() {
        return this.getData().getSorters();
    },
    
    
    onCollectionSort: function() {
        this.onSorterEndUpdate();
    },
    
    
    onCollectionFilter: function() {
        this.onFilterEndUpdate();
    },
    notifySorterChange: function() {
        this.getData().onSorterChange();
    },
    forceLocalSort: function() {
        this.getData().onSortChange();
    },
    
    contains: function(record) {
        return this.indexOf(record) > -1;
    },
    
    each: function(fn, scope) {
        var data = this.data.items,
            len = data.length,
            record, i;
        for (i = 0; i < len; ++i) {
            record = data[i];
            if (fn.call(scope || record, record, i, len) === false) {
                break;
            }
        }
    },
    
    collect: function(dataIndex, allowNull, bypassFilter) {
        var me = this,
            data = me.getData();
        if (bypassFilter === true && data.filtered) {
            data = data.getSource();
        }
        return data.collect(dataIndex, 'data', allowNull);
    },
    
    getById: function(id) {
        var data = this.getData();
        if (data.filtered) {
            data = data.getSource();
        }
        return data.get(id) || null;
    },
    
    getByInternalId: function(internalId) {
        var data = this.getData();
        if (data.filtered) {
            data = data.getSource();
        }
        return data.byInternalId.get(internalId) || null;
    },
    
    indexOf: function(record) {
        return this.getData().indexOf(record);
    },
    
    indexOfId: function(id) {
        return this.indexOf(this.getById(id));
    },
    
    insert: function(index, records) {
        var me = this,
            len, i;
        if (records) {
            if (!Ext.isIterable(records)) {
                records = [
                    records
                ];
            } else {
                records = Ext.Array.clone(records);
            }
            len = records.length;
        }
        if (!len) {
            return [];
        }
        for (i = 0; i < len; ++i) {
            records[i] = me.createModel(records[i]);
        }
        me.getData().insert(index, records);
        return records;
    },
    
    queryBy: function(fn, scope) {
        var data = this.getData();
        return (data.getSource() || data).createFiltered(fn, scope);
    },
    
    query: function(property, value, anyMatch, caseSensitive, exactMatch) {
        var data = this.getData();
        return (data.getSource() || data).createFiltered(property, value, anyMatch, caseSensitive, exactMatch);
    },
    
    first: function(grouped) {
        return this.getData().first(grouped) || null;
    },
    
    last: function(grouped) {
        return this.getData().last(grouped) || null;
    },
    
    sum: function(field, grouped) {
        var data = this.getData();
        return (grouped && this.isGrouped()) ? data.sumByGroup(field) : data.sum(field);
    },
    
    count: function(grouped) {
        var data = this.getData();
        return (grouped && this.isGrouped()) ? data.countByGroup() : data.count();
    },
    
    min: function(field, grouped) {
        var data = this.getData();
        return (grouped && this.isGrouped()) ? data.minByGroup(field) : data.min(field);
    },
    
    max: function(field, grouped) {
        var data = this.getData();
        return (grouped && this.isGrouped()) ? data.maxByGroup(field) : data.max(field);
    },
    
    average: function(field, grouped) {
        var data = this.getData();
        return (grouped && this.isGrouped()) ? data.averageByGroup(field) : data.average(field);
    },
    
    aggregate: function(fn, scope, grouped, field) {
        var me = this,
            groups, len, out, group, i;
        if (grouped && me.isGrouped()) {
            groups = me.getGroups().items;
            len = groups.length;
            out = {};
            for (i = 0; i < len; ++i) {
                group = groups[i];
                out[group.getGroupKey()] = me.getAggregate(fn, scope || me, group.items, field);
            }
            return out;
        } else {
            return me.getAggregate(fn, scope, me.getData().items, field);
        }
    },
    getAggregate: function(fn, scope, records, field) {
        var values = [],
            len = records.length,
            i;
        
        for (i = 0; i < len; ++i) {
            values[i] = records[i].get(field);
        }
        return fn.call(scope || this, records, values);
    },
    addObserver: function(observer) {
        var observers = this.observers;
        if (!observers) {
            this.observers = observers = new Ext.util.Collection();
        }
        observers.add(observer);
    },
    removeObserver: function(observer) {
        var observers = this.observers;
        if (observers) {
            observers.remove(observer);
        }
    },
    callObservers: function(action, args) {
        var observers = this.observers,
            len, items, i, methodName, item;
        if (observers) {
            items = observers.items;
            if (args) {
                args.unshift(this);
            } else {
                args = [
                    this
                ];
            }
            for (i = 0 , len = items.length; i < len; ++i) {
                item = items[i];
                methodName = 'onSource' + action;
                if (item[methodName]) {
                    item[methodName].apply(item, args);
                }
            }
        }
    },
    
    queryRecordsBy: function(fn, scope) {
        var data = this.getData(),
            matches = [],
            len, i, record;
        data = (data.getSource() || data).items;
        scope = scope || this;
        for (i = 0 , len = data.length; i < len; ++i) {
            record = data[i];
            if (fn.call(scope, record) === true) {
                matches.push(record);
            }
        }
        return matches;
    },
    
    queryRecords: function(field, value) {
        var data = this.getData(),
            matches = [],
            len, i, record;
        data = (data.getSource() || data).items;
        for (i = 0 , len = data.length; i < len; ++i) {
            record = data[i];
            if (record.get(field) === value) {
                matches.push(record);
            }
        }
        return matches;
    }
});


Ext.define('Ext.data.proxy.Server', {
    extend: 'Ext.data.proxy.Proxy',
    alias: 'proxy.server',
    alternateClassName: 'Ext.data.ServerProxy',
    uses: [
        'Ext.data.Request'
    ],
    isRemote: true,
    config: {
        
        url: '',
        
        pageParam: 'page',
        
        startParam: 'start',
        
        limitParam: 'limit',
        
        groupParam: 'group',
        
        groupDirectionParam: 'groupDir',
        
        sortParam: 'sort',
        
        filterParam: 'filter',
        
        directionParam: 'dir',
        
        idParam: 'id',
        
        simpleSortMode: false,
        
        simpleGroupMode: false,
        
        noCache: true,
        
        cacheString: "_dc",
        
        timeout: 30000,
        
        api: {
            create: undefined,
            read: undefined,
            update: undefined,
            destroy: undefined
        },
        
        extraParams: {}
    },
    
    
    create: function() {
        return this.doRequest.apply(this, arguments);
    },
    read: function() {
        return this.doRequest.apply(this, arguments);
    },
    update: function() {
        return this.doRequest.apply(this, arguments);
    },
    erase: function() {
        return this.doRequest.apply(this, arguments);
    },
    
    setExtraParam: function(name, value) {
        this.getExtraParams()[name] = value;
    },
    
    buildRequest: function(operation) {
        var me = this,
            initialParams = Ext.apply({}, operation.getParams()),
            
            params = Ext.applyIf(initialParams, me.getExtraParams() || {}),
            request, operationId, idParam;
        
        Ext.applyIf(params, me.getParams(operation));
        
        
        
        operationId = operation.getId();
        idParam = me.getIdParam();
        if (operationId !== undefined && params[idParam] === undefined) {
            params[idParam] = operationId;
        }
        request = new Ext.data.Request({
            params: params,
            action: operation.getAction(),
            records: operation.getRecords(),
            url: operation.getUrl(),
            operation: operation,
            
            
            proxy: me
        });
        request.setUrl(me.buildUrl(request));
        
        operation.setRequest(request);
        return request;
    },
    
    processResponse: function(success, operation, request, response) {
        var me = this,
            exception, reader, resultSet;
        
        
        
        
        
        
        
        me.fireEvent('beginprocessresponse', me, response, operation);
        if (success === true) {
            reader = me.getReader();
            if (response.status === 204) {
                resultSet = reader.getNullResultSet();
            } else {
                resultSet = reader.read(me.extractResponseData(response), {
                    
                    recordCreator: operation.getRecordCreator()
                });
            }
            operation.process(resultSet, request, response);
            exception = !operation.wasSuccessful();
        } else {
            me.setException(operation, response);
            exception = true;
        }
        if (exception) {
            me.fireEvent('exception', me, response, operation);
        }
        me.afterRequest(request, success);
        
        
        
        me.fireEvent('endprocessresponse', me, response, operation);
    },
    
    setException: function(operation, response) {
        operation.setException({
            status: response.status,
            statusText: response.statusText,
            response: response
        });
    },
    
    extractResponseData: Ext.identityFn,
    
    applyEncoding: function(value) {
        return Ext.encode(value);
    },
    
    encodeSorters: function(sorters, preventArray) {
        var out = [],
            length = sorters.length,
            i;
        for (i = 0; i < length; i++) {
            out[i] = sorters[i].serialize();
        }
        return this.applyEncoding(preventArray ? out[0] : out);
    },
    
    encodeFilters: function(filters) {
        var out = [],
            length = filters.length,
            i, op;
        for (i = 0; i < length; i++) {
            out[i] = filters[i].serialize();
        }
        return this.applyEncoding(out);
    },
    
    getParams: function(operation) {
        if (!operation.isReadOperation) {
            return {};
        }
        var me = this,
            params = {},
            grouper = operation.getGrouper(),
            sorters = operation.getSorters(),
            filters = operation.getFilters(),
            page = operation.getPage(),
            start = operation.getStart(),
            limit = operation.getLimit(),
            simpleSortMode = me.getSimpleSortMode(),
            simpleGroupMode = me.getSimpleGroupMode(),
            pageParam = me.getPageParam(),
            startParam = me.getStartParam(),
            limitParam = me.getLimitParam(),
            groupParam = me.getGroupParam(),
            groupDirectionParam = me.getGroupDirectionParam(),
            sortParam = me.getSortParam(),
            filterParam = me.getFilterParam(),
            directionParam = me.getDirectionParam(),
            hasGroups, index;
        if (pageParam && page) {
            params[pageParam] = page;
        }
        if (startParam && (start || start === 0)) {
            params[startParam] = start;
        }
        if (limitParam && limit) {
            params[limitParam] = limit;
        }
        hasGroups = groupParam && grouper;
        if (hasGroups) {
            
            if (simpleGroupMode) {
                params[groupParam] = grouper.getProperty();
                params[groupDirectionParam] = grouper.getDirection();
            } else {
                params[groupParam] = me.encodeSorters([
                    grouper
                ], true);
            }
        }
        if (sortParam && sorters && sorters.length > 0) {
            if (simpleSortMode) {
                index = 0;
                
                if (sorters.length > 1 && hasGroups) {
                    index = 1;
                }
                params[sortParam] = sorters[index].getProperty();
                params[directionParam] = sorters[index].getDirection();
            } else {
                params[sortParam] = me.encodeSorters(sorters);
            }
        }
        if (filterParam && filters && filters.length > 0) {
            params[filterParam] = me.encodeFilters(filters);
        }
        return params;
    },
    
    buildUrl: function(request) {
        var me = this,
            url = me.getUrl(request);
        if (!url) {
            Ext.Error.raise("You are using a ServerProxy but have not supplied it with a url.");
        }
        if (me.getNoCache()) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.getCacheString(), Ext.Date.now()));
        }
        return url;
    },
    
    getUrl: function(request) {
        var url;
        if (request) {
            url = request.getUrl() || this.getApi()[request.getAction()];
        }
        return url ? url : this.callParent();
    },
    
    doRequest: function(operation) {
        Ext.Error.raise("The doRequest function has not been implemented on your Ext.data.proxy.Server subclass. See src/data/ServerProxy.js for details");
    },
    
    afterRequest: Ext.emptyFn,
    onDestroy: function() {
        Ext.destroy(this.getReader(), this.getWriter());
    }
});


Ext.define('Ext.data.proxy.Ajax', {
    requires: [
        'Ext.Ajax'
    ],
    extend: 'Ext.data.proxy.Server',
    alias: 'proxy.ajax',
    alternateClassName: [
        'Ext.data.HttpProxy',
        'Ext.data.AjaxProxy'
    ],
    
    
    
    
    defaultActionMethods: {
        create: 'POST',
        read: 'GET',
        update: 'POST',
        destroy: 'POST'
    },
    config: {
        
        binary: false,
        
        headers: undefined,
        
        paramsAsJson: false,
        
        withCredentials: false,
        
        useDefaultXhrHeader: true,
        
        username: null,
        
        password: null,
        
        actionMethods: {
            create: 'POST',
            read: 'GET',
            update: 'POST',
            destroy: 'POST'
        }
    },
    doRequest: function(operation) {
        var me = this,
            writer = me.getWriter(),
            request = me.buildRequest(operation),
            method = me.getMethod(request),
            jsonData, params;
        if (writer && operation.allowWrite()) {
            request = writer.write(request);
        }
        request.setConfig({
            binary: me.getBinary(),
            headers: me.getHeaders(),
            timeout: me.getTimeout(),
            scope: me,
            callback: me.createRequestCallback(request, operation),
            method: method,
            useDefaultXhrHeader: me.getUseDefaultXhrHeader(),
            disableCaching: false
        });
        
        if (method.toUpperCase() !== 'GET' && me.getParamsAsJson()) {
            params = request.getParams();
            if (params) {
                jsonData = request.getJsonData();
                if (jsonData) {
                    jsonData = Ext.Object.merge({}, jsonData, params);
                } else {
                    jsonData = params;
                }
                request.setJsonData(jsonData);
                request.setParams(undefined);
            }
        }
        if (me.getWithCredentials()) {
            request.setWithCredentials(true);
            request.setUsername(me.getUsername());
            request.setPassword(me.getPassword());
        }
        return me.sendRequest(request);
    },
    
    sendRequest: function(request) {
        request.setRawRequest(Ext.Ajax.request(request.getCurrentConfig()));
        this.lastRequest = request;
        return request;
    },
    
    abort: function(request) {
        request = request || this.lastRequest;
        if (request) {
            Ext.Ajax.abort(request.getRawRequest());
        }
    },
    
    getMethod: function(request) {
        var actions = this.getActionMethods(),
            action = request.getAction(),
            method;
        if (actions) {
            method = actions[action];
        }
        return method || this.defaultActionMethods[action];
    },
    
    createRequestCallback: function(request, operation) {
        var me = this;
        return function(options, success, response) {
            if (request === me.lastRequest) {
                me.lastRequest = null;
            }
            me.processResponse(success, operation, request, response);
        };
    }
});


Ext.define('Ext.data.reader.Json', {
    extend: 'Ext.data.reader.Reader',
    requires: [
        'Ext.JSON'
    ],
    alternateClassName: 'Ext.data.JsonReader',
    alias: 'reader.json',
    config: {
        
        record: null,
        
        metaProperty: 'metaData',
        
        useSimpleAccessors: false,
        
        preserveRawData: false
    },
    updateRootProperty: function() {
        this.forceBuildExtractors();
    },
    updateMetaProperty: function() {
        this.forceBuildExtractors();
    },
    
    readRecords: function(data, readOptions) {
        var me = this,
            meta;
        
        if (me.getMeta) {
            meta = me.getMeta(data);
            if (meta) {
                me.onMetaChange(meta);
            }
        } else if (data.metaData) {
            me.onMetaChange(data.metaData);
        }
        return me.callParent([
            data,
            readOptions
        ]);
    },
    
    getResponseData: function(response) {
        try {
            return Ext.decode(response.responseText);
        } catch (ex) {
            Ext.Logger.warn('Unable to parse the JSON returned by the server');
            return this.createReadError(ex.message);
        }
    },
    
    buildExtractors: function() {
        var me = this,
            metaProp = me.getMetaProperty(),
            root = me.getRootProperty();
        
        if (me.callParent(arguments)) {
            if (root) {
                me.getRoot = me.createAccessor(root);
            } else {
                me.getRoot = Ext.identityFn;
            }
            if (metaProp) {
                me.getMeta = me.createAccessor(metaProp);
            }
        }
    },
    
    extractData: function(root, readOptions) {
        var recordName = this.getRecord(),
            data = [],
            length, i;
        if (recordName) {
            length = root.length;
            if (!length && Ext.isObject(root)) {
                length = 1;
                root = [
                    root
                ];
            }
            for (i = 0; i < length; i++) {
                data[i] = root[i][recordName];
            }
        } else {
            data = root;
        }
        return this.callParent([
            data,
            readOptions
        ]);
    },
    getModelData: function(raw) {
        return this.getPreserveRawData() ? Ext.apply({}, raw) : raw;
    },
    
    createAccessor: (function() {
        var re = /[\[\.]/;
        return function(expr) {
            var me = this,
                simple = me.getUseSimpleAccessors(),
                operatorIndex, result, current, parts, part, inExpr, isDot, isLeft, isRight, special, c, i, bracketed, len;
            if (!(expr || expr === 0)) {
                return;
            }
            if (typeof expr === 'function') {
                return expr;
            }
            if (!simple) {
                operatorIndex = String(expr).search(re);
            }
            if (simple === true || operatorIndex < 0) {
                result = function(raw) {
                    return raw[expr];
                };
            } else {
                
                
                
                current = 'raw';
                parts = [];
                part = '';
                inExpr = 0;
                len = expr.length;
                
                
                
                for (i = 0; i <= len; ++i) {
                    c = expr[i];
                    isDot = c === '.';
                    isLeft = c === '[';
                    isRight = c === ']';
                    special = isDot || isLeft || isRight || !c;
                    
                    
                    
                    
                    
                    if (!special || inExpr > 1 || (inExpr && !isRight)) {
                        part += c;
                    } else if (special) {
                        bracketed = false;
                        if (isLeft) {
                            ++inExpr;
                        } else if (isRight) {
                            --inExpr;
                            bracketed = true;
                        }
                        if (part) {
                            if (bracketed) {
                                part = '[' + part + ']';
                            } else {
                                part = '.' + part;
                            }
                            current += part;
                            
                            
                            
                            
                            parts.push('' + current);
                            part = '';
                        }
                    }
                }
                result = parts.join(' && ');
                result = Ext.functionFactory('raw', 'return ' + result);
            }
            return result;
        };
    }()),
    
    createFieldAccessor: function(field) {
        
        var me = this,
            mapping = field.mapping,
            hasMap = mapping || mapping === 0,
            map = hasMap ? mapping : field.name;
        if (hasMap) {
            if (typeof map === 'function') {
                return function(raw) {
                    return field.mapping(raw, me);
                };
            } else {
                return me.createAccessor(map);
            }
        }
    },
    privates: {
        copyFrom: function(reader) {
            var me = this,
                result;
            me.duringInit++;
            result = this.callParent([
                reader
            ]);
            me.getRoot = reader.getRoot;
            me.setRootProperty(reader.getRootProperty());
            --me.duringInit;
        }
    }
});


Ext.define('Ext.data.writer.Json', {
    extend: 'Ext.data.writer.Writer',
    alternateClassName: 'Ext.data.JsonWriter',
    alias: 'writer.json',
    config: {
        
        rootProperty: undefined,
        
        encode: false,
        
        allowSingle: true,
        
        expandData: false
    },
    constructor: function(config) {
        if (config && config.hasOwnProperty('root')) {
            config = Ext.apply({}, config);
            config.rootProperty = config.root;
            delete config.root;
            Ext.log.warn('Ext.data.writer.Json: Using the deprecated "root" configuration. Use "rootProperty" instead.');
        }
        this.callParent([
            config
        ]);
    },
    
    getExpandedData: function(data) {
        var dataLength = data.length,
            i = 0,
            item, prop, nameParts, j, tempObj,
            toObject = function(name, value) {
                var o = {};
                o[name] = value;
                return o;
            };
        for (; i < dataLength; i++) {
            item = data[i];
            for (prop in item) {
                if (item.hasOwnProperty(prop)) {
                    
                    nameParts = prop.split('.');
                    j = nameParts.length - 1;
                    if (j > 0) {
                        
                        
                        tempObj = item[prop];
                        for (; j > 0; j--) {
                            
                            
                            
                            tempObj = toObject(nameParts[j], tempObj);
                        }
                        
                        
                        
                        item[nameParts[0]] = item[nameParts[0]] || {};
                        
                        
                        Ext.Object.merge(item[nameParts[0]], tempObj);
                        
                        delete item[prop];
                    }
                }
            }
        }
        return data;
    },
    writeRecords: function(request, data) {
        var me = this,
            root = me.getRootProperty(),
            json, single, transform;
        if (me.getExpandData()) {
            data = me.getExpandedData(data);
        }
        if (me.getAllowSingle() && data.length === 1) {
            
            data = data[0];
            single = true;
        }
        transform = this.getTransform();
        if (transform) {
            data = transform(data, request);
        }
        if (me.getEncode()) {
            if (root) {
                
                request.setParam(root, Ext.encode(data));
            } else {
                Ext.Error.raise('Must specify a root when using encode');
            }
        } else if (single || (data && data.length)) {
            
            json = request.getJsonData() || {};
            if (root) {
                json[root] = data;
            } else {
                json = data;
            }
            request.setJsonData(json);
        }
        return request;
    }
});


Ext.define('Ext.util.Group', {
    extend: 'Ext.util.Collection',
    config: {
        groupKey: null
    },
    
    
    
    
    $endUpdatePriority: 2001
});


Ext.define('Ext.util.SorterCollection', {
    extend: 'Ext.util.Collection',
    requires: [
        'Ext.util.Sorter'
    ],
    isSorterCollection: true,
    
    $sortable: null,
    
    sortFn: null,
    config: {
        
        sorterOptionsFn: null,
        
        sorterOptionsScope: null
    },
    constructor: function(config) {
        var me = this;
        me.sortFn = Ext.util.Sorter.createComparator(me);
        me.callParent([
            config
        ]);
        me.setDecoder(me.decodeSorter);
    },
    addSort: function(property, direction, mode) {
        var me = this,
            count, index, limit, options, primary, sorter, sorters;
        if (!property) {
            
            me.beginUpdate();
            me.endUpdate();
        } else {
            options = me.getOptions();
            if (property instanceof Array) {
                sorters = property;
                mode = direction;
                direction = null;
            } else if (Ext.isString(property)) {
                if (!(sorter = me.get(property))) {
                    sorters = [
                        {
                            property: property,
                            direction: direction || options.getDefaultSortDirection()
                        }
                    ];
                } else {
                    sorters = [
                        sorter
                    ];
                }
            } else if (Ext.isFunction(property)) {
                sorters = [
                    {
                        sorterFn: property,
                        direction: direction || options.getDefaultSortDirection()
                    }
                ];
            } else {
                if (!Ext.isObject(property)) {
                    Ext.Error.raise('Invalid sort descriptor: ' + property);
                }
                sorters = [
                    property
                ];
                mode = direction;
                direction = null;
            }
            if (mode && !me._sortModes[mode]) {
                Ext.Error.raise('Sort mode should be "multi", "append", "prepend" or "replace", not "' + mode + '"');
            }
            mode = me._sortModes[mode || 'replace'];
            primary = me.getAt(0);
            count = me.length;
            index = mode.append ? count : 0;
            
            
            me.beginUpdate();
            
            
            me.splice(index, mode.replace ? count : 0, sorters);
            if (mode.multi) {
                count = me.length;
                limit = options.getMultiSortLimit();
                if (count > limit) {
                    me.removeAt(limit, count);
                }
            }
            
            if (sorter && direction) {
                sorter.setDirection(direction);
            } else if (index === 0 && primary && primary === me.getAt(0)) {
                
                
                primary.toggle();
            }
            me.endUpdate();
        }
    },
    
    getSortFn: function() {
        return this.sortFn;
    },
    
    getByProperty: function(prop) {
        var items = this.items,
            len = items.length,
            i, item;
        for (i = 0; i < len; ++i) {
            item = items[i];
            if (item.getProperty() === prop) {
                return item;
            }
        }
        return null;
    },
    
    
    _sortModes: {
        append: {
            append: 1
        },
        multi: {
            multi: 1
        },
        prepend: {
            prepend: 1
        },
        replace: {
            replace: 1
        }
    },
    decodeSorter: function(sorter, xclass) {
        var me = this,
            options = me.getOptions(),
            root = options.getRootProperty(),
            sorterOptionsFn = me.getSorterOptionsFn(),
            currentSorter, sorterConfig, type;
        if (sorter.isSorter) {
            if (!sorter.getRoot()) {
                sorter.setRoot(root);
            }
        } else {
            sorterConfig = {
                direction: options.getDefaultSortDirection(),
                root: root
            };
            type = typeof sorter;
            
            if (type === 'string') {
                currentSorter = me.get(sorter);
                if (currentSorter) {
                    return currentSorter;
                }
                sorterConfig.property = sorter;
            }
            
            else if (type === 'function') {
                sorterConfig.sorterFn = sorter;
            } else 
            
            {
                
                if (!Ext.isObject(sorter)) {
                    Ext.Error.raise('Invalid sorter specified: ' + sorter);
                }
                sorterConfig = Ext.apply(sorterConfig, sorter);
                if (sorterConfig.fn) {
                    sorterConfig.sorterFn = sorterConfig.fn;
                    delete sorterConfig.fn;
                }
            }
            
            sorter = Ext.create(xclass || 'Ext.util.Sorter', sorterConfig);
        }
        if (sorterOptionsFn) {
            sorterOptionsFn.call(me.getSorterOptionsScope() || me, sorter);
        }
        return sorter;
    },
    setSorterConfigure: function(fn, scope) {
        this.setSorterOptionsFn(fn);
        this.setSorterOptionsScope(scope);
    },
    decodeRemoveItems: function(args, index) {
        var me = this,
            ret = (index === undefined) ? args : args[index];
        if (!ret || !ret.$cloned) {
            if (args.length > index + 1 || !Ext.isIterable(ret)) {
                ret = Ext.Array.slice(args, index);
            }
            var currentSorters = me.items,
                ln = ret.length,
                remove = [],
                i, item, n, sorter, type;
            for (i = 0; i < ln; i++) {
                sorter = ret[i];
                if (sorter && sorter.isSorter) {
                    remove.push(sorter);
                } else {
                    type = typeof sorter;
                    if (type === 'string') {
                        sorter = me.get(sorter);
                        if (sorter) {
                            remove.push(sorter);
                        }
                    } else if (type === 'function') {
                        for (n = currentSorters.length; n-- > 0; ) {
                            item = currentSorters[n];
                            if (item.getSorterFn() === sorter) {
                                remove.push(item);
                            }
                        }
                    } else {
                        Ext.Error.raise('Invalid sorter specification: ' + sorter);
                    }
                }
            }
            ret = remove;
            ret.$cloned = true;
        }
        return ret;
    },
    getOptions: function() {
        
        
        
        
        return this.$sortable || this;
    }
});


Ext.define('Ext.util.FilterCollection', {
    extend: 'Ext.util.Collection',
    requires: [
        'Ext.util.Filter'
    ],
    isFilterCollection: true,
    
    $filterable: null,
    
    filterFn: null,
    constructor: function(config) {
        var me = this;
        
        
        me.filterFn = Ext.util.Filter.createFilterFn(me);
        me.callParent([
            config
        ]);
        me.setDecoder(me.decodeFilter);
    },
    
    filterData: function(data) {
        return this.filtered ? Ext.Array.filter(data, this.filterFn) : data;
    },
    
    getFilterFn: function() {
        return this.filterFn;
    },
    isItemFiltered: function(item) {
        return !this.filterFn(item);
    },
    
    
    decodeFilter: function(filter) {
        var options = this.getOptions(),
            filterRoot = options.getRootProperty(),
            filterConfig;
        if (filter.isFilter) {
            if (!filter.getRoot()) {
                filter.setRoot(filterRoot);
            }
        } else {
            filterConfig = {
                root: filterRoot
            };
            if (Ext.isFunction(filter)) {
                filterConfig.filterFn = filter;
            } else 
            
            {
                
                if (!Ext.isObject(filter)) {
                    Ext.Error.raise('Invalid filter specified: ' + filter);
                }
                filterConfig = Ext.apply(filterConfig, filter);
                if (filterConfig.fn) {
                    filterConfig.filterFn = filterConfig.fn;
                    delete filterConfig.fn;
                }
                if (Ext.util.Filter.isInvalid(filterConfig)) {
                    return false;
                }
            }
            filter = new Ext.util.Filter(filterConfig);
        }
        return filter;
    },
    decodeRemoveItems: function(args, index) {
        var me = this,
            ret = (index === undefined) ? args : args[index];
        if (!ret.$cloned) {
            if (args.length > index + 1 || !Ext.isIterable(ret)) {
                ret = Ext.Array.slice(args, index);
            }
            var currentFilters = me.items,
                ln = ret.length,
                remove = [],
                filter, i, isFunction, isProp, isString, item, match, n, type;
            for (i = 0; i < ln; i++) {
                filter = ret[i];
                if (filter && filter.isFilter) {
                    remove.push(filter);
                } else {
                    type = typeof filter;
                    isFunction = type === 'function';
                    isProp = filter.property !== undefined && filter.value !== undefined;
                    isString = type === 'string';
                    if (!isFunction && !isProp && !isString) {
                        Ext.Error.raise('Invalid filter specification: ' + filter);
                    }
                    for (n = currentFilters.length; n-- > 0; ) {
                        item = currentFilters[n];
                        match = false;
                        if (isString) {
                            match = item.getProperty() === filter;
                        } else if (isFunction) {
                            match = item.getFilterFn() === filter;
                        } else if (isProp) {
                            match = item.getProperty() === filter.property && item.getValue() === filter.value;
                        }
                        if (match) {
                            remove.push(item);
                        }
                    }
                }
            }
            ret = remove;
            ret.$cloned = true;
        }
        return ret;
    },
    getOptions: function() {
        
        
        
        
        return this.$filterable || this;
    }
});


Ext.define('Ext.util.GroupCollection', {
    extend: 'Ext.util.Collection',
    requires: [
        'Ext.util.Group',
        
        
        
        
        'Ext.util.SorterCollection',
        'Ext.util.FilterCollection'
    ],
    isGroupCollection: true,
    config: {
        grouper: null,
        itemRoot: null
    },
    observerPriority: -100,
    
    
    onCollectionAdd: function(source, details) {
        this.addItemsToGroups(source, details.items);
    },
    onCollectionBeforeItemChange: function(source, details) {
        this.changeDetails = details;
    },
    onCollectionBeginUpdate: function() {
        this.beginUpdate();
    },
    onCollectionEndUpdate: function() {
        this.endUpdate();
    },
    onCollectionItemChange: function(source, details) {
        var item = details.item;
        
        
        
        if (!details.indexChanged) {
            this.syncItemGrouping(source, item, source.getKey(item), details.oldKey, details.oldIndex);
        }
        this.changeDetails = null;
    },
    onCollectionRefresh: function(source) {
        this.removeAll();
        this.addItemsToGroups(source, source.items);
    },
    onCollectionRemove: function(source, details) {
        var me = this,
            changeDetails = me.changeDetails,
            entries, entry, group, i, n, removeGroups, item;
        if (changeDetails) {
            
            
            item = changeDetails.item;
            group = me.findGroupForItem(item);
            entries = [];
            if (group) {
                entries.push({
                    group: group,
                    items: [
                        item
                    ]
                });
            }
        } else {
            entries = me.groupItems(source, details.items, false);
        }
        for (i = 0 , n = entries.length; i < n; ++i) {
            group = (entry = entries[i]).group;
            if (group) {
                group.remove(entry.items);
                if (!group.length) {
                    (removeGroups || (removeGroups = [])).push(group);
                }
            }
        }
        if (removeGroups) {
            me.remove(removeGroups);
        }
    },
    
    
    
    onCollectionSort: function(source) {
        
        var me = this,
            sorters = source.getSorters(),
            items = me.items,
            length = me.length,
            i, group;
        for (i = 0; i < length; ++i) {
            group = items[i];
            if (group.getSorters() !== sorters) {
                group.setSorters(sorters);
            }
        }
    },
    onCollectionUpdateKey: function(source, details) {
        var index = details.index,
            item = details.item;
        if (!details.indexChanged) {
            index = source.indexOf(item);
            this.syncItemGrouping(source, item, details.newKey, details.oldKey, index);
        }
    },
    
    
    addItemsToGroups: function(source, items) {
        this.groupItems(source, items, true);
    },
    groupItems: function(source, items, adding) {
        var me = this,
            byGroup = {},
            entries = [],
            grouper = source.getGrouper(),
            groupKeys = me.itemGroupKeys,
            entry, group, groupKey, i, item, itemKey, len, newGroups;
        for (i = 0 , len = items.length; i < len; ++i) {
            groupKey = grouper.getGroupString(item = items[i]);
            itemKey = source.getKey(item);
            if (adding) {
                (groupKeys || (me.itemGroupKeys = groupKeys = {}))[itemKey] = groupKey;
            } else if (groupKeys) {
                delete groupKeys[itemKey];
            }
            if (!(entry = byGroup[groupKey])) {
                if (!(group = me.getByKey(groupKey)) && adding) {
                    (newGroups || (newGroups = [])).push(group = me.createGroup(source, groupKey));
                }
                entries.push(byGroup[groupKey] = entry = {
                    group: group,
                    items: []
                });
            }
            entry.items.push(item);
        }
        for (i = 0 , len = entries.length; i < len; ++i) {
            entry = entries[i];
            entry.group.add(entry.items);
        }
        if (newGroups) {
            me.add(newGroups);
        }
        return entries;
    },
    syncItemGrouping: function(source, item, itemKey, oldKey, itemIndex) {
        var me = this,
            itemGroupKeys = me.itemGroupKeys || (me.itemGroupKeys = {}),
            grouper = source.getGrouper(),
            groupKey = grouper.getGroupString(item),
            removeGroups = 0,
            index = -1,
            addGroups, group, oldGroup, oldGroupKey, firstIndex;
        if (oldKey) {
            oldGroupKey = itemGroupKeys[oldKey];
            delete itemGroupKeys[oldKey];
        } else {
            oldGroupKey = itemGroupKeys[itemKey];
        }
        itemGroupKeys[itemKey] = groupKey;
        if (!(group = me.get(groupKey))) {
            group = me.createGroup(source, groupKey);
            addGroups = [
                group
            ];
        }
        
        
        if (group.get(itemKey) !== item) {
            if (group.getCount() > 0 && source.getSorters().getCount() === 0) {
                
                
                firstIndex = source.indexOf(group.items[0]);
                if (itemIndex < firstIndex) {
                    index = 0;
                } else {
                    index = itemIndex - firstIndex;
                }
            }
            if (index === -1) {
                group.add(item);
            } else {
                group.insert(index, item);
            }
        } else {
            group.itemChanged(item);
        }
        if (groupKey !== oldGroupKey && (oldGroupKey === 0 || oldGroupKey)) {
            oldGroup = me.get(oldGroupKey);
            if (oldGroup) {
                oldGroup.remove(item);
                if (!oldGroup.length) {
                    removeGroups = [
                        oldGroup
                    ];
                }
            }
        }
        if (addGroups) {
            me.splice(0, removeGroups, addGroups);
        } else if (removeGroups) {
            me.splice(0, removeGroups);
        }
    },
    createGroup: function(source, key) {
        var group = new Ext.util.Group({
                groupKey: key,
                rootProperty: this.getItemRoot(),
                sorters: source.getSorters()
            });
        return group;
    },
    getKey: function(item) {
        return item.getGroupKey();
    },
    createSortFn: function() {
        var me = this,
            grouper = me.getGrouper(),
            sorterFn = me.getSorters().getSortFn();
        if (!grouper) {
            return sorterFn;
        }
        return function(lhs, rhs) {
            
            
            
            return grouper.sort(lhs.items[0], rhs.items[0]) || sorterFn(lhs, rhs);
        };
    },
    updateGrouper: function(grouper) {
        var me = this;
        me.grouped = !!grouper;
        me.onSorterChange();
        me.onEndUpdateSorters(me.getSorters());
    },
    privates: {
        findGroupForItem: function(item) {
            var items = this.items,
                len = items.length,
                i, group;
            for (i = 0; i < len; ++i) {
                group = items[i];
                if (group.contains(item)) {
                    return group;
                }
            }
        }
    }
});


Ext.define('Ext.data.Store', {
    extend: 'Ext.data.ProxyStore',
    alias: 'store.store',
    mixins: [
        'Ext.data.LocalStore'
    ],
    
    
    
    requires: [
        'Ext.data.Model',
        'Ext.data.proxy.Ajax',
        'Ext.data.reader.Json',
        'Ext.data.writer.Json',
        
        'Ext.util.GroupCollection',
        'Ext.util.DelayedTask'
    ],
    uses: [
        'Ext.data.StoreManager',
        'Ext.util.Grouper'
    ],
    config: {
        
        data: 0,
        
        
        clearRemovedOnLoad: true,
        
        clearOnPageLoad: true,
        
        associatedEntity: null,
        
        role: null,
        
        session: null
    },
    
    
    addRecordsOptions: {
        addRecords: true
    },
    
    loadCount: 0,
    
    complete: false,
    moveMapCount: 0,
    
    constructor: function(config) {
        var me = this,
            data;
        if (config) {
            if (config.buffered) {
                if (this.self !== Ext.data.Store) {
                    Ext.Error.raise('buffered config not supported on derived Store classes. ' + 'Please derive from Ext.data.BufferedStore.');
                }
                return new Ext.data.BufferedStore(config);
            }
            if (config.remoteGroup) {
                Ext.log.warn('Ext.data.Store: remoteGroup has been removed. Use remoteSort instead.');
            }
        }
        
        
        
        
        me.callParent(arguments);
        me.getData().addObserver(this);
        
        data = me.inlineData;
        if (data) {
            delete me.inlineData;
            me.loadInlineData(data);
        }
    },
    onCollectionBeginUpdate: function() {
        this.beginUpdate();
    },
    onCollectionEndUpdate: function() {
        this.endUpdate();
    },
    applyData: function(data, dataCollection) {
        
        
        
        var me = this;
        
        me.getFields();
        me.getModel();
        
        if (data && data.isCollection) {
            dataCollection = data;
        } else {
            if (!dataCollection) {
                dataCollection = me.constructDataCollection();
            }
            if (data) {
                if (me.isInitializing) {
                    
                    
                    
                    
                    
                    
                    
                    
                    me.inlineData = data;
                } else {
                    
                    
                    me.loadData(data);
                }
            }
        }
        return dataCollection;
    },
    loadInlineData: function(data) {
        var me = this,
            proxy = me.getProxy(),
            blocked;
        if (proxy && proxy.isMemoryProxy) {
            proxy.setData(data);
            blocked = me.blockLoadCounter;
            me.blockLoadCounter = 0;
            
            me.suspendEvents();
            me.read();
            me.resumeEvents();
            me.blockLoadCounter = blocked;
        } else {
            
            me.removeAll(true);
            
            
            
            me.suspendEvents();
            me.loadData(data);
            me.resumeEvents();
        }
    },
    updateRemoteFilter: function(remote) {
        var data = this.getData(),
            filters = this.getFilters();
        
        if (remote) {
            data.setFilters(null);
        } else {
            data.setFilters(filters);
        }
        this.callParent(arguments);
    },
    updateRemoteSort: function(remote) {
        var data = this.getData(),
            sorters = this.getSorters();
        
        if (remote) {
            data.setSorters(null);
        } else {
            data.setSorters(sorters);
        }
        this.callParent(arguments);
    },
    
    onCollectionAdd: function(collection, info) {
        this.onCollectionAddItems(collection, info.items, info);
    },
    onCollectionFilterAdd: function(collection, items) {
        this.onCollectionAddItems(collection, items);
    },
    onCollectionAddItems: function(collection, records, info) {
        var me = this,
            len = records.length,
            lastChunk = info ? !info.next : false,
            removed = me.getRemovedRecords(),
            ignoreAdd = me.ignoreCollectionAdd,
            session = me.getSession(),
            replaced = info && info.replaced,
            i, sync, record, replacedItems;
        for (i = 0; i < len; ++i) {
            record = records[i];
            if (session) {
                session.adopt(record);
            }
            
            
            if (!ignoreAdd) {
                record.join(me);
                if (removed && removed.length) {
                    Ext.Array.remove(removed, record);
                }
                sync = sync || record.phantom || record.dirty;
            }
        }
        if (ignoreAdd) {
            return;
        }
        if (replaced) {
            replacedItems = [];
            do {
                Ext.Array.push(replacedItems, replaced.items);
                replaced = replaced.next;
            } while (replaced);
            me.setMoving(replacedItems, true);
        }
        if (info) {
            me.fireEvent('add', me, records, info.at);
            
            
            
            if (lastChunk) {
                me.fireEvent('datachanged', me);
            }
        }
        if (replaced) {
            me.setMoving(replacedItems, false);
        }
        
        me.needsSync = me.needsSync || sync;
    },
    
    onCollectionFilteredItemChange: function() {
        this.onCollectionItemChange.apply(this, arguments);
    },
    onCollectionItemChange: function(collection, info) {
        var me = this,
            record = info.item,
            modifiedFieldNames = info.modified || null,
            type = info.meta;
        
        
        
        me.onUpdate(record, type, modifiedFieldNames, info);
        me.fireEvent('update', me, record, type, modifiedFieldNames, info);
    },
    afterChange: function(record, modifiedFieldNames, type) {
        this.getData().itemChanged(record, modifiedFieldNames || null, undefined, type);
    },
    afterCommit: function(record, modifiedFieldNames) {
        this.afterChange(record, modifiedFieldNames, Ext.data.Model.COMMIT);
    },
    afterEdit: function(record, modifiedFieldNames) {
        this.needsSync = this.needsSync || record.dirty;
        this.afterChange(record, modifiedFieldNames, Ext.data.Model.EDIT);
    },
    afterReject: function(record) {
        this.afterChange(record, null, Ext.data.Model.REJECT);
    },
    afterDrop: function(record) {
        this.getData().remove(record);
    },
    
    
    addSorted: function(record) {
        var me = this,
            remote = me.getRemoteSort(),
            data = me.getData(),
            index;
        if (remote) {
            data.setSorters(me.getSorters());
        }
        index = data.findInsertionIndex(record);
        if (remote) {
            data.setSorters(null);
        }
        return me.insert(index, record);
    },
    
    remove: function(records, 
    isMove, silent) {
        var me = this,
            data = me.getData(),
            len, i, toRemove, record;
        if (records) {
            if (records.isModel) {
                if (me.indexOf(records) > -1) {
                    toRemove = [
                        records
                    ];
                    len = 1;
                } else {
                    len = 0;
                }
            } else {
                toRemove = [];
                for (i = 0 , len = records.length; i < len; ++i) {
                    record = records[i];
                    if (record && record.isEntity) {
                        if (!data.contains(record)) {
                            
                            continue;
                        }
                    } else if (!(record = data.getAt(record))) {
                        
                        
                        continue;
                    }
                    toRemove.push(record);
                }
                len = toRemove.length;
            }
        }
        if (!len) {
            return [];
        }
        me.removeIsMove = isMove === true;
        me.removeIsSilent = silent;
        data.remove(toRemove);
        me.removeIsSilent = false;
        return toRemove;
    },
    onCollectionRemove: function(collection, info) {
        var me = this,
            
            
            
            removed = me.removed,
            records = info.items,
            len = records.length,
            index = info.at,
            isMove = me.removeIsMove,
            silent = me.removeIsSilent,
            lastChunk = !info.next,
            replacement = info.replacement,
            i, record;
        if (me.ignoreCollectionRemove) {
            return;
        }
        if (replacement) {
            me.setMoving(replacement.items, true);
        }
        for (i = 0; i < len; ++i) {
            record = records[i];
            record.unjoin(me);
            
            if (removed && !isMove && !record.phantom && !record.erasing) {
                
                
                record.removedFrom = index + i;
                removed.push(record);
                
                
                me.needsSync = true;
            }
        }
        if (!silent) {
            me.fireEvent('remove', me, records, index, isMove);
            
            
            
            if (lastChunk) {
                me.fireEvent('datachanged', me);
            }
        }
        if (replacement) {
            me.setMoving(replacement.items, false);
        }
    },
    onFilterEndUpdate: function() {
        this.callParent(arguments);
        this.callObservers('Filter');
    },
    
    removeAt: function(index, count) {
        var data = this.getData();
        
        index = Math.max(index, 0);
        if (index < data.length) {
            if (arguments.length === 1) {
                count = 1;
            } else if (!count) {
                return;
            }
            data.removeAt(index, count);
        }
    },
    
    removeAll: function(silent) {
        var me = this,
            data = me.getData(),
            hasClear = me.hasListeners.clear,
            records = data.getRange();
        
        if (data.length) {
            
            me.removeIsSilent = true;
            me.callObservers('BeforeRemoveAll');
            data.removeAll();
            me.removeIsSilent = false;
            if (!silent) {
                me.fireEvent('clear', me, records);
                me.fireEvent('datachanged', me);
            }
            me.callObservers('AfterRemoveAll', [
                !!silent
            ]);
        }
        return records;
    },
    
    setRecords: function(records) {
        var count = this.getCount();
        ++this.loadCount;
        if (count) {
            this.getData().splice(0, count, records);
        } else {
            this.add(records);
        }
    },
    
    splice: function(index, toRemove, toAdd) {
        return this.getData().splice(index, toRemove, toAdd);
    },
    
    load: function(options) {
        var me = this,
            pageSize = me.getPageSize(),
            session;
        if (typeof options === 'function') {
            options = {
                callback: options
            };
        } else {
            options = Ext.apply({}, options);
        }
        
        if (me.getRemoteSort() && !options.grouper && me.getGrouper()) {
            options.grouper = me.getGrouper();
        }
        if (pageSize || 'start' in options || 'limit' in options || 'page' in options) {
            options.page = options.page != null ? options.page : me.currentPage;
            options.start = (options.start !== undefined) ? options.start : (options.page - 1) * pageSize;
            options.limit = options.limit != null ? options.limit : pageSize;
        }
        options.addRecords = options.addRecords || false;
        if (!options.recordCreator) {
            session = me.getSession();
            if (session) {
                options.recordCreator = session.recordCreator;
            }
        }
        return me.callParent([
            options
        ]);
    },
    
    onProxyLoad: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful();
        if (me.isDestroyed) {
            return;
        }
        if (resultSet) {
            me.totalCount = resultSet.getTotal();
        }
        if (successful) {
            records = me.processAssociation(records);
            me.loadRecords(records, operation.getAddRecords() ? {
                addRecords: true
            } : undefined);
        } else {
            me.loading = false;
        }
        if (me.hasListeners.load) {
            me.fireEvent('load', me, records, successful, operation);
        }
        me.callObservers('AfterLoad', [
            records,
            successful,
            operation
        ]);
    },
    getUnfiltered: function() {
        var data = this.getData();
        return data.getSource() || data;
    },
    getNewRecords: function() {
        return this.getUnfiltered().createFiltered(this.filterNew).getRange();
    },
    getUpdatedRecords: function() {
        return this.getUnfiltered().createFiltered(this.filterUpdated).getRange();
    },
    
    loadData: function(data, append) {
        var me = this,
            length = data.length,
            newData = [],
            i;
        
        for (i = 0; i < length; i++) {
            newData.push(me.createModel(data[i]));
        }
        newData = me.processAssociation(newData);
        me.loadRecords(newData, append ? me.addRecordsOptions : undefined);
    },
    
    loadRawData: function(data, append) {
        var me = this,
            session = me.getSession(),
            result = me.getProxy().getReader().read(data, session ? {
                recordCreator: session.recordCreator
            } : undefined),
            records = result.getRecords(),
            success = result.getSuccess();
        if (success) {
            me.totalCount = result.getTotal();
            me.loadRecords(records, append ? me.addRecordsOptions : undefined);
        }
        return success;
    },
    
    loadRecords: function(records, options) {
        var me = this,
            length = records.length,
            data = me.getData(),
            addRecords, i, skipSort;
        if (options) {
            addRecords = options.addRecords;
        }
        if (!me.getRemoteSort() && !me.getSortOnLoad()) {
            skipSort = true;
            data.setAutoSort(false);
        }
        if (!addRecords) {
            me.clearData(true);
        }
        
        
        me.loading = false;
        me.ignoreCollectionAdd = true;
        me.callObservers('BeforePopulate');
        data.add(records);
        me.ignoreCollectionAdd = false;
        if (skipSort) {
            data.setAutoSort(true);
        }
        for (i = 0; i < length; i++) {
            records[i].join(me);
        }
        ++me.loadCount;
        me.complete = true;
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
        me.callObservers('AfterPopulate');
    },
    
    
    loadPage: function(page, options) {
        var me = this,
            size = me.getPageSize();
        me.currentPage = page;
        
        options = Ext.apply({
            page: page,
            start: (page - 1) * size,
            limit: size,
            addRecords: !me.getClearOnPageLoad()
        }, options);
        me.read(options);
    },
    
    nextPage: function(options) {
        this.loadPage(this.currentPage + 1, options);
    },
    
    previousPage: function(options) {
        this.loadPage(this.currentPage - 1, options);
    },
    
    clearData: function(isLoad, data) {
        var me = this,
            removed = me.removed,
            records, i, len;
        data = data || me.getData();
        
        
        
        
        
        if (data) {
            records = data.items;
            for (i = 0 , len = records.length; i < len; ++i) {
                records[i].unjoin(me);
            }
            me.ignoreCollectionRemove = true;
            me.callObservers('BeforeClear');
            data.removeAll();
            me.ignoreCollectionRemove = false;
            me.callObservers('AfterClear');
        }
        if (removed && (isLoad !== true || me.getClearRemovedOnLoad())) {
            removed.length = 0;
        }
    },
    onIdChanged: function(rec, oldId, newId) {
        this.getData().updateKey(rec, oldId);
        
        this.fireEvent('idchanged', this, rec, oldId, newId);
    },
    
    commitChanges: function() {
        var me = this,
            recs = me.getModifiedRecords(),
            len = recs.length,
            removed = me.removed,
            i = 0;
        for (; i < len; i++) {
            recs[i].commit();
        }
        
        
        if (removed) {
            removed.length = 0;
        }
    },
    filterNewOnly: function(item) {
        return item.phantom === true;
    },
    filterRejects: function(item) {
        return item.phantom || item.dirty;
    },
    getRejectRecords: function() {
        return this.getData().createFiltered(this.filterRejects).getRange();
    },
    
    rejectChanges: function() {
        var me = this,
            recs = me.getRejectRecords(),
            len = recs.length,
            i = 0,
            rec;
        for (; i < len; i++) {
            rec = recs[i];
            rec.reject();
            if (rec.phantom) {
                me.remove(rec);
            }
        }
        
        recs = me.removed;
        if (recs) {
            len = recs.length;
            for (i = len - 1; i >= 0; i--) {
                rec = recs[i];
                me.insert(rec.removedFrom || 0, rec);
                rec.reject();
            }
            
            
            recs.length = 0;
        }
    },
    onDestroy: function() {
        var me = this,
            task = me.loadTask,
            data = me.getData(),
            source = data.getSource();
        me.callParent();
        me.setSession(null);
        me.observers = null;
        if (task) {
            task.cancel();
            me.loadTask = null;
        }
        
        me.clearData(false, source || data);
        data.destroy();
        if (source) {
            source.destroy();
        }
        me.setData(null);
    },
    privates: {
        onBeforeLoad: function(operation) {
            this.callObservers('BeforeLoad', [
                operation
            ]);
        },
        
        isMoving: function(records, getMap) {
            var map = this.moveMap,
                moving = 0,
                len, i;
            if (map) {
                if (records) {
                    if (Ext.isArray(records)) {
                        for (i = 0 , len = records.length; i < len; ++i) {
                            moving += map[records[i].id] ? 1 : 0;
                        }
                    } else if (map[records.id]) {
                        ++moving;
                    }
                } else {
                    moving = getMap ? map : this.moveMapCount;
                }
            }
            return moving;
        },
        setMoving: function(records, isMoving) {
            var me = this,
                map = me.moveMap || (me.moveMap = {}),
                len = records.length,
                i, id;
            for (i = 0; i < len; ++i) {
                id = records[i].id;
                if (isMoving) {
                    if (map[id]) {
                        ++map[id];
                    } else {
                        map[id] = 1;
                        ++me.moveMapCount;
                    }
                } else {
                    if (--map[id] === 0) {
                        delete map[id];
                        --me.moveMapCount;
                    }
                }
            }
            if (me.moveMapCount === 0) {
                me.moveMap = null;
            }
        },
        processAssociation: function(records) {
            var me = this,
                associatedEntity = me.getAssociatedEntity();
            if (associatedEntity) {
                records = me.getRole().processLoad(me, associatedEntity, records, me.getSession());
            }
            return records;
        }
    }
});


Ext.define('Ext.data.reader.Array', {
    extend: 'Ext.data.reader.Json',
    alternateClassName: 'Ext.data.ArrayReader',
    alias: 'reader.array',
    
    config: {
        
        totalProperty: undefined,
        
        successProperty: undefined
    },
    
    createFieldAccessor: function(field) {
        
        
        var oldMap = field.mapping,
            index = field.hasMapping() ? oldMap : field.ordinal,
            result;
        
        field.mapping = index;
        result = this.callParent(arguments);
        field.mapping = oldMap;
        return result;
    },
    getModelData: function(raw) {
        
        return {};
    }
});


Ext.define('Ext.data.ArrayStore', {
    extend: 'Ext.data.Store',
    alias: 'store.array',
    alternateClassName: [
        'Ext.data.SimpleStore'
    ],
    requires: [
        'Ext.data.proxy.Memory',
        'Ext.data.reader.Array'
    ],
    config: {
        proxy: {
            type: 'memory',
            reader: 'array'
        }
    },
    loadData: function(data, append) {
        if (this.expandData) {
            var r = [],
                i = 0,
                ln = data.length;
            for (; i < ln; i++) {
                r[r.length] = [
                    data[i]
                ];
            }
            data = r;
        }
        this.callParent([
            data,
            append
        ]);
    }
});


Ext.define('Ext.data.StoreManager', {
    extend: 'Ext.util.MixedCollection',
    alternateClassName: [
        'Ext.StoreMgr',
        'Ext.data.StoreMgr',
        'Ext.StoreManager'
    ],
    singleton: true,
    requires: [
        'Ext.data.ArrayStore'
    ],
    
    
    register: function() {
        for (var i = 0,
            s; (s = arguments[i]); i++) {
            this.add(s);
        }
    },
    
    unregister: function() {
        for (var i = 0,
            s; (s = arguments[i]); i++) {
            this.remove(this.lookup(s));
        }
    },
    
    lookup: function(store) {
        
        if (Ext.isArray(store)) {
            var fields = [
                    'field1'
                ],
                expand = !Ext.isArray(store[0]),
                data = store,
                i, len;
            if (expand) {
                data = [];
                for (i = 0 , len = store.length; i < len; ++i) {
                    data.push([
                        store[i]
                    ]);
                }
            } else {
                for (i = 2 , len = store[0].length; i <= len; ++i) {
                    fields.push('field' + i);
                }
            }
            return new Ext.data.ArrayStore({
                data: data,
                fields: fields,
                autoDestroy: true,
                autoCreated: true,
                expanded: expand
            });
        }
        if (Ext.isString(store)) {
            
            return this.get(store);
        } else {
            
            return Ext.Factory.store(store);
        }
    },
    
    getKey: function(o) {
        return o.storeId;
    }
}, function() {
    
    Ext.regStore = function(name, config) {
        var store;
        if (Ext.isObject(name)) {
            config = name;
        } else {
            if (Ext.data.StoreManager.containsKey(name)) {
                return Ext.data.StoreManager.lookup(name);
            }
            config.storeId = name;
        }
        if (config instanceof Ext.data.Store) {
            store = config;
        } else {
            store = new Ext.data.Store(config);
        }
        Ext.data.StoreManager.register(store);
        return store;
    };
    
    Ext.getStore = function(name) {
        return Ext.data.StoreManager.lookup(name);
    };
    
    
    
    var emptyStore = Ext.regStore('ext-empty-store', {
            proxy: 'memory',
            useModelWarning: false
        });
    emptyStore.isEmptyStore = true;
    emptyStore.add = emptyStore.remove = emptyStore.insert = emptyStore.load = emptyStore.loadData = function() {
        Ext.Error.raise('Cannot modify ext-empty-store');
    };
});


Ext.define('Ext.app.domain.Store', {
    extend: 'Ext.app.EventDomain',
    singleton: true,
    requires: [
        'Ext.data.AbstractStore'
    ],
    type: 'store',
    prefix: 'store.',
    idMatchRe: /^\#/,
    constructor: function() {
        var me = this;
        me.callParent();
        me.monitor(Ext.data.AbstractStore);
    },
    match: function(target, selector) {
        var result = false,
            alias = target.alias;
        if (selector === '*') {
            result = true;
        } else if (this.idMatchRe.test(selector)) {
            result = target.getStoreId() === selector.substring(1);
        } else if (alias) {
            result = Ext.Array.indexOf(alias, this.prefix + selector) > -1;
        }
        return result;
    }
});


Ext.define('Ext.app.route.Queue', {
    
    queue: null,
    
    token: null,
    constructor: function(config) {
        Ext.apply(this, config);
        
        this.queue = new Ext.util.MixedCollection();
    },
    
    queueAction: function(route, args) {
        this.queue.add({
            route: route,
            args: args
        });
    },
    
    clearQueue: function() {
        this.queue.removeAll();
    },
    
    runQueue: function() {
        var queue = this.queue,
            action = queue.removeAt(0),
            route;
        if (action) {
            route = action && action.route;
            route.execute(this.token, action.args, this.onActionExecute, this);
        }
    },
    
    onActionExecute: function(clearQueue) {
        if (clearQueue) {
            
            this.clearQueue();
        } else {
            
            this.runQueue();
        }
    }
});


Ext.define('Ext.app.route.Route', {
    
    action: null,
    
    conditions: null,
    
    controller: null,
    
    allowInactive: false,
    
    url: null,
    
    before: null,
    
    caseInsensitive: false,
    
    matcherRegex: null,
    
    paramMatchingRegex: null,
    
    paramsInMatchString: null,
    constructor: function(config) {
        var me = this,
            url;
        Ext.apply(me, config, {
            conditions: {}
        });
        url = me.url;
        me.paramMatchingRegex = new RegExp(/:([0-9A-Za-z\_]*)/g);
        me.paramsInMatchString = url.match(me.paramMatchingRegex) || [];
        me.matcherRegex = me.createMatcherRegex(url);
    },
    
    recognize: function(url) {
        var me = this,
            controller = me.controller,
            matches, args;
        if ((me.allowInactive || controller.isActive()) && me.recognizes(url)) {
            
            matches = me.matchesFor(url);
            
            args = url.match(me.matcherRegex);
            
            args.shift();
            return Ext.applyIf(matches, {
                controller: controller,
                action: me.action,
                historyUrl: url,
                args: args
            });
        }
        return false;
    },
    
    recognizes: function(url) {
        return this.matcherRegex.test(url);
    },
    
    execute: function(token, argConfig, callback, scope) {
        var args = argConfig.args || [],
            before = this.before,
            controller = this.controller,
            beforeCallback = this.createCallback(argConfig, callback, scope);
        if (before) {
            args.push(beforeCallback);
            if (Ext.isString(before)) {
                
                before = this.before = controller[before];
            }
            if (before) {
                before.apply(controller, args);
            } else {
                Ext.log.warn('The before action: ' + this.before + ' was not found on the controller. The action method will not be executed.');
            }
        } else {
            
            beforeCallback.resume();
        }
    },
    
    matchesFor: function(url) {
        var params = {},
            keys = this.paramsInMatchString,
            values = url.match(this.matcherRegex),
            i = 0,
            len = keys.length;
        
        values.shift();
        for (; i < len; i++) {
            params[keys[i].replace(':', '')] = values[i];
        }
        return params;
    },
    
    createMatcherRegex: function(url) {
        
        
        
        var paramsInMatchString = this.paramsInMatchString,
            conditions = this.conditions,
            i = 0,
            len = paramsInMatchString.length,
            format = Ext.util.Format.format,
            modifiers = this.caseInsensitive ? 'i' : '',
            params, cond, matcher;
        for (; i < len; i++) {
            params = paramsInMatchString[i];
            cond = conditions[params];
            matcher = format('{0}', cond || '([%a-zA-Z0-9\\-\\_\\s,]+)');
            url = url.replace(new RegExp(params), matcher);
        }
        
        return new RegExp('^' + url + '$',modifiers);
    },
    
    createCallback: function(args, callback, scope) {
        var me = this;
        scope = scope || me;
        return {
            resume: function() {
                var controller = me.controller,
                    action = me.action,
                    resume;
                if (Ext.isString(action)) {
                    
                    action = controller[action];
                }
                
                args = args && args.args ? args.args : [];
                
                resume = args.pop();
                if (resume && !Ext.isObject(resume)) {
                    args.push(resume);
                }
                
                if (action) {
                    me.action = action;
                    
                    action.apply(controller, args);
                } else {
                    Ext.log.warn('The action: ' + me.action + ' was not found on the controller.');
                }
                if (callback) {
                    callback.call(scope);
                }
            },
            stop: function(all) {
                if (callback) {
                    callback.call(scope, all);
                }
            }
        };
    }
});


Ext.define('Ext.util.History', {
    singleton: true,
    alternateClassName: 'Ext.History',
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    useTopWindow: false,
    
    
    
    constructor: function() {
        var me = this;
        me.hiddenField = null;
        me.ready = false;
        me.currentToken = null;
        me.mixins.observable.constructor.call(me);
    },
    
    getHash: function() {
        return this.win.location.hash.substr(1);
    },
    
    setHash: function(hash) {
        try {
            this.win.location.hash = hash;
            this.currentToken = hash;
        } catch (e) {}
    },
    
    
    handleStateChange: function(token) {
        this.currentToken = token;
        this.fireEvent('change', token);
    },
    
    startUp: function() {
        var me = this;
        me.currentToken = me.getHash();
        if (Ext.supports.Hashchange) {
            Ext.get(me.win).on('hashchange', me.onHashChange, me);
        } else {
            Ext.TaskManager.start({
                fireIdleEvent: false,
                run: me.onHashChange,
                interval: 50,
                scope: me
            });
        }
        me.ready = true;
        me.fireEvent('ready', me);
    },
    onHashChange: function() {
        var me = this,
            newHash = me.getHash();
        if (newHash !== me.hash) {
            me.hash = newHash;
            me.handleStateChange(newHash);
        }
    },
    
    init: function(onReady, scope) {
        var me = this;
        if (me.ready) {
            Ext.callback(onReady, scope, [
                me
            ]);
            return;
        }
        if (!Ext.isReady) {
            Ext.onReady(function() {
                me.init(onReady, scope);
            });
            return;
        }
        me.win = me.useTopWindow ? window.top : window;
        me.hash = me.getHash();
        if (onReady) {
            me.on('ready', onReady, scope, {
                single: true
            });
        }
        me.startUp();
    },
    
    add: function(token, preventDuplicates) {
        var me = this,
            set = false;
        if (preventDuplicates === false || me.getToken() !== token) {
            me.setHash(token);
            set = true;
        }
        return set;
    },
    
    back: function() {
        var win = this.useTopWindow ? window.top : window;
        win.history.go(-1);
    },
    
    forward: function() {
        var win = this.useTopWindow ? window.top : window;
        win.history.go(1);
    },
    
    getToken: function() {
        return this.ready ? this.currentToken : this.getHash();
    }
});


Ext.define('Ext.app.route.Router', {
    singleton: true,
    requires: [
        'Ext.app.route.Queue',
        'Ext.app.route.Route',
        'Ext.util.History'
    ],
    
    multipleToken: '|',
    
    queueRoutes: true,
    
    constructor: function() {
        var History = Ext.util.History;
        if (!History.ready) {
            History.init();
        }
        History.on('change', this.onStateChange, this);
        this.clear();
    },
    
    onStateChange: function(token) {
        var me = this,
            app = me.application,
            routes = me.routes,
            len = routes.length,
            queueRoutes = me.queueRoutes,
            tokens = token.split(me.multipleToken),
            t = 0,
            length = tokens.length,
            i, queue, route, args, matched;
        for (; t < length; t++) {
            token = tokens[t];
            matched = false;
            if (queueRoutes) {
                
                queue = new Ext.app.route.Queue({
                    token: token
                });
            }
            for (i = 0; i < len; i++) {
                route = routes[i];
                args = route.recognize(token);
                if (args) {
                    matched = true;
                    if (queueRoutes) {
                        queue.queueAction(route, args);
                    } else {
                        route.execute(token, args);
                    }
                }
            }
            if (queueRoutes) {
                
                queue.runQueue();
            }
            if (!matched && app) {
                app.fireEvent('unmatchedroute', token);
            }
        }
    },
    
    connect: function(url, action, controller) {
        var config = {
                url: url,
                action: action,
                controller: controller
            };
        if (Ext.isObject(action)) {
            Ext.merge(config, action);
        }
        this.routes.push(new Ext.app.route.Route(config));
    },
    
    disconnectAll: function(controller) {
        var routes = this.routes,
            len = routes.length,
            newRoutes = [],
            i, route;
        for (i = 0; i < len; ++i) {
            route = routes[i];
            if (route.controller !== controller) {
                newRoutes.push(route);
            }
        }
        this.routes = newRoutes;
    },
    
    recognize: function(url) {
        var routes = this.routes || [],
            i = 0,
            len = routes.length,
            route, args;
        for (; i < len; i++) {
            route = routes[i];
            args = route.recognize(url);
            if (args) {
                
                return {
                    route: route,
                    args: args
                };
            }
        }
        return false;
    },
    
    draw: function(fn) {
        fn.call(this, this);
    },
    
    clear: function() {
        this.routes = [];
    }
});


Ext.define('Ext.app.Controller', {
    extend: 'Ext.app.BaseController',
    requires: [
        'Ext.app.Util',
        'Ext.data.StoreManager',
        'Ext.ComponentManager',
        'Ext.app.domain.Component',
        'Ext.app.domain.Store',
        'Ext.app.route.Router'
    ],
    statics: {
        strings: {
            model: {
                getter: 'getModel',
                upper: 'Model'
            },
            view: {
                getter: 'getView',
                upper: 'View'
            },
            controller: {
                getter: 'getController',
                upper: 'Controller'
            },
            store: {
                getter: 'getStore',
                upper: 'Store'
            },
            profile: {
                getter: 'getProfile',
                upper: 'Profiles'
            }
        },
        controllerRegex: /^(.*)\.controller\./,
        createGetter: function(baseGetter, name) {
            return function() {
                return this[baseGetter](name);
            };
        },
        getGetterName: function(name, kindUpper) {
            var fn = 'get',
                parts = name.split('.'),
                numParts = parts.length,
                index;
            
            for (index = 0; index < numParts; index++) {
                fn += Ext.String.capitalize(parts[index]);
            }
            fn += kindUpper;
            return fn;
        },
        resolveNamespace: function(cls, data) {
            var Controller = Ext.app.Controller,
                ctrlRegex = Controller.controllerRegex,
                className, namespace, match;
            
            className = Ext.getClassName(cls);
            namespace = data.$namespace || data.namespace || Ext.app.getNamespace(className) || ((match = ctrlRegex.exec(className)) && match[1]);
            if (!namespace) {
                Ext.log.warn("Missing namespace for " + className + ", please define it " + "in namespaces property of your Application class.");
            }
            return namespace;
        },
        
        processDependencies: function(cls, requires, namespace, kind, names) {
            if (!names || !names.length) {
                return;
            }
            var me = this,
                strings = me.strings[kind],
                o, absoluteName, shortName, name, j, subLn, getterName, getter;
            if (!Ext.isArray(names)) {
                names = [
                    names
                ];
            }
            for (j = 0 , subLn = names.length; j < subLn; j++) {
                name = names[j];
                o = me.getFullName(name, kind, namespace);
                absoluteName = o.absoluteName;
                shortName = o.shortName;
                requires.push(absoluteName);
                getterName = me.getGetterName(shortName, strings.upper);
                cls[getterName] = getter = me.createGetter(strings.getter, name);
                
                if (kind !== 'controller') {
                    
                    
                    
                    
                    getter['Ext.app.getter'] = true;
                }
            }
        },
        getFullName: function(name, kind, namespace) {
            var shortName = name,
                sep, absoluteName;
            if ((sep = name.indexOf('@')) > 0) {
                
                
                
                
                shortName = name.substring(0, sep);
                
                absoluteName = name.substring(sep + 1) + '.' + shortName;
            }
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            else if (name.indexOf('.') > 0 && (Ext.ClassManager.isCreated(name) || this.hasRegisteredPrefix(name))) {
                absoluteName = name;
            } else {
                if (!namespace) {
                    Ext.log.warn("Cannot find namespace for " + kind + " " + name + ", " + "assuming it is fully qualified class name");
                }
                if (namespace) {
                    absoluteName = namespace + '.' + kind + '.' + name;
                    shortName = name;
                } else {
                    absoluteName = name;
                }
            }
            return {
                absoluteName: absoluteName,
                shortName: shortName
            };
        },
        hasRegisteredPrefix: function(className) {
            var inventory = Ext.ClassManager,
                prefix = inventory.getPrefix(className);
            
            return prefix && prefix !== className;
        }
    },
    
    
    models: null,
    
    
    views: null,
    
    
    stores: null,
    
    controllers: null,
    config: {
        
        application: null,
        
        refs: null,
        active: true,
        
        moduleClassName: null
    },
    onClassExtended: function(cls, data, hooks) {
        var onBeforeClassCreated = hooks.onBeforeCreated;
        hooks.onBeforeCreated = function(cls, data) {
            var Controller = Ext.app.Controller,
                requires = [],
                namespace, proto;
            proto = cls.prototype;
            namespace = Controller.resolveNamespace(cls, data);
            if (namespace) {
                proto.$namespace = namespace;
            }
            Controller.processDependencies(proto, requires, namespace, 'model', data.models);
            Controller.processDependencies(proto, requires, namespace, 'view', data.views);
            Controller.processDependencies(proto, requires, namespace, 'store', data.stores);
            Controller.processDependencies(proto, requires, namespace, 'controller', data.controllers);
            Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));
        };
    },
    
    constructor: function(config) {
        this.initAutoGetters();
        this.callParent(arguments);
    },
    
    normalizeRefs: function(refs) {
        var me = this,
            newRefs = [];
        if (refs) {
            if (Ext.isObject(refs)) {
                Ext.Object.each(refs, function(key, value) {
                    if (Ext.isString(value)) {
                        value = {
                            selector: value
                        };
                    }
                    value.ref = key;
                    newRefs.push(value);
                });
            } else if (Ext.isArray(refs)) {
                newRefs = Ext.Array.merge(newRefs, refs);
            }
        }
        refs = me.refs;
        if (refs) {
            me.refs = null;
            refs = me.normalizeRefs(refs);
            if (refs) {
                newRefs = Ext.Array.merge(newRefs, refs);
            }
        }
        return newRefs;
    },
    
    getRefMap: function() {
        var me = this,
            refMap = me._refMap,
            refs, ref, ln, i;
        if (!refMap) {
            refs = me.getRefs();
            refMap = me._refMap = {};
            if (refs) {
                for (i = 0 , ln = refs.length; i < ln; i++) {
                    ref = refs[i];
                    refMap[ref.ref] = ref.selector;
                }
            }
        }
        return refMap;
    },
    applyRefs: function(refs) {
        return this.normalizeRefs(Ext.clone(refs));
    },
    
    updateRefs: function(refs) {
        if (refs) {
            this.ref(refs);
        }
    },
    initAutoGetters: function() {
        var proto = this.self.prototype,
            prop, fn;
        for (prop in proto) {
            fn = proto[prop];
            
            
            if (fn && fn['Ext.app.getter']) {
                fn.call(this);
            }
        }
    },
    doInit: function(app) {
        var me = this;
        if (!me._initialized) {
            me.init(app);
            me._initialized = true;
        }
    },
    finishInit: function(app) {
        var me = this,
            controllers = me.controllers,
            controller, i, l;
        if (me._initialized && controllers && controllers.length) {
            for (i = 0 , l = controllers.length; i < l; i++) {
                controller = me.getController(controllers[i]);
                controller.finishInit(app);
            }
        }
    },
    
    init: Ext.emptyFn,
    
    onLaunch: Ext.emptyFn,
    
    activate: function() {
        this.setActive(true);
    },
    
    deactivate: function() {
        this.setActive(false);
    },
    
    isActive: function() {
        return this.getActive();
    },
    ref: function(refs) {
        var me = this,
            i = 0,
            length = refs.length,
            info, ref, fn;
        refs = Ext.Array.from(refs);
        me.references = me.references || [];
        for (; i < length; i++) {
            info = refs[i];
            ref = info.ref;
            fn = 'get' + Ext.String.capitalize(ref);
            if (!me[fn]) {
                me[fn] = Ext.Function.pass(me.getRef, [
                    ref,
                    info
                ], me);
            }
            me.references.push(ref.toLowerCase());
        }
    },
    
    addRef: function(refs) {
        this.ref(refs);
    },
    getRef: function(ref, info, config) {
        var me = this,
            refCache = me.refCache || (me.refCache = {}),
            cached = refCache[ref];
        info = info || {};
        config = config || {};
        Ext.apply(info, config);
        if (info.forceCreate) {
            return Ext.ComponentManager.create(info, 'component');
        }
        if (!cached) {
            if (info.selector) {
                refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];
            }
            if (!cached && info.autoCreate) {
                refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
            }
            if (cached) {
                cached.on('beforedestroy', function() {
                    refCache[ref] = null;
                });
            }
        }
        return cached;
    },
    
    hasRef: function(ref) {
        var references = this.references;
        return references && Ext.Array.indexOf(references, ref.toLowerCase()) !== -1;
    },
    
    getController: function(id) {
        var app = this.getApplication();
        if (id === this.getId()) {
            return this;
        }
        return app && app.getController(id);
    },
    
    getStore: function(name) {
        var storeId, store;
        storeId = (name.indexOf('@') === -1) ? name : name.split('@')[0];
        store = Ext.StoreManager.get(storeId);
        if (!store) {
            name = Ext.app.Controller.getFullName(name, 'store', this.$namespace);
            if (name) {
                store = Ext.create(name.absoluteName, {
                    storeId: storeId
                });
            }
        }
        return store;
    },
    
    getModel: function(model) {
        var name = Ext.app.Controller.getFullName(model, 'model', this.$namespace),
            ret = Ext.ClassManager.get(name.absoluteName);
        if (!ret) {
            ret = Ext.data.schema.Schema.lookupEntity(model);
        }
        return ret;
    },
    
    getProfile: function(name) {
        name = Ext.app.Controller.getFullName(name, 'profile', this.$namespace);
        return name;
    },
    
    getView: function(view) {
        var name = Ext.app.Controller.getFullName(view, 'view', this.$namespace);
        return name && Ext.ClassManager.get(name.absoluteName);
    },
    ensureId: function() {
        var id = this.getId();
        if (!id) {
            this.setId(this.getModuleClassName(this.$className, 'controller'));
        }
    },
    destroy: function(destroyRefs, 
    fromApp) {
        var me = this,
            app = me.application,
            refCache, ref;
        if (!fromApp && app) {
            app.unregister(me);
        }
        me.application = null;
        if (destroyRefs) {
            
            refCache = me.refCache;
            for (ref in refCache) {
                if (refCache.hasOwnProperty(ref)) {
                    Ext.destroy(refCache[ref]);
                }
            }
        }
        me.callParent();
    }
});


Ext.define('Ext.app.Application', {
    extend: 'Ext.app.Controller',
    requires: [
        'Ext.util.History',
        'Ext.util.MixedCollection'
    ],
    isApplication: true,
    
    
    
    scope: undefined,
    
    namespaces: [],
    
    paths: null,
    
    appFolder: 'app',
    
    config: {
        
        name: '',
        
        appProperty: 'app',
        
        
        profiles: [],
        currentProfile: null,
        
        
        mainView: {
            $value: null,
            lazy: true
        },
        
        defaultToken: null,
        
        glyphFontFamily: null
    },
    onClassExtended: function(cls, data, hooks) {
        var Controller = Ext.app.Controller,
            proto = cls.prototype,
            requires = [],
            onBeforeClassCreated, paths, namespace, ns, appFolder;
        
        
        namespace = data.name || cls.superclass.name;
        appFolder = data.appFolder || cls.superclass.appFolder;
        if (namespace) {
            data.$namespace = namespace;
            Ext.app.addNamespaces(namespace);
        }
        if (data.namespaces) {
            Ext.app.addNamespaces(data.namespaces);
        }
        if (!data['paths processed']) {
            if (namespace && appFolder) {
                Ext.Loader.setPath(namespace, appFolder);
            }
            paths = data.paths;
            if (paths) {
                for (ns in paths) {
                    if (paths.hasOwnProperty(ns)) {
                        Ext.Loader.setPath(ns, paths[ns]);
                    }
                }
            }
        } else {
            delete data['paths processed'];
        }
        
        Controller.processDependencies(proto, requires, namespace, 'profile', data.profiles);
        proto.getDependencies(cls, data, requires);
        
        if (requires.length) {
            onBeforeClassCreated = hooks.onBeforeCreated;
            hooks.onBeforeCreated = function(cls, data) {
                var args = Ext.Array.clone(arguments);
                Ext.require(requires, function() {
                    return onBeforeClassCreated.apply(this, args);
                });
            };
        }
    },
    getDependencies: Ext.emptyFn,
    
    constructor: function(config) {
        var me = this;
        Ext.app.route.Router.application = me;
        me.callParent(arguments);
        if (Ext.isEmpty(me.getName())) {
            Ext.Error.raise("[Ext.app.Application] Name property is required");
        }
        me.doInit(me);
        me.initNamespace();
        Ext.Loader.setConfig({
            enabled: true
        });
        var profiles = this.getProfiles();
        if (profiles && profiles.length) {
            Ext.require(profiles, this.onProfilesLoaded, this);
        } else {
            this.onProfilesReady();
        }
    },
    onProfilesReady: function() {
        var me = this;
        me.initControllers();
        me.onBeforeLaunch();
        me.finishInitControllers();
    },
    initNamespace: function() {
        var me = this,
            appProperty = me.getAppProperty(),
            ns;
        ns = Ext.namespace(me.getName());
        if (ns) {
            ns.getApplication = function() {
                return me;
            };
            if (appProperty) {
                if (!ns[appProperty]) {
                    ns[appProperty] = me;
                } else if (ns[appProperty] !== me) {
                    Ext.log.warn('An existing reference is being overwritten for ' + name + '.' + appProperty + '. See the appProperty config.');
                }
            }
        }
    },
    initControllers: function() {
        var me = this,
            controllers = Ext.Array.from(me.controllers);
        me.controllers = new Ext.util.MixedCollection();
        for (var i = 0,
            ln = controllers.length; i < ln; i++) {
            me.getController(controllers[i]);
        }
    },
    finishInitControllers: function() {
        var me = this,
            controllers, i, l;
        controllers = me.controllers.getRange();
        for (i = 0 , l = controllers.length; i < l; i++) {
            controllers[i].finishInit(me);
        }
    },
    
    launch: Ext.emptyFn,
    
    onBeforeLaunch: function() {
        var me = this,
            History = Ext.util.History,
            defaultToken = me.getDefaultToken(),
            currentProfile = me.getCurrentProfile(),
            controllers, c, cLen, controller, token;
        me.initMainView();
        if (currentProfile) {
            currentProfile.launch();
        }
        me.launch.call(me.scope || me);
        me.launched = true;
        me.fireEvent('launch', me);
        controllers = me.controllers.items;
        cLen = controllers.length;
        for (c = 0; c < cLen; c++) {
            controller = controllers[c];
            controller.onLaunch(me);
        }
        if (!History.ready) {
            History.init();
        }
        token = History.getToken();
        if (token) {
            me.redirectTo(token, true);
        } else if (defaultToken) {
            History.add(defaultToken);
        }
    },
    getModuleClassName: function(name, kind) {
        return Ext.app.Controller.getFullName(name, kind, this.getName()).absoluteName;
    },
    initMainView: function() {
        var me = this,
            currentProfile = me.getCurrentProfile(),
            mainView;
        if (currentProfile) {
            mainView = currentProfile.getMainView();
        }
        if (mainView) {
            me.setMainView(mainView);
        } else {
            
            me.getMainView();
        }
    },
    applyMainView: function(value) {
        var view;
        view = this.getView(value);
        return view.create();
    },
    
    createController: function(name) {
        return this.getController(name);
    },
    
    destroyController: function(controller) {
        if (typeof controller === 'string') {
            controller = this.getController(controller, true);
        }
        Ext.destroy(controller);
    },
    getController: function(name, 
    preventCreate) {
        var me = this,
            controllers = me.controllers,
            className, controller, len, i, c, all;
        controller = controllers.get(name);
        
        
        
        
        if (!controller) {
            all = controllers.items;
            for (i = 0 , len = all.length; i < len; ++i) {
                c = all[i];
                className = c.getModuleClassName();
                if (className && className === name) {
                    controller = c;
                    break;
                }
            }
        }
        if (!controller && !preventCreate) {
            className = me.getModuleClassName(name, 'controller');
            controller = Ext.create(className, {
                application: me,
                moduleClassName: name
            });
            controllers.add(controller);
            if (me._initialized) {
                controller.doInit(me);
            }
        }
        return controller;
    },
    
    unregister: function(controller) {
        this.controllers.remove(controller);
    },
    
    getApplication: function() {
        return this;
    },
    
    destroy: function(destroyRefs) {
        var me = this,
            controllers = me.controllers,
            ns = Ext.namespace(me.getName()),
            appProp = me.getAppProperty();
        Ext.destroy(me.viewport);
        if (controllers) {
            controllers.each(function(controller) {
                controller.destroy(destroyRefs, true);
            });
        }
        me.controllers = null;
        me.callParent([
            destroyRefs,
            true
        ]);
        
        if (ns && ns[appProp] === me) {
            delete ns[appProp];
        }
    },
    updateGlyphFontFamily: function(fontFamily) {
        Ext.setGlyphFontFamily(fontFamily);
    },
    
    applyProfiles: function(profiles) {
        var me = this;
        return Ext.Array.map(profiles, function(profile) {
            return me.getModuleClassName(profile, "profile");
        });
    },
    
    onProfilesLoaded: function() {
        var me = this,
            profiles = me.getProfiles(),
            length = profiles.length,
            instances = [],
            current, i, profileDeps, requires, controllers, profileControllers, views, profileViews, stores, profileStores, models, profileModels;
        for (i = 0; i < length; i++) {
            instances[i] = Ext.create(profiles[i], {
                application: me
            });
            if (instances[i].isActive() && !current) {
                current = instances[i];
                profileDeps = current.getDependencies();
                requires = profileDeps.all;
                me.setCurrentProfile(current);
                profileControllers = profileDeps.controller;
                if (profileControllers.length) {
                    controllers = me.controllers = (me.controllers || []);
                    controllers.push.apply(controllers, profileControllers);
                }
                profileViews = profileDeps.view;
                if (profileViews.length) {
                    views = me.views = (me.views || []);
                    views.push.apply(views, profileViews);
                }
                profileStores = profileDeps.store;
                if (profileStores.length) {
                    stores = me.stores = (me.stores || []);
                    stores.push.apply(stores, profileStores);
                }
                profileModels = profileDeps.model;
                if (profileModels.length) {
                    models = me.models = (me.models || []);
                    models.push.apply(models, profileModels);
                }
            }
        }
        if (requires) {
            Ext.require(requires, me.onProfilesReady, me);
        } else {
            me.onProfilesReady();
        }
    }
});








Ext.application = function(config) {
    var createApp = function(App) {
            
            Ext.onReady(function() {
                Ext.app.Application.instance = new App();
            });
        },
        paths = config.paths,
        ns;
    if (typeof config === "string") {
        Ext.require(config, function() {
            createApp(Ext.ClassManager.get(config));
        });
    } else {
        config = Ext.apply({
            extend: 'Ext.app.Application'
        }, 
        config);
        
        
        Ext.Loader.setPath(config.name, config.appFolder || 'app');
        if (paths) {
            for (ns in paths) {
                if (paths.hasOwnProperty(ns)) {
                    Ext.Loader.setPath(ns, paths[ns]);
                }
            }
        }
        config['paths processed'] = true;
        
        Ext.define(config.name + ".$application", config, function() {
            createApp(this);
        });
    }
};


Ext.define('Ext.overrides.app.Application', {
    override: 'Ext.app.Application',
    uses: [
        'Ext.tip.QuickTipManager'
    ],
    
    
    autoCreateViewport: false,
    config: {
        
        enableQuickTips: true
    },
    applyMainView: function(value) {
        var view = this.getView(value),
            proto = view.prototype,
            config, plugins;
        if (!proto.isViewport) {
            plugins = proto.plugins;
            
            plugins = [
                'viewport'
            ].concat(plugins ? Ext.Array.from(plugins, true) : []);
            config = {
                plugins: plugins
            };
        }
        return view.create(config);
    },
    getDependencies: function(cls, data, requires) {
        var Controller = Ext.app.Controller,
            proto = cls.prototype,
            namespace = data.$namespace,
            viewportClass = data.autoCreateViewport;
        if (viewportClass) {
            if (!namespace) {
                Ext.Error.raise("[Ext.app.Application] Can't resolve namespace for " + data.$className + ", did you forget to specify 'name' property?");
            }
            if (viewportClass === true) {
                viewportClass = 'Viewport';
            } else {
                requires.push('Ext.plugin.Viewport');
            }
            Controller.processDependencies(proto, requires, namespace, 'view', viewportClass);
        }
    },
    onBeforeLaunch: function() {
        var me = this,
            autoCreateViewport = me.autoCreateViewport;
        if (me.getEnableQuickTips()) {
            me.initQuickTips();
        }
        if (autoCreateViewport) {
            me.initViewport();
        }
        this.callParent(arguments);
    },
    getViewportName: function() {
        var name = null,
            autoCreate = this.autoCreateViewport;
        if (autoCreate) {
            name = (autoCreate === true) ? 'Viewport' : autoCreate;
        }
        return name;
    },
    initViewport: function() {
        this.setMainView(this.getViewportName());
    },
    initQuickTips: function() {
        Ext.tip.QuickTipManager.init();
    }
});


Ext.define('Ext.app.Profile', {
    mixins: {
        observable: "Ext.mixin.Observable"
    },
    config: {
        
        namespace: 'auto',
        
        name: 'auto',
        
        mainView: {
            $value: null,
            lazy: true
        },
        
        application: null,
        
        
        controllers: [],
        
        
        models: [],
        
        
        views: [],
        
        
        stores: []
    },
    
    constructor: function(config) {
        this.initConfig(config);
        this.mixins.observable.constructor.apply(this, arguments);
    },
    
    isActive: function() {
        return false;
    },
    
    launch: Ext.emptyFn,
    
    applyNamespace: function(name) {
        if (name == 'auto') {
            name = this.getName();
        }
        return name.toLowerCase();
    },
    
    applyName: function(name) {
        if (name == 'auto') {
            var pieces = this.$className.split('.');
            name = pieces[pieces.length - 1];
        }
        return name;
    },
    onClassExtended: function(cls, data, hooks) {
        var onBeforeClassCreated = hooks.onBeforeCreated;
        hooks.onBeforeCreated = function(cls, data) {
            var Controller = Ext.app.Controller,
                requires = [],
                proto = cls.prototype,
                namespace;
            namespace = Controller.resolveNamespace(cls, data);
            Controller.processDependencies(proto, requires, namespace, 'model', data.models);
            Controller.processDependencies(proto, requires, namespace, 'view', data.views);
            Controller.processDependencies(proto, requires, namespace, 'store', data.stores);
            Controller.processDependencies(proto, requires, namespace, 'controller', data.controllers);
            Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));
        };
    },
    
    getDependencies: function() {
        var allClasses = [],
            appName = this.getApplication().getName(),
            namespace = this.getNamespace(),
            map = {
                model: this.getModels(),
                view: this.getViews(),
                controller: this.getControllers(),
                store: this.getStores()
            },
            classType, classNames;
        for (classType in map) {
            classNames = [];
            Ext.each(map[classType], function(className) {
                if (Ext.isString(className)) {
                    
                    if (Ext.isString(className) && (Ext.ClassManager.getPrefix(className) === "" || className === appName)) {
                        className = appName + '.' + classType + '.' + namespace + '.' + className;
                    }
                    classNames.push(className);
                    allClasses.push(className);
                }
            }, this);
            map[classType] = classNames;
        }
        map.all = allClasses;
        return map;
    }
});


Ext.define('Ext.app.domain.View', {
    extend: 'Ext.app.EventDomain',
    isInstance: true,
    constructor: function(controller) {
        this.callParent(arguments);
        this.controller = controller;
        this.monitoredClasses = [
            Ext.Component
        ];
    },
    match: function(target, selector, controller) {
        var out = false;
        if (selector === '#') {
            out = controller === target.getController();
        } else {
            out = target.is(selector);
        }
        return out;
    },
    destroy: function() {
        this.controller = null;
        this.callParent();
    }
});


Ext.define('Ext.app.ViewController', {
    extend: 'Ext.app.BaseController',
    requires: [
        'Ext.app.domain.View'
    ],
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    isViewController: true,
    factoryConfig: {
        
        type: 'controller'
    },
    config: {
        closeViewAction: 'destroy'
    },
    constructor: function() {
        this.compDomain = new Ext.app.domain.View(this);
        this.callParent(arguments);
    },
    
    beforeInit: Ext.emptyFn,
    
    init: Ext.emptyFn,
    
    initViewModel: Ext.emptyFn,
    
    destroy: function() {
        var me = this,
            domain = me.compDomain;
        if (domain) {
            domain.unlisten(me);
            domain.destroy();
        }
        me.compDomain = me.view = null;
        me.callParent();
    },
    
    closeView: function() {
        var view = this.getView(),
            action;
        if (view) {
            action = this.getCloseViewAction();
            view[action]();
        }
    },
    control: function(selectors, listeners) {
        var obj = selectors;
        if (Ext.isString(selectors)) {
            obj = {};
            obj[selectors] = listeners;
        }
        this.compDomain.listen(obj, this);
    },
    listen: function(to, controller) {
        var component = to.component;
        if (component) {
            to = Ext.apply({}, to);
            delete to.component;
            this.control(component);
        }
        this.callParent([
            to,
            controller
        ]);
    },
    
    getReferences: function() {
        return this.view.getReferences();
    },
    
    getView: function() {
        return this.view;
    },
    
    lookupReference: function(key) {
        var view = this.view;
        return view && view.lookupReference(key);
    },
    
    getSession: function() {
        var view = this.view;
        return view && view.lookupSession();
    },
    
    getViewModel: function() {
        var view = this.view;
        return view && view.lookupViewModel();
    },
    
    getStore: function(name) {
        var viewModel = this.getViewModel();
        return viewModel ? viewModel.getStore(name) : null;
    },
    
    fireViewEvent: function(eventName) {
        var view = this.view,
            result = false;
        if (view) {
            result = view.fireEvent.apply(view, arguments);
        }
        return result;
    },
    
    privates: {
        view: null,
        ensureId: function() {
            var id = this.getId();
            if (!id) {
                this.setId(Ext.id(null, 'controller-'));
            }
        },
        
        attachReference: function(component) {
            var view = this.view;
            if (view) {
                view.attachReference(component);
            }
        },
        
        clearReference: function(ref) {
            var view = this.view;
            if (view) {
                view.clearReference(ref);
            }
        },
        
        clearReferences: function() {
            var view = this.view;
            if (view) {
                view.clearReferences();
            }
        },
        
        setView: function(view) {
            this.view = view;
            if (!this.beforeInit.$nullFn) {
                this.beforeInit();
            }
        }
    }
});


Ext.define('Ext.util.Scheduler', {
    mixins: [
        'Ext.mixin.Observable'
    ],
    requires: [
        'Ext.util.Collection'
    ],
    busyCounter: 0,
    lastBusyCounter: 0,
    destroyed: false,
    firing: null,
    notifyIndex: -1,
    nextId: 0,
    orderedItems: null,
    passes: 0,
    scheduledCount: 0,
    validIdRe: null,
    config: {
        
        cycleLimit: 5,
        
        preSort: null,
        
        tickDelay: 5
    },
    constructor: function(config) {
        if (Ext.util.Scheduler.instances) {
            Ext.util.Scheduler.instances.push(this);
        } else {
            Ext.util.Scheduler.instances = [
                this
            ];
        }
        this.id = Ext.util.Scheduler.count = (Ext.util.Scheduler.count || 0) + 1;
        this.mixins.observable.constructor.call(this, config);
        this.items = new Ext.util.Collection();
    },
    destroy: function() {
        var me = this,
            timer = me.timer;
        if (timer) {
            window.clearTimeout(timer);
            me.timer = null;
        }
        me.destroyed = true;
        me.items.destroy();
        me.items = me.orderedItems = null;
        me.destroy = Ext.emptyFn;
        Ext.Array.remove(Ext.util.Scheduler.instances, this);
    },
    
    add: function(item) {
        var me = this,
            items = me.items;
        if (items === me.firing) {
            me.items = items = items.clone();
        }
        item.id = item.id || ++me.nextId;
        item.scheduler = me;
        items.add(item);
        if (!me.sortMap) {
            
            
            me.orderedItems = null;
        }
    },
    
    remove: function(item) {
        var me = this,
            items = me.items;
        if (me.destroyed) {
            return;
        }
        if (me.sortMap) {
            Ext.Error.raise('Items cannot be removed during sort');
        }
        if (items === me.firing) {
            me.items = items = items.clone();
        }
        if (item.scheduled) {
            me.unscheduleItem(item);
            item.scheduled = false;
        }
        items.remove(item);
        me.orderedItems = null;
    },
    
    sort: function() {
        var me = this,
            items = me.items,
            sortMap = {},
            preSort = me.getPreSort(),
            i, item;
        me.orderedItems = [];
        me.sortMap = sortMap;
        me.sortStack = [];
        if (preSort) {
            items.sortItems(preSort);
        }
        items = items.items;
        
        
        for (i = 0; i < items.length; ++i) {
            item = items[i];
            if (!sortMap[item.id]) {
                me.sortItem(item);
            }
        }
        me.sortMap = null;
        me.sortStack = null;
    },
    
    sortItem: function(item) {
        var me = this,
            sortMap = me.sortMap,
            orderedItems = me.orderedItems,
            itemId;
        if (!item.scheduler) {
            me.add(item);
        }
        itemId = item.id;
        if (item.scheduler !== me) {
            Ext.Error.raise('Item ' + itemId + ' belongs to another Scheduler');
        }
        me.sortStack.push(item);
        if (sortMap[itemId] === 0) {
            for (var cycle = [],
                i = 0; i < me.sortStack.length; ++i) {
                cycle[i] = me.sortStack[i].getFullName();
            }
            Ext.Error.raise('Dependency cycle detected: ' + cycle.join('\n --> '));
        }
        if (!(itemId in sortMap)) {
            
            
            
            
            
            sortMap[itemId] = 0;
            if (!item.sort.$nullFn) {
                item.sort();
            }
            sortMap[itemId] = 1;
            item.order = me.orderedItems.length;
            orderedItems.push(item);
        }
        me.sortStack.pop();
        return me;
    },
    
    sortItems: function(items) {
        var me = this,
            sortItem = me.sortItem;
        if (items) {
            if (items instanceof Array) {
                Ext.each(items, sortItem, me);
            } else {
                Ext.Object.eachValue(items, sortItem, me);
            }
        }
        return me;
    },
    applyPreSort: function(preSort) {
        if (typeof preSort === 'function') {
            return preSort;
        }
        var parts = preSort.split(','),
            direction = [],
            length = parts.length,
            c, i, s;
        for (i = 0; i < length; ++i) {
            direction[i] = 1;
            s = parts[i];
            if ((c = s.charAt(0)) === '-') {
                direction[i] = -1;
            } else if (c !== '+') {
                c = 0;
            }
            if (c) {
                parts[i] = s.substring(1);
            }
        }
        return function(lhs, rhs) {
            var ret = 0,
                i, prop, v1, v2;
            for (i = 0; !ret && i < length; ++i) {
                prop = parts[i];
                v1 = lhs[prop];
                v2 = rhs[prop];
                ret = direction[i] * ((v1 < v2) ? -1 : ((v2 < v1) ? 1 : 0));
            }
            return ret;
        };
    },
    
    
    
    
    notify: function() {
        var me = this,
            timer = me.timer,
            cyclesLeft = me.getCycleLimit(),
            globalEvents = Ext.GlobalEvents,
            busyCounter, i, item, len, queue, firedEvent;
        if (timer) {
            window.clearTimeout(timer);
            me.timer = null;
        }
        if (me.firing) {
            Ext.Error.raise('Notify cannot be called recursively');
        }
        while (me.scheduledCount) {
            if (cyclesLeft) {
                --cyclesLeft;
            } else {
                me.firing = null;
                if (me.onCycleLimitExceeded) {
                    me.onCycleLimitExceeded();
                }
                break;
            }
            if (!firedEvent) {
                firedEvent = true;
                if (globalEvents.hasListeners.beforebindnotify) {
                    globalEvents.fireEvent('beforebindnotify', me);
                }
            }
            ++me.passes;
            
            
            if (!(queue = me.orderedItems)) {
                me.sort();
                queue = me.orderedItems;
            }
            len = queue.length;
            if (len) {
                me.firing = me.items;
                for (i = 0; i < len; ++i) {
                    item = queue[i];
                    if (item.scheduled) {
                        item.scheduled = false;
                        --me.scheduledCount;
                        me.notifyIndex = i;
                        
                        
                        
                        
                        item.react();
                        if (!me.scheduledCount) {
                            break;
                        }
                    }
                }
            }
        }
        me.firing = null;
        me.notifyIndex = -1;
        
        
        if ((busyCounter = me.busyCounter) !== me.lastBusyCounter) {
            if (!(me.lastBusyCounter = busyCounter)) {
                
                
                me.fireEvent('idle', me);
            }
        }
    },
    
    onTick: function() {
        this.timer = null;
        this.notify();
    },
    
    scheduleItem: function(item) {
        var me = this;
        ++me.scheduledCount;
        
        if (!me.timer && !me.firing) {
            me.scheduleTick();
        }
    },
    
    scheduleTick: function() {
        var me = this;
        if (!me.destroyed && !me.timer) {
            me.timer = Ext.Function.defer(me.onTick, me.getTickDelay(), me);
        }
    },
    
    unscheduleItem: function(item) {
        if (this.scheduledCount) {
            --this.scheduledCount;
        }
    },
    
    
    
    
    
    adjustBusy: function(adjustment) {
        var me = this,
            busyCounter = me.busyCounter + adjustment;
        me.busyCounter = busyCounter;
        if (busyCounter) {
            
            
            if (!me.lastBusyCounter) {
                me.lastBusyCounter = busyCounter;
                me.fireEvent('busy', me);
            }
        } else if (me.lastBusyCounter && !me.timer) {
            
            
            me.scheduleTick();
        }
    },
    
    isBusy: function() {
        return !this.isIdle();
    },
    
    isIdle: function() {
        return !(this.busyCounter + this.lastBusyCounter);
    },
    
    debugHooks: {
        $enabled: false,
        
        onCycleLimitExceeded: function() {
            Ext.Error.raise('Exceeded cycleLimit ' + this.getCycleLimit());
        },
        scheduleItem: function(item) {
            if (!item) {
                Ext.Error.raise('scheduleItem: Invalid argument');
            }
            Ext.log('Schedule item: ' + item.getFullName() + ' - ' + (this.scheduledCount + 1));
            if (item.order <= this.notifyIndex) {
                Ext.log.warn('Suboptimal order: ' + item.order + ' < ' + this.notifyIndex);
            }
            this.callParent([
                item
            ]);
        },
        unscheduleItem: function(item) {
            if (!this.scheduledCount) {
                Ext.Error.raise('Invalid scheduleCount');
            }
            this.callParent([
                item
            ]);
            Ext.log('Unschedule item: ' + item.getFullName() + ' - ' + this.scheduledCount);
        }
    }
});


Ext.define('Ext.data.Batch', {
    mixins: {
        observable: 'Ext.mixin.Observable'
    },
    config: {
        
        pauseOnException: false
    },
    
    current: -1,
    
    total: 0,
    
    running: false,
    
    complete: false,
    
    exception: false,
    
    constructor: function(config) {
        var me = this;
        me.mixins.observable.constructor.call(me, config);
        
        
        
        
        me.operations = [];
        
        me.exceptions = [];
    },
    
    add: function(operation) {
        var me = this,
            i, len;
        if (Ext.isArray(operation)) {
            for (i = 0 , len = operation.length; i < len; ++i) {
                me.add(operation[i]);
            }
        } else {
            me.total++;
            operation.setBatch(me);
            me.operations.push(operation);
        }
        return me;
    },
    
    sort: function() {
        this.operations.sort(this.sortFn);
    },
    sortFn: function(operation1, operation2) {
        var ret = operation1.order - operation2.order;
        if (ret) {
            return ret;
        }
        var entityType1 = operation1.entityType,
            entityType2 = operation2.entityType,
            rank;
        
        
        if (!entityType1 || !entityType2) {
            return 0;
        }
        
        
        if (!(rank = entityType1.rank)) {
            
            entityType1.schema.rankEntities();
            
            rank = entityType1.rank;
        }
        return (rank - entityType2.rank) * operation1.foreignKeyDirection;
    },
    
    start: function(
    index) {
        var me = this;
        if (!me.operations.length || me.running) {
            return me;
        }
        me.exceptions.length = 0;
        me.exception = false;
        me.running = true;
        return me.runOperation(Ext.isDefined(index) ? index : me.current + 1);
    },
    
    retry: function() {
        return this.start(this.current);
    },
    
    runNextOperation: function() {
        var me = this;
        if (me.running) {
            me.runOperation(me.current + 1);
        }
        return me;
    },
    
    pause: function() {
        this.running = false;
        return this;
    },
    
    getOperations: function() {
        return this.operations;
    },
    
    getExceptions: function() {
        return this.exceptions;
    },
    
    getCurrent: function() {
        var out = null,
            current = this.current;
        if (!(current === -1 || this.complete)) {
            out = this.operations[current];
        }
        return out;
    },
    
    getTotal: function() {
        return this.total;
    },
    
    isRunning: function() {
        return this.running;
    },
    
    isComplete: function() {
        return this.complete;
    },
    
    hasException: function() {
        return this.exception;
    },
    
    runOperation: function(index) {
        var me = this,
            operations = me.operations,
            operation = operations[index];
        if (operation === undefined) {
            me.running = false;
            me.complete = true;
            me.fireEvent('complete', me, operations[operations.length - 1]);
        } else {
            me.current = index;
            operation.setInternalCallback(me.onOperationComplete);
            operation.setInternalScope(me);
            operation.execute();
        }
        return me;
    },
    onOperationComplete: function(operation) {
        var me = this,
            exception = operation.hasException();
        if (exception) {
            me.exception = true;
            me.exceptions.push(operation);
            me.fireEvent('exception', me, operation);
        }
        if (exception && me.getPauseOnException()) {
            me.pause();
        } else {
            me.fireEvent('operationcomplete', me, operation);
            me.runNextOperation();
        }
    }
});


Ext.define('Ext.data.matrix.Slice', {
    stub: null,
    constructor: function(side, id) {
        
        this.id = id;
        
        this.side = side;
        
        this.members = {};
    },
    attach: function(store) {
        var me = this;
        Ext.Assert.falsey(me.store, 'Store is already attached');
        me.store = store;
        store.matrix = me;
        store.on('load', me.onStoreLoad, me, {
            single: true
        });
    },
    changeId: function(newId) {
        var me = this,
            oldId = me.id,
            side = me.side,
            slices = side.slices,
            slice = slices[oldId],
            members = slice.members,
            index = side.index,
            otherSlices = side.inverse.slices,
            assoc, otherId, otherMembers;
        me.id = newId;
        slices[newId] = slice;
        delete slices[oldId];
        for (otherId in members) {
            assoc = members[otherId];
            assoc[index] = newId;
            otherMembers = otherSlices[otherId].members;
            otherMembers[newId] = otherMembers[oldId];
            delete otherMembers[oldId];
        }
    },
    onStoreLoad: function(store) {
        this.update(store.getData().items, 0);
    },
    update: function(recordsOrIds, state) {
        if (!(recordsOrIds instanceof Array)) {
            Ext.Error.raise('Only array of records or record ids are supported');
        }
        var me = this,
            MatrixSlice = Ext.data.matrix.Slice,
            side = me.side,
            assocIndex = side.index,
            length = recordsOrIds.length,
            id = me.id,
            members = me.members,
            otherSide = side.inverse,
            otherSlices = otherSide.slices,
            assoc, call, i, item, otherId, otherSlice, record;
        for (i = 0; i < length; ++i) {
            call = record = null;
            item = recordsOrIds[i];
            otherId = item.isEntity ? (record = item).id : item;
            assoc = members[otherId];
            
            
            if (state < 0 && assoc && assoc[2] === 1) {
                delete members[otherId];
                otherSlice = otherSlices[otherId];
                if (otherSlice) {
                    delete otherSlice.members[id];
                }
                call = 1;
            } else {
                if (!assoc) {
                    
                    
                    
                    assoc = [
                        otherId,
                        otherId,
                        state
                    ];
                    assoc[assocIndex] = id;
                    members[otherId] = assoc;
                    otherSlice = otherSlices[otherId];
                    if (!otherSlice) {
                        otherSlices[otherId] = otherSlice = new MatrixSlice(otherSide,otherId);
                    }
                    otherSlice.members[id] = assoc;
                    call = 1;
                } else if (state !== assoc[2] && state !== 0) {
                    
                    assoc[2] = state;
                    otherSlice = otherSlices[otherId];
                    
                    call = 1;
                }
            }
            if (call) {
                if (me.notify) {
                    me.notify.call(me.scope, me, otherId, state);
                }
                if (otherSlice && otherSlice.notify) {
                    otherSlice.notify.call(otherSlice.scope, otherSlice, id, state);
                }
            }
        }
    },
    destroy: function() {
        var me = this,
            store = me.store;
        if (store) {
            store.matrix = null;
            store.un('load', me.onStoreLoad, me);
        }
        me.notify = me.scope = me.store = me.side = me.members = null;
        me.callParent();
    }
});


Ext.define('Ext.data.matrix.Side', {
    requires: [
        'Ext.data.matrix.Slice'
    ],
    
    constructor: function(matrix, index, role) {
        var me = this;
        
        me.matrix = matrix;
        
        me.index = index;
        
        me.role = role;
        
        me.slices = {};
    },
    get: function(id1, id2) {
        var me = this,
            slices = me.slices,
            slice = slices[id1] || (slices[id1] = new Ext.data.matrix.Slice(me,id1));
        return (id2 || id2 === 0) ? slice.members[id2] : slice;
    },
    update: function(id1, id2, state) {
        var slice = this.get(id1);
        return slice.update(id2, state);
    },
    destroy: function() {
        var me = this,
            slices = me.slices,
            id;
        for (id in slices) {
            slices[id].destroy();
        }
        me.inverse = me.matrix = me.role = me.slices = null;
        me.callParent();
    }
});


Ext.define('Ext.data.matrix.Matrix', {
    requires: [
        'Ext.data.matrix.Side'
    ],
    
    
    
    constructor: function(session, matrix) {
        var me = this,
            association = matrix.isManyToMany ? matrix : session.getSchema().getAssociation(matrix),
            Side = Ext.data.matrix.Side,
            left = new Side(me,0,association.left),
            right = new Side(me,1,association.right);
        Ext.Assert.truthy(association.isManyToMany, 'Association is not many-to-many');
        me.association = association;
        me.session = session;
        me.left = left;
        me.right = right;
        left.inverse = right;
        right.inverse = left;
    },
    update: function(id1, id2, state) {
        return this.left.update(id1, id2, state);
    },
    destroy: function() {
        var me = this;
        me.left.destroy();
        me.right.destroy();
        me.association = me.session = me.left = me.right = null;
        me.callParent();
    }
});


Ext.define('Ext.data.session.ChangesVisitor', {
    constructor: function(session) {
        var me = this,
            crud;
        me.session = session;
        crud = session.getCrudProperties();
        me.result = null;
        me.writerOptions = {};
        
        me.createKey = crud.create;
        me.readKey = crud.read;
        me.updateKey = crud.update;
        me.dropKey = crud.drop;
    },
    onDirtyRecord: function(record) {
        var me = this,
            crud = me.crud,
            created = record.phantom,
            dropped = record.dropped,
            updated = !created && !dropped,
            type = record.$className,
            prop = (created || dropped) ? 'allDataOptions' : 'partialDataOptions',
            writerOptions = me.writerOptions,
            name = record.entityName,
            options, bucket, entry, result;
        if (created && dropped) {
            return false;
        }
        crud = created ? me.createKey : (dropped ? me.dropKey : me.updateKey);
        writerOptions = writerOptions[type] || (writerOptions[type] = {});
        if (dropped) {
            
            
            if (!(options = writerOptions.drop)) {
                writerOptions.drop = options = {
                    all: record.getProxy().getWriter().getWriteAllFields()
                };
            }
            if (!options.all) {
                entry = record.id;
            }
        }
        
        if (!entry) {
            
            
            
            if (!(options = writerOptions[prop])) {
                options = record.getProxy().getWriter().getConfig(prop);
                writerOptions[prop] = options = Ext.Object.chain(options);
                me.setupOptions(options);
            }
            entry = record.getData(options);
        }
        
        
        
        
        
        
        
        
        
        result = me.result || (me.result = {});
        bucket = result[name] || (result[name] = {});
        bucket = bucket[crud] || (bucket[crud] = []);
        bucket.push(entry);
    },
    setupOptions: function(options) {
        options.serialize = true;
    },
    onMatrixChange: function(association, id1, id2, state) {
        var me = this,
            name = association.left.type,
            
            assocName = association.right.role,
            
            operation = state < 0 ? me.dropKey : me.createKey,
            bucket, result;
        
        
        
        
        
        
        
        
        
        
        result = me.result || (me.result = {});
        bucket = result[name] || (result[name] = {});
        
        bucket = bucket[assocName] || (bucket[assocName] = {});
        
        bucket = bucket[operation] || (bucket[operation] = {});
        
        bucket = bucket[id1] || (bucket[id1] = []);
        bucket.push(id2);
    }
});


Ext.define('Ext.data.session.ChildChangesVisitor', {
    extend: 'Ext.data.session.ChangesVisitor',
    constructor: function() {
        this.seen = {};
        this.callParent(arguments);
    },
    setupOptions: function(options) {
        this.callParent([
            options
        ]);
        options.serialize = false;
    },
    onDirtyRecord: function(record) {
        if (this.callParent(arguments) !== false) {
            
            
            if (!record.$source && (record.dropped || !record.phantom)) {
                this.readEntity(record);
            }
        }
    },
    readEntity: function(record) {
        var me = this,
            readKey = me.readKey,
            name = record.entityName,
            id = record.id,
            seen = me.seen,
            seenKey = name + id,
            result, bucket;
        
        if (seen[seenKey]) {
            return;
        }
        seen[seenKey] = true;
        result = me.result || (me.result = {});
        bucket = result[name] || (result[name] = {});
        bucket = bucket[readKey] || (bucket[readKey] = []);
        bucket.push(Ext.apply({}, record.modified, record.data));
    }
});


Ext.define('Ext.data.session.BatchVisitor', {
    map: null,
    constructor: function(batch) {
        this.batch = batch;
    },
    getBatch: function(sort) {
        var map = this.map,
            batch = this.batch,
            bucket, entity, name, operation, proxy;
        if (map) {
            if (!batch) {
                batch = new Ext.data.Batch();
            }
            for (name in map) {
                bucket = map[name];
                entity = bucket.entity;
                
                proxy = entity.getProxy();
                delete bucket.entity;
                
                for (operation in bucket) {
                    operation = proxy.createOperation(operation, {
                        records: bucket[operation]
                    });
                    operation.entityType = entity;
                    batch.add(operation);
                }
            }
        }
        if (batch && sort !== false) {
            batch.sort();
        }
        return batch;
    },
    onDirtyRecord: function(record) {
        var me = this,
            operation = record.phantom ? 'create' : (record.dropped ? 'destroy' : 'update'),
            name = record.$className,
            map = (me.map || (me.map = {})),
            bucket = (map[name] || (map[name] = {
                entity: record.self
            }));
        
        
        
        
        
        
        bucket = bucket[operation] || (bucket[operation] = []);
        bucket.push(record);
    }
});


Ext.define('Ext.data.Session', {
    requires: [
        'Ext.data.schema.Schema',
        'Ext.data.Batch',
        'Ext.data.matrix.Matrix',
        'Ext.data.session.ChangesVisitor',
        'Ext.data.session.ChildChangesVisitor',
        'Ext.data.session.BatchVisitor'
    ],
    isSession: true,
    config: {
        
        schema: 'default',
        
        parent: null,
        
        autoDestroy: true,
        crudProperties: {
            create: 'C',
            read: 'R',
            update: 'U',
            drop: 'D'
        }
    },
    destroyed: false,
    crudOperations: [
        {
            type: 'R',
            entityMethod: 'readEntities'
        },
        {
            type: 'C',
            entityMethod: 'createEntities'
        },
        {
            type: 'U',
            entityMethod: 'updateEntities'
        },
        {
            type: 'D',
            entityMethod: 'dropEntities'
        }
    ],
    crudKeys: {
        C: 1,
        R: 1,
        U: 1,
        D: 1
    },
    constructor: function(config) {
        var me = this;
        
        me.data = {};
        
        me.matrices = {};
        me.identifierCache = {};
        
        me.recordCreator = me.recordCreator.bind(me);
        me.initConfig(config);
    },
    destroy: function() {
        var me = this,
            matrices = me.matrices,
            data = me.data,
            entityName, entities, record, id;
        for (id in matrices) {
            matrices[id].destroy();
        }
        for (entityName in data) {
            entities = data[entityName];
            for (id in entities) {
                record = entities[id].record;
                if (record) {
                    
                    
                    record.$source = record.session = null;
                }
            }
        }
        me.recordCreator = me.matrices = me.data = null;
        me.setSchema(null);
        me.callParent();
    },
    
    adopt: function(record) {
        var me = this,
            associations = record.associations,
            roleName;
        me.checkModelType(record.self);
        if (record.session && record.session !== me) {
            Ext.Error.raise('Record already belongs to an existing session');
        }
        if (record.session !== me) {
            record.session = me;
            me.add(record);
            if (associations) {
                for (roleName in associations) {
                    associations[roleName].adoptAssociated(record, me);
                }
            }
        }
    },
    
    commit: function() {
        var data = this.data,
            entityName, entities, id, record;
        for (entityName in data) {
            entities = data[entityName];
            for (id in entities) {
                record = entities[id].record;
                if (record) {
                    record.commit();
                }
            }
        }
    },
    
    createRecord: function(type, data) {
        this.checkModelType(type);
        var Model = type.$isClass ? type : this.getSchema().getEntity(type),
            parent = this.getParent(),
            id;
        
        if (data && parent) {
            id = Model.getIdFromData(data);
            if (parent.peekRecord(Model, id)) {
                Ext.Error.raise('A parent session already contains an entry for ' + Model.entityName + ': ' + id);
            }
        }
        
        return new Model(data,this);
    },
    
    getChanges: function() {
        var visitor = new Ext.data.session.ChangesVisitor(this);
        this.visitData(visitor);
        return visitor.result;
    },
    
    getChangesForParent: function() {
        var visitor = new Ext.data.session.ChildChangesVisitor(this);
        this.visitData(visitor);
        return visitor.result;
    },
    
    getRecord: function(type, id, autoLoad) {
        var me = this,
            record = me.peekRecord(type, id),
            Model, parent, parentRec;
        if (!record) {
            Model = type.$isClass ? type : me.getSchema().getEntity(type);
            parent = me.getParent();
            if (parent) {
                parentRec = parent.peekRecord(Model, id);
            }
            if (parentRec && !parentRec.isLoading()) {
                record = parentRec.copy(undefined, me);
                record.$source = parentRec;
            } else {
                record = Model.createWithId(id, null, me);
                if (autoLoad !== false) {
                    record.load(Ext.isObject(autoLoad) ? autoLoad : undefined);
                }
            }
        }
        return record;
    },
    
    getSaveBatch: function(sort) {
        var visitor = new Ext.data.session.BatchVisitor();
        this.visitData(visitor);
        return visitor.getBatch(sort);
    },
    
    onInvalidAssociationEntity: function(entityType, id) {
        Ext.Error.raise('Unable to read association entity: ' + this.getModelIdentifier(entityType, id));
    },
    
    onInvalidEntityCreate: function(entityType, id) {
        Ext.Error.raise('Cannot create, record already not exists: ' + this.getModelIdentifier(entityType, id));
    },
    
    onInvalidEntityDrop: function(entityType, id) {
        Ext.Error.raise('Cannot drop, record does not exist: ' + this.getModelIdentifier(entityType, id));
    },
    
    onInvalidEntityRead: function(entityType, id) {
        Ext.Error.raise('Cannot read, record already not exists: ' + this.getModelIdentifier(entityType, id));
    },
    
    onInvalidEntityUpdate: function(entityType, id, dropped) {
        if (dropped) {
            Ext.Error.raise('Cannot update, record dropped: ' + this.getModelIdentifier(entityType, id));
        } else {
            Ext.Error.raise('Cannot update, record does not exist: ' + this.getModelIdentifier(entityType, id));
        }
    },
    
    peekRecord: function(type, id, deep) {
        
        
        this.checkModelType(type);
        var entityType = type.$isClass ? type : this.getSchema().getEntity(type),
            entityName = entityType.entityName,
            entry = this.data[entityName],
            ret, parent;
        entry = entry && entry[id];
        ret = entry && entry.record;
        if (!ret && deep) {
            parent = this.getParent();
            ret = parent && parent.peekRecord(type, id, deep);
        }
        return ret || null;
    },
    
    save: function() {
        if (!this.getParent()) {
            Ext.Error.raise('Cannot commit session, no parent exists');
        }
        var visitor = new Ext.data.session.ChildChangesVisitor(this);
        this.visitData(visitor);
        this.getParent().update(visitor.result);
    },
    
    spawn: function() {
        return new this.self({
            schema: this.getSchema(),
            parent: this
        });
    },
    
    update: function(data) {
        var me = this,
            schema = me.getSchema(),
            crudOperations = me.crudOperations,
            len = crudOperations.length,
            crudKeys = me.crudKeys,
            entityName, entityType, entityInfo, i, operation, item, associations, key, role, associationData;
        
        me.getSchema().processKeyChecks(true);
        
        for (entityName in data) {
            entityType = schema.getEntity(entityName);
            if (!entityType) {
                Ext.Error.raise('Invalid entity type: ' + entityName);
            }
            entityInfo = data[entityName];
            for (i = 0; i < len; ++i) {
                operation = crudOperations[i];
                item = entityInfo[operation.type];
                if (item) {
                    me[operation.entityMethod](entityType, item);
                }
            }
        }
        
        for (entityName in data) {
            entityType = schema.getEntity(entityName);
            associations = entityType.associations;
            entityInfo = data[entityName];
            for (key in entityInfo) {
                
                if (crudKeys[key]) {
                    
                    continue;
                }
                role = associations[key];
                if (!role) {
                    Ext.Error.raise('Invalid association key for ' + entityName + ', "' + key + '"');
                }
                associationData = entityInfo[role.role];
                role.processUpdate(me, associationData);
            }
        }
    },
    
    privates: {
        
        add: function(record) {
            var me = this,
                id = record.id,
                entry = me.getEntry(record.self, id),
                associations, roleName;
            if (entry.record) {
                Ext.Error.raise('Duplicate id ' + record.id + ' for ' + record.entityName);
            }
            entry.record = record;
            me.registerReferences(record);
            associations = record.associations;
            for (roleName in associations) {
                associations[roleName].checkMembership(me, record);
            }
        },
        
        afterErase: function(record) {
            this.evict(record);
        },
        
        applySchema: function(schema) {
            return Ext.data.schema.Schema.get(schema);
        },
        
        checkModelType: function(name) {
            if (name.$isClass) {
                name = name.entityName;
            }
            if (!name) {
                Ext.Error.raise('Unable to use anonymous models in a Session');
            } else if (!this.getSchema().getEntity(name)) {
                Ext.Error.raise('Unknown entity type ' + name);
            }
        },
        
        createEntities: function(entityType, items) {
            var len = items.length,
                i, data, rec, id;
            for (i = 0; i < len; ++i) {
                data = items[i];
                id = entityType.getIdFromData(data);
                rec = this.peekRecord(entityType, id);
                if (!rec) {
                    rec = this.createRecord(entityType, data);
                } else {
                    this.onInvalidEntityCreate(entityType, id);
                }
                
                
                rec.phantom = true;
            }
        },
        
        dropEntities: function(entityType, ids) {
            var len = ids.length,
                i, rec, id;
            for (i = 0; i < len; ++i) {
                id = ids[i];
                rec = this.peekRecord(entityType, id);
                if (rec) {
                    rec.drop();
                } else {
                    this.onInvalidEntityDrop(entityType, id);
                }
            }
        },
        
        evict: function(record) {
            var entityName = record.entityName,
                entities = this.data[entityName],
                id = record.id,
                entry;
            if (entities) {
                delete entities[id];
            }
        },
        
        getEntityList: function(entityType, ids) {
            var len = ids.length,
                i, id, rec, invalid;
            for (i = 0; i < len; ++i) {
                id = ids[i];
                rec = this.peekRecord(entityType, id);
                if (rec) {
                    ids[i] = rec;
                } else {
                    invalid = true;
                    ids[i] = null;
                    this.onInvalidAssociationEntity(entityType, id);
                }
            }
            if (invalid) {
                ids = Ext.Array.clean(ids);
            }
            return ids;
        },
        
        getEntry: function(type, id) {
            if (type.isModel) {
                id = type.getId();
                type = type.self;
            }
            var entityType = type.$isClass ? type : this.getSchema().getEntity(type),
                entityName = entityType.entityName,
                data = this.data,
                entry;
            entry = data[entityName] || (data[entityName] = {});
            entry = entry[id] || (entry[id] = {});
            return entry;
        },
        getRefs: function(record, role, includeParent) {
            var entry = this.getEntry(record),
                refs = entry && entry.refs && entry.refs[role.role],
                parent = includeParent && this.getParent(),
                parentRefs, id, rec;
            if (parent) {
                parentRefs = parent.getRefs(record, role);
                if (parentRefs) {
                    for (id in parentRefs) {
                        rec = parentRefs[id];
                        if ((!refs || !refs[id])) {
                            
                            
                            this.getRecord(rec.self, rec.id);
                        }
                    }
                    
                    refs = entry && entry.refs && entry.refs[role.role];
                }
            }
            return refs || null;
        },
        getIdentifier: function(entityType) {
            var parent = this.getParent(),
                cache, identifier, key, ret;
            if (parent) {
                ret = parent.getIdentifier(entityType);
            } else {
                cache = this.identifierCache;
                identifier = entityType.identifier;
                key = identifier.id || entityType.entityName;
                ret = cache[key];
                if (!ret) {
                    if (identifier.clone) {
                        ret = identifier.clone({
                            cache: cache
                        });
                    } else {
                        ret = identifier;
                    }
                    cache[key] = ret;
                }
            }
            return ret;
        },
        getMatrix: function(matrix, preventCreate) {
            var name = matrix.isManyToMany ? matrix.name : matrix,
                matrices = this.matrices,
                ret;
            ret = matrices[name];
            if (!ret && !preventCreate) {
                ret = matrices[name] = new Ext.data.matrix.Matrix(this,matrix);
            }
            return ret || null;
        },
        getMatrixSlice: function(role, id) {
            var matrix = this.getMatrix(role.association),
                side = matrix[role.side];
            return side.get(id);
        },
        
        getModelIdentifier: function(entityType, id) {
            return id + '@' + entityType.entityName;
        },
        onIdChanged: function(record, oldId, newId) {
            var me = this,
                entityName = record.entityName,
                id = record.id,
                bucket = me.data[entityName],
                entry = bucket[oldId],
                associations = record.associations,
                refs = entry.refs,
                setNoRefs = me._setNoRefs,
                association, fieldName, matrix, refId, role, roleName, roleRefs, store;
            if (bucket[newId]) {
                Ext.Error.raise('Cannot change ' + entityName + ' id from ' + oldId + ' to ' + newId + ' id already exists');
            }
            delete bucket[oldId];
            bucket[newId] = entry;
            for (roleName in associations) {
                role = associations[roleName];
                if (role.isMany) {
                    store = role.getAssociatedItem(record);
                    if (store) {
                        matrix = store.matrix;
                        if (matrix) {
                            matrix.changeId(newId);
                        }
                    }
                }
            }
            if (refs) {
                for (roleName in refs) {
                    roleRefs = refs[roleName];
                    role = associations[roleName];
                    association = role.association;
                    if (association.isManyToMany) {} else 
                    {
                        fieldName = association.field.name;
                        for (refId in roleRefs) {
                            roleRefs[refId].set(fieldName, id, setNoRefs);
                        }
                    }
                }
            }
            me.registerReferences(record, oldId);
        },
        processManyBlock: function(entityType, role, items, processor) {
            var me = this,
                id, record, store;
            if (items) {
                for (id in items) {
                    record = me.peekRecord(entityType, id);
                    if (record) {
                        records = me.getEntityList(role.cls, items[id]);
                        store = role.getAssociatedItem(record);
                        me[processor](role, store, record, records);
                    } else {
                        me.onInvalidAssociationEntity(entityType, id);
                    }
                }
            }
        },
        processManyCreate: function(role, store, record, records) {
            if (store) {
                
                store.add(records);
            } else {
                record[role.getterName](null, null, records);
            }
        },
        processManyDrop: function(role, store, record, records) {
            if (store) {
                store.remove(records);
            }
        },
        processManyRead: function(role, store, record, records) {
            if (store) {
                store.setRecords(records);
            } else {
                
                record[role.getterName](null, null, records);
            }
        },
        
        readEntities: function(entityType, items) {
            var len = items.length,
                i, data, rec, id;
            for (i = 0; i < len; ++i) {
                data = items[i];
                id = entityType.getIdFromData(data);
                rec = this.peekRecord(entityType, id);
                if (!rec) {
                    rec = this.createRecord(entityType, data);
                } else {
                    this.onInvalidEntityRead(entityType, id);
                }
                
                
                rec.phantom = false;
            }
        },
        recordCreator: function(data, Model) {
            var me = this,
                id = Model.getIdFromData(data),
                record = me.peekRecord(Model, id, true);
            
            if (!record) {
                
                
                
                
                record = new Model(data,me);
            } else {
                
                
                
                
                
                record = me.getRecord(Model, id);
            }
            return record;
        },
        registerReferences: function(record, oldId) {
            var entityName = record.entityName,
                id = record.id,
                recordData = record.data,
                remove = oldId || oldId === 0,
                entry, i, fk, len, reference, references, refs, roleName;
            
            len = (references = record.references).length;
            for (i = 0; i < len; ++i) {
                reference = references[i];
                
                fk = recordData[reference.name];
                
                if (fk || fk === 0) {
                    reference = reference.reference;
                    
                    entityName = reference.type;
                    roleName = reference.inverse.role;
                    
                    entry = this.getEntry(reference.cls, fk);
                    refs = entry.refs || (entry.refs = {});
                    refs = refs[roleName] || (refs[roleName] = {});
                    refs[id] = record;
                    if (remove) {
                        delete refs[oldId];
                    }
                }
            }
        },
        
        updateEntities: function(entityType, items) {
            var len = items.length,
                i, data, rec, id, modified;
            
            if (Ext.isArray(items)) {
                for (i = 0; i < len; ++i) {
                    data = items[i];
                    id = entityType.getIdFromData(data);
                    rec = this.peekRecord(entityType, id);
                    if (rec) {
                        rec.set(data);
                    } else {
                        this.onInvalidEntityUpdate(entityType, id);
                    }
                }
            } else {
                for (id in items) {
                    data = items[id];
                    rec = this.peekRecord(entityType, id);
                    if (rec && !rec.dropped) {
                        modified = rec.set(data);
                    } else {
                        this.onInvalidEntityUpdate(entityType, id, !!rec);
                    }
                }
            }
        },
        updateReference: function(record, field, newValue, oldValue) {
            var reference = field.reference,
                entityName = reference.type,
                roleName = reference.inverse.role,
                id = record.id,
                entry, refs;
            if (oldValue || oldValue === 0) {
                
                refs = this.getEntry(entityName, oldValue).refs[roleName];
                delete refs[id];
            }
            if (newValue || newValue === 0) {
                entry = this.getEntry(entityName, newValue);
                refs = entry.refs || (entry.refs = {});
                refs = refs[roleName] || (refs[roleName] = {});
                refs[id] = record;
            }
        },
        
        visitData: function(visitor) {
            var me = this,
                data = me.data,
                matrices = me.matrices,
                all, assoc, id, id2, matrix, members, name, record, slice, slices, state;
            
            me.getSchema().processKeyChecks(true);
            for (name in data) {
                all = data[name];
                
                for (id in all) {
                    record = all[id].record;
                    if (record) {
                        if (record.phantom || record.dirty || record.dropped) {
                            if (visitor.onDirtyRecord) {
                                visitor.onDirtyRecord(record);
                            }
                        } else if (visitor.onCleanRecord) {
                            visitor.onCleanRecord(record);
                        }
                    }
                }
            }
            if (visitor.onMatrixChange) {
                for (name in matrices) {
                    matrix = matrices[name].left;
                    
                    slices = matrix.slices;
                    assoc = matrix.role.association;
                    for (id in slices) {
                        slice = slices[id];
                        members = slice.members;
                        for (id2 in members) {
                            state = (record = members[id2])[2];
                            if (state) {
                                visitor.onMatrixChange(assoc, record[0], record[1], state);
                            }
                        }
                    }
                }
            }
            return visitor;
        },
        
        
        _setNoRefs: {
            refs: false
        }
    }
});


Ext.define('Ext.util.Schedulable', {
    'abstract': true,
    isSchedulable: true,
    scheduled: false,
    constructor: function() {
        this.getScheduler().add(this);
    },
    destroy: function() {
        var me = this,
            scheduler = me.getScheduler();
        if (scheduler) {
            scheduler.remove(me);
        }
        me.destroyed = true;
        me.scheduler = null;
        me.schedule = me.destroy = me.react = Ext.emptyFn;
    },
    getFullName: function() {
        return this.name || this.id;
    },
    privates: {
        
        getScheduler: function() {
            return this.scheduler;
        },
        
        schedule: function() {
            var me = this,
                scheduler;
            if (!me.scheduled) {
                scheduler = me.getScheduler();
                if (scheduler) {
                    me.scheduled = true;
                    if (me.onSchedule) {
                        me.onSchedule();
                    }
                    scheduler.scheduleItem(me);
                }
            }
        },
        
        unschedule: function() {
            var me = this,
                scheduler;
            if (me.scheduled) {
                scheduler = me.getScheduler();
                if (scheduler) {
                    scheduler.unscheduleItem(me);
                }
                me.scheduled = false;
            }
        },
        
        
        sort: function() {}
    }
});


Ext.define('Ext.app.bind.BaseBinding', {
    extend: 'Ext.util.Schedulable',
    calls: 0,
    kind: 20,
    defaultOptions: {},
    lastValue: undefined,
    
    constructor: function(owner, callback, scope, options) {
        var me = this;
        me.options = options;
        me.owner = owner;
        me.scope = scope;
        me.callback = callback;
        if (!callback) {
            Ext.Error.raise('Callback is required');
        }
        
        me.lateBound = Ext.isString(callback);
        if (options && options.deep) {
            me.deep = true;
        }
        me.callParent();
    },
    destroy: function() {
        var me = this,
            owner = me.owner;
        me.callParent();
        if (owner) {
            owner.onBindDestroy();
        }
        me.scope = me.callback = me.owner = null;
    },
    privates: {
        getScheduler: function() {
            var owner = this.owner;
            return owner && owner.getScheduler();
        },
        getSession: function() {
            var owner = this.owner;
            return owner.isSession ? owner : owner.getSession();
        },
        notify: function(value) {
            var me = this,
                options = me.options || me.defaultOptions,
                previous = me.lastValue;
            
            
            
            
            
            
            if (!me.calls || me.deep || previous !== value || Ext.isArray(value)) {
                ++me.calls;
                me.lastValue = value;
                if (me.lateBound) {
                    
                    
                    me.scope[me.callback](value, previous, me);
                } else {
                    me.callback.call(me.scope, value, previous, me);
                }
                if (options.single) {
                    me.destroy();
                }
            }
        }
    }
});


Ext.define('Ext.app.bind.Binding', {
    extend: 'Ext.app.bind.BaseBinding',
    
    constructor: function(stub, callback, scope, options) {
        var me = this;
        me.callParent([
            stub.owner,
            callback,
            scope,
            options
        ]);
        me.stub = stub;
        me.depth = stub.depth;
        
        
        
        if (!stub.isLoading() && !stub.scheduled) {
            me.schedule();
        }
    },
    
    destroy: function(
    fromParent) {
        var me = this,
            stub = me.stub;
        if (stub && !fromParent) {
            stub.unbind(me);
            me.stub = null;
        }
        me.callParent();
    },
    
    bindValidation: function(callback, scope) {
        var stub = this.stub;
        return stub && stub.bindValidation(callback, scope);
    },
    
    bindValidationField: function(callback, scope) {
        var stub = this.stub;
        return stub && stub.bindValidationField(callback, scope);
    },
    
    getFullName: function() {
        return this.fullName || (this.fullName = '@(' + this.stub.getFullName() + ')');
    },
    
    getValue: function() {
        var me = this,
            stub = me.stub,
            ret = stub && stub.getValue();
        if (me.transform) {
            ret = me.transform(ret);
        }
        return ret;
    },
    
    isLoading: function() {
        var stub = this.stub;
        return stub && stub.isLoading();
    },
    
    isReadOnly: function() {
        var stub = this.stub,
            options = this.options;
        if (!(options && options.twoWay === false)) {
            if (stub) {
                return stub.isReadOnly();
            }
        }
        return true;
    },
    
    
    refresh: function() {},
    
    
    setValue: function(value) {
        if (this.isReadOnly()) {
            Ext.Error.raise('Cannot setValue on a readonly binding');
        }
        this.stub.set(value);
    },
    privates: {
        getDataObject: function() {
            var stub = this.stub;
            return stub && stub.getDataObject();
        },
        getRawValue: function() {
            var me = this,
                stub = me.stub,
                ret = stub && stub.getRawValue();
            if (me.transform) {
                ret = me.transform(ret);
            }
            return ret;
        },
        isDescendantOf: function(item) {
            var stub = this.stub;
            return stub ? (item === stub) || stub.isDescendantOf(item) : false;
        },
        react: function() {
            this.notify(this.getValue());
        },
        schedule: function() {
            
            
            if (!this.stub.scheduled) {
                this.callParent();
            }
        },
        sort: function() {
            var stub = this.stub;
            stub.scheduler.sortItem(stub);
        }
    }
});


Ext.define('Ext.app.bind.AbstractStub', {
    extend: 'Ext.util.Schedulable',
    requires: [
        'Ext.app.bind.Binding'
    ],
    children: null,
    depth: 0,
    generation: 1,
    kind: 10,
    parent: null,
    constructor: function(owner, name) {
        var me = this;
        
        me.owner = owner;
        me.name = name;
        me.callParent();
    },
    destroy: function() {
        var me = this,
            children = me.children,
            bindings = me.bindings,
            len, i, key;
        if (bindings) {
            for (i = 0 , len = bindings.length; i < len; ++i) {
                bindings[i].destroy(true);
            }
        }
        for (key in children) {
            children[key].destroy();
        }
        me.callParent();
        me.bindings = me.children = me.owner = null;
    },
    add: function(child) {
        var me = this;
        (me.children || (me.children = {}))[child.name] = child;
        child.depth = me.depth + 1;
        child.parent = me;
    },
    getChild: function(path) {
        var pathArray = Ext.isString(path) ? path.split('.') : path;
        if (pathArray && pathArray.length) {
            return this.descend(pathArray, 0);
        }
        return this;
    },
    getFullName: function() {
        var me = this,
            name = me.fullName,
            parent = me.parent,
            s;
        if (!name) {
            name = me.name || me.id;
            if (parent && (s = parent.getFullName())) {
                name = ((s.charAt(s.length - 1) !== ':') ? s + '.' : s) + name;
            }
            me.fullName = name;
        }
        return name;
    },
    getSession: function() {
        var owner = this.owner;
        return owner.isSession ? owner : owner.getSession();
    },
    bind: function(callback, scope, options) {
        var me = this,
            binding = new Ext.app.bind.Binding(me,callback,scope,options),
            bindings = (me.bindings || (me.bindings = []));
        binding.depth = me.depth;
        bindings.push(binding);
        return binding;
    },
    getValue: function() {
        return this.isLoading() ? null : this.getRawValue();
    },
    graft: function(replacement) {
        var me = this,
            bindings = me.bindings,
            name = me.name,
            i;
        
        me.parent = me.bindings = null;
        me.destroy();
        
        replacement.depth = me.depth;
        replacement.bindings = bindings;
        replacement.generation = me.generation + 1;
        replacement.name = name;
        replacement.id = me.id;
        replacement.path = me.path;
        
        if (bindings) {
            for (i = bindings.length; i-- > 0; ) {
                bindings[i].stub = replacement;
            }
        }
        return replacement;
    },
    isDescendantOf: function(item) {
        for (var parent = this; parent = parent.parent; ) {
            if (parent === item) {
                return true;
            }
        }
        return false;
    },
    onSchedule: function() {
        
        
        
        
        for (var i, len, binding, bindings,
            p = this.parent; p; p = p.parent) {
            bindings = p.bindings;
            if (bindings) {
                for (i = 0 , len = bindings.length; i < len; ++i) {
                    binding = bindings[i];
                    if (binding.deep && !binding.scheduled) {
                        binding.schedule();
                    }
                }
            }
        }
    },
    react: function() {
        var bindings = this.bindings,
            binding, i, len;
        if (bindings) {
            for (i = 0 , len = bindings.length; i < len; ++i) {
                binding = bindings[i];
                if (!binding.scheduled) {
                    binding.schedule();
                }
            }
        }
    },
    unbind: function(binding) {
        var bindings = this.bindings;
        if (bindings && bindings.length) {
            Ext.Array.remove(bindings, binding);
        }
    },
    privates: {
        collect: function() {
            var children = this.children,
                bindings = this.bindings,
                totalCount = 0,
                count = 0,
                child, key;
            if (children) {
                for (key in children) {
                    child = children[key];
                    count = child.collect();
                    if (count === 0) {
                        
                        
                        child.destroy();
                        delete children[key];
                    }
                    totalCount += count;
                }
            }
            if (bindings) {
                totalCount += bindings.length;
            }
            return totalCount;
        },
        getScheduler: function() {
            var owner = this.owner;
            return owner && owner.getScheduler();
        },
        sort: function() {
            var parent = this.parent;
            if (parent) {
                
                
                
                
                this.scheduler.sortItem(parent);
            }
        }
    }
});


Ext.define('Ext.app.bind.Stub', {
    extend: 'Ext.app.bind.AbstractStub',
    requires: [
        'Ext.app.bind.Binding'
    ],
    isStub: true,
    dirty: true,
    formula: null,
    validationKey: 'validation',
    statics: {
        trackHadValue: function(value, owner, path, stub) {
            var children = stub && stub.children,
                child, key, hadValue;
            
            
            
            hadValue = value !== undefined;
            if (!owner.hadValue[path]) {
                owner.hadValue[path] = hadValue;
            }
            if (stub) {
                stub.hadValue = hadValue;
            }
            if (value && (value.constructor === Object || value.isModel)) {
                if (value.isModel) {
                    value = value.data;
                }
                for (key in value) {
                    Ext.app.bind.Stub.trackHadValue(value[key], owner, path + '.' + key, children && children[key]);
                }
            }
        }
    },
    constructor: function(owner, name, parent) {
        var me = this,
            path = name;
        me.callParent([
            owner,
            name
        ]);
        me.boundValue = null;
        if (parent) {
            parent.add(me);
            if (!parent.isRootStub) {
                path = parent.path + '.' + name;
            }
        }
        me.hadValue = owner.hadValue[path];
        me.path = path;
    },
    destroy: function() {
        var me = this,
            formula = me.formula,
            parent = me.parent,
            storeBinding = me.storeBinding;
        if (formula) {
            formula.destroy();
        }
        if (storeBinding) {
            storeBinding.destroy();
        }
        me.detachBound();
        me.parentValue = me.formula = me.storeBinding = null;
        me.callParent();
    },
    bindValidation: function(callback, scope) {
        var parent = this.parent;
        return parent && parent.descend([
            this.validationKey,
            this.name
        ]).bind(callback, scope);
    },
    bindValidationField: function(callback, scope) {
        var parent = this.parent,
            name = this.name,
            lateBound = typeof callback === 'string',
            ret;
        if (parent) {
            ret = parent.bind(function(value) {
                var field = null;
                if (value && value.isModel) {
                    field = value.getField(name);
                }
                if (lateBound) {
                    scope[callback](field, null, this);
                } else {
                    callback.call(scope, field, null, this);
                }
            });
        }
        return ret || null;
    },
    descend: function(path, index) {
        var me = this,
            children = me.children || (me.children = {}),
            pos = index || 0,
            name = path[pos++],
            ret;
        if (!(ret = children[name])) {
            ret = new Ext.app.bind.Stub(me.owner,name,me);
        }
        if (pos < path.length) {
            ret = ret.descend(path, pos);
        }
        return ret;
    },
    getChildValue: function(parentData) {
        var me = this,
            name = me.name,
            ret;
        if (!parentData && !Ext.isString(parentData)) {
            
            
            ret = me.hadValue ? null : undefined;
        } else {
            ret = me.inspectValue(parentData);
            if (!ret) {
                if (parentData.isEntity) {
                    
                    ret = parentData.data[name];
                } else {
                    ret = parentData[name];
                }
            }
        }
        return ret;
    },
    getDataObject: function() {
        var me = this,
            parentData = me.parent.getDataObject(),
            
            name = me.name,
            ret = parentData ? parentData[name] : null;
        if (!ret || !(ret.$className || Ext.isObject(ret))) {
            if (ret) {}
            
            parentData[name] = ret = {};
            
            me.hadValue = me.owner.hadValue[me.path] = true;
            
            
            me.invalidate(true, true);
        }
        return ret;
    },
    getRawValue: function() {
        
        
        return this.getChildValue(this.getParentValue());
    },
    graft: function(replacement) {
        var me = this,
            parent = me.parent,
            children = me.children,
            name = me.name,
            i;
        replacement.parent = parent;
        replacement.children = children;
        if (parent) {
            parent.children[name] = replacement;
        }
        if (children) {
            for (i in children) {
                children[i].parent = replacement;
            }
        }
        me.children = null;
        return me.callParent([
            replacement
        ]);
    },
    isLoading: function() {
        var me = this,
            parent = me.parent,
            loading = false,
            associations, parentValue, value, loadSet;
        if (parent && !(loading = parent.isLoading())) {
            parentValue = me.getParentValue();
            value = me.inspectValue(parentValue);
            
            if (value) {
                loading = value.isLoading();
            } else {
                if (parentValue && parentValue.isModel) {
                    associations = parentValue.associations;
                    
                    
                    
                    
                    if (!(associations && me.name in associations)) {
                        loading = false;
                        loadSet = true;
                    }
                }
                if (!loadSet) {
                    loading = !me.hadValue && me.getRawValue() === undefined;
                }
            }
        }
        return loading;
    },
    invalidate: function(deep, dirtyOnly) {
        var me = this,
            children = me.children,
            name;
        me.dirty = true;
        if (!dirtyOnly && !me.isLoading()) {
            if (!me.scheduled) {
                
                me.schedule();
            }
        }
        if (deep && children) {
            for (name in children) {
                children[name].invalidate(deep, dirtyOnly);
            }
        }
    },
    isReadOnly: function() {
        var formula = this.formula;
        return !!(formula && !formula.set);
    },
    set: function(value) {
        var me = this,
            parent = me.parent,
            name = me.name,
            formula = me.formula,
            parentData, associations, association, formulaStub;
        if (formula && !formula.settingValue && formula.set) {
            formula.setValue(value);
            return;
        } else if (me.isLinkStub) {
            formulaStub = me.getLinkFormulaStub();
            formula = formulaStub ? formulaStub.formula : null;
            if (formula) {
                if (formulaStub.isReadOnly()) {
                    Ext.Error.raise('Cannot setValue on a readonly formula');
                }
                formula.setValue(value);
                return;
            }
        }
        
        parentData = parent.getDataObject();
        if (parentData.isEntity) {
            associations = parentData.associations;
            if (associations && (name in associations)) {
                association = associations[name];
                parentData[association.setterName](value);
                
                me.invalidate(true);
            } else {
                
                parentData.set(name, value);
            }
        }
        
        
        else if ((value && value.constructor === Object) || value !== parentData[name]) {
            if (!me.setByLink(value)) {
                if (value === undefined) {
                    delete parentData[name];
                } else {
                    parentData[name] = value;
                    Ext.app.bind.Stub.trackHadValue(value, me.owner, me.path, me);
                }
                me.inspectValue(parentData);
                
                
                me.invalidate(true);
            }
        }
    },
    onStoreLoad: function() {
        this.invalidate(true);
    },
    afterLoad: function(record) {
        this.invalidate(true);
    },
    afterCommit: function(record) {
        
        this.afterEdit(record, null);
    },
    afterEdit: function(record, modifiedFieldNames) {
        var children = this.children,
            len = modifiedFieldNames && modifiedFieldNames.length,
            associations = record.associations,
            key, i, child, scheduled;
        
        if (children) {
            if (len) {
                
                for (i = 0; i < len; ++i) {
                    child = children[modifiedFieldNames[i]];
                    if (child) {
                        child.invalidate();
                    }
                }
            } else {
                
                
                
                for (key in children) {
                    if (!(associations && key in associations)) {
                        children[key].invalidate();
                    }
                }
            }
        }
        this.invalidate();
    },
    afterReject: function(record) {
        
        this.afterEdit(record, null);
    },
    setByLink: function(value) {
        var me = this,
            n = 0,
            i, link, path, stub;
        for (stub = me; stub; stub = stub.parent) {
            if (stub.isLinkStub) {
                link = stub;
                if (n) {
                    for (path = [] , i = 0 , stub = me; stub !== link; stub = stub.parent) {
                        ++i;
                        path[n - i] = stub.name;
                    }
                }
                break;
            }
            ++n;
        }
        if (!link || !(stub = link.getTargetStub())) {
            return false;
        }
        
        
        
        if (path) {
            stub = stub.descend(path);
        }
        stub.set(value);
        return true;
    },
    setFormula: function(formula) {
        var me = this,
            oldFormula = me.formula;
        if (oldFormula) {
            oldFormula.destroy();
        }
        
        
        me.formula = new Ext.app.bind.Formula(me,formula);
    },
    react: function() {
        var me = this,
            bound = this.boundValue,
            children = me.children,
            generation;
        if (bound) {
            if (bound.isValidation) {
                bound.refresh();
                generation = bound.generation;
                
                if (me.lastValidationGeneration === generation) {
                    return;
                }
                me.lastValidationGeneration = generation;
            } else if (bound.isModel) {
                
                
                
                if (children && children[me.validationKey]) {
                    
                    bound.isValid();
                }
            } else if (bound.isStore) {
                
                if (bound.isLoading() && !bound.loadCount) {
                    return;
                }
            }
        }
        this.callParent();
    },
    privates: {
        collect: function() {
            var me = this,
                result = me.callParent(),
                storeBinding = me.storeBinding ? 1 : 0,
                formula = me.formula ? 1 : 0;
            return result + storeBinding + formula;
        },
        getLinkFormulaStub: function() {
            
            
            
            var stub = this;
            while (stub.isLinkStub) {
                stub = stub.binding.stub;
            }
            return stub.formula ? stub : null;
        },
        getParentValue: function() {
            var me = this;
            
            
            if (me.dirty) {
                me.parentValue = me.parent.getValue();
                me.dirty = false;
            }
            return me.parentValue;
        },
        setStore: function(storeBinding) {
            this.storeBinding = storeBinding;
        },
        inspectValue: function(parentData) {
            var me = this,
                name = me.name,
                current = me.boundValue,
                boundValue = null,
                associations, association, raw, changed, associatedEntity;
            if (parentData && parentData.isEntity) {
                associations = parentData.associations;
                if (associations && (name in associations)) {
                    association = associations[name];
                    boundValue = parentData[association.getterName]();
                    if (boundValue && boundValue.isStore) {
                        boundValue.$associatedStore = true;
                    }
                } else if (name === me.validationKey) {
                    boundValue = parentData.getValidation();
                    
                    me.lastValidationGeneration = null;
                }
            } else if (parentData) {
                raw = parentData[name];
                if (raw && (raw.isModel || raw.isStore)) {
                    boundValue = raw;
                }
            }
            
            
            changed = current !== boundValue;
            if (changed) {
                if (current) {
                    me.detachBound();
                }
                if (boundValue) {
                    if (boundValue.isModel) {
                        boundValue.join(me);
                    } else {
                        
                        
                        associatedEntity = boundValue.associatedEntity;
                        if (associatedEntity && !associatedEntity.phantom && !boundValue.complete && !boundValue.hasPendingLoad()) {
                            boundValue.load();
                        }
                        
                        
                        boundValue.on('load', me.onStoreLoad, me, {
                            single: true
                        });
                    }
                }
                me.boundValue = boundValue;
            }
            return boundValue;
        },
        detachBound: function() {
            var me = this,
                current = me.boundValue;
            if (current) {
                if (current.isModel) {
                    current.unjoin(me);
                } else {
                    current.un('load', me.onStoreLoad, me);
                }
            }
        },
        sort: function() {
            var me = this,
                formula = me.formula,
                scheduler = me.scheduler,
                storeBinding = me.storeBinding;
            me.callParent();
            if (storeBinding) {
                scheduler.sortItem(storeBinding);
            }
            if (formula) {
                
                
                
                scheduler.sortItem(formula);
            }
        }
    }
});


Ext.define('Ext.app.bind.LinkStub', {
    extend: 'Ext.app.bind.Stub',
    isLinkStub: true,
    binding: null,
    destroy: function() {
        var me = this,
            binding = me.binding,
            owner = me.owner;
        if (binding) {
            me.binding = null;
            binding.destroy();
            if (owner) {
                delete owner.linkData[me.name];
            }
        }
        me.target = null;
        me.callParent();
    },
    getFullName: function() {
        var me = this;
        return me.fullName || (me.fullName = '(' + me.callParent() + ' -> ' + me.binding.getFullName() + ')');
    },
    getDataObject: function() {
        var binding = this.binding;
        return binding && binding.getDataObject();
    },
    getRawValue: function() {
        var binding = this.binding;
        return binding && binding.getRawValue();
    },
    getValue: function() {
        var binding = this.binding;
        return binding && binding.getValue();
    },
    getTargetStub: function() {
        var binding = this.binding;
        return binding && binding.stub;
    },
    isLoading: function() {
        var binding = this.binding;
        return binding ? binding.isLoading() : false;
    },
    link: function(bindDescriptor, target) {
        var me = this,
            binding = me.binding;
        if (binding) {
            binding.destroy();
        }
        target = me.target = target || me.owner;
        me.linkDescriptor = bindDescriptor;
        me.binding = target.bind(bindDescriptor, me.onChange, me);
        me.binding.deep = true;
    },
    onChange: function() {
        this.invalidate(true);
    },
    react: function() {
        var me = this,
            linkData = me.owner.linkData;
        linkData[me.name] = me.getValue();
        me.callParent();
    },
    privates: {
        collect: function() {
            var me = this,
                result = me.callParent(),
                binding = me.binding ? 1 : 0;
            return result + binding;
        },
        sort: function() {
            var binding = this.binding;
            if (binding) {
                
                
                this.scheduler.sortItem(binding);
            }
        }
    }
});


Ext.define('Ext.app.bind.RootStub', {
    extend: 'Ext.app.bind.AbstractStub',
    requires: [
        'Ext.app.bind.LinkStub',
        'Ext.app.bind.Stub'
    ],
    isRootStub: true,
    depth: 0,
    createRootChild: function(name, direct) {
        var me = this,
            owner = me.owner,
            ownerData = owner.getData(),
            children = me.children,
            previous = children && children[name],
            parentStub = previous ? null : me,
            parentVM, stub;
        if (direct || ownerData.hasOwnProperty(name) || !(parentVM = owner.getParent())) {
            stub = new Ext.app.bind.Stub(owner,name,parentStub);
        } else {
            stub = new Ext.app.bind.LinkStub(owner,name,previous ? null : parentStub);
            stub.link('{' + name + '}', parentVM);
        }
        if (previous) {
            previous.graft(stub);
        }
        return stub;
    },
    createStubChild: function(name) {
        return this.createRootChild(name, true);
    },
    descend: function(path, index) {
        var me = this,
            children = me.children,
            pos = index || 0,
            name = path[pos++],
            ret = (children && children[name]) || me.createRootChild(name);
        if (pos < path.length) {
            ret = ret.descend(path, pos);
        }
        return ret;
    },
    getFullName: function() {
        return this.fullName || (this.fullName = this.owner.id + ':');
    },
    
    getDataObject: function() {
        return this.owner.data;
    },
    getRawValue: function() {
        return this.owner.data;
    },
    getValue: function() {
        return this.owner.data;
    },
    isDescendantOf: function() {
        return false;
    },
    isLoading: function() {
        return false;
    },
    set: function(value) {
        if (!value || value.constructor !== Object) {
            Ext.Error.raise('Only an object can be set at the root');
        }
        var me = this,
            children = me.children || (me.children = {}),
            owner = me.owner,
            data = owner.data,
            parentVM = owner.getParent(),
            linkStub, stub, v, key;
        for (key in value) {
            if (key.indexOf('.') >= 0) {
                Ext.Error.raise('Value names cannot contain dots');
            }
            if ((v = value[key]) !== undefined) {
                if (!(stub = children[key])) {
                    stub = new Ext.app.bind.Stub(owner,key,me);
                } else if (stub.isLinkStub) {
                    if (!stub.getLinkFormulaStub()) {
                        
                        linkStub = stub;
                        stub = new Ext.app.bind.Stub(owner,key);
                        linkStub.graft(stub);
                    }
                }
                stub.set(v);
            } else if (data.hasOwnProperty(key)) {
                delete data[key];
                stub = children[key];
                if (stub && !stub.isLinkStub && parentVM) {
                    stub = me.createRootChild(key);
                }
                stub.invalidate(true);
            }
        }
    },
    schedule: Ext.emptyFn,
    unschedule: Ext.emptyFn
});


Ext.define('Ext.app.bind.Multi', {
    extend: 'Ext.app.bind.BaseBinding',
    isMultiBinding: true,
    missing: 1,
    
    
    
    
    deep: true,
    
    constructor: function(descriptor, owner, callback, scope, options) {
        var me = this,
            trackStatics = options && options.trackStatics;
        me.callParent([
            owner,
            callback,
            scope,
            options
        ]);
        me.bindings = [];
        me.literal = descriptor.$literal;
        if (descriptor.constructor === Object) {
            if (trackStatics) {
                me.staticKeys = [];
            }
            me.addObject(descriptor, me.lastValue = {}, me.staticKeys);
        } else {
            me.addArray(descriptor, me.lastValue = []);
        }
        
        
        
        if (!--me.missing && !me.scheduled) {
            me.schedule();
        }
    },
    destroy: function() {
        var me = this;
        me.bindings = Ext.destroy(me.bindings);
        me.callParent();
    },
    add: function(descriptor, data, property) {
        var me = this,
            owner = me.owner,
            bindings = me.bindings,
            method = me.literal ? (descriptor.reference ? 'bindEntity' : 'bindExpression') : 'bind',
            binding, depth;
        ++me.missing;
        binding = owner[method](descriptor, function(value) {
            data[property] = value;
            if (binding.calls === 1) {
                --me.missing;
            }
            if (!me.missing && !me.scheduled) {
                me.schedule();
            }
        }, 
        me, null);
        depth = binding.depth;
        if (!bindings.length || depth < me.depth) {
            me.depth = depth;
        }
        bindings.push(binding);
        return !this.isBindingStatic(binding);
    },
    addArray: function(multiBindDescr, array) {
        var me = this,
            n = multiBindDescr.length,
            hasDynamic = false,
            dynamic, b, i;
        for (i = 0; i < n; ++i) {
            b = multiBindDescr[i];
            if (b && (b.reference || Ext.isString(b))) {
                dynamic = me.add(b, array, i);
            } else if (Ext.isArray(b)) {
                dynamic = me.addArray(b, array[i] = []);
            } else if (b && b.constructor === Object) {
                dynamic = me.addObject(b, array[i] = {});
            } else {
                array[i] = b;
                dynamic = false;
            }
            hasDynamic = hasDynamic || dynamic;
        }
        return hasDynamic;
    },
    addObject: function(multiBindDescr, object, staticKeys) {
        var me = this,
            hasDynamic = false,
            dynamic, b, name;
        for (name in multiBindDescr) {
            b = multiBindDescr[name];
            if (b && (b.reference || Ext.isString(b))) {
                dynamic = me.add(b, object, name);
            } else if (Ext.isArray(b)) {
                dynamic = me.addArray(b, object[name] = []);
            } else if (b && b.constructor === Object) {
                dynamic = me.addObject(b, object[name] = {});
            } else {
                object[name] = b;
                dynamic = false;
            }
            if (staticKeys && !dynamic) {
                staticKeys.push(name);
            }
            hasDynamic = hasDynamic || dynamic;
        }
        return hasDynamic;
    },
    getFullName: function() {
        var me = this,
            fullName = me.fullName,
            bindings = me.bindings,
            length = bindings.length,
            i;
        if (!fullName) {
            fullName = '@[';
            for (i = 0; i < length; ++i) {
                if (i) {
                    fullName += ',';
                }
                fullName += bindings[i].getFullName();
            }
            fullName += ']';
            me.fullName = fullName;
        }
        return fullName;
    },
    getRawValue: function() {
        return this.lastValue;
    },
    isDescendantOf: function() {
        return false;
    },
    isLoading: function() {
        for (var bindings = this.bindings,
            n = bindings.length; n-- > 0; ) {
            if (bindings[n].isLoading()) {
                return true;
            }
        }
        return false;
    },
    isBindingStatic: function(binding) {
        return binding.isTemplateBinding && binding.isStatic;
    },
    isStatic: function() {
        var bindings = this.bindings,
            len = bindings.length,
            i, binding;
        for (i = 0; i < len; ++i) {
            binding = bindings[i];
            if (!this.isBindingStatic(binding)) {
                return false;
            }
        }
        return true;
    },
    pruneStaticKeys: function() {
        var value = Ext.apply({}, this.lastValue),
            keys = this.staticKeys,
            len = keys.length,
            i;
        for (i = 0; i < len; ++i) {
            delete value[keys[i]];
        }
        return value;
    },
    react: function() {
        this.notify(this.lastValue);
    },
    refresh: function() {},
    
    privates: {
        sort: function() {
            this.scheduler.sortItems(this.bindings);
        }
    }
});


Ext.define('Ext.app.bind.Formula', {
    extend: 'Ext.util.Schedulable',
    requires: [
        'Ext.util.LruCache'
    ],
    statics: {
        getFormulaParser: function(name) {
            var cache = this.formulaCache,
                parser, s;
            if (!cache) {
                cache = this.formulaCache = new Ext.util.LruCache({
                    maxSize: 20
                });
            }
            parser = cache.get(name);
            if (!parser) {
                
                s = '[^\\.a-z0-9_]' + name + '\\(\\s*([\'"])(.*?)\\1\\s*\\)';
                parser = new RegExp(s,'gi');
                cache.add(name, parser);
            }
            return parser;
        }
    },
    isFormula: true,
    calculation: null,
    explicit: false,
    
    
    
    set: null,
    
    single: false,
    argumentNamesRe: /^function\s*\(\s*([^,\)\s]+)/,
    constructor: function(stub, formula) {
        var me = this,
            owner = stub.owner,
            bindTo, expressions, getter, options;
        me.owner = owner;
        me.stub = stub;
        me.callParent();
        if (formula instanceof Function) {
            me.get = getter = formula;
        } else {
            me.get = getter = formula.get;
            me.set = formula.set;
            expressions = formula.bind;
            if (formula.single) {
                me.single = formula.single;
            }
            if (expressions) {
                bindTo = expressions.bindTo;
                if (bindTo) {
                    options = Ext.apply({}, expressions);
                    delete options.bindTo;
                    expressions = bindTo;
                }
            }
        }
        if (!getter) {
            Ext.Error.raise('Must specify a getter method for a formula');
        }
        if (expressions) {
            me.explicit = true;
        } else {
            expressions = getter.$expressions || me.parseFormula(getter);
        }
        me.binding = owner.bind(expressions, me.onChange, me, options);
    },
    destroy: function() {
        var me = this,
            binding = me.binding,
            stub = me.stub;
        if (binding) {
            binding.destroy();
            me.binding = null;
        }
        if (stub) {
            stub.formula = null;
        }
        me.callParent();
        
        me.getterFn = me.owner = null;
    },
    getFullName: function() {
        return this.fullName || (this.fullName = this.stub.getFullName() + '=' + this.callParent() + ')');
    },
    getRawValue: function() {
        return this.calculation;
    },
    onChange: function() {
        if (!this.scheduled) {
            this.schedule();
        }
    },
    parseFormula: function(formula) {
        var str = formula.toString(),
            expressions = {
                $literal: true
            },
            match, getterProp, formulaRe, expr;
        match = this.argumentNamesRe.exec(str);
        getterProp = match ? match[1] : 'get';
        formulaRe = Ext.app.bind.Formula.getFormulaParser(getterProp);
        while ((match = formulaRe.exec(str))) {
            expr = match[2];
            expressions[expr] = expr;
        }
        expressions.$literal = true;
        
        
        formula.$expressions = expressions;
        return expressions;
    },
    react: function() {
        var me = this,
            owner = me.owner,
            data = me.binding.lastValue,
            getterFn = me.getterFn,
            arg;
        if (me.explicit) {
            arg = data;
        } else {
            arg = owner.getFormulaFn(data);
        }
        me.settingValue = true;
        me.stub.set(me.calculation = me.get.call(owner, arg));
        me.settingValue = false;
        if (me.single) {
            me.destroy();
        }
    },
    setValue: function(value) {
        this.set.call(this.stub.owner, value);
    },
    privates: {
        getScheduler: function() {
            var owner = this.owner;
            return owner && owner.getScheduler();
        },
        sort: function() {
            var me = this,
                binding = me.binding;
            
            if (!binding.destroyed) {
                me.scheduler.sortItem(binding);
            }
        }
    }
});


Ext.define('Ext.app.bind.Template', {
    requires: [
        'Ext.util.Format'
    ],
    numberRe: /^(?:\d+(?:\.\d*)?)$/,
    stringRe: /^(?:["][^"]*["])$/,
    
    tokenRe: /\{[!]?(?:(?:(\d+)|([a-z_][\w\-\.]*))(?::([a-z_\.]+)(?:\(([^\)]*?)?\))?)?)\}/gi,
    formatRe: /^([a-z_]+)(?:\(([^\)]*?)?\))?$/i,
    
    buffer: null,
    
    slots: null,
    
    tokens: null,
    
    constructor: function(text) {
        var me = this,
            initters = me._initters,
            name;
        me.text = text;
        for (name in initters) {
            me[name] = initters[name];
        }
    },
    
    _initters: {
        apply: function(values, scope) {
            return this.parse().apply(values, scope);
        },
        getTokens: function() {
            return this.parse().getTokens();
        }
    },
    
    apply: function(values, scope) {
        var me = this,
            slots = me.slots,
            buffer = me.buffer,
            length = slots.length,
            
            i, slot, value;
        for (i = 0; i < length; ++i) {
            slot = slots[i];
            if (slot) {
                if ((value = values[slot.pos]) == null) {
                    
                    value = '';
                }
                if (slot.not) {
                    value = !value;
                }
                if (slot.format) {
                    value = slot.format(value, scope);
                }
                buffer[i] = value;
            }
        }
        return buffer.join('');
    },
    
    getTokens: function() {
        return this.tokens;
    },
    
    parse: function() {
        
        
        var me = this,
            text = me.text,
            buffer = [],
            slots = [],
            tokens = [],
            tokenMap = {},
            last = 0,
            tokenRe = me.tokenRe,
            pos = 0,
            fmt, i, length, match, s, slot, token;
        
        for (i in me._initters) {
            delete me[i];
        }
        me.buffer = buffer;
        me.slots = slots;
        me.tokens = tokens;
        
        while ((match = tokenRe.exec(text))) {
            
            
            
            
            length = match.index - last;
            if (length) {
                buffer[pos++] = text.substring(last, last + length);
                last += length;
            }
            last += (s = match[0]).length;
            slot = {
                fmt: (fmt = match[3] || null),
                index: match[1] ? parseInt(match[1], 10) : null,
                not: s.charAt(1) === '!',
                token: match[2] || null
            };
            token = slot.token || String(slot.index);
            if (token in tokenMap) {
                slot.pos = tokenMap[token];
            } else {
                tokenMap[token] = slot.pos = tokens.length;
                tokens.push(token);
            }
            if (fmt) {
                if (fmt.substring(0, 5) === 'this.') {
                    slot.fmt = fmt.substring(5);
                } else {
                    if (!(fmt in Ext.util.Format)) {
                        Ext.Error.raise('Invalid format specified: "' + fmt + '"');
                    }
                    slot.scope = Ext.util.Format;
                }
                me.parseArgs(match[4], slot);
            }
            slots[pos++] = slot;
        }
        if (last < text.length) {
            buffer[pos++] = text.substring(last);
        }
        return me;
    },
    parseArgs: function(argsString, slot) {
        var me = this,
            numberRe = me.numberRe,
            stringRe = me.stringRe,
            arg, args, i, length;
        if (!argsString) {
            args = [];
        } else if (argsString.indexOf(',') < 0) {
            args = [
                argsString
            ];
        } else {
            args = argsString.split(',');
        }
        slot = slot || {};
        length = args.length;
        slot.args = args;
        for (i = 0; i < length; ++i) {
            arg = args[i];
            if (arg === 'true') {
                args[i] = true;
            } else if (arg === 'false') {
                args[i] = false;
            } else if (arg === 'null') {
                args[i] = null;
            } else if (numberRe.test(arg)) {
                args[i] = parseFloat(arg);
            } else if (stringRe.test(arg)) {
                args[i] = arg.substring(1, arg.length - 1);
            } else {
                slot.fn = Ext.functionFactory('return [' + argsString + '];');
                slot.format = me._formatEval;
                break;
            }
        }
        if (!slot.format) {
            
            args.unshift(0);
            slot.format = me._formatArgs;
        }
        return slot;
    },
    
    parseFormat: function(fmt) {
        var me = this,
            match = me.formatRe.exec(fmt),
            slot = {
                fmt: fmt,
                scope: Ext.util.Format
            },
            args;
        if (!match) {
            Ext.Error.raise('Invalid format syntax: "' + slot + '"');
        }
        args = match[2];
        if (args) {
            slot.fmt = match[1];
            me.parseArgs(args, slot);
        } else {
            slot.args = [
                0
            ];
            
            slot.format = me._formatArgs;
        }
        return slot;
    },
    
    _formatArgs: function(value, scope) {
        
        scope = this.scope || scope;
        this.args[0] = value;
        
        return scope[this.fmt].apply(scope, this.args);
    },
    
    _formatEval: function(value, scope) {
        
        var args = this.fn();
        
        args.unshift(value);
        
        scope = this.scope || scope;
        return scope[this.fmt].apply(scope, args);
    }
});


Ext.define('Ext.app.bind.TemplateBinding', {
    extend: 'Ext.app.bind.BaseBinding',
    requires: [
        'Ext.app.bind.Multi',
        'Ext.app.bind.Template'
    ],
    isTemplateBinding: true,
    lastValue: undefined,
    value: undefined,
    constructor: function(template, owner, callback, scope, options) {
        var me = this,
            tpl = new Ext.app.bind.Template(template),
            tokens = tpl.getTokens();
        me.callParent([
            owner,
            callback,
            scope,
            options
        ]);
        me.tpl = tpl;
        me.tokens = tokens;
        tokens.$literal = true;
        
        if (tokens.length) {
            me.multiBinding = new Ext.app.bind.Multi(tokens,owner,me.onBindData,me);
        } else {
            me.isStatic = true;
            me.onData(tpl.text);
        }
    },
    destroy: function() {
        var me = this;
        Ext.destroy(me.multiBinding);
        me.tpl = me.multiBinding = null;
        me.callParent();
    },
    getFullName: function() {
        var multi = this.multiBinding;
        return this.fullName || (this.fullName = '$' + (multi ? multi.getFullName() : this.callParent()));
    },
    getRawValue: function() {
        return this.value;
    },
    getTemplateScope: function() {
        return null;
    },
    isDescendantOf: function() {
        return false;
    },
    isLoading: function() {
        var multi = this.multiBinding;
        return multi ? multi.isLoading() : false;
    },
    onBindData: function(data) {
        this.onData(this.tpl.apply(data, this.getTemplateScope()));
    },
    onData: function(value) {
        var me = this,
            lastValue = me.value;
        if (lastValue !== (me.value = value)) {
            me.lastValue = lastValue;
            me.schedule();
        }
    },
    react: function() {
        this.notify(this.value);
    },
    refresh: function() {
        var multi = this.multiBinding;
        if (multi) {
            multi.refresh();
        }
    },
    privates: {
        sort: function() {
            var multi = this.multiBinding;
            if (multi) {
                this.scheduler.sortItem(multi);
            }
        }
    }
});


Ext.define('Ext.data.ChainedStore', {
    extend: 'Ext.data.AbstractStore',
    alias: 'store.chained',
    config: {
        
        source: null,
        
        remoteFilter: false,
        
        remoteSort: false
    },
    mixins: [
        'Ext.data.LocalStore'
    ],
    constructor: function() {
        this.callParent(arguments);
        this.getData().addObserver(this);
    },
    blockLoad: Ext.emptyFn,
    unblockLoad: Ext.emptyFn,
    updateRemoteFilter: function(value) {
        if (value) {
            Ext.Error.raise('Remote filtering cannot be used with chained stores.');
        }
    },
    updateRemoteSort: function(value) {
        if (value) {
            Ext.Error.raise('Remote sorting cannot be used with chained stores.');
        }
    },
    remove: function() {
        var source = this.getSource();
        return source.remove.apply(source, arguments);
    },
    getData: function() {
        var me = this,
            data = me.data;
        if (!data) {
            me.data = data = me.constructDataCollection();
        }
        return data;
    },
    getSession: function() {
        return this.getSource().getSession();
    },
    applySource: function(source) {
        if (source) {
            var original = source,
                s;
            source = Ext.data.StoreManager.lookup(source);
            if (!source) {
                s = 'Invalid source {0}specified for Ext.data.ChainedStore';
                s = Ext.String.format(s, typeof original === 'string' ? '"' + original + '" ' : '');
                Ext.Error.raise(s);
            }
        }
        return source;
    },
    updateSource: function(source, oldSource) {
        var me = this,
            data;
        if (oldSource) {
            oldSource.removeObserver(me);
        }
        if (source) {
            data = me.getData();
            data.setSource(source.getData());
            if (!me.isInitializing) {
                me.fireEvent('refresh', me);
                me.fireEvent('datachanged', me);
            }
            source.addObserver(me);
        }
    },
    
    getModel: function() {
        return this.getSource().getModel();
    },
    getProxy: function() {
        return null;
    },
    onCollectionAdd: function(collection, info) {
        var me = this,
            records = info.items,
            lastChunk = !info.next;
        if (me.ignoreCollectionAdd) {
            return;
        }
        me.fireEvent('add', me, records, info.at);
        
        
        
        if (lastChunk) {
            me.fireEvent('datachanged', me);
        }
    },
    
    onCollectionItemChange: function(collection, info) {
        var me = this,
            record = info.item,
            modifiedFieldNames = info.modified || null,
            type = info.meta;
        
        
        
        me.onUpdate(record, type, modifiedFieldNames, info);
        me.fireEvent('update', me, record, type, modifiedFieldNames, info);
    },
    onUpdate: Ext.emptyFn,
    onCollectionRemove: function(collection, info) {
        var me = this,
            records = info.items,
            lastChunk = !info.next;
        if (me.ignoreCollectionRemove) {
            return;
        }
        me.fireEvent('remove', me, records, info.at, false);
        
        
        
        if (lastChunk) {
            me.fireEvent('datachanged', me);
        }
    },
    onSourceBeforeLoad: function(source, operation) {
        this.fireEvent('beforeload', this, operation);
    },
    onSourceAfterLoad: function(source, records, successful, operation) {
        this.fireEvent('load', this, records, successful, operation);
    },
    onFilterEndUpdate: function() {
        this.callParent(arguments);
        this.callObservers('Filter');
    },
    onSourceBeforePopulate: function() {
        this.ignoreCollectionAdd = true;
        this.callObservers('BeforePopulate');
    },
    onSourceAfterPopulate: function() {
        var me = this;
        me.ignoreCollectionAdd = false;
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
        this.callObservers('AfterPopulate');
    },
    onSourceBeforeClear: function() {
        this.ignoreCollectionRemove = true;
        this.callObservers('BeforeClear');
    },
    onSourceAfterClear: function() {
        this.ignoreCollectionRemove = false;
        this.callObservers('AfterClear');
    },
    onSourceBeforeRemoveAll: function() {
        this.ignoreCollectionRemove = true;
        this.callObservers('BeforeRemoveAll');
    },
    onSourceAfterRemoveAll: function(source, silent) {
        var me = this;
        me.ignoreCollectionRemove = false;
        if (!silent) {
            me.fireEvent('clear', me);
            me.fireEvent('datachanged', me);
        }
        this.callObservers('AfterRemoveAll', [
            silent
        ]);
    },
    onSourceFilter: function() {
        var me = this;
        me.fireEvent('refresh', me);
        me.fireEvent('datachanged', me);
    },
    hasPendingLoad: function() {
        return this.getSource().hasPendingLoad();
    },
    isLoaded: function() {
        return this.getSource().isLoaded();
    },
    isLoading: function() {
        return this.getSource().isLoading();
    },
    onDestroy: function() {
        var me = this;
        me.observers = null;
        me.setSource(null);
        me.getData().destroy(true);
        me.data = null;
    },
    privates: {
        isMoving: function() {
            var source = this.getSource();
            return source.isMoving ? source.isMoving.apply(source, arguments) : false;
        },
        loadsSynchronously: function() {
            return this.getSource().loadsSynchronously();
        }
    }
});


Ext.define('Ext.app.ViewModel', {
    mixins: [
        'Ext.mixin.Factoryable',
        'Ext.mixin.Identifiable'
    ],
    requires: [
        'Ext.util.Scheduler',
        'Ext.data.Session',
        'Ext.app.bind.RootStub',
        'Ext.app.bind.LinkStub',
        'Ext.app.bind.Multi',
        'Ext.app.bind.Formula',
        'Ext.app.bind.TemplateBinding',
        
        
        'Ext.data.ChainedStore'
    ],
    alias: 'viewmodel.default',
    
    isViewModel: true,
    factoryConfig: {
        name: 'viewModel'
    },
    destroyed: false,
    collectTimeout: 100,
    expressionRe: /^(?:\{[!]?(?:(\d+)|([a-z_][\w\-\.]*))\})$/i,
    $configStrict: false,
    
    config: {
        
        data: true,
        
        formulas: {
            $value: null,
            merge: function(newValue, currentValue, target, mixinClass) {
                return this.mergeNew(newValue, currentValue, target, mixinClass);
            }
        },
        
        links: null,
        
        parent: null,
        
        root: true,
        
        scheduler: null,
        
        schema: 'default',
        
        session: null,
        
        
        stores: null,
        
        view: null
    },
    constructor: function(config) {
        this.hadValue = {};
        
        this.initConfig(config);
    },
    destroy: function() {
        var me = this,
            scheduler = me._scheduler,
            stores = me.storeInfo,
            parent = me.getParent(),
            task = me.collectTask,
            key, store, autoDestroy;
        me.destroy = Ext.emptyFn;
        me.destroying = true;
        if (task) {
            task.cancel();
            me.collectTask = null;
        }
        if (stores) {
            for (key in stores) {
                store = stores[key];
                autoDestroy = store.autoDestroy;
                if (autoDestroy || (!store.$wasInstance && autoDestroy !== false)) {
                    store.destroy();
                }
                Ext.destroy(store.$binding);
            }
        }
        if (parent) {
            parent.unregisterChild(me);
        }
        me.getRoot().destroy();
        if (scheduler && scheduler.$owner === me) {
            scheduler.$owner = null;
            scheduler.destroy();
        }
        me.hadValue = me.children = me.storeInfo = me._session = me._view = me._scheduler = me._root = me._parent = me.formulaFn = me.$formulaData = null;
        me.destroying = false;
    },
    
    bind: function(descriptor, callback, scope, options) {
        var me = this,
            binding;
        scope = scope || me;
        if (!options && descriptor.bindTo !== undefined && !Ext.isString(descriptor)) {
            options = descriptor;
            descriptor = options.bindTo;
        }
        if (!Ext.isString(descriptor)) {
            binding = new Ext.app.bind.Multi(descriptor,me,callback,scope,options);
        } else if (me.expressionRe.test(descriptor)) {
            
            descriptor = descriptor.substring(1, descriptor.length - 1);
            binding = me.bindExpression(descriptor, callback, scope, options);
        } else {
            binding = new Ext.app.bind.TemplateBinding(descriptor,me,callback,scope,options);
        }
        return binding;
    },
    
    getSession: function() {
        var me = this,
            session = me._session,
            parent;
        if (!session && (parent = me.getParent())) {
            me.setSession(session = parent.getSession());
        }
        return session || null;
    },
    
    getStore: function(key) {
        var storeInfo = this.storeInfo,
            store;
        if (storeInfo) {
            store = storeInfo[key];
        }
        return store || null;
    },
    
    linkTo: function(key, reference) {
        var me = this,
            stub = me.getStub(key),
            id, modelType, linkStub;
        if (stub.depth - me.getRoot().depth > 1) {
            Ext.Error.raise('Links can only be at the top-level: "' + key + '"');
        }
        if (reference.isModel) {
            reference = {
                type: reference.entityName,
                id: reference.id
            };
        }
        
        modelType = reference.type || reference.reference;
        if (modelType) {
            
            id = reference.id;
            if (!reference.create && Ext.isEmpty(id)) {
                Ext.Error.raise('No id specified. To create a phantom model, specify "create: true" as part of the reference.');
            }
            if (reference.create) {
                id = undefined;
            }
            stub.set(me.getRecord(modelType, id));
        } else {
            if (!stub.isLinkStub) {
                
                linkStub = new Ext.app.bind.LinkStub(me,stub.name);
                stub.graft(linkStub);
                stub = linkStub;
            }
            stub.link(reference);
        }
    },
    notify: function() {
        this.getScheduler().notify();
    },
    
    get: function(path) {
        return this.getStub(path).getValue();
    },
    
    set: function(path, value) {
        var me = this,
            obj, stub;
        
        me.getData();
        if (value === undefined && path && path.constructor === Object) {
            stub = me.getRoot();
            value = path;
        } else if (path && path.indexOf('.') < 0) {
            obj = {};
            obj[path] = value;
            value = obj;
            stub = me.getRoot();
        } else {
            stub = me.getStub(path);
        }
        stub.set(value);
    },
    
    privates: {
        registerChild: function(child) {
            var children = this.children;
            if (!children) {
                this.children = children = {};
            }
            children[child.getId()] = child;
        },
        unregisterChild: function(child) {
            var children = this.children;
            if (children) {
                delete children[child.getId()];
            }
        },
        
        getRecord: function(type, id) {
            var session = this.getSession(),
                Model = type,
                hasId = id !== undefined,
                record;
            if (session) {
                if (hasId) {
                    record = session.getRecord(type, id);
                } else {
                    record = session.createRecord(type);
                }
            } else {
                if (!Model.$isClass) {
                    Model = this.getSchema().getEntity(Model);
                    if (!Model) {
                        Ext.Error.raise('Invalid model name: ' + type);
                    }
                }
                if (hasId) {
                    record = Model.createWithId(id);
                    record.load();
                } else {
                    record = new Model();
                }
            }
            return record;
        },
        notFn: function(v) {
            return !v;
        },
        bindExpression: function(descriptor, callback, scope, options) {
            var ch = descriptor.charAt(0),
                not = (ch === '!'),
                path = not ? descriptor.substring(1) : descriptor,
                stub = this.getStub(path),
                binding;
            binding = stub.bind(callback, scope, options);
            if (not) {
                binding.transform = this.notFn;
            }
            return binding;
        },
        applyScheduler: function(scheduler) {
            if (scheduler && !scheduler.isInstance) {
                scheduler = new Ext.util.Scheduler(scheduler);
                scheduler.$owner = this;
            }
            return scheduler;
        },
        getScheduler: function() {
            var me = this,
                scheduler = me._scheduler,
                parent, session;
            if (!scheduler) {
                if (!(parent = me.getParent())) {
                    scheduler = new Ext.util.Scheduler({
                        
                        preSort: 'kind,-depth'
                    });
                    scheduler.$owner = me;
                } else {
                    scheduler = parent.getScheduler();
                }
                me.setScheduler(scheduler);
            }
            return scheduler;
        },
        
        getStub: function(bindDescr) {
            var root = this.getRoot();
            return bindDescr ? root.getChild(bindDescr) : root;
        },
        collect: function() {
            var me = this,
                parent = me.getParent(),
                task = me.collectTask;
            if (parent) {
                parent.collect();
                return;
            }
            if (!task) {
                task = me.collectTask = new Ext.util.DelayedTask(me.doCollect,me);
            }
            
            if (me.collectTimeout === 0) {
                me.doCollect();
            } else {
                task.delay(me.collectTimeout);
            }
        },
        doCollect: function() {
            var children = this.children,
                key;
            
            
            if (children) {
                for (key in children) {
                    children[key].doCollect();
                }
            }
            this.getRoot().collect();
        },
        onBindDestroy: function() {
            var me = this,
                parent;
            if (me.destroying) {
                return;
            }
            parent = me.getParent();
            if (parent) {
                parent.onBindDestroy();
            } else {
                me.collect();
            }
        },
        
        
        
        applyData: function(newData, data) {
            var me = this,
                linkData, parent;
            
            me.getSession();
            if (!data) {
                parent = me.getParent();
                
                me.linkData = linkData = parent ? Ext.Object.chain(parent.getData()) : {};
                
                me.data = me._data = Ext.Object.chain(linkData);
            }
            if (newData && newData.constructor === Object) {
                me.getRoot().set(newData);
            }
        },
        applyParent: function(parent) {
            if (parent) {
                parent.registerChild(this);
            }
            return parent;
        },
        applyStores: function(stores) {
            var me = this,
                root = me.getRoot(),
                key, cfg, storeBind, stub, listeners, isStatic;
            me.storeInfo = {};
            me.listenerScopeFn = function() {
                return me.getView().getInheritedConfig('defaultListenerScope');
            };
            for (key in stores) {
                cfg = stores[key];
                if (cfg.isStore) {
                    cfg.$wasInstance = true;
                    me.setupStore(cfg, key);
                    
                    continue;
                } else if (Ext.isString(cfg)) {
                    cfg = {
                        source: cfg
                    };
                } else {
                    cfg = Ext.apply({}, cfg);
                }
                
                listeners = cfg.listeners;
                delete cfg.listeners;
                storeBind = me.bind(cfg, me.onStoreBind, me, {
                    trackStatics: true
                });
                if (storeBind.isStatic()) {
                    
                    
                    storeBind.destroy();
                    me.createStore(key, cfg, listeners);
                } else {
                    storeBind.$storeKey = key;
                    storeBind.$listeners = listeners;
                    stub = root.createStubChild(key);
                    stub.setStore(storeBind);
                }
            }
        },
        onStoreBind: function(cfg, oldValue, binding) {
            var info = this.storeInfo,
                key = binding.$storeKey,
                store = info[key],
                proxy;
            if (!store) {
                this.createStore(key, cfg, binding.$listeners, binding);
            } else {
                cfg = Ext.merge({}, binding.pruneStaticKeys());
                proxy = cfg.proxy;
                delete cfg.type;
                delete cfg.model;
                delete cfg.fields;
                delete cfg.proxy;
                delete cfg.listeners;
                
                
                if (proxy) {
                    delete proxy.reader;
                    delete proxy.writer;
                    store.getProxy().setConfig(proxy);
                }
                store.blockLoad();
                store.setConfig(cfg);
                store.unblockLoad(true);
            }
        },
        createStore: function(key, cfg, listeners, binding) {
            var session = this.getSession(),
                store;
            cfg = Ext.apply({}, cfg);
            if (cfg.session) {
                cfg.session = session;
            }
            if (cfg.source) {
                cfg.type = cfg.type || 'chained';
            }
            
            cfg.listeners = listeners;
            store = Ext.Factory.store(cfg);
            store.$binding = binding;
            this.setupStore(store, key);
        },
        setupStore: function(store, key) {
            store.resolveListenerScope = this.listenerScopeFn;
            this.storeInfo[key] = store;
            this.set(key, store);
        },
        applyFormulas: function(formulas) {
            var me = this,
                root = me.getRoot(),
                name, stub;
            me.getData();
            
            for (name in formulas) {
                if (name.indexOf('.') >= 0) {
                    Ext.Error.raise('Formula names cannot contain dots: ' + name);
                }
                
                root.createStubChild(name);
                stub = me.getStub(name);
                stub.setFormula(formulas[name]);
            }
            return formulas;
        },
        applyLinks: function(links) {
            for (var link in links) {
                this.linkTo(link, links[link]);
            }
        },
        applySchema: function(schema) {
            return Ext.data.schema.Schema.get(schema);
        },
        applyRoot: function() {
            var root = new Ext.app.bind.RootStub(this),
                parent = this.getParent();
            if (parent) {
                
                
                root.depth = parent.getRoot().depth - 1000;
            }
            return root;
        },
        getFormulaFn: function(data) {
            var me = this,
                fn = me.formulaFn;
            if (!fn) {
                fn = me.formulaFn = function(name) {
                    
                    
                    return me.$formulaData[name];
                };
            }
            me.$formulaData = data;
            return fn;
        }
    }
});


Ext.define('Ext.app.domain.Controller', {
    extend: 'Ext.app.EventDomain',
    singleton: true,
    requires: [
        'Ext.app.Controller'
    ],
    type: 'controller',
    prefix: 'controller.',
    idMatchRe: /^\#/,
    constructor: function() {
        var me = this;
        me.callParent();
        me.monitor(Ext.app.BaseController);
    },
    match: function(target, selector) {
        var result = false,
            alias = target.alias;
        if (selector === '*') {
            result = true;
        } else if (selector === '#') {
            result = !!target.isApplication;
        } else if (this.idMatchRe.test(selector)) {
            result = target.getId() === selector.substring(1);
        } else if (alias) {
            result = Ext.Array.indexOf(alias, this.prefix + selector) > -1;
        }
        return result;
    }
});


Ext.define('Ext.direct.Manager', {
    singleton: true,
    requires: [
        'Ext.util.MixedCollection'
    ],
    mixins: [
        'Ext.mixin.Observable'
    ],
    
    exceptions: {
        TRANSPORT: 'xhr',
        PARSE: 'parse',
        DATA: 'data',
        LOGIN: 'login',
        SERVER: 'exception'
    },
    
    providerClasses: {},
    
    remotingMethods: {},
    config: {
        
        varName: 'Ext.app.REMOTING_API'
    },
    apiNotFoundError: 'Ext Direct API was not found at {0}',
    
    
    
    
    constructor: function() {
        var me = this;
        me.mixins.observable.constructor.call(me);
        me.transactions = new Ext.util.MixedCollection();
        me.providers = new Ext.util.MixedCollection();
    },
    
    addProvider: function(provider) {
        var me = this,
            args = arguments,
            relayers = me.relayers || (me.relayers = {}),
            i, len;
        if (args.length > 1) {
            for (i = 0 , len = args.length; i < len; ++i) {
                me.addProvider(args[i]);
            }
            return;
        }
        
        if (!provider.isProvider) {
            provider = Ext.create('direct.' + provider.type + 'provider', provider);
        }
        me.providers.add(provider);
        provider.on('data', me.onProviderData, me);
        if (provider.relayedEvents) {
            relayers[provider.id] = me.relayEvents(provider, provider.relayedEvents);
        }
        if (!provider.isConnected()) {
            provider.connect();
        }
        return provider;
    },
    
    loadProvider: function(config, callback, scope) {
        var me = this,
            classes = me.providerClasses,
            type, url, varName, provider, i, len;
        if (Ext.isArray(config)) {
            for (i = 0 , len = config.length; i < len; i++) {
                me.loadProvider(config[i], callback, scope);
            }
            return;
        }
        
        
        type = config.type;
        url = config.url;
        if (classes[type] && classes[type].checkConfig(config)) {
            provider = me.addProvider(config);
            me.fireEventArgs('providerload', [
                url,
                provider
            ]);
            Ext.callback(callback, scope, [
                url,
                provider
            ]);
            
            
            
            
            return;
        }
        
        
        
        varName = config.varName || me.getVarName();
        delete config.varName;
        if (!url) {
            Ext.Error.raise("Need API discovery URL to load a Remoting provider!");
        }
        
        
        delete config.url;
        
        
        Ext.Loader.loadScript({
            url: url,
            scope: me,
            onLoad: function() {
                this.onApiLoadSuccess({
                    url: url,
                    varName: varName,
                    config: config,
                    callback: callback,
                    scope: scope
                });
            },
            onError: function() {
                this.onApiLoadFailure({
                    url: url,
                    callback: callback,
                    scope: scope
                });
            }
        });
    },
    
    getProvider: function(id) {
        return id.isProvider ? id : this.providers.get(id);
    },
    
    removeProvider: function(provider) {
        var me = this,
            providers = me.providers,
            relayers = me.relayers,
            id;
        provider = provider.isProvider ? provider : providers.get(provider);
        if (provider) {
            provider.un('data', me.onProviderData, me);
            id = provider.id;
            if (relayers[id]) {
                relayers[id].destroy();
                delete relayers[id];
            }
            providers.remove(provider);
            return provider;
        }
        return null;
    },
    
    addTransaction: function(transaction) {
        this.transactions.add(transaction);
        return transaction;
    },
    
    removeTransaction: function(transaction) {
        var me = this;
        transaction = me.getTransaction(transaction);
        me.transactions.remove(transaction);
        return transaction;
    },
    
    getTransaction: function(transaction) {
        return typeof transaction === 'object' ? transaction : this.transactions.get(transaction);
    },
    onProviderData: function(provider, event) {
        var me = this,
            i, len;
        if (Ext.isArray(event)) {
            for (i = 0 , len = event.length; i < len; ++i) {
                me.onProviderData(provider, event[i]);
            }
            return;
        }
        if (event.name && event.name != 'event' && event.name != 'exception') {
            me.fireEvent(event.name, event);
        } else if (event.status === false) {
            me.fireEvent('exception', event);
        }
        me.fireEvent('event', event, provider);
    },
    
    parseMethod: function(fn) {
        var current = Ext.global,
            i = 0,
            resolved, parts, len;
        if (Ext.isFunction(fn)) {
            resolved = fn;
        } else if (Ext.isString(fn)) {
            resolved = this.remotingMethods[fn];
            
            
            if (!resolved) {
                parts = fn.split('.');
                len = parts.length;
                while (current && i < len) {
                    current = current[parts[i]];
                    ++i;
                }
                resolved = Ext.isFunction(current) ? current : null;
            }
        }
        return resolved || null;
    },
    privates: {
        addProviderClass: function(type, cls) {
            this.providerClasses[type] = cls;
        },
        onApiLoadSuccess: function(options) {
            var me = this,
                url = options.url,
                varName = options.varName,
                api, provider, error;
            try {
                
                
                api = Ext.apply(options.config, eval(varName));
                provider = me.addProvider(api);
            } catch (e) {
                error = e + '';
            }
            if (error) {
                me.fireEventArgs('providerloaderror', [
                    url,
                    error
                ]);
                Ext.callback(options.callback, options.scope, [
                    url,
                    error
                ]);
            } else {
                me.fireEventArgs('providerload', [
                    url,
                    provider
                ]);
                Ext.callback(options.callback, options.scope, [
                    url,
                    provider
                ]);
            }
        },
        onApiLoadFailure: function(options) {
            var url = options.url,
                error;
            error = Ext.String.format(this.apiNotFoundError, url);
            this.fireEventArgs('providerloaderror', [
                url,
                error
            ]);
            Ext.callback(options.callback, options.scope, [
                url,
                error
            ]);
        },
        registerMethod: function(name, method) {
            this.remotingMethods[name] = method;
        }
    }
}, function() {
    
    Ext.Direct = Ext.direct.Manager;
});


Ext.define('Ext.direct.Provider', {
    alias: 'direct.provider',
    mixins: [
        'Ext.util.Observable'
    ],
    requires: [
        'Ext.direct.Manager'
    ],
    isProvider: true,
    
    
    
    
    
    
    subscribers: 0,
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);
        Ext.applyIf(me, {
            id: Ext.id(null, 'provider-')
        });
        me.mixins.observable.constructor.call(me, config);
    },
    destroy: function() {
        var me = this;
        me.disconnect(true);
        me.mixins.observable.destroy.call(me);
    },
    
    isConnected: function() {
        return this.subscribers > 0;
    },
    
    connect: function() {
        var me = this;
        if (me.subscribers === 0) {
            me.doConnect();
            me.fireEventArgs('connect', [
                me
            ]);
        }
        me.subscribers++;
    },
    
    doConnect: Ext.emptyFn,
    
    disconnect: function(
    force) {
        var me = this;
        if (me.subscribers > 0) {
            if (force) {
                me.subscribers = 0;
            } else {
                me.subscribers--;
            }
            if (me.subscribers === 0) {
                me.doDisconnect();
                me.fireEventArgs('disconnect', [
                    me
                ]);
            }
        }
    },
    
    doDisconnect: Ext.emptyFn,
    inheritableStatics: {
        
        checkConfig: Ext.returnFalse
    },
    onClassExtended: function(cls, data, hooks) {
        if (data.type) {
            Ext.direct.Manager.addProviderClass(data.type, cls);
        }
    }
});


Ext.define('Ext.app.domain.Direct', {
    extend: 'Ext.app.EventDomain',
    singleton: true,
    requires: [
        'Ext.direct.Provider'
    ],
    type: 'direct',
    idProperty: 'id',
    constructor: function() {
        var me = this;
        me.callParent();
        me.monitor(Ext.direct.Provider);
    }
});


Ext.define('Ext.data.PageMap', {
    extend: 'Ext.util.LruCache',
    config: {
        store: null,
        
        pageSize: 0,
        
        rootProperty: ''
    },
    
    clear: function(initial) {
        var me = this;
        me.pageMapGeneration = (me.pageMapGeneration || 0) + 1;
        
        me.indexMap = {};
        me.callParent(arguments);
    },
    forEach: function(fn, scope) {
        var me = this,
            pageNumbers = Ext.Object.getKeys(me.map),
            pageCount = pageNumbers.length,
            pageSize = me.getPageSize(),
            i, j, pageNumber, page, len;
        for (i = 0; i < pageCount; i++) {
            pageNumbers[i] = +pageNumbers[i];
        }
        Ext.Array.sort(pageNumbers, Ext.Array.numericSortFn);
        scope = scope || me;
        for (i = 0; i < pageCount; i++) {
            pageNumber = pageNumbers[i];
            page = me.getPage(pageNumber);
            len = page.length;
            for (j = 0; j < len; j++) {
                if (fn.call(scope, page[j], (pageNumber - 1) * pageSize + j) === false) {
                    return;
                }
            }
        }
    },
    
    findBy: function(fn, scope) {
        var me = this,
            result = null;
        scope = scope || me;
        me.forEach(function(rec, index) {
            if (fn.call(scope, rec, index)) {
                result = rec;
                return false;
            }
        });
        return result;
    },
    
    findIndexBy: function(fn, scope) {
        var me = this,
            result = -1;
        scope = scope || me;
        me.forEach(function(rec, index) {
            if (fn.call(scope, rec)) {
                result = index;
                return false;
            }
        });
        return result;
    },
    find: function(property, value, start, startsWith, endsWith, ignoreCase) {
        if (Ext.isEmpty(value, false)) {
            return null;
        }
        var regex = Ext.String.createRegex(value, startsWith, endsWith, ignoreCase),
            root = this.getRootProperty();
        return this.findBy(function(item) {
            return item && regex.test((root ? item[root] : item)[property]);
        }, null, start);
    },
    findIndex: function(property, value, start, startsWith, endsWith, ignoreCase) {
        if (Ext.isEmpty(value, false)) {
            return null;
        }
        var regex = Ext.String.createRegex(value, startsWith, endsWith, ignoreCase),
            root = this.getRootProperty();
        return this.findIndexBy(function(item) {
            return item && regex.test((root ? item[root] : item)[property]);
        }, null, start);
    },
    getPageFromRecordIndex: function(index) {
        return Math.floor(index / this.getPageSize()) + 1;
    },
    addAll: function(records) {
        if (this.getCount()) {
            Ext.Error.raise('Cannot addAll to a non-empty PageMap');
        }
        this.addPage(1, records);
    },
    addPage: function(pageNumber, records) {
        var me = this,
            pageSize = me.getPageSize(),
            lastPage = pageNumber + Math.floor((records.length - 1) / pageSize),
            startIdx,
            storeIndex = (pageNumber - 1) * pageSize,
            indexMap = me.indexMap,
            page, i, len;
        
        
        for (startIdx = 0; pageNumber <= lastPage; pageNumber++ , startIdx += pageSize) {
            page = Ext.Array.slice(records, startIdx, startIdx + pageSize);
            
            for (i = 0 , len = page.length; i < len; i++) {
                indexMap[page[i].internalId] = storeIndex++;
            }
            me.add(pageNumber, page);
            me.fireEvent('pageadd', me, pageNumber, page);
        }
    },
    getCount: function() {
        var result = this.callParent();
        if (result) {
            result = (result - 1) * this.getPageSize() + this.last.value.length;
        }
        return result;
    },
    getByInternalId: function(internalId) {
        var index = this.indexMap[internalId];
        if (index !== -1) {
            return this.getAt(index);
        }
    },
    indexOf: function(record) {
        var result = -1;
        if (record) {
            result = this.indexMap[record.internalId];
            if (result == null) {
                result = -1;
            }
        }
        return result;
    },
    insert: function() {
        Ext.Error.raise('insert operation not suppported into buffered Store');
    },
    remove: function() {
        Ext.Error.raise('remove operation not suppported from buffered Store');
    },
    removeAt: function() {
        Ext.Error.raise('removeAt operation not suppported from buffered Store');
    },
    removeAtKey: function(page) {
        
        var me = this,
            thePage = me.getPage(page),
            len, i, result;
        if (thePage) {
            if (me.fireEvent('beforepageremove', me, page, thePage) !== false) {
                len = thePage.length;
                for (i = 0; i < len; i++) {
                    delete me.indexMap[thePage[i].internalId];
                }
                result = me.callParent(arguments);
                me.fireEvent('pageremove', me, page, thePage);
                
                thePage.length = 0;
            }
        }
        return result;
    },
    getPage: function(pageNumber) {
        return this.get(pageNumber);
    },
    hasRange: function(start, end) {
        var pageNumber = this.getPageFromRecordIndex(start),
            endPageNumber = this.getPageFromRecordIndex(end);
        for (; pageNumber <= endPageNumber; pageNumber++) {
            if (!this.hasPage(pageNumber)) {
                return false;
            }
        }
        return true;
    },
    hasPage: function(pageNumber) {
        
        return !!this.get(pageNumber);
    },
    peekPage: function(pageNumber) {
        return this.map[pageNumber];
    },
    getAt: function(index) {
        return this.getRange(index, index + 1)[0];
    },
    getRange: function(start, end) {
        
        
        end--;
        if (!this.hasRange(start, end)) {
            Ext.Error.raise('PageMap asked for range which it does not have');
        }
        var me = this,
            pageSize = me.getPageSize(),
            startPageNumber = me.getPageFromRecordIndex(start),
            endPageNumber = me.getPageFromRecordIndex(end),
            dataStart = (startPageNumber - 1) * pageSize,
            dataEnd = (endPageNumber * pageSize) - 1,
            pageNumber = startPageNumber,
            result = [],
            sliceBegin, sliceEnd, doSlice;
        for (; pageNumber <= endPageNumber; pageNumber++) {
            
            if (pageNumber === startPageNumber) {
                sliceBegin = start - dataStart;
                doSlice = true;
            } else {
                sliceBegin = 0;
                doSlice = false;
            }
            if (pageNumber === endPageNumber) {
                sliceEnd = pageSize - (dataEnd - end);
                doSlice = true;
            }
            
            if (doSlice) {
                Ext.Array.push(result, Ext.Array.slice(me.getPage(pageNumber), sliceBegin, sliceEnd));
            } else {
                Ext.Array.push(result, me.getPage(pageNumber));
            }
        }
        return result;
    }
});


Ext.define('Ext.data.BufferedStore', {
    extend: 'Ext.data.ProxyStore',
    alias: 'store.buffered',
    requires: [
        'Ext.data.PageMap',
        'Ext.util.Filter',
        'Ext.util.Sorter',
        'Ext.util.Grouper'
    ],
    uses: [
        'Ext.util.SorterCollection',
        'Ext.util.FilterCollection',
        'Ext.util.GroupCollection'
    ],
    
    isBufferedStore: true,
    
    buffered: true,
    config: {
        data: 0,
        pageSize: 25,
        remoteSort: true,
        remoteFilter: true,
        sortOnLoad: false,
        
        purgePageCount: 5,
        
        trailingBufferZone: 25,
        
        leadingBufferZone: 200,
        
        defaultViewSize: 100,
        
        viewSize: 0
    },
    
    applyData: function(data) {
        var dataCollection = this.data || (this.data = this.createDataCollection());
        if (data && data !== true) {
            Ext.Error.raise('Cannot load a buffered store with local data - the store is a map of remote data');
        }
        return dataCollection;
    },
    applyProxy: function(proxy) {
        proxy = this.callParent([
            proxy
        ]);
        
        
        if (proxy && proxy.setEnablePaging) {
            proxy.setEnablePaging(true);
        }
        return proxy;
    },
    createFiltersCollection: function() {
        return new Ext.util.FilterCollection();
    },
    createSortersCollection: function() {
        return new Ext.util.SorterCollection();
    },
    updateRemoteFilter: function(value) {
        if (value === false) {
            Ext.Error.raise('Buffered stores are always remotely filtered.');
        }
        this.callParent(arguments);
    },
    updateRemoteSort: function(value) {
        if (value === false) {
            Ext.Error.raise('Buffered stores are always remotely sorted.');
        }
        this.callParent(arguments);
    },
    updateGroupField: function(field) {
        var me = this;
        if (me.isInitializing) {
            me.blockLoad();
        }
        me.group(field);
        if (me.isInitializing) {
            me.unblockLoad();
        }
    },
    getGrouper: function() {
        return this.grouper;
    },
    isGrouped: function() {
        return !!this.grouper;
    },
    createDataCollection: function() {
        var me = this,
            result = new Ext.data.PageMap({
                store: me,
                rootProperty: 'data',
                pageSize: me.getPageSize(),
                maxSize: me.getPurgePageCount(),
                listeners: {
                    
                    
                    clear: me.onPageMapClear,
                    scope: me
                }
            });
        
        me.relayEvents(result, [
            'beforepageremove',
            'pageadd',
            'pageremove'
        ]);
        me.pageRequests = {};
        return result;
    },
    add: function() {
        Ext.Error.raise('add method may not be called on a buffered store - the store is a map of remote data');
    },
    insert: function() {
        Ext.Error.raise('insert method may not be called on a buffered store - the store is a map of remote data');
    },
    removeAll: function(silent) {
        var me = this,
            data = me.getData();
        if (data) {
            if (silent) {
                me.suspendEvent('clear');
            }
            data.clear();
            if (silent) {
                me.resumeEvent('clear');
            }
        }
    },
    load: function(options) {
        var me = this;
        options = options || {};
        
        me.getData().clear();
        options.page = 1;
        options.start = 0;
        options.limit = me.getViewSize() || me.getDefaultViewSize();
        
        
        options.loadCallback = options.callback;
        delete options.callback;
        return me.loadToPrefetch(options);
    },
    reload: function(options) {
        var me = this,
            startIdx, endIdx, startPage, endPage, i, waitForReload, bufferZone, records,
            data = me.getData();
        
        if (me.loading) {
            return;
        }
        if (!options) {
            options = {};
        }
        
        delete me.totalCount;
        
        data.clear(true);
        waitForReload = function() {
            if (me.rangeCached(startIdx, Math.min(endIdx, me.getTotalCount()))) {
                me.loading = false;
                data.un('pageadd', waitForReload);
                records = data.getRange(startIdx, endIdx + 1);
                me.fireEvent('load', me, records, true);
                me.fireEvent('refresh', me);
            }
        };
        bufferZone = Math.ceil((me.getLeadingBufferZone() + me.getTrailingBufferZone()) / 2);
        
        if (!me.lastRequestStart) {
            startIdx = options.start || 0;
            endIdx = startIdx + (options.count || me.getPageSize()) - 1;
        } else {
            startIdx = me.lastRequestStart;
            endIdx = me.lastRequestEnd;
        }
        
        startIdx = Math.max(startIdx - bufferZone, 0);
        endIdx += bufferZone;
        startPage = me.getPageFromRecordIndex(startIdx);
        endPage = me.getPageFromRecordIndex(endIdx);
        if (me.fireEvent('beforeload', me, options) !== false) {
            me.loading = true;
            
            
            data.on('pageadd', waitForReload);
            
            for (i = startPage; i <= endPage; i++) {
                me.prefetchPage(i, options);
            }
        }
    },
    filter: function() {
        
        
        
        if (this.remoteSort) {
            this.getData().clear();
            this.callParent(arguments);
        } else {
            Ext.Error.raise('Local filtering may not be used on a buffered store - the store is a map of remote data');
        }
        this.callParent(arguments);
    },
    clearFilter: function() {
        
        
        
        this.getData().clear();
        this.callParent(arguments);
    },
    filterBy: function(fn, scope) {
        Ext.Error.raise('Local filtering may not be used on a buffered store - the store is a map of remote data');
    },
    loadData: function(data, append) {
        Ext.Error.raise('LoadData may not be used on a buffered store - the store is a map of remote data');
    },
    loadPage: function(page, options) {
        var me = this;
        options = options || {};
        options.page = me.currentPage = page;
        options.start = (page - 1) * me.getPageSize();
        options.limit = me.getViewSize() || me.getDefaultViewSize();
        options.loadCallback = options.callback;
        delete options.callback;
        return me.loadToPrefetch(options);
    },
    clearData: function(isLoad) {
        var me = this,
            data = me.getData();
        if (data) {
            data.clear();
        }
        if (isLoad !== true || me.clearRemovedOnLoad) {
            me.removed.length = 0;
        }
    },
    
    
    
    
    getCount: function() {
        return this.totalCount || 0;
    },
    getRange: function(start, end, options) {
        var me = this,
            maxIndex = me.totalCount - 1,
            lastRequestStart = me.lastRequestStart,
            result = [],
            data = me.getData(),
            pageAddHandler, requiredStart, requiredEnd, requiredStartPage, requiredEndPage;
        options = Ext.apply({
            prefetchStart: start,
            prefetchEnd: end
        }, options);
        
        end = (end >= me.totalCount) ? maxIndex : end;
        
        
        
        
        requiredStart = start === 0 ? 0 : start - 1;
        requiredEnd = end === maxIndex ? end : end + 1;
        
        me.lastRequestStart = start;
        me.lastRequestEnd = end;
        
        if (me.rangeCached(requiredStart, requiredEnd)) {
            me.onRangeAvailable(options);
            result = data.getRange(start, end + 1);
        } else 
        {
            
            me.fireEvent('cachemiss', me, start, end);
            requiredStartPage = me.getPageFromRecordIndex(requiredStart);
            requiredEndPage = me.getPageFromRecordIndex(requiredEnd);
            
            pageAddHandler = function(pageMap, page, records) {
                if (page >= requiredStartPage && page <= requiredEndPage && me.rangeCached(requiredStart, requiredEnd)) {
                    
                    me.fireEvent('cachefilled', me, start, end);
                    data.un('pageadd', pageAddHandler);
                    me.onRangeAvailable(options);
                }
            };
            data.on('pageadd', pageAddHandler);
            
            
            
            me.prefetchRange(start, end);
        }
        
        me.primeCache(start, end, start < lastRequestStart ? -1 : 1);
        return result;
    },
    
    getById: function(id) {
        var result = this.data.findBy(function(record) {
                return record.getId() === id;
            });
        return result;
    },
    
    getAt: function(index) {
        var data = this.getData();
        if (data.hasRange(index, index)) {
            return data.getAt(index);
        }
    },
    
    getByInternalId: function(internalId) {
        return this.data.getByInternalId(internalId);
    },
    
    indexOf: function(record) {
        return this.getData().indexOf(record);
    },
    
    indexOfId: function(id) {
        return this.indexOf(this.getById(id));
    },
    group: function(grouper, direction) {
        var me = this,
            oldGrouper;
        if (grouper && typeof grouper === 'string') {
            oldGrouper = me.grouper;
            if (!oldGrouper) {
                me.grouper = new Ext.util.Grouper({
                    property: grouper,
                    direction: direction || 'ASC',
                    root: 'data'
                });
            } else if (direction === undefined) {
                oldGrouper.toggle();
            } else {
                oldGrouper.setDirection(direction);
            }
        } else {
            me.grouper = grouper ? me.getSorters().decodeSorter(grouper, 'Ext.util.Grouper') : null;
        }
        if (me.isLoadBlocked()) {
            return;
        }
        me.getData().clear();
        me.loadPage(1, {
            callback: function() {
                me.fireEvent('groupchange', me, me.getGrouper());
            }
        });
    },
    
    getPageFromRecordIndex: function(index) {
        return Math.floor(index / this.getPageSize()) + 1;
    },
    loadToPrefetch: function(options) {
        var me = this,
            prefetchOptions = options,
            purgePageCount = me.getPurgePageCount(),
            i, records, dataSetSize,
            
            startIdx = options.start,
            endIdx = options.start + options.limit - 1,
            
            loadEndIdx = Math.min(endIdx, options.start + (me.getViewSize() || options.limit) - 1),
            
            
            startPage = me.getPageFromRecordIndex(Math.max(startIdx - me.getTrailingBufferZone(), 0)),
            endPage = me.getPageFromRecordIndex(endIdx + me.getLeadingBufferZone()),
            data = me.getData(),
            
            waitForRequestedRange = function() {
                if (me.rangeCached(startIdx, loadEndIdx)) {
                    me.loading = false;
                    records = data.getRange(startIdx, loadEndIdx + 1);
                    data.un('pageadd', waitForRequestedRange);
                    
                    if (me.hasListeners.guaranteedrange) {
                        me.guaranteeRange(startIdx, loadEndIdx, options.callback, options.scope);
                    }
                    
                    if (options.loadCallback) {
                        options.loadCallback.call(options.scope || me, records, operation, true);
                    }
                    if (options.callback) {
                        options.callback.call(options.scope || me, records, startIdx, endIdx, options);
                    }
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                    me.fireEvent('load', me, records, true);
                }
            },
            operation;
        if (isNaN(me.pageSize) || !me.pageSize) {
            Ext.Error.raise('Buffered store configured without a pageSize', me);
        }
        
        
        if (purgePageCount) {
            data.setMaxSize(purgePageCount ? (endPage - startPage + 1) + purgePageCount : 0);
        }
        if (me.fireEvent('beforeload', me, options) !== false) {
            
            delete me.totalCount;
            me.loading = true;
            
            
            if (options.callback) {
                prefetchOptions = Ext.apply({}, options);
                delete prefetchOptions.callback;
            }
            
            
            
            
            me.on('prefetch', function(store, records, successful, op) {
                if (successful) {
                    
                    operation = op;
                    
                    
                    if ((dataSetSize = me.getTotalCount())) {
                        
                        data.on('pageadd', waitForRequestedRange);
                        
                        loadEndIdx = Math.min(loadEndIdx, dataSetSize - 1);
                        
                        endPage = me.getPageFromRecordIndex(Math.min(loadEndIdx + me.getLeadingBufferZone(), dataSetSize - 1));
                        for (i = startPage + 1; i <= endPage; ++i) {
                            me.prefetchPage(i, prefetchOptions);
                        }
                    } else {
                        me.fireEvent('datachanged', me);
                        me.fireEvent('refresh', me);
                        me.fireEvent('load', me, records, true);
                    }
                } else 
                {
                    me.fireEvent('load', me, records, false);
                }
            }, null, {
                single: true
            });
            me.prefetchPage(startPage, prefetchOptions);
        }
    },
    
    
    prefetch: function(options) {
        var me = this,
            pageSize = me.getPageSize(),
            data = me.getData(),
            proxy, operation;
        
        if (pageSize) {
            if (me.lastPageSize && pageSize != me.lastPageSize) {
                Ext.Error.raise("pageSize cannot be dynamically altered");
            }
            if (!data.getPageSize()) {
                data.setPageSize(pageSize);
            }
        } else 
        {
            me.pageSize = data.setPageSize(pageSize = options.limit);
        }
        
        me.lastPageSize = pageSize;
        
        if (!options.page) {
            options.page = me.getPageFromRecordIndex(options.start);
            options.start = (options.page - 1) * pageSize;
            options.limit = Math.ceil(options.limit / pageSize) * pageSize;
        }
        
        if (!me.pageRequests[options.page]) {
            proxy = me.proxy;
            
            options = Ext.apply({
                action: 'read',
                filters: me.getFilters().items,
                sorters: me.getSorters().items,
                grouper: me.getGrouper(),
                internalCallback: me.onProxyPrefetch,
                internalScope: me
            }, options);
            operation = proxy.createOperation('read', options);
            
            
            operation.pageMapGeneration = data.pageMapGeneration;
            if (me.fireEvent('beforeprefetch', me, operation) !== false) {
                me.pageRequests[options.page] = operation.execute();
                if (proxy.isSynchronous) {
                    delete me.pageRequests[options.page];
                }
            }
        }
        return me;
    },
    
    onPageMapClear: function() {
        var me = this,
            loadingFlag = me.wasLoading,
            reqs = me.pageRequests,
            data = me.getData(),
            req, page;
        
        data.clearListeners();
        
        data.on('clear', me.onPageMapClear, me);
        me.relayEvents(data, [
            'beforepageremove',
            'pageadd',
            'pageremove'
        ]);
        
        
        
        me.loading = true;
        me.totalCount = 0;
        
        for (page in reqs) {
            if (reqs.hasOwnProperty(page)) {
                req = reqs[page];
                delete reqs[page];
                delete req.callback;
            }
        }
        
        me.fireEvent('clear', me);
        
        
        me.loading = loadingFlag;
    },
    
    prefetchPage: function(page, options) {
        var me = this,
            pageSize = me.getPageSize(),
            start = (page - 1) * pageSize,
            total = me.totalCount;
        
        if (total !== undefined && me.data.getCount() === total) {
            return;
        }
        
        me.prefetch(Ext.applyIf({
            page: page,
            start: start,
            limit: pageSize
        }, options));
    },
    
    onProxyPrefetch: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful(),
            page = operation.getPage(),
            oldTotal = me.totalCount;
        
        
        if (operation.pageMapGeneration === me.getData().pageMapGeneration) {
            if (resultSet) {
                me.totalCount = resultSet.getTotal();
                if (me.totalCount !== oldTotal) {
                    me.fireEvent('totalcountchange', me.totalCount);
                }
            }
            
            if (page !== undefined) {
                delete me.pageRequests[page];
            }
            
            me.loading = false;
            me.fireEvent('prefetch', me, records, successful, operation);
            
            
            if (successful) {
                me.cachePage(records, operation.getPage());
            }
            
            Ext.callback(operation.getCallback(), operation.getScope() || me, [
                records,
                operation,
                successful
            ]);
        }
    },
    
    cachePage: function(records, page) {
        var me = this,
            len = records.length,
            i;
        if (!Ext.isDefined(me.totalCount)) {
            me.totalCount = records.length;
            me.fireEvent('totalcountchange', me.totalCount);
        }
        
        for (i = 0; i < len; i++) {
            records[i].join(me);
        }
        me.getData().addPage(page, records);
    },
    
    rangeCached: function(start, end) {
        return this.getData().hasRange(start, end);
    },
    
    pageCached: function(page) {
        return this.getData().hasPage(page);
    },
    
    pagePending: function(page) {
        return !!this.pageRequests[page];
    },
    
    rangeSatisfied: function(start, end) {
        return this.rangeCached(start, end);
    },
    
    onRangeAvailable: function(options) {
        var me = this,
            totalCount = me.getTotalCount(),
            start = options.prefetchStart,
            end = (options.prefetchEnd > totalCount - 1) ? totalCount - 1 : options.prefetchEnd,
            range;
        end = Math.max(0, end);
        if (start > end) {
            Ext.log({
                level: 'warn',
                msg: 'Start (' + start + ') was greater than end (' + end + ') for the range of records requested (' + start + '-' + options.prefetchEnd + ')' + (this.storeId ? ' from store "' + this.storeId + '"' : '')
            });
        }
        range = me.getData().getRange(start, end + 1);
        if (options.fireEvent !== false) {
            me.fireEvent('guaranteedrange', range, start, end, options);
        }
        if (options.callback) {
            options.callback.call(options.scope || me, range, start, end, options);
        }
    },
    
    guaranteeRange: function(start, end, callback, scope, options) {
        options = Ext.apply({
            callback: callback,
            scope: scope
        }, options);
        this.getRange(start, end + 1, options);
    },
    
    prefetchRange: function(start, end) {
        var me = this,
            purgePageCount = me.getPurgePageCount(),
            startPage, endPage, page;
        if (!me.rangeCached(start, end)) {
            startPage = me.getPageFromRecordIndex(start);
            endPage = me.getPageFromRecordIndex(end);
            
            
            
            me.getData().setMaxSize(purgePageCount ? (endPage - startPage + 1) + purgePageCount : 0);
            
            for (page = startPage; page <= endPage; page++) {
                if (!me.pageCached(page)) {
                    me.prefetchPage(page);
                }
            }
        }
    },
    primeCache: function(start, end, direction) {
        var me = this,
            leadingBufferZone = me.getLeadingBufferZone(),
            trailingBufferZone = me.getTrailingBufferZone(),
            pageSize = me.getPageSize(),
            totalCount = me.totalCount;
        
        if (direction === -1) {
            start = Math.max(start - leadingBufferZone, 0);
            end = Math.min(end + trailingBufferZone, totalCount - 1);
        }
        
        else if (direction === 1) {
            start = Math.max(Math.min(start - trailingBufferZone, totalCount - pageSize), 0);
            end = Math.min(end + leadingBufferZone, totalCount - 1);
        } else 
        {
            start = Math.min(Math.max(Math.floor(start - ((leadingBufferZone + trailingBufferZone) / 2)), 0), totalCount - me.pageSize);
            end = Math.min(Math.max(Math.ceil(end + ((leadingBufferZone + trailingBufferZone) / 2)), 0), totalCount - 1);
        }
        me.prefetchRange(start, end);
    },
    sort: function(field, direction, mode) {
        if (arguments.length === 0) {
            this.clearAndLoad();
        } else {
            this.getSorters().addSort(field, direction, mode);
        }
    },
    onSorterEndUpdate: function() {
        var me = this,
            sorters = me.getSorters().getRange();
        
        if (sorters.length) {
            me.clearAndLoad({
                callback: function() {
                    me.fireEvent('sort', me, sorters);
                }
            });
        } else {
            
            me.fireEvent('sort', me, sorters);
        }
    },
    clearAndLoad: function(options) {
        this.getData().clear();
        this.loadPage(1, options);
    },
    privates: {
        isMoving: function() {
            return false;
        }
    }
});


Ext.define('Ext.data.proxy.Direct', {
    
    extend: 'Ext.data.proxy.Server',
    alternateClassName: 'Ext.data.DirectProxy',
    alias: 'proxy.direct',
    requires: [
        'Ext.direct.Manager'
    ],
    
    
    config: {
        
        paramOrder: undefined,
        
        paramsAsHash: true,
        
        directFn: undefined,
        
        api: undefined,
        
        metadata: undefined
    },
    
    paramOrderRe: /[\s,|]/,
    applyParamOrder: function(paramOrder) {
        if (Ext.isString(paramOrder)) {
            paramOrder = paramOrder.split(this.paramOrderRe);
        }
        return paramOrder;
    },
    updateApi: function() {
        this.methodsResolved = false;
    },
    updateDirectFn: function() {
        this.methodsResolved = false;
    },
    resolveMethods: function() {
        var me = this,
            fn = me.getDirectFn(),
            api = me.getApi(),
            Manager = Ext.direct.Manager,
            method;
        if (fn) {
            me.setDirectFn(method = Manager.parseMethod(fn));
            if (!Ext.isFunction(method)) {
                Ext.Error.raise('Cannot resolve directFn ' + fn);
            }
        }
        if (api) {
            for (fn in api) {
                if (api.hasOwnProperty(fn)) {
                    method = api[fn];
                    api[fn] = Manager.parseMethod(method);
                    if (!Ext.isFunction(api[fn])) {
                        Ext.Error.raise('Cannot resolve Direct api ' + fn + ' method ' + method);
                    }
                }
            }
        }
        me.methodsResolved = true;
    },
    doRequest: function(operation) {
        var me = this,
            writer, request, action, params, args, api, fn, callback;
        if (!me.methodsResolved) {
            me.resolveMethods();
        }
        request = me.buildRequest(operation);
        action = request.getAction();
        api = me.getApi();
        if (api) {
            fn = api[action];
        }
        fn = fn || me.getDirectFn();
        if (!fn) {
            Ext.Error.raise('No Ext.Direct function specified for this proxy');
        }
        writer = me.getWriter();
        if (writer && operation.allowWrite()) {
            request = writer.write(request);
        }
        
        
        
        
        
        
        
        
        if (action === 'read') {
            params = request.getParams();
        } else {
            params = request.getJsonData();
        }
        args = fn.directCfg.method.getArgs({
            params: params,
            paramOrder: me.getParamOrder(),
            paramsAsHash: me.getParamsAsHash(),
            metadata: me.getMetadata(),
            callback: me.createRequestCallback(request, operation),
            scope: me
        });
        request.setConfig({
            args: args,
            directFn: fn
        });
        fn.apply(window, args);
        
        
        
        return request;
    },
    
    applyEncoding: Ext.identityFn,
    createRequestCallback: function(request, operation) {
        var me = this;
        return function(data, event) {
            me.processResponse(event.status, operation, request, event);
        };
    },
    
    extractResponseData: function(response) {
        return Ext.isDefined(response.result) ? response.result : response.data;
    },
    
    setException: function(operation, response) {
        operation.setException(response.message);
    },
    
    buildUrl: function() {
        return '';
    }
});


Ext.define('Ext.data.DirectStore', {
    
    extend: 'Ext.data.Store',
    alias: 'store.direct',
    requires: [
        'Ext.data.proxy.Direct'
    ],
    
    constructor: function(config) {
        config = Ext.apply({}, config);
        if (!config.proxy) {
            var proxy = {
                    type: 'direct',
                    reader: {
                        type: 'json'
                    }
                };
            Ext.copyTo(proxy, config, 'paramOrder,paramsAsHash,directFn,api,simpleSortMode,extraParams');
            Ext.copyTo(proxy.reader, config, 'totalProperty,root,rootProperty,idProperty');
            config.proxy = proxy;
        }
        this.callParent([
            config
        ]);
    }
});


Ext.define('Ext.data.JsonP', {
    
    singleton: true,
    
    
    requestCount: 0,
    
    requests: {},
    
    timeout: 30000,
    
    disableCaching: true,
    
    disableCachingParam: '_dc',
    
    callbackKey: 'callback',
    
    request: function(options) {
        options = Ext.apply({}, options);
        if (!options.url) {
            Ext.Error.raise('A url must be specified for a JSONP request.');
        }
        var me = this,
            disableCaching = Ext.isDefined(options.disableCaching) ? options.disableCaching : me.disableCaching,
            cacheParam = options.disableCachingParam || me.disableCachingParam,
            id = ++me.requestCount,
            callbackName = options.callbackName || 'callback' + id,
            callbackKey = options.callbackKey || me.callbackKey,
            timeout = Ext.isDefined(options.timeout) ? options.timeout : me.timeout,
            params = Ext.apply({}, options.params),
            url = options.url,
            name = Ext.name,
            request, script;
        
        if (disableCaching && !params[cacheParam]) {
            params[cacheParam] = Ext.Date.now();
        }
        options.params = params;
        params[callbackKey] = name + '.data.JsonP.' + callbackName;
        script = me.createScript(url, params, options);
        me.requests[id] = request = {
            url: url,
            params: params,
            script: script,
            id: id,
            scope: options.scope,
            success: options.success,
            failure: options.failure,
            callback: options.callback,
            callbackKey: callbackKey,
            callbackName: callbackName
        };
        if (timeout > 0) {
            request.timeout = Ext.defer(me.handleTimeout, timeout, me, [
                request
            ]);
        }
        me.setupErrorHandling(request);
        me[callbackName] = Ext.bind(me.handleResponse, me, [
            request
        ], true);
        me.loadScript(request);
        return request;
    },
    
    abort: function(request) {
        var me = this,
            requests = me.requests,
            key;
        if (request) {
            if (!request.id) {
                request = requests[request];
            }
            me.handleAbort(request);
        } else {
            for (key in requests) {
                if (requests.hasOwnProperty(key)) {
                    me.abort(requests[key]);
                }
            }
        }
    },
    
    setupErrorHandling: function(request) {
        request.script.onerror = Ext.bind(this.handleError, this, [
            request
        ]);
    },
    
    handleAbort: function(request) {
        request.errorType = 'abort';
        this.handleResponse(null, request);
    },
    
    handleError: function(request) {
        request.errorType = 'error';
        this.handleResponse(null, request);
    },
    
    cleanupErrorHandling: function(request) {
        request.script.onerror = null;
    },
    
    handleTimeout: function(request) {
        request.errorType = 'timeout';
        this.handleResponse(null, request);
    },
    
    handleResponse: function(result, request) {
        var success = true,
            globalEvents = Ext.GlobalEvents;
        if (request.timeout) {
            clearTimeout(request.timeout);
        }
        delete this[request.callbackName];
        delete this.requests[request.id];
        this.cleanupErrorHandling(request);
        Ext.fly(request.script).destroy();
        if (request.errorType) {
            success = false;
            Ext.callback(request.failure, request.scope, [
                request.errorType
            ]);
        } else {
            Ext.callback(request.success, request.scope, [
                result
            ]);
        }
        Ext.callback(request.callback, request.scope, [
            success,
            result,
            request.errorType
        ]);
        if (globalEvents.hasListeners.idle) {
            globalEvents.fireEvent('idle');
        }
    },
    
    createScript: function(url, params, options) {
        var script = document.createElement('script');
        script.setAttribute("src", Ext.urlAppend(url, Ext.Object.toQueryString(params)));
        script.setAttribute("async", true);
        script.setAttribute("type", "text/javascript");
        return script;
    },
    
    loadScript: function(request) {
        Ext.getHead().appendChild(request.script);
    }
});


Ext.define('Ext.data.proxy.JsonP', {
    extend: 'Ext.data.proxy.Server',
    alternateClassName: 'Ext.data.ScriptTagProxy',
    alias: [
        'proxy.jsonp',
        'proxy.scripttag'
    ],
    requires: [
        'Ext.data.JsonP'
    ],
    config: {
        
        callbackKey: 'callback',
        
        recordParam: 'records',
        
        autoAppendParams: true
    },
    
    doRequest: function(operation) {
        
        var me = this,
            request = me.buildRequest(operation),
            params = request.getParams();
        
        request.setConfig({
            callbackKey: me.callbackKey,
            timeout: me.timeout,
            scope: me,
            disableCaching: false,
            
            callback: me.createRequestCallback(request, operation)
        });
        
        
        if (me.getAutoAppendParams()) {
            request.setParams({});
        }
        request.setRawRequest(Ext.data.JsonP.request(request.getCurrentConfig()));
        
        request.setParams(params);
        me.lastRequest = request;
        return request;
    },
    
    createRequestCallback: function(request, operation) {
        var me = this;
        return function(success, response, errorType) {
            if (request === me.lastRequest) {
                me.lastRequest = null;
            }
            me.processResponse(success, operation, request, response);
        };
    },
    
    setException: function(operation, response) {
        operation.setException(operation.getRequest().getRawRequest().errorType);
    },
    
    buildUrl: function(request) {
        var me = this,
            url = me.callParent(arguments),
            records = request.getRecords(),
            writer = me.getWriter(),
            params, filters, filter, i, v;
        
        
        if (writer && request.getOperation().allowWrite()) {
            request = writer.write(request);
        }
        
        params = request.getParams();
        filters = params.filters;
        delete params.filters;
        if (filters && filters.length) {
            for (i = 0; i < filters.length; i++) {
                filter = filters[i];
                v = filter.getValue();
                if (v) {
                    params[filter.getProperty()] = v;
                }
            }
        }
        
        if (Ext.isArray(records) && records.length > 0 && (!writer || !writer.getEncode())) {
            params[me.getRecordParam()] = me.encodeRecords(records);
        }
        
        
        if (me.getAutoAppendParams()) {
            url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
        }
        return url;
    },
    
    abort: function(request) {
        request = request || this.lastRequest;
        if (request) {
            Ext.data.JsonP.abort(request.getRawRequest());
        }
    },
    
    encodeRecords: function(records) {
        var encoded = [],
            i = 0,
            len = records.length;
        for (; i < len; i++) {
            encoded.push(Ext.encode(records[i].getData()));
        }
        return encoded;
    }
});


Ext.define('Ext.data.JsonPStore', {
    extend: 'Ext.data.Store',
    alias: 'store.jsonp',
    requires: [
        'Ext.data.proxy.JsonP',
        'Ext.data.reader.Json'
    ],
    constructor: function(config) {
        config = Ext.apply({
            proxy: {
                type: 'jsonp',
                reader: 'json'
            }
        }, config);
        this.callParent([
            config
        ]);
    }
});


Ext.define('Ext.data.JsonStore', {
    extend: 'Ext.data.Store',
    alias: 'store.json',
    requires: [
        'Ext.data.proxy.Ajax',
        'Ext.data.reader.Json',
        'Ext.data.writer.Json'
    ],
    constructor: function(config) {
        config = Ext.apply({
            proxy: {
                type: 'ajax',
                reader: 'json',
                writer: 'json'
            }
        }, config);
        this.callParent([
            config
        ]);
    }
});


Ext.define('Ext.data.ModelManager', {
    alternateClassName: 'Ext.ModelMgr',
    requires: [
        'Ext.data.schema.Schema'
    ],
    singleton: true,
    deprecated: {
        5: {
            methods: {
                clear: null,
                create: function(data, name, id) {
                    var T = name;
                    if (!T.isEntity) {
                        T = this.getModel(name || data.name);
                    }
                    return T.createWithId(id, data);
                },
                each: function(fn, scope) {
                    Ext.data.Model.schema.eachEntity(fn, scope);
                },
                get: function(name) {
                    return this.getModel(name);
                },
                getCount: function() {
                    return Ext.data.Model.schema.entityCount;
                },
                
                getModel: function(id) {
                    return Ext.data.schema.Schema.lookupEntity(id);
                },
                isRegistered: function(name) {
                    return !!this.getModel(name);
                }
            }
        }
    }
});


Ext.define('Ext.data.NodeInterface', {
    requires: [
        'Ext.data.field.Boolean',
        'Ext.data.field.Integer',
        'Ext.data.field.String',
        'Ext.data.writer.Json',
        'Ext.mixin.Observable'
    ],
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    statics: {
        
        decorate: function(modelClass) {
            var model = Ext.data.schema.Schema.lookupEntity(modelClass),
                proto = model.prototype,
                idName, idField, idType;
            if (!model.prototype.isObservable) {
                model.mixin(Ext.mixin.Observable.prototype.mixinId, Ext.mixin.Observable);
            }
            if (proto.isNode) {
                
                return;
            }
            idName = proto.idProperty;
            idField = model.getField(idName);
            idType = idField.type;
            model.override(this.getPrototypeBody());
            model.addFields([
                {
                    name: 'parentId',
                    type: idType,
                    defaultValue: null,
                    allowNull: idField.allowNull
                },
                {
                    name: 'index',
                    type: 'int',
                    defaultValue: -1,
                    persist: false,
                    convert: null
                },
                {
                    name: 'depth',
                    type: 'int',
                    defaultValue: 0,
                    persist: false,
                    convert: null
                },
                {
                    name: 'expanded',
                    type: 'bool',
                    defaultValue: false,
                    persist: false,
                    convert: null
                },
                {
                    name: 'expandable',
                    type: 'bool',
                    defaultValue: true,
                    persist: false,
                    convert: null
                },
                {
                    name: 'checked',
                    type: 'auto',
                    defaultValue: null,
                    persist: false,
                    convert: null
                },
                {
                    name: 'leaf',
                    type: 'bool',
                    defaultValue: false
                },
                {
                    name: 'cls',
                    type: 'string',
                    defaultValue: '',
                    persist: false,
                    convert: null
                },
                {
                    name: 'iconCls',
                    type: 'string',
                    defaultValue: '',
                    persist: false,
                    convert: null
                },
                {
                    name: 'icon',
                    type: 'string',
                    defaultValue: '',
                    persist: false,
                    convert: null
                },
                {
                    name: 'root',
                    type: 'boolean',
                    defaultValue: false,
                    persist: false,
                    convert: null
                },
                {
                    name: 'isLast',
                    type: 'boolean',
                    defaultValue: false,
                    persist: false,
                    convert: null
                },
                {
                    name: 'isFirst',
                    type: 'boolean',
                    defaultValue: false,
                    persist: false,
                    convert: null
                },
                {
                    name: 'allowDrop',
                    type: 'boolean',
                    defaultValue: true,
                    persist: false,
                    convert: null
                },
                {
                    name: 'allowDrag',
                    type: 'boolean',
                    defaultValue: true,
                    persist: false,
                    convert: null
                },
                {
                    name: 'loaded',
                    type: 'boolean',
                    defaultValue: false,
                    persist: false,
                    convert: null
                },
                {
                    name: 'loading',
                    type: 'boolean',
                    defaultValue: false,
                    persist: false,
                    convert: null
                },
                {
                    name: 'href',
                    type: 'string',
                    defaultValue: '',
                    persist: false,
                    convert: null
                },
                {
                    name: 'hrefTarget',
                    type: 'string',
                    defaultValue: '',
                    persist: false,
                    convert: null
                },
                {
                    name: 'qtip',
                    type: 'string',
                    defaultValue: '',
                    persist: false,
                    convert: null
                },
                {
                    name: 'qtitle',
                    type: 'string',
                    defaultValue: '',
                    persist: false,
                    convert: null
                },
                {
                    name: 'qshowDelay',
                    type: 'int',
                    defaultValue: 0,
                    persist: false,
                    convert: null
                },
                {
                    name: 'children',
                    type: 'auto',
                    defaultValue: null,
                    persist: false,
                    convert: null
                },
                {
                    name: 'visible',
                    type: 'boolean',
                    defaultValue: true,
                    persist: false
                },
                {
                    name: 'text',
                    type: 'string',
                    persist: false
                }
            ]);
        },
        getPrototypeBody: function() {
            var bubbledEvents = {
                    idchanged: true,
                    append: true,
                    remove: true,
                    move: true,
                    insert: true,
                    beforeappend: true,
                    beforeremove: true,
                    beforemove: true,
                    beforeinsert: true,
                    expand: true,
                    collapse: true,
                    beforeexpand: true,
                    beforecollapse: true,
                    sort: true
                },
                silently = {
                    silent: true
                };
            
            
            
            
            
            
            
            
            
            return {
                
                isNode: true,
                firstChild: null,
                lastChild: null,
                parentNode: null,
                previousSibling: null,
                nextSibling: null,
                constructor: function() {
                    var me = this;
                    me.mixins.observable.constructor.call(me);
                    me.callParent(arguments);
                    me.childNodes = [];
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    return me;
                },
                
                createNode: function(node) {
                    var me = this,
                        childType = me.childType,
                        store, storeReader, nodeProxy, nodeReader, reader, typeProperty,
                        T = me.self;
                    
                    if (!node.isModel) {
                        
                        if (childType) {
                            T = me.schema.getEntity(childType);
                        } else 
                        {
                            store = me.getTreeStore();
                            storeReader = store && store.getProxy().getReader();
                            nodeProxy = me.getProxy();
                            nodeReader = nodeProxy ? nodeProxy.getReader() : null;
                            
                            reader = !storeReader || (nodeReader && nodeReader.initialConfig.typeProperty) ? nodeReader : storeReader;
                            if (reader) {
                                typeProperty = reader.getTypeProperty();
                                if (typeProperty) {
                                    T = reader.getChildType(me.schema, node, typeProperty);
                                }
                            }
                        }
                        node = new T(node);
                    }
                    
                    
                    
                    if (!node.childNodes) {
                        node.firstChild = node.lastChild = node.parentNode = node.previousSibling = node.nextSibling = null;
                        node.childNodes = [];
                    }
                    return node;
                },
                
                isLeaf: function() {
                    return this.get('leaf') === true;
                },
                
                setFirstChild: function(node) {
                    this.firstChild = node;
                },
                
                setLastChild: function(node) {
                    this.lastChild = node;
                },
                
                updateInfo: function(commit, info) {
                    var me = this,
                        dataObject = me.data,
                        oldDepth = dataObject.depth,
                        childInfo = {},
                        children = me.childNodes,
                        childCount = children.length,
                        phantom = me.phantom,
                        fields = me.fields,
                        modified = me.modified || (me.modified = {}),
                        propName, newValue, field, currentValue, key,
                        newParentId = info.parentId,
                        settingIndexInNewParent, persistentField, i;
                    if (!info) {
                        Ext.Error.raise('NodeInterface expects update info to be passed');
                    }
                    
                    
                    
                    for (propName in info) {
                        field = fields[me.fieldOrdinals[propName]];
                        newValue = info[propName];
                        persistentField = field && field.persist;
                        currentValue = dataObject[propName];
                        
                        
                        
                        
                        
                        settingIndexInNewParent = persistentField && (propName === 'index') && (currentValue !== -1) && (newParentId && newParentId !== modified.parentId);
                        
                        if (!settingIndexInNewParent && me.isEqual(currentValue, newValue)) {
                            
                            continue;
                        }
                        dataObject[propName] = newValue;
                        
                        if (persistentField) {
                            
                            if (!settingIndexInNewParent && modified.hasOwnProperty(propName)) {
                                
                                if (me.isEqual(modified[propName], newValue)) {
                                    
                                    
                                    delete modified[propName];
                                    
                                    
                                    
                                    me.dirty = false;
                                    for (key in modified) {
                                        if (modified.hasOwnProperty(key)) {
                                            me.dirty = true;
                                            break;
                                        }
                                    }
                                }
                            } else 
                            {
                                me.dirty = true;
                                modified[propName] = currentValue;
                            }
                        }
                    }
                    if (commit) {
                        me.commit();
                        me.phantom = phantom;
                    }
                    
                    if (me.data.depth !== oldDepth) {
                        childInfo = {
                            depth: me.data.depth + 1
                        };
                        for (i = 0; i < childCount; i++) {
                            children[i].updateInfo(commit, childInfo);
                        }
                    }
                },
                
                isLast: function() {
                    return this.get('isLast');
                },
                
                isFirst: function() {
                    return this.get('isFirst');
                },
                
                hasChildNodes: function() {
                    return !this.isLeaf() && this.childNodes.length > 0;
                },
                
                isExpandable: function() {
                    var me = this;
                    if (me.get('expandable')) {
                        return !(me.isLeaf() || (me.isLoaded() && !me.phantom && !me.hasChildNodes()));
                    }
                    return false;
                },
                triggerUIUpdate: function() {
                    
                    
                    
                    this.callJoined('afterEdit', []);
                },
                
                appendChild: function(node, suppressEvents, commit) {
                    var me = this,
                        i, ln, index, oldParent, previousSibling,
                        childInfo = {
                            isLast: true,
                            parentId: me.getId(),
                            depth: (me.data.depth || 0) + 1
                        },
                        result,
                        treeStore = me.getTreeStore(),
                        bulkUpdate = treeStore && treeStore.bulkUpdate;
                    
                    Ext.suspendLayouts();
                    
                    if (Ext.isArray(node)) {
                        ln = node.length;
                        result = new Array(ln);
                        
                        me.callTreeStore('beginFill');
                        for (i = 0; i < ln; i++) {
                            result[i] = me.appendChild(node[i], suppressEvents, commit);
                        }
                        
                        
                        me.callTreeStore('endFill', [
                            result
                        ]);
                    } else {
                        
                        node = me.createNode(node);
                        if (suppressEvents !== true && me.fireEventArgs('beforeappend', [
                            me,
                            node
                        ]) === false) {
                            return false;
                        }
                        index = me.childNodes.length;
                        oldParent = node.parentNode;
                        
                        if (oldParent) {
                            
                            if (oldParent.removeChild(node, false, suppressEvents, oldParent.getTreeStore() === treeStore) === false) {
                                return false;
                            }
                        }
                        
                        
                        treeStore && treeStore.beginUpdate();
                        index = me.childNodes.length;
                        if (index === 0) {
                            me.setFirstChild(node);
                        }
                        me.childNodes[index] = node;
                        node.parentNode = me;
                        node.nextSibling = null;
                        me.setLastChild(node);
                        previousSibling = me.childNodes[index - 1];
                        if (previousSibling) {
                            node.previousSibling = previousSibling;
                            previousSibling.nextSibling = node;
                            previousSibling.updateInfo(commit, {
                                isLast: false
                            });
                            
                            if (!bulkUpdate) {
                                previousSibling.triggerUIUpdate();
                            }
                        } else {
                            node.previousSibling = null;
                        }
                        
                        childInfo.isFirst = index === 0;
                        childInfo.index = index;
                        node.updateInfo(commit, childInfo);
                        
                        if (me.isLeaf()) {
                            me.set('leaf', false);
                        }
                        
                        if (!me.isLoaded()) {
                            if (bulkUpdate) {
                                me.data.loaded = true;
                            } else {
                                me.set('loaded', true);
                            }
                        } else if (me.childNodes.length === 1 && !bulkUpdate) {
                            me.triggerUIUpdate();
                        }
                        
                        if (index && me.childNodes[index - 1].isExpanded() && !bulkUpdate) {
                            me.childNodes[index - 1].cascadeBy(me.triggerUIUpdate);
                        }
                        
                        
                        
                        
                        
                        
                        if (treeStore) {
                            treeStore.registerNode(me, !bulkUpdate);
                            if (bulkUpdate) {
                                treeStore.registerNode(node);
                            }
                        }
                        
                        
                        
                        if (suppressEvents !== true) {
                            me.fireEventArgs('append', [
                                me,
                                node,
                                index
                            ]);
                            if (oldParent) {
                                node.fireEventArgs('move', [
                                    node,
                                    oldParent,
                                    me,
                                    index
                                ]);
                            }
                        }
                        
                        
                        me.callTreeStore('onNodeAppend', [
                            node,
                            index
                        ]);
                        result = node;
                        
                        
                        if (treeStore) {
                            treeStore.endUpdate();
                        }
                    }
                    
                    Ext.resumeLayouts(true);
                    return result;
                },
                
                getOwnerTree: function() {
                    var store = this.getTreeStore();
                    if (store) {
                        return store.ownerTree;
                    }
                },
                
                getTreeStore: function() {
                    var root = this;
                    while (root && !root.treeStore) {
                        root = root.parentNode;
                    }
                    return root && root.treeStore;
                },
                
                removeChild: function(node, erase, suppressEvents, isMove) {
                    var me = this,
                        index = me.indexOf(node),
                        i, childCount, previousSibling,
                        treeStore = me.getTreeStore(),
                        bulkUpdate = treeStore && treeStore.bulkUpdate;
                    if (index === -1 || (suppressEvents !== true && me.fireEventArgs('beforeremove', [
                        me,
                        node,
                        !!isMove
                    ]) === false)) {
                        return false;
                    }
                    
                    Ext.suspendLayouts();
                    
                    treeStore && treeStore.beginUpdate();
                    
                    Ext.Array.erase(me.childNodes, index, 1);
                    
                    if (me.firstChild === node) {
                        me.setFirstChild(node.nextSibling);
                    }
                    if (me.lastChild === node) {
                        me.setLastChild(node.previousSibling);
                    }
                    
                    previousSibling = node.previousSibling;
                    if (previousSibling) {
                        node.previousSibling.nextSibling = node.nextSibling;
                    }
                    
                    if (node.nextSibling) {
                        node.nextSibling.previousSibling = node.previousSibling;
                        
                        if (index === 0) {
                            node.nextSibling.updateInfo(false, {
                                isFirst: true
                            });
                        }
                        
                        for (i = index , childCount = me.childNodes.length; i < childCount; i++) {
                            me.childNodes[i].updateInfo(false, {
                                index: i
                            });
                        }
                    }
                    
                    
                    else if (previousSibling) {
                        previousSibling.updateInfo(false, {
                            isLast: true
                        });
                        
                        
                        if (!bulkUpdate) {
                            if (previousSibling.isExpanded()) {
                                previousSibling.cascadeBy(me.triggerUIUpdate);
                            } else 
                            {
                                previousSibling.triggerUIUpdate();
                            }
                        }
                    }
                    
                    if (!me.childNodes.length && !bulkUpdate) {
                        me.triggerUIUpdate();
                    }
                    
                    Ext.resumeLayouts(true);
                    if (suppressEvents !== true) {
                        
                        me.callTreeStore('beforeNodeRemove', [
                            [
                                node
                            ],
                            !!isMove
                        ]);
                        node.previousSibling = node.nextSibling = node.parentNode = null;
                        me.fireEventArgs('remove', [
                            me,
                            node,
                            !!isMove
                        ]);
                        
                        me.callTreeStore('onNodeRemove', [
                            [
                                node
                            ],
                            !!isMove
                        ]);
                    }
                    
                    
                    if (erase) {
                        node.erase(true);
                    } else {
                        node.clear();
                    }
                    
                    
                    
                    
                    
                    
                    if (!isMove) {
                        node.set({
                            parentId: null,
                            lastParentId: me.getId()
                        }, silently);
                    }
                    
                    if (treeStore) {
                        treeStore.endUpdate();
                    }
                    return node;
                },
                
                copy: function(newId, deep) {
                    var me = this,
                        result = me.callParent([
                            newId,
                            deep
                        ]),
                        len = me.childNodes ? me.childNodes.length : 0,
                        i;
                    
                    if (deep) {
                        for (i = 0; i < len; i++) {
                            result.appendChild(me.childNodes[i].copy(undefined, true));
                        }
                    }
                    return result;
                },
                
                clear: function(erase) {
                    var me = this;
                    
                    me.parentNode = me.previousSibling = me.nextSibling = null;
                    if (erase) {
                        me.firstChild = me.lastChild = me.childNodes = null;
                    }
                },
                drop: function() {
                    var me = this,
                        childNodes = me.childNodes,
                        parentNode = me.parentNode,
                        len = childNodes ? childNodes.length : 0,
                        i, node, treeStore;
                    
                    
                    
                    me.callParent();
                    
                    if (parentNode) {
                        treeStore = me.getTreeStore();
                        
                        parentNode.removeChild(me);
                    }
                    
                    else if (me.get('root')) {
                        treeStore = me.getTreeStore();
                        treeStore.setRoot(null);
                    }
                    
                    
                    
                    
                    
                    treeStore && treeStore.beginUpdate();
                    
                    
                    for (i = 0; i < len; i++) {
                        node = childNodes[i];
                        
                        node.clear();
                        
                        node.drop();
                    }
                    
                    treeStore && treeStore.endUpdate();
                },
                
                erase: function(options) {
                    var me = this,
                        childNodes = me.childNodes,
                        len = childNodes && childNodes.length,
                        i, node;
                    
                    
                    
                    me.remove();
                    
                    
                    me.clear(true);
                    me.callParent([
                        options
                    ]);
                    for (i = 0; i < len; i++) {
                        node = childNodes[i];
                        
                        
                        node.parentNode = null;
                        node.erase(options);
                    }
                },
                
                insertBefore: function(node, refNode, suppressEvents) {
                    var me = this,
                        index = me.indexOf(refNode),
                        oldParent = node.parentNode,
                        refIndex = index,
                        childCount, previousSibling, i,
                        treeStore = me.getTreeStore(),
                        bulkUpdate = treeStore && treeStore.bulkUpdate;
                    if (!refNode) {
                        
                        return me.appendChild(node);
                    }
                    
                    if (node === refNode) {
                        return false;
                    }
                    
                    node = me.createNode(node);
                    if (suppressEvents !== true && me.fireEventArgs('beforeinsert', [
                        me,
                        node,
                        refNode
                    ]) === false) {
                        return false;
                    }
                    
                    if (oldParent === me && me.indexOf(node) < index) {
                        refIndex--;
                    }
                    
                    if (oldParent) {
                        
                        if (oldParent.removeChild(node, false, suppressEvents, oldParent.getTreeStore() === treeStore) === false) {
                            return false;
                        }
                    }
                    
                    
                    
                    
                    treeStore && treeStore.beginUpdate();
                    if (refIndex === 0) {
                        me.setFirstChild(node);
                    }
                    Ext.Array.splice(me.childNodes, refIndex, 0, node);
                    node.parentNode = me;
                    node.nextSibling = refNode;
                    refNode.previousSibling = node;
                    previousSibling = me.childNodes[refIndex - 1];
                    if (previousSibling) {
                        node.previousSibling = previousSibling;
                        previousSibling.nextSibling = node;
                    } else {
                        node.previousSibling = null;
                    }
                    
                    node.updateInfo(false, {
                        parentId: me.getId(),
                        index: refIndex,
                        isFirst: refIndex === 0,
                        isLast: false,
                        depth: (me.data.depth || 0) + 1
                    });
                    
                    for (i = refIndex + 1 , childCount = me.childNodes.length; i < childCount; i++) {
                        me.childNodes[i].updateInfo(false, {
                            index: i
                        });
                    }
                    if (!me.isLoaded()) {
                        if (bulkUpdate) {
                            me.data.loaded = true;
                        } else {
                            me.set('loaded', true);
                        }
                    }
                    
                    else if (me.childNodes.length === 1 && !bulkUpdate) {
                        me.triggerUIUpdate();
                    }
                    
                    
                    
                    if (treeStore) {
                        treeStore.registerNode(me, !bulkUpdate);
                    }
                    
                    
                    
                    if (suppressEvents !== true) {
                        me.fireEventArgs('insert', [
                            me,
                            node,
                            refNode
                        ]);
                        if (oldParent) {
                            node.fireEventArgs('move', [
                                node,
                                oldParent,
                                me,
                                refIndex,
                                refNode
                            ]);
                        }
                    }
                    
                    me.callTreeStore('onNodeInsert', [
                        node,
                        refIndex
                    ]);
                    
                    
                    if (treeStore) {
                        treeStore.endUpdate();
                    }
                    return node;
                },
                
                insertChild: function(index, node) {
                    var sibling = this.childNodes[index];
                    if (sibling) {
                        return this.insertBefore(node, sibling);
                    } else {
                        return this.appendChild(node);
                    }
                },
                
                isLastVisible: function() {
                    var me = this,
                        result = me.data.isLast,
                        next = me.nextSibling;
                    
                    
                    
                    if (!result && me.getTreeStore().isFiltered()) {
                        while (next) {
                            if (next.data.visible) {
                                return false;
                            }
                            next = next.nextSibling;
                        }
                        return true;
                    }
                    return result;
                },
                
                remove: function(erase, suppressEvents) {
                    var me = this,
                        parentNode = me.parentNode;
                    if (parentNode) {
                        parentNode.removeChild(me, erase, suppressEvents);
                    } else if (erase) {
                        
                        me.erase(true);
                    }
                    return me;
                },
                
                removeAll: function(erase, suppressEvents, fromParent) {
                    
                    
                    
                    var me = this,
                        childNodes = me.childNodes,
                        i = 0,
                        len = childNodes.length,
                        node, treeStore;
                    
                    if (!len) {
                        return;
                    }
                    
                    if (!fromParent) {
                        treeStore = me.getTreeStore();
                        
                        treeStore && treeStore.beginUpdate();
                        
                        
                        
                        treeStore.suspendEvent('remove');
                        me.callTreeStore('beforeNodeRemove', [
                            childNodes,
                            false
                        ]);
                    }
                    for (; i < len; ++i) {
                        node = childNodes[i];
                        node.previousSibling = node.nextSibling = node.parentNode = null;
                        me.fireEventArgs('remove', [
                            me,
                            node,
                            false
                        ]);
                        if (erase) {
                            node.erase(true);
                        } else 
                        {
                            node.removeAll(false, suppressEvents, true);
                        }
                    }
                    
                    if (!fromParent) {
                        treeStore.resumeEvent('remove');
                        me.callTreeStore('onNodeRemove', [
                            childNodes,
                            false
                        ]);
                        
                        treeStore && treeStore.endUpdate();
                    }
                    me.firstChild = me.lastChild = null;
                    me.childNodes.length = 0;
                    if (!fromParent) {
                        me.triggerUIUpdate();
                    }
                    return me;
                },
                
                getChildAt: function(index) {
                    return this.childNodes[index];
                },
                
                replaceChild: function(newChild, oldChild, suppressEvents) {
                    var s = oldChild ? oldChild.nextSibling : null;
                    this.removeChild(oldChild, false, suppressEvents);
                    this.insertBefore(newChild, s, suppressEvents);
                    return oldChild;
                },
                
                indexOf: function(child) {
                    return Ext.Array.indexOf(this.childNodes, child);
                },
                
                indexOfId: function(id) {
                    var childNodes = this.childNodes,
                        len = childNodes.length,
                        i = 0;
                    for (; i < len; ++i) {
                        if (childNodes[i].getId() === id) {
                            return i;
                        }
                    }
                    return -1;
                },
                
                getPath: function(field, separator) {
                    field = field || this.idProperty;
                    separator = separator || '/';
                    var path = [
                            this.get(field)
                        ],
                        parent = this.parentNode;
                    while (parent) {
                        path.unshift(parent.get(field));
                        parent = parent.parentNode;
                    }
                    return separator + path.join(separator);
                },
                
                getDepth: function() {
                    return this.get('depth');
                },
                
                bubble: function(fn, scope, args) {
                    var p = this;
                    while (p) {
                        if (fn.apply(scope || p, args || [
                            p
                        ]) === false) {
                            break;
                        }
                        p = p.parentNode;
                    }
                },
                cascade: function() {
                    if (Ext.isDefined(Ext.global.console)) {
                        Ext.global.console.warn('Ext.data.Node: cascade has been deprecated. Please use cascadeBy instead.');
                    }
                    return this.cascadeBy.apply(this, arguments);
                },
                
                cascadeBy: function(before, scope, args, after) {
                    var me = this;
                    if (arguments.length === 1 && !Ext.isFunction(before)) {
                        after = before.after;
                        scope = before.scope;
                        args = before.args;
                        before = before.before;
                    }
                    if (!before || before.apply(scope || me, args || [
                        me
                    ]) !== false) {
                        var childNodes = me.childNodes,
                            length = childNodes.length,
                            i;
                        for (i = 0; i < length; i++) {
                            childNodes[i].cascadeBy.call(childNodes[i], before, scope, args, after);
                        }
                        if (after) {
                            after.apply(scope || me, args || [
                                me
                            ]);
                        }
                    }
                },
                
                eachChild: function(fn, scope, args) {
                    var childNodes = this.childNodes,
                        length = childNodes.length,
                        i;
                    for (i = 0; i < length; i++) {
                        if (fn.apply(scope || this, args || [
                            childNodes[i]
                        ]) === false) {
                            break;
                        }
                    }
                },
                
                findChild: function(attribute, value, deep) {
                    return this.findChildBy(function() {
                        return this.get(attribute) == value;
                    }, null, deep);
                },
                
                findChildBy: function(fn, scope, deep) {
                    var cs = this.childNodes,
                        len = cs.length,
                        i = 0,
                        n, res;
                    for (; i < len; i++) {
                        n = cs[i];
                        if (fn.call(scope || n, n) === true) {
                            return n;
                        } else if (deep) {
                            res = n.findChildBy(fn, scope, deep);
                            if (res !== null) {
                                return res;
                            }
                        }
                    }
                    return null;
                },
                
                contains: function(node) {
                    return node.isAncestor(this);
                },
                
                isAncestor: function(node) {
                    var p = this.parentNode;
                    while (p) {
                        if (p === node) {
                            return true;
                        }
                        p = p.parentNode;
                    }
                    return false;
                },
                
                sort: function(sortFn, recursive, suppressEvent) {
                    var me = this,
                        childNodes = me.childNodes,
                        ln = childNodes.length,
                        i, n,
                        info = {
                            isFirst: true
                        };
                    if (ln > 0) {
                        if (!sortFn) {
                            sortFn = me.getTreeStore().getSortFn();
                        }
                        Ext.Array.sort(childNodes, sortFn);
                        me.setFirstChild(childNodes[0]);
                        me.setLastChild(childNodes[ln - 1]);
                        for (i = 0; i < ln; i++) {
                            n = childNodes[i];
                            n.previousSibling = childNodes[i - 1];
                            n.nextSibling = childNodes[i + 1];
                            
                            info.isLast = (i === ln - 1);
                            info.index = i;
                            n.updateInfo(false, info);
                            info.isFirst = false;
                            if (recursive && !n.isLeaf()) {
                                n.sort(sortFn, true, true);
                            }
                        }
                        
                        if (suppressEvent !== true) {
                            me.fireEventArgs('sort', [
                                me,
                                childNodes
                            ]);
                            
                            me.callTreeStore('onNodeSort', [
                                childNodes
                            ]);
                        }
                    }
                },
                
                isExpanded: function() {
                    return this.get('expanded');
                },
                
                isLoaded: function() {
                    return this.get('loaded');
                },
                
                isBranchLoaded: function() {
                    var isBranchLoaded = !this.isLeaf() && this.isLoaded();
                    if (isBranchLoaded) {
                        this.cascadeBy(function(node) {
                            if (!node.isLeaf()) {
                                isBranchLoaded = isBranchLoaded || node.isBranchLoaded();
                            }
                            return isBranchLoaded;
                        });
                    }
                    return isBranchLoaded;
                },
                
                isLoading: function() {
                    return this.get('loading');
                },
                
                isRoot: function() {
                    return !this.parentNode;
                },
                
                isVisible: function() {
                    var parent = this.parentNode;
                    while (parent) {
                        if (!parent.isExpanded()) {
                            return false;
                        }
                        parent = parent.parentNode;
                    }
                    return true;
                },
                
                expand: function(recursive, callback, scope) {
                    var me = this,
                        treeStore, resumeAddEvent;
                    
                    
                    
                    if (!me.isLeaf()) {
                        
                        if (me.isLoading()) {
                            me.on('expand', function() {
                                me.expand(recursive, callback, scope);
                            }, me, {
                                single: true
                            });
                        } else {
                            
                            if (!me.isExpanded()) {
                                if (me.fireEventArgs('beforeexpand', [
                                    me
                                ]) !== false) {
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    if (recursive) {
                                        
                                        
                                        
                                        if (me.parentNode && me.parentNode.isSynchronousRecursiveExpand) {
                                            me.isSynchronousRecursiveExpand = true;
                                        } else {
                                            treeStore = me.getTreeStore();
                                            if (treeStore.getProxy().isSynchronous || me.isBranchLoaded()) {
                                                me.isSynchronousRecursiveExpand = true;
                                                treeStore.suspendEvent('add');
                                                resumeAddEvent = true;
                                            }
                                        }
                                    }
                                    
                                    
                                    me.callTreeStore('onBeforeNodeExpand', [
                                        me.onChildNodesAvailable,
                                        me,
                                        [
                                            recursive,
                                            callback,
                                            scope
                                        ]
                                    ]);
                                    
                                    
                                    if (resumeAddEvent) {
                                        treeStore.resumeEvent('add');
                                        treeStore.fireEvent('refresh', treeStore);
                                    }
                                    me.isSynchronousRecursiveExpand = false;
                                }
                            } else if (recursive) {
                                
                                me.expandChildren(true, callback, scope);
                            } else {
                                Ext.callback(callback, scope || me, [
                                    me.childNodes
                                ]);
                            }
                        }
                    } else {
                        
                        Ext.callback(callback, scope || me);
                    }
                },
                
                
                onChildNodesAvailable: function(records, recursive, callback, scope) {
                    var me = this,
                        treeStore = me.getTreeStore(),
                        bulkUpdate = treeStore && treeStore.bulkUpdate,
                        ancestor, i, collapsedAncestors;
                    
                    
                    Ext.suspendLayouts();
                    
                    
                    
                    for (ancestor = me.parentNode; ancestor; ancestor = ancestor.parentNode) {
                        if (!ancestor.isExpanded()) {
                            (collapsedAncestors || (collapsedAncestors = [])).unshift(ancestor);
                        }
                    }
                    
                    if (bulkUpdate) {
                        me.data.expanded = true;
                    } else {
                        me.set('expanded', true);
                    }
                    
                    
                    if (collapsedAncestors) {
                        
                        
                        for (i = 1; i < collapsedAncestors.length; i++) {
                            ancestor = collapsedAncestors[i];
                            if (bulkUpdate) {
                                ancestor.data.expanded = true;
                            } else {
                                ancestor.set('expanded', true);
                            }
                        }
                        
                        
                        
                        collapsedAncestors[0].expand();
                        
                        for (i = 1; i < collapsedAncestors.length; i++) {
                            ancestor = collapsedAncestors[i];
                            ancestor.fireEventArgs('expand', [
                                ancestor,
                                ancestor.childNodes
                            ]);
                        }
                    } else {
                        
                        me.callTreeStore('onNodeExpand', [
                            records,
                            false
                        ]);
                    }
                    me.fireEventArgs('expand', [
                        me,
                        records
                    ]);
                    
                    if (recursive) {
                        me.expandChildren(true, callback, scope);
                    } else {
                        Ext.callback(callback, scope || me, [
                            me.childNodes
                        ]);
                    }
                    Ext.resumeLayouts(true);
                },
                
                expandChildren: function(recursive, callback, scope, 
                singleExpand) {
                    var me = this,
                        origCallback, i, allNodes, expandNodes, ln, node, treeStore;
                    
                    
                    
                    
                    if (Ext.isBoolean(callback)) {
                        origCallback = callback;
                        callback = scope;
                        scope = singleExpand;
                        singleExpand = origCallback;
                    }
                    if (singleExpand === undefined) {
                        treeStore = me.getTreeStore();
                        singleExpand = treeStore && treeStore.singleExpand;
                    }
                    allNodes = me.childNodes;
                    expandNodes = [];
                    ln = singleExpand ? Math.min(allNodes.length, 1) : allNodes.length;
                    for (i = 0; i < ln; ++i) {
                        node = allNodes[i];
                        if (!node.isLeaf()) {
                            expandNodes[expandNodes.length] = node;
                        }
                    }
                    ln = expandNodes.length;
                    for (i = 0; i < ln; ++i) {
                        expandNodes[i].expand(recursive);
                    }
                    if (callback) {
                        Ext.callback(callback, scope || me, [
                            me.childNodes
                        ]);
                    }
                },
                
                collapse: function(recursive, callback, scope) {
                    var me = this,
                        expanded = me.isExpanded(),
                        treeStore = me.getTreeStore(),
                        bulkUpdate = treeStore && treeStore.bulkUpdate,
                        len = me.childNodes.length,
                        i, collapseChildren;
                    
                    
                    
                    
                    if (!me.isLeaf() && ((!expanded && recursive) || me.fireEventArgs('beforecollapse', [
                        me
                    ]) !== false)) {
                        
                        
                        Ext.suspendLayouts();
                        
                        if (me.isExpanded()) {
                            
                            
                            
                            
                            
                            if (recursive) {
                                collapseChildren = function() {
                                    for (i = 0; i < len; i++) {
                                        me.childNodes[i].setCollapsed(true);
                                    }
                                };
                                if (callback) {
                                    callback = Ext.Function.createSequence(collapseChildren, Ext.Function.bind(callback, scope, [
                                        me.childNodes
                                    ]));
                                } else {
                                    callback = collapseChildren;
                                }
                            } else if (callback) {
                                callback = Ext.Function.bind(callback, scope, [
                                    me.childNodes
                                ]);
                            }
                            
                            if (bulkUpdate) {
                                me.data.expanded = false;
                            } else {
                                me.set('expanded', false);
                            }
                            
                            
                            
                            me.callTreeStore('onNodeCollapse', [
                                me.childNodes,
                                callback,
                                scope
                            ]);
                            me.fireEventArgs('collapse', [
                                me,
                                me.childNodes
                            ]);
                            
                            callback = null;
                        }
                        
                        
                        
                        
                        else if (recursive) {
                            for (i = 0; i < len; i++) {
                                me.childNodes[i].setCollapsed(true);
                            }
                        }
                        Ext.resumeLayouts(true);
                    }
                    
                    Ext.callback(callback, scope || me, [
                        me.childNodes
                    ]);
                },
                
                setCollapsed: function(recursive) {
                    var me = this,
                        len = me.childNodes.length,
                        i;
                    
                    if (!me.isLeaf() && me.fireEventArgs('beforecollapse', [
                        me
                    ]) !== false) {
                        
                        me.data.expanded = false;
                        
                        
                        
                        me.fireEventArgs('collapse', [
                            me,
                            me.childNodes
                        ]);
                        if (recursive) {
                            for (i = 0; i < len; i++) {
                                me.childNodes[i].setCollapsed(true);
                            }
                        }
                    }
                },
                
                collapseChildren: function(recursive, callback, scope) {
                    var me = this,
                        i,
                        allNodes = me.childNodes,
                        ln = allNodes.length,
                        collapseNodes = [],
                        node;
                    
                    for (i = 0; i < ln; ++i) {
                        node = allNodes[i];
                        if (!node.isLeaf() && node.isLoaded() && node.isExpanded()) {
                            collapseNodes.push(node);
                        }
                    }
                    ln = collapseNodes.length;
                    if (ln) {
                        
                        
                        for (i = 0; i < ln; ++i) {
                            node = collapseNodes[i];
                            if (i === ln - 1) {
                                node.collapse(recursive, callback, scope);
                            } else {
                                node.collapse(recursive);
                            }
                        }
                    } else {
                        
                        Ext.callback(callback, scope);
                    }
                },
                
                fireEvent: function(eventName) {
                    return this.fireEventArgs(eventName, Ext.Array.slice(arguments, 1));
                },
                
                
                
                fireEventArgs: function(eventName, args) {
                    
                    
                    
                    var fireEventArgs = Ext.mixin.Observable.prototype.fireEventArgs,
                        result, eventSource, topNode;
                    
                    if (bubbledEvents[eventName]) {
                        for (eventSource = this; result !== false && eventSource; eventSource = (topNode = eventSource).parentNode) {
                            if (eventSource.hasListeners && eventSource.hasListeners[eventName]) {
                                result = fireEventArgs.call(eventSource, eventName, args);
                            }
                        }
                        
                        
                        if (result !== false) {
                            eventSource = topNode.getTreeStore();
                            if (eventSource && eventSource.hasListeners && eventSource.hasListeners[eventName = 'node' + eventName]) {
                                result = eventSource.fireEventArgs(eventName, args);
                            }
                        }
                        return result;
                    } else 
                    {
                        return fireEventArgs.apply(this, arguments);
                    }
                },
                
                serialize: function() {
                    var result = Ext.data.writer.Json.prototype.getRecordData(this),
                        childNodes = this.childNodes,
                        len = childNodes.length,
                        children, i;
                    if (len > 0) {
                        children = [];
                        for (i = 0; i < len; i++) {
                            children.push(childNodes[i].serialize());
                        }
                        result.children = children;
                    }
                    return result;
                },
                
                callTreeStore: function(funcName, args) {
                    var me = this,
                        target = me.getTreeStore(),
                        fn = target && target[funcName];
                    if (target && fn) {
                        args = args || [];
                        if (args[0] !== me) {
                            args.unshift(me);
                        }
                        fn.apply(target, args);
                    }
                },
                
                privates: {
                    join: function(store) {
                        
                        if (store.isTreeStore) {
                            if (this.isRoot()) {
                                this.treeStore = this.store = store;
                            }
                        } else 
                        
                        {
                            this.callParent([
                                store
                            ]);
                        }
                    },
                    
                    callJoined: function(funcName, args) {
                        this.callParent([
                            funcName,
                            args
                        ]);
                        this.callTreeStore(funcName, args);
                    }
                }
            };
        }
    }
});


Ext.define('Ext.data.NodeStore', {
    extend: 'Ext.data.Store',
    alias: 'store.node',
    requires: [
        'Ext.data.NodeInterface'
    ],
    
    isNodeStore: true,
    config: {
        
        node: null,
        
        recursive: false,
        
        rootVisible: false,
        
        folderSort: false
    },
    
    isExpandingOrCollapsing: 0,
    
    
    
    getTotalCount: function() {
        return this.getCount();
    },
    afterEdit: function(record, modifiedFields) {
        
        if (this.getNode() && modifiedFields) {
            if (Ext.Array.indexOf(modifiedFields, 'loaded') !== -1) {
                return this.add(this.retrieveChildNodes(record));
            }
            if (Ext.Array.indexOf(modifiedFields, 'expanded') !== -1) {
                return this.filter();
            }
            if (Ext.Array.indexOf(modifiedFields, 'sorted') !== -1) {
                return this.sort();
            }
        }
        this.callParent(arguments);
    },
    afterReject: function(record) {
        var me = this;
        
        
        
        
        
        if (me.contains(record)) {
            me.onUpdate(record, Ext.data.Model.REJECT, null);
            me.fireEvent('update', me, record, Ext.data.Model.REJECT, null);
        }
    },
    afterCommit: function(record, modifiedFieldNames) {
        var me = this;
        if (!modifiedFieldNames) {
            modifiedFieldNames = null;
        }
        if (me.contains(record)) {
            me.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames);
            me.fireEvent('update', me, record, Ext.data.Model.COMMIT, modifiedFieldNames);
        }
    },
    onNodeAppend: function(parent, node) {
        this.add([
            node
        ].concat(this.retrieveChildNodes(node)));
    },
    onNodeInsert: function(parent, node) {
        this.add([
            node
        ].concat(this.retrieveChildNodes(node)));
    },
    onNodeRemove: function(parent, node) {
        this.remove([
            node
        ].concat(this.retrieveChildNodes(node)));
    },
    onNodeExpand: function(parent, records) {
        this.loadRecords(records);
    },
    applyNode: function(node) {
        if (node) {
            Ext.data.NodeInterface.decorate(node);
        }
        return node;
    },
    updateNode: function(node, oldNode) {
        var me = this,
            data;
        if (oldNode && !oldNode.isDestroyed) {
            oldNode.un({
                append: 'onNodeAppend',
                insert: 'onNodeInsert',
                remove: 'onNodeRemove',
                load: 'onNodeLoad',
                scope: me
            });
            oldNode.unjoin(me);
        }
        if (node) {
            node.on({
                scope: me,
                append: 'onNodeAppend',
                insert: 'onNodeInsert',
                remove: 'onNodeRemove',
                load: 'onNodeLoad'
            });
            node.join(me);
            data = [];
            if (node.childNodes.length) {
                data = data.concat(me.retrieveChildNodes(node));
            }
            if (me.getRootVisible()) {
                data.push(node);
            } else if (node.isLoaded() || node.isLoading()) {
                node.set('expanded', true);
            }
            me.getData().clear();
            me.fireEvent('clear', me);
            me.suspendEvents();
            me.add(data);
            me.resumeEvents();
            if (data.length === 0) {
                me.loaded = node.loaded = true;
            }
            me.fireEvent('refresh', me, me.data);
        }
    },
    
    retrieveChildNodes: function(root) {
        var node = this.getNode(),
            recursive = this.getRecursive(),
            added = [],
            child = root;
        if (!root.childNodes.length || (!recursive && root !== node)) {
            return added;
        }
        if (!recursive) {
            return root.childNodes;
        }
        while (child) {
            if (child._added) {
                delete child._added;
                if (child === root) {
                    break;
                } else {
                    child = child.nextSibling || child.parentNode;
                }
            } else {
                if (child !== root) {
                    added.push(child);
                }
                if (child.firstChild) {
                    child._added = true;
                    child = child.firstChild;
                } else {
                    child = child.nextSibling || child.parentNode;
                }
            }
        }
        return added;
    },
    
    isVisible: function(node) {
        var parent = node.parentNode;
        if (!this.getRecursive() && parent !== this.getNode()) {
            return false;
        }
        while (parent) {
            if (!parent.isExpanded()) {
                return false;
            }
            
            
            
            if (parent === this.getNode()) {
                break;
            }
            parent = parent.parentNode;
        }
        return true;
    }
});


Ext.define('Ext.data.Request', {
    config: {
        
        action: undefined,
        
        params: undefined,
        
        method: 'GET',
        
        url: null,
        
        operation: null,
        
        proxy: null,
        
        disableCaching: false,
        
        headers: {},
        
        callbackKey: null,
        
        rawRequest: null,
        
        jsonData: undefined,
        
        xmlData: undefined,
        
        withCredentials: false,
        
        username: null,
        
        password: null,
        
        binary: false,
        callback: null,
        scope: null,
        timeout: 30000,
        records: null,
        
        
        directFn: null,
        args: null,
        useDefaultXhrHeader: null
    },
    
    constructor: function(config) {
        this.initConfig(config);
    },
    
    getParam: function(key) {
        var params = this.getParams(),
            val;
        if (params) {
            return params[key];
        }
        return val;
    },
    
    setParam: function(key, value) {
        var params = this.getParams() || {};
        params[key] = value;
        this.setParams(params);
    }
});


Ext.define('Ext.mixin.Queryable', {
    mixinId: 'queryable',
    isQueryable: true,
    
    query: function(selector) {
        selector = selector || '*';
        return Ext.ComponentQuery.query(selector, this.getQueryRoot());
    },
    
    queryBy: function(fn, scope) {
        var out = [],
            items = this.getQueryRoot().getRefItems(true),
            i = 0,
            len = items.length,
            item;
        for (; i < len; ++i) {
            item = items[i];
            if (fn.call(scope || item, item) !== false) {
                out.push(item);
            }
        }
        return out;
    },
    
    queryById: function(id) {
        return this.down(Ext.makeIdSelector(id));
    },
    
    child: function(selector) {
        var children = this.getQueryRoot().getRefItems();
        if (selector && selector.isComponent) {
            return this.matchById(children, selector.getItemId());
        }
        
        if (selector) {
            children = Ext.ComponentQuery.query(selector, children);
        }
        
        if (children.length) {
            return children[0];
        }
        return null;
    },
    
    down: function(selector) {
        if (selector && selector.isComponent) {
            return this.matchById(this.getRefItems(true), selector.getItemId());
        }
        selector = selector || '';
        return this.query(selector)[0] || null;
    },
    
    visitPreOrder: function(selector, fn, scope, extraArgs) {
        Ext.ComponentQuery._visit(true, selector, this.getQueryRoot(), fn, scope, extraArgs);
    },
    
    visitPostOrder: function(selector, fn, scope, extraArgs) {
        Ext.ComponentQuery._visit(false, selector, this.getQueryRoot(), fn, scope, extraArgs);
    },
    getRefItems: function() {
        return [];
    },
    getQueryRoot: function() {
        return this;
    },
    privates: {
        matchById: function(items, id) {
            var len = items.length,
                i, item;
            for (i = 0; i < len; ++i) {
                item = items[i];
                if (item.getItemId() === id) {
                    return item;
                }
            }
            return null;
        }
    }
});


Ext.define('Ext.data.TreeModel', {
    extend: 'Ext.data.Model',
    requires: [
        'Ext.data.NodeInterface'
    ],
    mixins: [
        'Ext.mixin.Queryable'
    ],
    
    getRefItems: function() {
        return this.childNodes;
    },
    getRefOwner: function() {
        return this.parentNode;
    }
}, function() {
    Ext.data.NodeInterface.decorate(this);
});


Ext.define('Ext.data.TreeStore', {
    extend: 'Ext.data.NodeStore',
    alias: 'store.tree',
    requires: [
        'Ext.util.Sorter',
        'Ext.data.TreeModel',
        'Ext.data.NodeInterface'
    ],
    
    isTreeStore: true,
    config: {
        
        root: null,
        rootVisible: false,
        recursive: true,
        
        defaultRootProperty: 'children',
        
        parentIdProperty: null,
        
        clearOnLoad: true,
        
        clearRemovedOnLoad: true,
        
        nodeParam: 'node',
        
        defaultRootId: 'root',
        
        defaultRootText: 'Root',
        
        folderSort: false
    },
    fillCount: 0,
    bulkUpdate: 0,
    
    _silentOptions: {
        silent: true
    },
    constructor: function(config) {
        var me = this;
        me.byIdMap = {};
        me.byInternalIdMap = {};
        me.callParent([
            config
        ]);
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        if (Ext.isDefined(me.nodeParameter)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.data.TreeStore: nodeParameter has been deprecated. Please use nodeParam instead.');
            }
            me.nodeParam = me.nodeParameter;
            delete me.nodeParameter;
        }
    },
    applyFields: function(fields) {
        var me = this;
        if (fields) {
            if (me.defaultRootProperty !== me.self.prototype.config.defaultRootProperty) {
                
                fields = fields.concat({
                    name: me.defaultRootProperty,
                    type: 'auto',
                    defaultValue: null,
                    persist: false
                });
            }
            
            me.setModel(Ext.define(null, {
                extend: 'Ext.data.TreeModel',
                fields: fields,
                proxy: me.getProxy()
            }));
            me.implicitModel = true;
        }
    },
    
    onSorterEndUpdate: function() {
        var me = this,
            sorterCollection = me.getSorters(),
            sorters = sorterCollection.getRange(),
            rootNode = me.getRoot(),
            folderSort = me.getFolderSort();
        
        if (rootNode && (folderSort || sorters.length)) {
            if (me.getRemoteSort()) {
                if (sorters.length) {
                    me.attemptLoad({
                        callback: function() {
                            me.fireEvent('sort', me, sorters);
                        }
                    });
                }
            } else {
                rootNode.sort(this.getSortFn(), true);
                
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
                me.fireEvent('sort', me, sorters);
            }
        } else 
        {
            me.fireEvent('sort', me, sorters);
        }
    },
    
    
    
    
    updateRemoteFilter: function() {
        var data = this.getData(),
            filters = this.getFilters();
        
        data.setFilters(null);
        filters.on('endupdate', this.onFilterEndUpdate, this);
    },
    
    
    
    
    updateRemoteSort: function() {
        var data = this.getData(),
            sorters = this.getSorters();
        
        data.setSorters(null);
        sorters.on('endupdate', this.onSorterEndUpdate, this);
    },
    updateFolderSort: function(folderSort) {
        this.needsFolderSort = folderSort;
        this.onSorterEndUpdate();
    },
    getSortFn: function() {
        return this._sortFn || (this._sortFn = this.createSortFn());
    },
    createSortFn: function() {
        var me = this,
            sortersSortFn = this.sorters.getSortFn();
        return function(node1, node2) {
            var node1FolderOrder, node2FolderOrder,
                result = 0;
            if (me.needsFolderSort) {
                
                node1FolderOrder = node1.data.leaf ? 1 : 0;
                node2FolderOrder = node2.data.leaf ? 1 : 0;
                result = node1FolderOrder - node2FolderOrder;
            }
            if (me.needsIndexSort && result === 0) {
                result = node1.data.index - node2.data.index;
            }
            return result || sortersSortFn(node1, node2);
        };
    },
    afterEdit: function(node, modifiedFieldNames) {
        var me = this;
        if (me.needsLocalFilter()) {
            me.doFilter(node);
        }
        me.callParent([
            node,
            modifiedFieldNames
        ]);
    },
    updateRootVisible: function(rootVisible) {
        var rootNode = this.getRoot(),
            data;
        if (rootNode) {
            data = this.getData();
            if (rootVisible) {
                data.insert(0, rootNode);
            } else {
                data.remove(rootNode);
            }
        }
    },
    updateTrackRemoved: function(trackRemoved) {
        this.callParent(arguments);
        this.removedNodes = this.removed;
        this.removed = null;
    },
    getRemovedRecords: function() {
        return this.removedNodes;
    },
    onDestroyRecords: function(records, operation, success) {
        if (success) {
            this.removedNodes.length = 0;
        }
    },
    updateProxy: function(proxy) {
        var reader;
        
        
        
        if (proxy) {
            if (proxy.setIdParam) {
                proxy.setIdParam(this.getNodeParam());
            }
            
            reader = proxy.getReader();
            if (Ext.isEmpty(reader.getRootProperty())) {
                reader.setRootProperty(this.getDefaultRootProperty());
            }
        }
    },
    setProxy: function(proxy) {
        this.changingProxy = true;
        this.callParent([
            proxy
        ]);
        this.changingProxy = false;
    },
    applyModel: function(model) {
        return this.callParent(arguments) || Ext.data.TreeModel;
    },
    updateModel: function(model) {
        var isNode = model.prototype.isNode;
        
        Ext.data.NodeInterface.decorate(model);
        
        
        if (!isNode && !this.changingProxy) {
            this.getProxy().getReader().buildExtractors(true);
        }
    },
    onFilterEndUpdate: function(filters) {
        var me = this,
            length = filters.length,
            root = me.getRoot(),
            childNodes, childNode, filteredNodes, i;
        if (!me.getRemoteFilter()) {
            if (length) {
                me.doFilter(root);
            } else {
                root.cascadeBy({
                    after: function(node) {
                        
                        
                        node.set('visible', true, me._silentOptions);
                    }
                });
            }
            if (length) {
                filteredNodes = [];
                childNodes = root.childNodes;
                for (i = 0 , length = childNodes.length; i < length; i++) {
                    childNode = childNodes[i];
                    if (childNode.get('visible')) {
                        filteredNodes.push(childNode);
                    }
                }
            } else {
                filteredNodes = root.childNodes;
            }
            me.onNodeFilter(root, filteredNodes);
            root.fireEvent('filterchange', root, filteredNodes);
            me.fireEvent('filterchange', me, filters);
            
            
            me.suppressNextFilter = true;
            me.callParent([
                filters
            ]);
            me.suppressNextFilter = false;
        } else {
            me.callParent([
                filters
            ]);
        }
    },
    
    onNodeFilter: function(root, childNodes) {
        var me = this,
            data = me.getData(),
            toAdd = [];
        
        if (me.getRootVisible()) {
            if (childNodes.length) {
                toAdd.push(root);
            } else {
                root.set('visible', false, me._silentOptions);
            }
        }
        me.handleNodeExpand(root, childNodes, toAdd);
        
        
        me.suspendEvents();
        data.splice(0, data.getCount(), toAdd);
        me.resumeEvents();
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },
    
    onBeforeNodeExpand: function(node, callback, scope, args) {
        var me = this,
            storeReader, nodeProxy, nodeReader, reader, children, callbackArgs, childType;
        
        
        if (node.isLoaded()) {
            callbackArgs = [
                node.childNodes
            ];
            if (args) {
                callbackArgs.push.apply(callbackArgs, args);
            }
            Ext.callback(callback, scope || node, callbackArgs);
        }
        
        else if (node.isLoading()) {
            me.on('load', function() {
                callbackArgs = [
                    node.childNodes
                ];
                if (args) {
                    callbackArgs.push.apply(callbackArgs, args);
                }
                Ext.callback(callback, scope || node, callbackArgs);
            }, me, {
                single: true,
                priority: 1001
            });
        } else 
        {
            
            
            
            storeReader = me.getProxy().getReader();
            nodeProxy = node.getProxy();
            nodeReader = nodeProxy ? nodeProxy.getReader() : null;
            
            reader = nodeReader && nodeReader.initialConfig.rootProperty ? nodeReader : storeReader;
            
            
            
            children = reader.getRoot(node.raw || node.data);
            
            
            if (children || (node.phantom && !node.isRoot())) {
                childType = node.childType;
                
                if (children) {
                    me.fillNode(node, reader.extractData(children, childType ? {
                        model: childType
                    } : undefined));
                }
                callbackArgs = [
                    node.childNodes
                ];
                if (args) {
                    callbackArgs.push.apply(callbackArgs, args);
                }
                Ext.callback(callback, scope || node, callbackArgs);
            } else 
            {
                if (node.isRoot()) {
                    me.clearLoadTask();
                }
                me.read({
                    node: node,
                    
                    
                    onChildNodesAvailable: function() {
                        
                        
                        delete me.lastOptions.onChildNodesAvailable;
                        callbackArgs = [
                            node.childNodes
                        ];
                        if (args) {
                            callbackArgs.push.apply(callbackArgs, args);
                        }
                        Ext.callback(callback, scope || node, callbackArgs);
                    }
                });
            }
        }
    },
    
    
    onNodeExpand: function(parent, records) {
        var me = this,
            insertIndex = me.indexOf(parent) + 1,
            toAdd = [];
        me.handleNodeExpand(parent, records, toAdd);
        
        if (!me.refreshCounter && parent.isRoot() && !parent.get('visible')) {
            me.loadRecords(toAdd);
        } else 
        
        {
            me.insert(insertIndex, toAdd);
        }
    },
    
    
    handleNodeExpand: function(parent, records, toAdd) {
        var me = this,
            ln = records ? records.length : 0,
            i, record;
        
        if (parent !== this.getRoot() && !me.isVisible(parent)) {
            return;
        }
        if (ln) {
            
            
            for (i = 0; i < ln; i++) {
                record = records[i];
                
                if (record.get('visible')) {
                    
                    
                    
                    toAdd.push(record);
                    if (record.isExpanded()) {
                        if (record.isLoaded()) {
                            
                            me.handleNodeExpand(record, record.childNodes, toAdd);
                        } else {
                            
                            record.set('expanded', false);
                            record.expand();
                        }
                    }
                }
            }
        }
    },
    
    onNodeCollapse: function(parent, records, callback, scope) {
        var me = this,
            collapseIndex = me.indexOf(parent) + 1,
            lastNodeIndexPlus;
        if (!me.recursive && parent !== me.getRoot()) {
            return;
        }
        
        if (me.needsLocalFilter()) {
            records = Ext.Array.filter(records, me.filterVisible);
        }
        
        
        
        
        if (records.length && me.data.contains(records[0])) {
            
            lastNodeIndexPlus = me.indexOfNextVisibleNode(parent);
            
            me.removeAt(collapseIndex, lastNodeIndexPlus - collapseIndex);
        }
        Ext.callback(callback, scope);
    },
    
    indexOfNextVisibleNode: function(node) {
        var result;
        while (node.parentNode) {
            
            for (result = node.nextSibling; result && !result.get('visible'); result = result.nextSibling) {}
            
            ;
            
            if (result) {
                return this.indexOf(result);
            }
            
            node = node.parentNode;
        }
        
        return this.getCount();
    },
    
    filterNew: function(item) {
        
        
        return this.callParent([
            item
        ]) && !item.get('root');
    },
    getNewRecords: function() {
        return Ext.Array.filter(Ext.Object.getValues(this.byIdMap), this.filterNew, this);
    },
    getUpdatedRecords: function() {
        return Ext.Array.filter(Ext.Object.getValues(this.byIdMap), this.filterUpdated);
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    beforeNodeRemove: function(parentNode, childNodes) {
        if (!Ext.isArray(childNodes)) {
            childNodes = [
                childNodes
            ];
        }
        var me = this,
            len = childNodes.length,
            i, startNode;
        
        for (i = 0; !startNode && i < len; i++) {
            if (childNodes[i].get('visible')) {
                startNode = childNodes[i];
            }
        }
        
        if (startNode) {
            me.startRemoveIndex = me.indexOf(childNodes[0]);
            me.lastRemoveIndexPlusOne = me.indexOfNextVisibleNode(childNodes[childNodes.length - 1]);
        } else {
            me.startRemoveIndex = -1;
            me.lastRemoveIndexPlusOne = 0;
        }
    },
    
    
    
    
    
    afterDrop: Ext.emptyFn,
    
    
    onNodeRemove: function(parentNode, childNodes, isMove) {
        var me = this,
            removed = me.removedNodes,
            len = childNodes.length,
            startRemoveIndex = me.startRemoveIndex,
            lastRemoveIndexPlusOne = me.lastRemoveIndexPlusOne,
            i;
        
        
        me.suspendAutoSync();
        
        
        
        
        
        if (startRemoveIndex !== -1) {
            me.removeIsMove = isMove;
            me.removeAt(startRemoveIndex, lastRemoveIndexPlusOne - startRemoveIndex);
            me.removeIsMove = false;
        }
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        for (i = 0; i < len; i++) {
            childNodes[i].cascadeBy(function(node) {
                
                me.unregisterNode(node);
                
                
                
                if (removed && !isMove) {
                    
                    
                    if (!node.phantom && !node.erasing && !me.loading) {
                        
                        
                        node.removedFrom = me.indexOf(node);
                        removed.push(node);
                        
                        
                        me.needsSync = true;
                    }
                }
            });
        }
        me.resumeAutoSync();
    },
    
    onNodeAppend: function(parent, node, index) {
        this.onNodeInsert(parent, node, index);
    },
    
    onNodeInsert: function(parent, node, index) {
        var me = this,
            data = node.raw || node.data,
            refNode, sibling, storeReader, nodeProxy, nodeReader, reader, dataRoot, childType;
        if (parent && me.needsLocalFilter()) {
            me.doFilter(parent);
        }
        me.beginUpdate();
        
        if (me.isVisible(node)) {
            if (index === 0 || !node.previousSibling) {
                refNode = parent;
            } else {
                
                for (sibling = node.previousSibling; sibling && !sibling.get('visible'); sibling = sibling.previousSibling){}
                while (sibling.isExpanded() && sibling.lastChild) {
                    sibling = sibling.lastChild;
                }
                refNode = sibling;
            }
            
            me.insert(me.indexOf(refNode) + 1, node);
            if (!node.isLeaf() && node.isExpanded()) {
                if (node.isLoaded()) {
                    
                    me.onNodeExpand(node, node.childNodes);
                } else if (!me.fillCount) {
                    
                    
                    
                    
                    node.set('expanded', false);
                    node.expand();
                }
            }
        }
        
        me.needsSync = me.needsSync || node.phantom || node.dirty;
        if (!node.isLeaf() && !node.isLoaded() && !me.lazyFill) {
            
            
            
            storeReader = me.getProxy().getReader();
            nodeProxy = node.getProxy();
            nodeReader = nodeProxy ? nodeProxy.getReader() : null;
            
            reader = nodeReader && nodeReader.initialConfig.rootProperty ? nodeReader : storeReader;
            dataRoot = reader.getRoot(data);
            if (dataRoot) {
                childType = node.childType;
                me.fillNode(node, reader.extractData(dataRoot, childType ? {
                    model: childType
                } : undefined));
            }
        }
        me.endUpdate();
    },
    
    registerNode: function(node, includeChildren) {
        var me = this,
            children, length, i;
        
        me.byIdMap[node.id] = node;
        me.byInternalIdMap[node.internalId] = node;
        if (includeChildren === true) {
            children = node.childNodes;
            length = children.length;
            for (i = 0; i < length; i++) {
                me.registerNode(children[i], true);
            }
        }
    },
    
    unregisterNode: function(node, includeChildren) {
        var me = this,
            children, length, i;
        delete me.byIdMap[node.id];
        delete me.byInternalIdMap[node.internalId];
        if (includeChildren === true) {
            children = node.childNodes;
            length = children.length;
            for (i = 0; i < length; i++) {
                me.unregisterNode(children[i], true);
            }
        }
    },
    onNodeSort: function(node, childNodes) {
        var me = this;
        
        
        me.suspendAutoSync();
        
        if ((me.indexOf(node) !== -1 || (node === me.getRoot() && !me.getRootVisible()) && node.isExpanded())) {
            Ext.suspendLayouts();
            me.onNodeCollapse(node, childNodes);
            me.onNodeExpand(node, childNodes);
            Ext.resumeLayouts(true);
        }
        
        
        me.resumeAutoSync(me.autoSync);
    },
    applyRoot: function(newRoot) {
        var me = this,
            Model = me.getModel(),
            idProperty = Model.prototype.idProperty,
            defaultRootId = me.getDefaultRootId();
        
        
        
        if (newRoot && !newRoot.isNode) {
            
            newRoot = Ext.apply({
                text: me.getDefaultRootText(),
                root: true,
                isFirst: true,
                isLast: true,
                depth: 0,
                index: 0,
                parentId: null,
                allowDrag: false
            }, newRoot);
            
            if (defaultRootId && newRoot[idProperty] === undefined) {
                newRoot[idProperty] = defaultRootId;
            }
            
            newRoot = new Model(newRoot);
            
            
            
            newRoot.store = newRoot.treeStore = me;
        }
        return newRoot;
    },
    updateRoot: function(newRoot, oldRoot) {
        var me = this,
            oldOwner,
            initial = !oldRoot;
        
        me.byIdMap = {};
        me.byInternalIdMap = {};
        
        me.getTrackRemoved();
        
        
        me.suspendEvent('add', 'remove');
        
        
        if (oldRoot && oldRoot.isModel) {
            me.beforeNodeRemove(null, [
                oldRoot
            ]);
            oldRoot.set('root', false);
            me.onNodeRemove(null, [
                oldRoot
            ]);
            oldRoot.fireEvent('remove', null, oldRoot, false);
            oldRoot.fireEvent('rootchange', null);
            oldRoot.store = oldRoot.treeStore = null;
        }
        me.getData().clear();
        
        
        if (newRoot) {
            
            if (newRoot.fireEventArgs('beforeappend', [
                null,
                newRoot
            ]) === false) {
                newRoot = null;
            } else {
                
                oldOwner = newRoot.parentNode;
                if (oldOwner) {
                    
                    
                    
                    
                    if (!oldOwner.removeChild(newRoot, false, false, oldOwner.getTreeStore() === me)) {
                        return;
                    }
                }
                
                else if ((oldOwner = newRoot.getTreeStore()) && oldOwner !== me && newRoot === oldOwner.getRoot()) {
                    oldOwner.setRoot(null);
                }
                newRoot.set('root', true);
                
                newRoot.updateInfo(true, {
                    isFirst: true,
                    isLast: true,
                    depth: 0,
                    index: 0,
                    parentId: null
                });
                
                
                me.registerNode(newRoot, true);
                
                newRoot.fireEvent('append', null, newRoot, false);
                newRoot.fireEvent('rootchange', newRoot);
                
                me.onNodeAppend(null, newRoot, 0);
                
                
                
                newRoot.phantom = true;
            }
        }
        me.fireEvent('rootchange', newRoot, oldRoot);
        
        if (newRoot && (me.getAutoLoad() || newRoot.isExpanded())) {
            
            if (newRoot.isLoaded()) {
                me.onNodeExpand(newRoot, newRoot.childNodes);
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
            } else 
            {
                newRoot.data.expanded = false;
                newRoot.expand(false, function() {
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                });
            }
        } else if (!initial) {
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        
        me.resumeEvent('add', 'remove');
        return newRoot;
    },
    
    getNodeById: function(id) {
        return this.byIdMap[id] || null;
    },
    
    findNode: function(property, value, startsWith, endsWith, ignoreCase) {
        if (Ext.isEmpty(value, false)) {
            return null;
        }
        
        if (value === this.model.idProperty && arguments.length < 3) {
            return this.byIdMap[value];
        }
        var regex = Ext.String.createRegex(value, startsWith, endsWith, ignoreCase),
            result = null;
        Ext.Object.eachValue(this.byIdMap, function(node) {
            if (node && regex.test(node.get(property))) {
                result = node;
                return false;
            }
        });
        return result;
    },
    
    load: function(options) {
        options = options || {};
        options.params = options.params || {};
        var me = this,
            node = options.node || me.getRoot(),
            proxy = me.getProxy(),
            callback = options.callback,
            scope = options.scope,
            clearOnLoad = me.getClearOnLoad(),
            operation;
        
        
        if (!node) {
            me.setRoot({
                expanded: true
            });
            return;
        }
        
        if (node.data.expanded) {
            node.data.loaded = false;
            
            
            if (clearOnLoad) {
                node.data.expanded = false;
            }
            options.callback = function(loadedNodes, operation, success) {
                
                
                
                if (!clearOnLoad) {
                    node.collapse();
                }
                node.expand();
                
                Ext.callback(callback, scope, [
                    loadedNodes,
                    operation,
                    success
                ]);
            };
        }
        
        
        options.id = node.getId();
        options = Ext.apply({
            filters: me.getFilters().items,
            sorters: me.getSorters().items,
            node: options.node || node,
            internalScope: me,
            internalCallback: me.onProxyLoad
        }, options);
        me.lastOptions = Ext.apply({}, options);
        operation = proxy.createOperation('read', options);
        if (me.fireEvent('beforeload', me, operation) !== false) {
            
            
            me.loading = true;
            me.clearLoadTask();
            if (clearOnLoad) {
                if (me.getClearRemovedOnLoad()) {
                    
                    me.clearRemoved(node);
                }
                
                node.removeAll(false);
            }
            operation.execute();
        }
        if (me.loading && node) {
            node.set('loading', true);
        }
        return me;
    },
    
    clearRemoved: function(node) {
        var me = this,
            removed = me.removedNodes,
            id = node.getId(),
            removedLength = removed.length,
            i = removedLength,
            recordsToClear = {},
            newRemoved = [],
            removedHash = {},
            removedNode, targetNode, targetId;
        if (node === me.getRoot()) {
            
            me.removedNodes.length = 0;
            return;
        }
        
        for (; i--; ) {
            removedNode = removed[i];
            removedHash[removedNode.getId()] = removedNode;
        }
        for (i = removedLength; i--; ) {
            removedNode = removed[i];
            targetNode = removedNode;
            while (targetNode && targetNode.getId() !== id) {
                
                
                targetId = targetNode.get('parentId') || targetNode.get('lastParentId');
                targetNode = targetNode.parentNode || me.getNodeById(targetId) || removedHash[targetId];
            }
            if (targetNode) {
                
                recordsToClear[removedNode.getId()] = removedNode;
            }
        }
        
        for (i = 0; i < removedLength; i++) {
            removedNode = removed[i];
            if (!recordsToClear[removedNode.getId()]) {
                newRemoved.push(removedNode);
            }
        }
        me.removedNodes = newRemoved;
    },
    
    fillNode: function(node, newNodes) {
        var me = this,
            newNodeCount = newNodes ? newNodes.length : 0,
            sorters = me.getSorters(),
            needsIndexSort = false,
            performLocalSort = me.sortOnLoad && newNodeCount > 1 && !me.remoteSort && me.getFolderSort() || sorters.length,
            node1, node2, i, filterFn;
        
        ++me.bulkUpdate;
        if (newNodeCount) {
            
            if (me.needsLocalFilter()) {
                filterFn = me.getFilters().getFilterFn();
                newNodes[0].set('visible', filterFn(newNodes[0]));
            }
            
            for (i = 1; i < newNodeCount; i++) {
                node1 = newNodes[i];
                node2 = newNodes[i - 1];
                
                if (filterFn) {
                    node1.set('visible', filterFn(node1));
                }
                needsIndexSort = node1.data.index !== node2.data.index;
            }
            
            if (performLocalSort) {
                
                me.needsIndexSort = true;
                Ext.Array.sort(newNodes, me.getSortFn());
                me.needsIndexSort = false;
            } else if (needsIndexSort) {
                Ext.Array.sort(newNodes, me.sortByIndex);
            }
        }
        if (me.bulkUpdate === 1) {
            node.set('loaded', true);
        } else {
            node.data.loaded = true;
        }
        if (newNodes.length) {
            node.appendChild(newNodes, undefined, true);
        }
        --me.bulkUpdate;
        
        return newNodes;
    },
    
    beginFill: function() {
        var me = this;
        if (!me.fillCount++) {
            me.beginUpdate();
            me.suspendEvent('add', 'update');
            me.suspendAutoSync();
            me.fillArray = [];
        }
    },
    
    endFill: function(parent, nodes) {
        var me = this,
            fillArray = me.fillArray,
            i, len, index;
        
        fillArray.push(nodes);
        if (!--me.fillCount) {
            me.resumeAutoSync();
            me.resumeEvent('add', 'update');
            
            
            
            for (i = 0 , len = fillArray.length; i < len; i++) {
                index = me.indexOf(fillArray[i][0]);
                
                if (index !== -1) {
                    me.fireEvent('add', me, fillArray[i], index);
                }
            }
            me.fillArray = null;
            me.endUpdate();
        }
    },
    
    sortByIndex: function(node1, node2) {
        return node1.data.index - node2.data.index;
    },
    onIdChanged: function(node, oldId, newId) {
        var childNodes = node.childNodes,
            len = childNodes && childNodes.length,
            i;
        this.callParent(arguments);
        delete this.byIdMap[oldId];
        this.byIdMap[newId] = node;
        
        for (i = 0; i < len; i++) {
            childNodes[i].set('parentId', newId);
        }
    },
    onProxyLoad: function(operation) {
        var me = this,
            options = operation.initialConfig,
            successful = operation.wasSuccessful(),
            records = operation.getRecords(),
            node = options.node,
            scope = operation.getScope() || me,
            args = [
                records,
                operation,
                successful
            ];
        if (me.isDestroyed) {
            return;
        }
        me.loading = false;
        node.set('loading', false);
        if (successful) {
            ++me.loadCount;
            if (!me.getClearOnLoad()) {
                records = me.cleanRecords(node, records);
            }
            
            if (me.parentIdProperty) {
                records = me.treeify(node, records);
            }
            records = me.fillNode(node, records);
        }
        
        
        
        Ext.callback(options.onChildNodesAvailable, scope, args);
        me.fireEvent('load', me, records, successful, operation, node);
    },
    
    
    
    treeify: function(parentNode, records) {
        var me = this,
            parentNodeId = parentNode.getId(),
            len = records.length,
            i, node, parentId,
            result = [],
            nodeMap = {};
        
        for (i = 0; i < len; i++) {
            node = records[i];
            nodeMap[node.getId()] = node;
        }
        
        for (i = 0; i < len; i++) {
            node = records[i];
            parentId = node.data[me.parentIdProperty];
            
            if (!parentId || parentId === parentNodeId) {
                result.push(node);
            } else 
            {
                nodeMap[parentId].appendChild(node);
            }
        }
        return result;
    },
    cleanRecords: function(node, records) {
        var nodeHash = {},
            childNodes = node.childNodes,
            i = 0,
            len = childNodes.length,
            out = [],
            rec;
        
        for (; i < len; ++i) {
            nodeHash[childNodes[i].getId()] = true;
        }
        for (i = 0 , len = records.length; i < len; ++i) {
            rec = records[i];
            if (!nodeHash[rec.getId()]) {
                out.push(rec);
            }
        }
        return out;
    },
    removeAll: function() {
        this.suspendEvents();
        this.setRoot(null);
        this.resumeEvents();
        this.callParent();
    },
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.tree.sort(sorterFn, true);
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        me.fireEvent('sort', me, me.sorters.getRange());
    },
    filterVisible: function(node) {
        return node.get('visible');
    },
    isVisible: function(node) {
        var parentNode = node.parentNode,
            visible = node.data.visible,
            root = this.getRoot();
        while (visible && parentNode) {
            visible = parentNode.data.expanded && parentNode.data.visible;
            parentNode = parentNode.parentNode;
        }
        
        
        return visible && !(node === root && !this.getRootVisible());
    },
    
    getRootNode: function() {
        return this.getRoot();
    },
    
    setRootNode: function(root) {
        this.setRoot(root);
        return this.getRoot();
    },
    privates: {
        doFilter: function(node) {
            var root = this.getRoot(),
                filterFn = this.getFilters().getFilterFn();
            this.filterNodes(root, node, filterFn);
        },
        filterNodes: function(root, node, filterFn) {
            var match = false,
                childNodes = node.childNodes,
                len = childNodes && childNodes.length,
                i, child;
            if (len) {
                for (i = 0; i < len; ++i) {
                    this.filterNodes(root, childNodes[i], filterFn);
                }
            }
            match = node === root || filterFn(node);
            node.set('visible', match, this._silentOptions);
            return match;
        },
        needsLocalFilter: function() {
            return !this.getRemoteFilter() && this.getFilters().length;
        }
    },
    deprecated: {
        5: {
            properties: {
                tree: null
            }
        }
    }
});


Ext.define('Ext.data.Types', {
    singleton: true,
    requires: [
        'Ext.data.SortTypes'
    ]
}, function(Types) {
    var SortTypes = Ext.data.SortTypes;
    Ext.apply(Types, {
        
        stripRe: /[\$,%]/g,
        
        AUTO: {
            sortType: SortTypes.none,
            type: 'auto'
        },
        
        STRING: {
            convert: function(v) {
                var defaultValue = this.getAllowNull() ? null : '';
                return (v === undefined || v === null) ? defaultValue : String(v);
            },
            sortType: SortTypes.asUCString,
            type: 'string'
        },
        
        INT: {
            convert: function(v) {
                
                
                
                if (typeof v === 'number') {
                    return parseInt(v, 10);
                }
                return v !== undefined && v !== null && v !== '' ? parseInt(String(v).replace(Types.stripRe, ''), 10) : (this.getAllowNull() ? null : 0);
            },
            sortType: SortTypes.none,
            type: 'int'
        },
        
        FLOAT: {
            convert: function(v) {
                if (typeof v === 'number') {
                    return v;
                }
                return v !== undefined && v !== null && v !== '' ? parseFloat(String(v).replace(Types.stripRe, ''), 10) : (this.getAllowNull() ? null : 0);
            },
            sortType: SortTypes.none,
            type: 'float'
        },
        
        BOOL: {
            convert: function(v) {
                if (typeof v === 'boolean') {
                    return v;
                }
                if (this.getAllowNull() && (v === undefined || v === null || v === '')) {
                    return null;
                }
                return v === 'true' || v == 1;
            },
            sortType: SortTypes.none,
            type: 'bool'
        },
        
        DATE: {
            convert: function(v) {
                var df = this.getDateReadFormat() || this.getDateFormat(),
                    parsed;
                if (!v) {
                    return null;
                }
                
                if (v instanceof Date) {
                    return v;
                }
                if (df) {
                    return Ext.Date.parse(v, df);
                }
                parsed = Date.parse(v);
                return parsed ? new Date(parsed) : null;
            },
            sortType: SortTypes.asDate,
            type: 'date'
        }
    });
    
    Types.BOOLEAN = Types.BOOL;
    
    Types.INTEGER = Types.INT;
    
    Types.NUMBER = Types.FLOAT;
});


Ext.define('Ext.data.Validation', {
    extend: 'Ext.data.Model',
    isValidation: true,
    
    syncGeneration: 0,
    
    
    attach: function(record) {
        
        this.record = record;
        
        
        delete this.data.id;
    },
    getValidation: function() {
        return null;
    },
    
    isValid: function() {
        var me = this;
        if (me.syncGeneration !== me.record.generation) {
            me.refresh();
        }
        return me.dirty;
    },
    
    refresh: function(force) {
        var me = this,
            data = me.data,
            record = me.record,
            fields = record.fields,
            generation = record.generation,
            recordData = record.data,
            sep = record.validationSeparator,
            values = null,
            defaultMessage, currentValue, error, field, item, i, j, jLen, len, msg, val, name;
        if (force || me.syncGeneration !== generation) {
            me.syncGeneration = generation;
            for (i = 0 , len = fields.length; i < len; ++i) {
                field = fields[i];
                name = field.name;
                val = recordData[name];
                defaultMessage = field.defaultInvalidMessage;
                error = 0;
                if (!(name in data)) {
                    
                    
                    
                    data[name] = currentValue = true;
                } else 
                {
                    currentValue = data[name];
                }
                if (field.validate !== Ext.emptyFn) {
                    msg = field.validate(val, sep);
                    if (msg !== true) {
                        error = msg || defaultMessage;
                    }
                }
                if (!error) {
                    error = true;
                }
                
                if (error !== currentValue) {
                    (values || (values = {}))[name] = error;
                }
            }
            if (values) {
                
                me.set(values);
            }
        }
    }
});


Ext.define('Ext.dom.Helper', function() {
    var afterbegin = 'afterbegin',
        afterend = 'afterend',
        beforebegin = 'beforebegin',
        beforeend = 'beforeend',
        bbValues = [
            'BeforeBegin',
            'previousSibling'
        ],
        aeValues = [
            'AfterEnd',
            'nextSibling'
        ],
        bb_ae_PositionHash = {
            beforebegin: bbValues,
            afterend: aeValues
        },
        fullPositionHash = {
            beforebegin: bbValues,
            afterend: aeValues,
            afterbegin: [
                'AfterBegin',
                'firstChild'
            ],
            beforeend: [
                'BeforeEnd',
                'lastChild'
            ]
        };
    return {
        singleton: true,
        alternateClassName: [
            'Ext.DomHelper',
            'Ext.core.DomHelper'
        ],
        emptyTags: /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
        confRe: /^(?:tag|children|cn|html|tpl|tplData)$/i,
        endRe: /end/i,
        
        attributeTransform: {
            cls: 'class',
            htmlFor: 'for'
        },
        closeTags: {},
        detachedDiv: document.createElement('div'),
        decamelizeName: function() {
            var camelCaseRe = /([a-z])([A-Z])/g,
                cache = {};
            function decamel(match, p1, p2) {
                return p1 + '-' + p2.toLowerCase();
            }
            return function(s) {
                return cache[s] || (cache[s] = s.replace(camelCaseRe, decamel));
            };
        }(),
        generateMarkup: function(spec, buffer) {
            var me = this,
                specType = typeof spec,
                attr, val, tag, i, closeTags;
            if (specType === "string" || specType === "number") {
                buffer.push(spec);
            } else if (Ext.isArray(spec)) {
                for (i = 0; i < spec.length; i++) {
                    if (spec[i]) {
                        me.generateMarkup(spec[i], buffer);
                    }
                }
            } else {
                tag = spec.tag || 'div';
                buffer.push('<', tag);
                for (attr in spec) {
                    if (spec.hasOwnProperty(attr)) {
                        val = spec[attr];
                        if (val !== undefined && !me.confRe.test(attr)) {
                            if (typeof val === "object") {
                                buffer.push(' ', attr, '="');
                                me.generateStyles(val, buffer, true).push('"');
                            } else {
                                buffer.push(' ', me.attributeTransform[attr] || attr, '="', val, '"');
                            }
                        }
                    }
                }
                
                if (me.emptyTags.test(tag)) {
                    buffer.push('/>');
                } else {
                    buffer.push('>');
                    
                    if ((val = spec.tpl)) {
                        val.applyOut(spec.tplData, buffer);
                    }
                    if ((val = spec.html)) {
                        buffer.push(val);
                    }
                    if ((val = spec.cn || spec.children)) {
                        me.generateMarkup(val, buffer);
                    }
                    
                    closeTags = me.closeTags;
                    buffer.push(closeTags[tag] || (closeTags[tag] = '</' + tag + '>'));
                }
            }
            return buffer;
        },
        
        generateStyles: function(styles, buffer, encode) {
            var a = buffer || [],
                name, val;
            for (name in styles) {
                if (styles.hasOwnProperty(name)) {
                    val = styles[name];
                    
                    
                    
                    name = this.decamelizeName(name);
                    if (encode && Ext.String.hasHtmlCharacters(val)) {
                        val = Ext.String.htmlEncode(val);
                    }
                    a.push(name, ':', val, ';');
                }
            }
            return buffer || a.join('');
        },
        
        markup: function(spec) {
            if (typeof spec === "string") {
                return spec;
            }
            var buf = this.generateMarkup(spec, []);
            return buf.join('');
        },
        
        applyStyles: function(el, styles) {
            Ext.fly(el).applyStyles(styles);
        },
        
        createContextualFragment: function(html) {
            var div = this.detachedDiv,
                fragment = document.createDocumentFragment(),
                length, childNodes;
            div.innerHTML = html;
            childNodes = div.childNodes;
            length = childNodes.length;
            
            while (length--) {
                fragment.appendChild(childNodes[0]);
            }
            return fragment;
        },
        
        createDom: function(o, parentNode) {
            var me = this,
                markup = me.markup(o),
                div = me.detachedDiv,
                child;
            div.innerHTML = markup;
            child = div.firstChild;
            
            
            
            
            
            
            
            
            
            return Ext.supports.ChildContentClearedWhenSettingInnerHTML ? child.cloneNode(true) : child;
        },
        
        insertHtml: function(where, el, html) {
            var me = this,
                hashVal, range, rangeEl, setStart, frag;
            where = where.toLowerCase();
            
            if (el.insertAdjacentHTML) {
                if (me.ieInsertHtml) {
                    
                    frag = me.ieInsertHtml(where, el, html);
                    if (frag) {
                        return frag;
                    }
                }
                hashVal = fullPositionHash[where];
                if (hashVal) {
                    el.insertAdjacentHTML(hashVal[0], html);
                    return el[hashVal[1]];
                }
            } else 
            {
                
                if (el.nodeType === 3) {
                    where = where === afterbegin ? beforebegin : where;
                    where = where === beforeend ? afterend : where;
                }
                range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
                setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before');
                if (bb_ae_PositionHash[where]) {
                    if (range) {
                        range[setStart](el);
                        frag = range.createContextualFragment(html);
                    } else {
                        frag = this.createContextualFragment(html);
                    }
                    el.parentNode.insertBefore(frag, where === beforebegin ? el : el.nextSibling);
                    return el[(where === beforebegin ? 'previous' : 'next') + 'Sibling'];
                } else {
                    rangeEl = (where === afterbegin ? 'first' : 'last') + 'Child';
                    if (el.firstChild) {
                        if (range) {
                            
                            
                            
                            try {
                                range[setStart](el[rangeEl]);
                                frag = range.createContextualFragment(html);
                            } catch (e) {
                                frag = this.createContextualFragment(html);
                            }
                        } else {
                            frag = this.createContextualFragment(html);
                        }
                        if (where === afterbegin) {
                            el.insertBefore(frag, el.firstChild);
                        } else {
                            el.appendChild(frag);
                        }
                    } else {
                        el.innerHTML = html;
                    }
                    return el[rangeEl];
                }
            }
            Ext.Error.raise({
                sourceClass: 'Ext.DomHelper',
                sourceMethod: 'insertHtml',
                htmlToInsert: html,
                targetElement: el,
                msg: 'Illegal insertion point reached: "' + where + '"'
            });
        },
        
        insertBefore: function(el, o, returnElement) {
            return this.doInsert(el, o, returnElement, beforebegin);
        },
        
        insertAfter: function(el, o, returnElement) {
            return this.doInsert(el, o, returnElement, afterend);
        },
        
        insertFirst: function(el, o, returnElement) {
            return this.doInsert(el, o, returnElement, afterbegin);
        },
        
        append: function(el, o, returnElement) {
            return this.doInsert(el, o, returnElement, beforeend);
        },
        
        overwrite: function(el, html, returnElement) {
            var me = this,
                newNode;
            el = Ext.getDom(el);
            html = me.markup(html);
            if (me.ieOverwrite) {
                
                newNode = me.ieOverwrite(el, html);
            }
            if (!newNode) {
                el.innerHTML = html;
                newNode = el.firstChild;
            }
            return returnElement ? Ext.get(newNode) : newNode;
        },
        doInsert: function(el, o, returnElement, where) {
            var me = this,
                newNode;
            el = el.dom || Ext.getDom(el);
            if ('innerHTML' in el) {
                
                
                
                
                newNode = me.insertHtml(where, el, me.markup(o));
            } else {
                
                newNode = me.createDom(o, null);
                
                if (el.nodeType === 3) {
                    where = where === afterbegin ? beforebegin : where;
                    where = where === beforeend ? afterend : where;
                }
                if (bb_ae_PositionHash[where]) {
                    el.parentNode.insertBefore(newNode, where === beforebegin ? el : el.nextSibling);
                } else if (el.firstChild && where === afterbegin) {
                    el.insertBefore(newNode, el.firstChild);
                } else {
                    el.appendChild(newNode);
                }
            }
            return returnElement ? Ext.get(newNode) : newNode;
        },
        
        createTemplate: function(o) {
            var html = this.markup(o);
            return new Ext.Template(html);
        },
        
        createHtml: function(spec) {
            return this.markup(spec);
        }
    };
});

Ext.define('Ext.overrides.dom.Helper', (function() {
    var tableRe = /^(?:table|thead|tbody|tr|td)$/i,
        tableElRe = /td|tr|tbody|thead/i,
        ts = '<table>',
        te = '</table>',
        tbs = ts + '<tbody>',
        tbe = '</tbody>' + te,
        trs = tbs + '<tr>',
        tre = '</tr>' + tbe;
    return {
        override: 'Ext.dom.Helper',
        ieInsertHtml: function(where, el, html) {
            var frag = null;
            
            if (Ext.isIE9m && tableRe.test(el.tagName)) {
                frag = this.insertIntoTable(el.tagName.toLowerCase(), where, el, html);
            }
            return frag;
        },
        ieOverwrite: function(el, html) {
            
            
            if (Ext.isIE9m && tableRe.test(el.tagName)) {
                
                while (el.firstChild) {
                    el.removeChild(el.firstChild);
                }
                if (html) {
                    return this.insertHtml('afterbegin', el, html);
                }
            }
        },
        ieTable: function(depth, openingTags, htmlContent, closingTags) {
            var i = -1,
                el = this.detachedDiv,
                ns, nx;
            el.innerHTML = [
                openingTags,
                htmlContent,
                closingTags
            ].join('');
            while (++i < depth) {
                el = el.firstChild;
            }
            
            ns = el.nextSibling;
            if (ns) {
                ns = el;
                el = document.createDocumentFragment();
                while (ns) {
                    nx = ns.nextSibling;
                    el.appendChild(ns);
                    ns = nx;
                }
            }
            return el;
        },
        
        insertIntoTable: function(tag, where, destinationEl, html) {
            var node, before,
                bb = where === 'beforebegin',
                ab = where === 'afterbegin',
                be = where === 'beforeend',
                ae = where === 'afterend';
            if (tag === 'td' && (ab || be) || !tableElRe.test(tag) && (bb || ae)) {
                return null;
            }
            before = bb ? destinationEl : ae ? destinationEl.nextSibling : ab ? destinationEl.firstChild : null;
            if (bb || ae) {
                destinationEl = destinationEl.parentNode;
            }
            if (tag === 'td' || (tag === 'tr' && (be || ab))) {
                node = this.ieTable(4, trs, html, tre);
            } else if (((tag === 'tbody' || tag === 'thead') && (be || ab)) || (tag === 'tr' && (bb || ae))) {
                node = this.ieTable(3, tbs, html, tbe);
            } else {
                node = this.ieTable(2, ts, html, te);
            }
            destinationEl.insertBefore(node, before);
            return node;
        }
    };
})());


Ext.define('Ext.dom.Query', function() {
    var DQ,
        doc = document,
        cache, simpleCache, valueCache,
        useClassList = !!doc.documentElement.classList,
        useElementPointer = !!doc.documentElement.firstElementChild,
        useChildrenCollection = (function() {
            var d = doc.createElement('div');
            d.innerHTML = '<!-- -->text<!-- -->';
            return d.children && (d.children.length === 0);
        })(),
        nonSpace = /\S/,
        trimRe = /^\s+|\s+$/g,
        tplRe = /\{(\d+)\}/g,
        modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
        tagTokenRe = /^(#)?([\w\-\*\|\\]+)/,
        nthRe = /(\d*)n\+?(\d*)/,
        nthRe2 = /\D/,
        startIdRe = /^\s*#/,
        
        
        
        isIE = window.ActiveXObject ? true : false,
        key = 30803,
        longHex = /\\([0-9a-fA-F]{6})/g,
        shortHex = /\\([0-9a-fA-F]{1,6})\s{0,1}/g,
        nonHex = /\\([^0-9a-fA-F]{1})/g,
        escapes = /\\/g,
        num, hasEscapes,
        
        
        supportsColonNsSeparator = (function() {
            var xmlDoc,
                xmlString = '<r><a:b xmlns:a="n"></a:b></r>';
            if (window.DOMParser) {
                xmlDoc = (new DOMParser()).parseFromString(xmlString, "application/xml");
            } else {
                xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
                xmlDoc.loadXML(xmlString);
            }
            return !!xmlDoc.getElementsByTagName('a:b').length;
        })(),
        
        
        longHexToChar = function($0, $1) {
            return String.fromCharCode(parseInt($1, 16));
        },
        
        shortToLongHex = function($0, $1) {
            while ($1.length < 6) {
                $1 = '0' + $1;
            }
            return '\\' + $1;
        },
        
        charToLongHex = function($0, $1) {
            num = $1.charCodeAt(0).toString(16);
            if (num.length === 1) {
                num = '0' + num;
            }
            return '\\0000' + num;
        },
        
        
        
        unescapeCssSelector = function(selector) {
            return (hasEscapes) ? selector.replace(longHex, longHexToChar) : selector;
        },
        
        setupEscapes = function(path) {
            hasEscapes = (path.indexOf('\\') > -1);
            if (hasEscapes) {
                path = path.replace(shortHex, shortToLongHex).replace(nonHex, charToLongHex).replace(escapes, '\\\\');
            }
            
            return path;
        };
    
    
    eval("var batch = 30803, child, next, prev, byClassName;");
    
    
    child = useChildrenCollection ? function child(parent, index) {
        return parent.children[index];
    } : function child(parent, index) {
        var i = 0,
            n = parent.firstChild;
        while (n) {
            if (n.nodeType == 1) {
                if (++i == index) {
                    return n;
                }
            }
            n = n.nextSibling;
        }
        return null;
    };
    
    next = useElementPointer ? function(n) {
        return n.nextElementSibling;
    } : function(n) {
        while ((n = n.nextSibling) && n.nodeType != 1){}
        return n;
    };
    
    prev = useElementPointer ? function(n) {
        return n.previousElementSibling;
    } : function(n) {
        while ((n = n.previousSibling) && n.nodeType != 1){}
        return n;
    };
    
    
    function children(parent) {
        var n = parent.firstChild,
            nodeIndex = -1,
            nextNode;
        while (n) {
            nextNode = n.nextSibling;
            
            if (n.nodeType == 3 && !nonSpace.test(n.nodeValue)) {
                parent.removeChild(n);
            } else {
                
                n.nodeIndex = ++nodeIndex;
            }
            n = nextNode;
        }
        return this;
    }
    
    
    byClassName = useClassList ? 
    function(nodeSet, cls) {
        cls = unescapeCssSelector(cls);
        if (!cls) {
            return nodeSet;
        }
        var result = [],
            ri = -1,
            i, ci, classList;
        for (i = 0; ci = nodeSet[i]; i++) {
            classList = ci.classList;
            if (classList) {
                if (classList.contains(cls)) {
                    result[++ri] = ci;
                }
            } else if ((' ' + ci.className + ' ').indexOf(cls) !== -1) {
                
                
                result[++ri] = ci;
            }
        }
        return result;
    } : function(nodeSet, cls) {
        cls = unescapeCssSelector(cls);
        if (!cls) {
            return nodeSet;
        }
        var result = [],
            ri = -1,
            i, ci;
        for (i = 0; ci = nodeSet[i]; i++) {
            if ((' ' + ci.className + ' ').indexOf(cls) !== -1) {
                result[++ri] = ci;
            }
        }
        return result;
    };
    function attrValue(n, attr) {
        
        if (!n.tagName && typeof n.length != "undefined") {
            n = n[0];
        }
        if (!n) {
            return null;
        }
        if (attr == "for") {
            return n.htmlFor;
        }
        if (attr == "class" || attr == "className") {
            return n.className;
        }
        return n.getAttribute(attr) || n[attr];
    }
    
    
    
    function getNodes(ns, mode, tagName) {
        var result = [],
            ri = -1,
            cs, i, ni, j, ci, cn, utag, n, cj;
        if (!ns) {
            return result;
        }
        tagName = tagName.replace('|', ':') || "*";
        
        if (typeof ns.getElementsByTagName != "undefined") {
            ns = [
                ns
            ];
        }
        
        
        if (!mode) {
            tagName = unescapeCssSelector(tagName);
            if (!supportsColonNsSeparator && DQ.isXml(ns[0]) && tagName.indexOf(':') !== -1) {
                
                
                
                
                
                
                for (i = 0; ni = ns[i]; i++) {
                    cs = ni.getElementsByTagName(tagName.split(':').pop());
                    for (j = 0; ci = cs[j]; j++) {
                        if (ci.tagName === tagName) {
                            result[++ri] = ci;
                        }
                    }
                }
            } else {
                for (i = 0; ni = ns[i]; i++) {
                    cs = ni.getElementsByTagName(tagName);
                    for (j = 0; ci = cs[j]; j++) {
                        result[++ri] = ci;
                    }
                }
            }
        }
        
        
        else if (mode == "/" || mode == ">") {
            utag = tagName.toUpperCase();
            for (i = 0; ni = ns[i]; i++) {
                cn = ni.childNodes;
                for (j = 0; cj = cn[j]; j++) {
                    if (cj.nodeName == utag || cj.nodeName == tagName || tagName == '*') {
                        result[++ri] = cj;
                    }
                }
            }
        }
        
        
        else if (mode == "+") {
            utag = tagName.toUpperCase();
            for (i = 0; n = ns[i]; i++) {
                while ((n = n.nextSibling) && n.nodeType != 1){}
                if (n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')) {
                    result[++ri] = n;
                }
            }
        }
        
        
        else if (mode == "~") {
            utag = tagName.toUpperCase();
            for (i = 0; n = ns[i]; i++) {
                while ((n = n.nextSibling)) {
                    if (n.nodeName == utag || n.nodeName == tagName || tagName == '*') {
                        result[++ri] = n;
                    }
                }
            }
        }
        return result;
    }
    function concat(a, b) {
        a.push.apply(a, b);
        return a;
    }
    function byTag(cs, tagName) {
        if (cs.tagName || cs === doc) {
            cs = [
                cs
            ];
        }
        if (!tagName) {
            return cs;
        }
        var result = [],
            ri = -1,
            i, ci;
        tagName = tagName.toLowerCase();
        for (i = 0; ci = cs[i]; i++) {
            if (ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName) {
                result[++ri] = ci;
            }
        }
        return result;
    }
    function byId(cs, id) {
        id = unescapeCssSelector(id);
        if (cs.tagName || cs === doc) {
            cs = [
                cs
            ];
        }
        if (!id) {
            return cs;
        }
        var result = [],
            ri = -1,
            i, ci;
        for (i = 0; ci = cs[i]; i++) {
            if (ci && ci.id == id) {
                result[++ri] = ci;
                return result;
            }
        }
        return result;
    }
    
    
    function byAttribute(cs, attr, value, op, custom) {
        var result = [],
            ri = -1,
            useGetStyle = custom == "{",
            fn = DQ.operators[op],
            a, xml, hasXml, i, ci;
        value = unescapeCssSelector(value);
        for (i = 0; ci = cs[i]; i++) {
            
            if (ci.nodeType === 1) {
                
                if (!hasXml) {
                    xml = DQ.isXml(ci);
                    hasXml = true;
                }
                
                if (!xml) {
                    if (useGetStyle) {
                        a = DQ.getStyle(ci, attr);
                    } else if (attr == "class" || attr == "className") {
                        a = ci.className;
                    } else if (attr == "for") {
                        a = ci.htmlFor;
                    } else if (attr == "href") {
                        
                        
                        a = ci.getAttribute("href", 2);
                    } else {
                        a = ci.getAttribute(attr);
                    }
                } else {
                    a = ci.getAttribute(attr);
                }
                if ((fn && fn(a, value)) || (!fn && a)) {
                    result[++ri] = ci;
                }
            }
        }
        return result;
    }
    function byPseudo(cs, name, value) {
        value = unescapeCssSelector(value);
        return DQ.pseudos[name](cs, value);
    }
    function nodupIEXml(cs) {
        var d = ++key,
            r, i, len, c;
        cs[0].setAttribute("_nodup", d);
        r = [
            cs[0]
        ];
        for (i = 1 , len = cs.length; i < len; i++) {
            c = cs[i];
            if (!c.getAttribute("_nodup") != d) {
                c.setAttribute("_nodup", d);
                r[r.length] = c;
            }
        }
        for (i = 0 , len = cs.length; i < len; i++) {
            cs[i].removeAttribute("_nodup");
        }
        return r;
    }
    function nodup(cs) {
        if (!cs) {
            return [];
        }
        var len = cs.length,
            c, i,
            r = cs,
            cj,
            ri = -1,
            d, j;
        if (!len || typeof cs.nodeType != "undefined" || len == 1) {
            return cs;
        }
        if (isIE && typeof cs[0].selectSingleNode != "undefined") {
            return nodupIEXml(cs);
        }
        d = ++key;
        cs[0]._nodup = d;
        for (i = 1; c = cs[i]; i++) {
            if (c._nodup != d) {
                c._nodup = d;
            } else {
                r = [];
                for (j = 0; j < i; j++) {
                    r[++ri] = cs[j];
                }
                for (j = i + 1; cj = cs[j]; j++) {
                    if (cj._nodup != d) {
                        cj._nodup = d;
                        r[++ri] = cj;
                    }
                }
                return r;
            }
        }
        return r;
    }
    function quickDiffIEXml(c1, c2) {
        var d = ++key,
            r = [],
            i, len;
        for (i = 0 , len = c1.length; i < len; i++) {
            c1[i].setAttribute("_qdiff", d);
        }
        for (i = 0 , len = c2.length; i < len; i++) {
            if (c2[i].getAttribute("_qdiff") != d) {
                r[r.length] = c2[i];
            }
        }
        for (i = 0 , len = c1.length; i < len; i++) {
            c1[i].removeAttribute("_qdiff");
        }
        return r;
    }
    function quickDiff(c1, c2) {
        var len1 = c1.length,
            d = ++key,
            r = [],
            i, len;
        if (!len1) {
            return c2;
        }
        if (isIE && typeof c1[0].selectSingleNode != "undefined") {
            return quickDiffIEXml(c1, c2);
        }
        for (i = 0; i < len1; i++) {
            c1[i]._qdiff = d;
        }
        for (i = 0 , len = c2.length; i < len; i++) {
            if (c2[i]._qdiff != d) {
                r[r.length] = c2[i];
            }
        }
        return r;
    }
    function quickId(ns, mode, root, id) {
        if (ns == root) {
            id = unescapeCssSelector(id);
            var d = root.ownerDocument || root;
            return d.getElementById(id);
        }
        ns = getNodes(ns, mode, "*");
        return byId(ns, id);
    }
    return {
        singleton: true,
        alternateClassName: [
            'Ext.core.DomQuery',
            'Ext.DomQuery'
        ],
        requires: [
            'Ext.dom.Helper',
            'Ext.util.Operators'
        ],
        _init: function() {
            DQ = this;
            DQ.operators = Ext.Object.chain(Ext.util.Operators);
            
            DQ._cache = cache = new Ext.util.LruCache({
                maxSize: 200
            });
            DQ._valueCache = valueCache = new Ext.util.LruCache({
                maxSize: 200
            });
            DQ._simpleCache = simpleCache = new Ext.util.LruCache({
                maxSize: 200
            });
        },
        clearCache: function() {
            cache.clear();
            valueCache.clear();
            simpleCache.clear();
        },
        getStyle: function(el, name) {
            return Ext.fly(el, '_DomQuery').getStyle(name);
        },
        
        compile: function(path, type) {
            type = type || "select";
            
            var fn = [
                    "var f = function(root) {\n var mode; ++batch; var n = root || document;\n"
                ],
                lastPath,
                matchers = DQ.matchers,
                matchersLn = matchers.length,
                modeMatch,
                
                lmode = path.match(modeRe),
                tokenMatch, matched, j, t, m;
            path = setupEscapes(path);
            if (lmode && lmode[1]) {
                fn[fn.length] = 'mode="' + lmode[1].replace(trimRe, "") + '";';
                path = path.replace(lmode[1], "");
            }
            
            while (path.substr(0, 1) == "/") {
                path = path.substr(1);
            }
            while (path && lastPath != path) {
                lastPath = path;
                tokenMatch = path.match(tagTokenRe);
                if (type == "select") {
                    if (tokenMatch) {
                        
                        if (tokenMatch[1] == "#") {
                            fn[fn.length] = 'n = quickId(n, mode, root, "' + tokenMatch[2] + '");';
                        } else {
                            fn[fn.length] = 'n = getNodes(n, mode, "' + tokenMatch[2] + '");';
                        }
                        path = path.replace(tokenMatch[0], "");
                    } else if (path.substr(0, 1) != '@') {
                        fn[fn.length] = 'n = getNodes(n, mode, "*");';
                    }
                } else 
                {
                    if (tokenMatch) {
                        if (tokenMatch[1] == "#") {
                            fn[fn.length] = 'n = byId(n, "' + tokenMatch[2] + '");';
                        } else {
                            fn[fn.length] = 'n = byTag(n, "' + tokenMatch[2] + '");';
                        }
                        path = path.replace(tokenMatch[0], "");
                    }
                }
                while (!(modeMatch = path.match(modeRe))) {
                    matched = false;
                    for (j = 0; j < matchersLn; j++) {
                        t = matchers[j];
                        m = path.match(t.re);
                        if (m) {
                            fn[fn.length] = t.select.replace(tplRe, function(x, i) {
                                return m[i];
                            });
                            path = path.replace(m[0], "");
                            matched = true;
                            break;
                        }
                    }
                    
                    if (!matched) {
                        Ext.Error.raise({
                            sourceClass: 'Ext.DomQuery',
                            sourceMethod: 'compile',
                            msg: 'Error parsing selector. Parsing failed at "' + path + '"'
                        });
                    }
                }
                if (modeMatch[1]) {
                    fn[fn.length] = 'mode="' + modeMatch[1].replace(trimRe, "") + '";';
                    path = path.replace(modeMatch[1], "");
                }
            }
            
            fn[fn.length] = "return nodup(n);\n}";
            
            eval(fn.join(""));
            return f;
        },
        
        jsSelect: function(path, root, type) {
            
            root = root || doc;
            if (typeof root == "string") {
                root = doc.getElementById(root);
            }
            var paths = Ext.splitAndUnescape(path, ","),
                results = [],
                query, i, len, subPath, result;
            
            for (i = 0 , len = paths.length; i < len; i++) {
                subPath = paths[i].replace(trimRe, "");
                
                query = cache.get(subPath);
                if (!query) {
                    
                    query = DQ.compile(subPath, type);
                    if (!query) {
                        Ext.Error.raise({
                            sourceClass: 'Ext.DomQuery',
                            sourceMethod: 'jsSelect',
                            msg: subPath + ' is not a valid selector'
                        });
                    }
                    cache.add(subPath, query);
                } else {
                    
                    
                    setupEscapes(subPath);
                }
                result = query(root);
                if (result && result !== doc) {
                    results = results.concat(result);
                }
            }
            
            
            if (paths.length > 1) {
                return nodup(results);
            }
            return results;
        },
        isXml: function(el) {
            var docEl = (el ? el.ownerDocument || el : 0).documentElement;
            return docEl ? docEl.nodeName !== "HTML" : false;
        },
        
        select: doc.querySelectorAll ? function(path, root, type, single) {
            root = root || doc;
            if (!DQ.isXml(root)) {
                try {
                    
                    if (root.parentNode && (root.nodeType !== 9) && path.indexOf(',') === -1 && !startIdRe.test(path)) {
                        path = Ext.makeIdSelector(Ext.id(root)) + ' ' + path;
                        root = root.parentNode;
                    }
                    return single ? [
                        root.querySelector(path)
                    ] : Ext.Array.toArray(root.querySelectorAll(path));
                } catch (e) {}
            }
            return DQ.jsSelect.call(this, path, root, type);
        } : function(path, root, type) {
            return DQ.jsSelect.call(this, path, root, type);
        },
        
        selectNode: function(path, root) {
            return Ext.DomQuery.select(path, root, null, true)[0];
        },
        
        selectValue: function(path, root, defaultValue) {
            path = path.replace(trimRe, "");
            var query = valueCache.get(path),
                n, v;
            if (!query) {
                query = DQ.compile(path, "select");
                valueCache.add(path, query);
            } else {
                setupEscapes(path);
            }
            n = query(root);
            return DQ.getNodeValue(n[0] ? n[0] : n);
        },
        
        getNodeValue: function(node, defaultValue) {
            
            
            
            
            if (typeof node.normalize == 'function') {
                node.normalize();
            }
            var v = (node && node.firstChild ? node.firstChild.nodeValue : null);
            return ((v === null || v === undefined || v === '') ? defaultValue : v);
        },
        
        selectNumber: function(path, root, defaultValue) {
            var v = DQ.selectValue(path, root, defaultValue || 0);
            return parseFloat(v);
        },
        
        is: function(el, ss) {
            if (typeof el == "string") {
                el = doc.getElementById(el);
            }
            var isArray = Ext.isArray(el),
                result = DQ.filter(isArray ? el : [
                    el
                ], ss);
            return isArray ? (result.length == el.length) : (result.length > 0);
        },
        
        filter: function(els, ss, nonMatches) {
            ss = ss.replace(trimRe, "");
            var query = simpleCache.get(ss),
                result;
            if (!query) {
                query = DQ.compile(ss, "simple");
                simpleCache.add(ss, query);
            } else {
                setupEscapes(ss);
            }
            result = query(els);
            return nonMatches ? quickDiff(result, els) : result;
        },
        
        matchers: [
            {
                re: /^\.([\w\-\\]+)/,
                select: useClassList ? 'n = byClassName(n, "{1}");' : 'n = byClassName(n, " {1} ");'
            },
            {
                re: /^\:([\w\-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
                select: 'n = byPseudo(n, "{1}", "{2}");'
            },
            {
                re: /^(?:([\[\{])(?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
                select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
            },
            {
                re: /^#([\w\-\\]+)/,
                select: 'n = byId(n, "{1}");'
            },
            {
                re: /^@([\w\-\.]+)/,
                select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
            }
        ],
        
        
        pseudos: {
            "first-child": function(c) {
                var r = [],
                    ri = -1,
                    n, i, ci;
                for (i = 0; (ci = n = c[i]); i++) {
                    while ((n = n.previousSibling) && n.nodeType != 1){}
                    if (!n) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            "last-child": function(c) {
                var r = [],
                    ri = -1,
                    n, i, ci;
                for (i = 0; (ci = n = c[i]); i++) {
                    while ((n = n.nextSibling) && n.nodeType != 1){}
                    if (!n) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            "nth-child": function(c, a) {
                var r = [],
                    ri = -1,
                    m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
                    f = (m[1] || 1) - 0,
                    l = m[2] - 0,
                    i, n, j, cn, pn;
                for (i = 0; n = c[i]; i++) {
                    pn = n.parentNode;
                    if (batch != pn._batch) {
                        j = 0;
                        for (cn = pn.firstChild; cn; cn = cn.nextSibling) {
                            if (cn.nodeType == 1) {
                                cn.nodeIndex = ++j;
                            }
                        }
                        pn._batch = batch;
                    }
                    if (f == 1) {
                        if (l === 0 || n.nodeIndex == l) {
                            r[++ri] = n;
                        }
                    } else if ((n.nodeIndex + l) % f === 0) {
                        r[++ri] = n;
                    }
                }
                return r;
            },
            "only-child": function(c) {
                var r = [],
                    ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if (!prev(ci) && !next(ci)) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            "empty": function(c) {
                var r = [],
                    ri = -1,
                    i, ci, cns, j, cn, empty;
                for (i = 0; ci = c[i]; i++) {
                    cns = ci.childNodes;
                    j = 0;
                    empty = true;
                    while (cn = cns[j]) {
                        ++j;
                        if (cn.nodeType == 1 || cn.nodeType == 3) {
                            empty = false;
                            break;
                        }
                    }
                    if (empty) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            "contains": function(c, v) {
                var r = [],
                    ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if ((ci.textContent || ci.innerText || ci.text || '').indexOf(v) != -1) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            "nodeValue": function(c, v) {
                var r = [],
                    ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if (ci.firstChild && ci.firstChild.nodeValue == v) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            "checked": function(c) {
                var r = [],
                    ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if (ci.checked === true) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            "not": function(c, ss) {
                return DQ.filter(c, ss, true);
            },
            "any": function(c, selectors) {
                var ss = selectors.split('|'),
                    r = [],
                    ri = -1,
                    s, i, ci, j;
                for (i = 0; ci = c[i]; i++) {
                    for (j = 0; s = ss[j]; j++) {
                        if (DQ.is(ci, s)) {
                            r[++ri] = ci;
                            break;
                        }
                    }
                }
                return r;
            },
            "odd": function(c) {
                return this["nth-child"](c, "odd");
            },
            "even": function(c) {
                return this["nth-child"](c, "even");
            },
            "nth": function(c, a) {
                return c[a - 1] || [];
            },
            "first": function(c) {
                return c[0] || [];
            },
            "last": function(c) {
                return c[c.length - 1] || [];
            },
            "has": function(c, ss) {
                var s = DQ.select,
                    r = [],
                    ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if (s(ss, ci).length > 0) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            "next": function(c, ss) {
                var is = DQ.is,
                    r = [],
                    ri = -1,
                    i, ci, n;
                for (i = 0; ci = c[i]; i++) {
                    n = next(ci);
                    if (n && is(n, ss)) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            "prev": function(c, ss) {
                var is = DQ.is,
                    r = [],
                    ri = -1,
                    i, ci, n;
                for (i = 0; ci = c[i]; i++) {
                    n = prev(ci);
                    if (n && is(n, ss)) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },
            focusable: function(candidates) {
                var len = candidates.length,
                    results = [],
                    i = 0,
                    c;
                for (; i < len; i++) {
                    c = candidates[i];
                    if (Ext.fly(c, '_DomQuery').isFocusable()) {
                        results.push(c);
                    }
                }
                return results;
            },
            visible: function(candidates, deep) {
                var len = candidates.length,
                    results = [],
                    i = 0,
                    c;
                for (; i < len; i++) {
                    c = candidates[i];
                    if (Ext.fly(c, '_DomQuery').isVisible(deep)) {
                        results.push(c);
                    }
                }
                return results;
            },
            isScrolled: function(c) {
                var r = [],
                    ri = -1,
                    i, ci, s;
                for (i = 0; ci = c[i]; i++) {
                    s = Ext.fly(ci, '_DomQuery').getScroll();
                    if (s.top > 0 || s.left > 0) {
                        r[++ri] = ci;
                    }
                }
                return r;
            }
        }
    };
}, function() {
    this._init();
});


Ext.define('Ext.data.reader.Xml', {
    extend: 'Ext.data.reader.Reader',
    requires: [
        'Ext.dom.Query'
    ],
    alternateClassName: 'Ext.data.XmlReader',
    alias: 'reader.xml',
    config: {
        
        record: '',
        
        namespace: ''
    },
    
    createAccessor: function(expr) {
        var me = this;
        if (Ext.isEmpty(expr)) {
            return Ext.emptyFn;
        }
        if (Ext.isFunction(expr)) {
            return expr;
        }
        return function(root) {
            return me.getNodeValue(Ext.DomQuery.selectNode(expr, root));
        };
    },
    getNodeValue: function(node) {
        if (node) {
            
            
            
            if (typeof node.normalize === 'function') {
                node.normalize();
            }
            node = node.firstChild;
            if (node) {
                return node.nodeValue;
            }
        }
        return undefined;
    },
    
    getResponseData: function(response) {
        var xml = response.responseXML,
            error = 'XML data not found in the response';
        if (!xml) {
            Ext.Logger.warn(error);
            return this.createReadError(error);
        }
        return xml;
    },
    
    getData: function(data) {
        return data.documentElement || data;
    },
    
    getRoot: function(data) {
        var nodeName = data.nodeName,
            root = this.getRootProperty();
        if (!root || (nodeName && nodeName == root)) {
            return data;
        } else if (Ext.DomQuery.isXml(data)) {
            
            
            
            return Ext.DomQuery.selectNode(root, data);
        }
    },
    
    extractData: function(root, readOptions) {
        var recordName = this.getRecord();
        if (!recordName) {
            Ext.Error.raise('Record is a required parameter');
        }
        if (recordName !== root.nodeName) {
            root = Ext.DomQuery.select(recordName, root);
        } else {
            root = [
                root
            ];
        }
        return this.callParent([
            root,
            readOptions
        ]);
    },
    
    readRecords: function(doc, readOptions) {
        
        
        if (Ext.isArray(doc)) {
            doc = doc[0];
        }
        
        this.xmlData = doc;
        return this.callParent([
            doc,
            readOptions
        ]);
    },
    
    createFieldAccessor: function(field) {
        var me = this,
            namespace = me.getNamespace(),
            selector, result;
        selector = field.mapping || ((namespace ? namespace + '|' : '') + field.name);
        if (typeof selector === 'function') {
            result = function(raw) {
                return field.mapping(raw, me);
            };
        } else {
            result = function(raw) {
                return me.getNodeValue(Ext.DomQuery.selectNode(selector, raw));
            };
        }
        return result;
    }
});


Ext.define('Ext.data.writer.Xml', {
    
    extend: 'Ext.data.writer.Writer',
    alternateClassName: 'Ext.data.XmlWriter',
    alias: 'writer.xml',
    
    config: {
        
        documentRoot: 'xmlData',
        
        defaultDocumentRoot: 'xmlData',
        
        header: '',
        
        record: 'record'
    },
    
    selectorRe: /[^>\s]+/g,
    
    writeRecords: function(request, data) {
        var me = this,
            xml = [],
            i = 0,
            len = data.length,
            root = me.getDocumentRoot(),
            recordName = me.getRecord(),
            
            record = recordName.match(this.selectorRe),
            recLen = record.length,
            
            
            needsRoot = data.length !== 1 && recLen === 1,
            transform;
        transform = this.getTransform();
        if (transform) {
            data = transform(data, request);
        }
        
        xml.push(me.getHeader() || '');
        if (!root && needsRoot) {
            root = me.getDefaultDocumentRoot();
        }
        
        if (root) {
            xml.push('<', root, '>');
        }
        
        for (i = 0; i < recLen - 1; i++) {
            xml.push('<', record[i], '>');
        }
        recordName = record[i];
        for (i = 0; i < len; ++i) {
            this.objectToElement(recordName, data[i], xml);
        }
        
        for (i = recLen - 2; i > -1; i--) {
            xml.push('</', record[i], '>');
        }
        if (root) {
            xml.push('</', root, '>');
        }
        request.setXmlData(xml.join(''));
        return request;
    },
    
    objectToElement: function(name, o, output) {
        var key, datum,
            subOutput = [],
            subKeys, subKeyLen, i, subObject, subObjects, lastObject, lastKey;
        if (!output) {
            output = [];
        }
        
        
        output.push('<', name);
        for (key in o) {
            datum = o[key];
            
            if (key[0] === '@') {
                output.push(' ', key.substr(1), '="', datum, '"');
            } else 
            {
                
                if (typeof datum === 'object') {
                    this.objectToElement(key, datum, subOutput);
                } else {
                    
                    subKeys = key.match(this.selectorRe);
                    
                    
                    if ((subKeyLen = subKeys.length) > 1) {
                        subObjects = subObjects || {};
                        for (subObject = subObjects , i = 0; i < subKeyLen; i++) {
                            lastObject = subObject;
                            lastKey = subKeys[i];
                            subObject = subObject[lastKey] || (subObject[lastKey] = {});
                        }
                        
                        lastObject[lastKey] = datum;
                    } else {
                        subOutput.push('<', key, '>', datum, '</', key, '>');
                    }
                }
            }
        }
        output.push('>');
        output.push.apply(output, subOutput);
        
        if (subObjects) {
            for (key in subObjects) {
                datum = subObjects[key];
                this.objectToElement(key, datum, output);
            }
        }
        
        output.push('</', name, '>');
        return output;
    }
});


Ext.define('Ext.data.XmlStore', {
    extend: 'Ext.data.Store',
    alias: 'store.xml',
    requires: [
        'Ext.data.proxy.Ajax',
        'Ext.data.reader.Xml',
        'Ext.data.writer.Xml'
    ],
    constructor: function(config) {
        config = Ext.apply({
            proxy: {
                type: 'ajax',
                reader: 'xml',
                writer: 'xml'
            }
        }, config);
        this.callParent([
            config
        ]);
    }
});


Ext.define('Ext.data.identifier.Negative', {
    extend: 'Ext.data.identifier.Sequential',
    alias: 'data.identifier.negative',
    config: {
        increment: -1,
        seed: -1
    }
});


Ext.define('Ext.data.identifier.Uuid', {
    extend: 'Ext.data.identifier.Generator',
    alias: 'data.identifier.uuid',
    
    isUnique: true,
    config: {
        
        id: null
    },
    
    
    
    
    constructor: function(config) {
        this.callParent([
            config
        ]);
        this.reconfigure(config);
    },
    
    reconfigure: function(config) {
        var cls = this.self;
        this.generate = (config && config.version === 1) ? cls.createSequential(config.salt, config.timestamp, config.clockSeq) : cls.createRandom();
    },
    clone: null,
    statics: {
        createRandom: function() {
            var pattern = 'xxxxxxxx-xxxx-4xxx-Rxxx-xMxxxxxxxxxx'.split(''),
                hex = '0123456789abcdef'.split(''),
                length = pattern.length,
                parts = [];
            return function() {
                for (var r, c,
                    i = 0; i < length; ++i) {
                    c = pattern[i];
                    if (c !== '-' && c !== '4') {
                        r = Math.random() * 16;
                        r = (c === 'R') ? (r & 3 | 8) : (r | ((c === 'M') ? 1 : 0));
                        c = hex[r];
                    }
                    
                    parts[i] = c;
                }
                return parts.join('');
            };
        },
        createSequential: function(salt, time, clockSeq) {
            var parts = [],
                twoPow32 = Math.pow(2, 32),
                saltLo = salt.lo,
                saltHi = salt.hi,
                timeLo = time.lo,
                timeHi = time.hi,
                toHex = function(value, length) {
                    var ret = value.toString(16).toLowerCase();
                    if (ret.length > length) {
                        ret = ret.substring(ret.length - length);
                    }
                    
                    else if (ret.length < length) {
                        ret = Ext.String.leftPad(ret, length, '0');
                    }
                    return ret;
                };
            if (typeof salt === 'number') {
                saltHi = Math.floor(salt / twoPow32);
                saltLo = Math.floor(salt - saltHi * twoPow32);
            }
            if (typeof time === 'number') {
                timeHi = Math.floor(time / twoPow32);
                timeLo = Math.floor(time - timeHi * twoPow32);
            }
            
            
            saltHi |= 256;
            parts[3] = toHex(128 | ((clockSeq >>> 8) & 63), 2) + toHex(clockSeq & 255, 2);
            parts[4] = toHex(saltHi, 4) + toHex(saltLo, 8);
            
            return function() {
                parts[0] = toHex(timeLo, 8);
                parts[1] = toHex(timeHi & 65535, 4);
                parts[2] = toHex(((timeHi >>> 16) & 4095) | (1 << 12), 4);
                
                ++timeLo;
                if (timeLo >= twoPow32) {
                    
                    timeLo = 0;
                    ++timeHi;
                }
                return parts.join('-');
            };
        }
    }
}, function() {
    this.Global = new this({
        id: 'uuid'
    });
});


Ext.define('Ext.data.proxy.WebStorage', {
    extend: 'Ext.data.proxy.Client',
    alternateClassName: 'Ext.data.WebStorageProxy',
    requires: [
        'Ext.data.identifier.Sequential'
    ],
    config: {
        
        id: undefined
    },
    
    
    
    constructor: function(config) {
        this.callParent(arguments);
        
        this.cache = {};
        if (this.getStorageObject() === undefined) {
            Ext.Error.raise("Local Storage is not supported in this browser, please use another type of data proxy");
        }
        if (this.getId() === undefined) {
            Ext.Error.raise("No unique id was provided to the local storage proxy. See Ext.data.proxy.LocalStorage documentation for details");
        }
        this.initialize();
    },
    //@inheritdoc

    create: function(operation) {
        var me = this,
            records = operation.getRecords(),
            length = records.length,
            ids = me.getIds(),
            id, record, i, identifier;
        if (me.isHierarchical === undefined) {
            
            
            me.isHierarchical = !!records[0].isNode;
            if (me.isHierarchical) {
                me.getStorageObject().setItem(me.getTreeKey(), true);
            }
        }
        for (i = 0; i < length; i++) {
            record = records[i];
            if (record.phantom) {
                record.phantom = false;
                identifier = record.identifier;
                if (identifier && identifier.isUnique) {
                    id = record.getId();
                } else {
                    id = me.getNextId();
                }
            } else {
                id = record.getId();
            }
            me.setRecord(record, id);
            record.commit();
            ids.push(id);
        }
        me.setIds(ids);
        operation.setSuccessful(true);
    },
    //@inheritdoc

    read: function(operation) {
        var me = this,
            allRecords,
            records = [],
            success = true,
            Model = me.getModel(),
            validCount = 0,
            recordCreator = operation.getRecordCreator(),
            filters, sorters, limit, filterLen, valid, record, ids, length, data, id, i, j;
        if (me.isHierarchical) {
            records = me.getTreeData();
        } else {
            ids = me.getIds();
            length = ids.length;
            id = operation.getId();
            
            if (id) {
                data = me.getRecord(id);
                if (data !== null) {
                    record = recordCreator ? recordCreator(data, Model) : new Model(data);
                }
                if (record) {
                    records.push(record);
                } else {
                    success = false;
                }
            } else {
                sorters = operation.getSorters();
                filters = operation.getFilters();
                limit = operation.getLimit();
                allRecords = [];
                
                
                
                for (i = 0; i < length; i++) {
                    data = me.getRecord(ids[i]);
                    record = recordCreator ? recordCreator(data, Model) : new Model(data);
                    allRecords.push(record);
                }
                if (sorters) {
                    Ext.Array.sort(allRecords, Ext.util.Sorter.createComparator(sorters));
                }
                for (i = operation.getStart() || 0; i < length; i++) {
                    record = allRecords[i];
                    valid = true;
                    if (filters) {
                        for (j = 0 , filterLen = filters.length; j < filterLen; j++) {
                            valid = filters[j].filter(record);
                        }
                    }
                    if (valid) {
                        records.push(record);
                        validCount++;
                    }
                    if (limit && validCount === limit) {
                        break;
                    }
                }
            }
        }
        if (success) {
            operation.setResultSet(new Ext.data.ResultSet({
                records: records,
                total: records.length,
                loaded: true
            }));
            operation.setSuccessful(true);
        } else {
            operation.setException('Unable to load records');
        }
    },
    //@inheritdoc

    update: function(operation) {
        var records = operation.getRecords(),
            length = records.length,
            ids = this.getIds(),
            record, id, i;
        for (i = 0; i < length; i++) {
            record = records[i];
            this.setRecord(record);
            record.commit();
            
            
            id = record.getId();
            if (id !== undefined && Ext.Array.indexOf(ids, id) === -1) {
                ids.push(id);
            }
        }
        this.setIds(ids);
        operation.setSuccessful(true);
    },
    //@inheritdoc

    erase: function(operation) {
        var me = this,
            records = operation.getRecords(),
            ids = me.getIds(),
            idLength = ids.length,
            newIds = [],
            removedHash = {},
            i = records.length,
            id;
        for (; i--; ) {
            Ext.apply(removedHash, me.removeRecord(records[i]));
        }
        for (i = 0; i < idLength; i++) {
            id = ids[i];
            if (!removedHash[id]) {
                newIds.push(id);
            }
        }
        me.setIds(newIds);
        operation.setSuccessful(true);
    },
    
    getRecord: function(id) {
        var me = this,
            cache = me.cache,
            data = !cache[id] ? Ext.decode(me.getStorageObject().getItem(me.getRecordKey(id))) : cache[id];
        if (!data) {
            return null;
        }
        cache[id] = data;
        data[me.getModel().prototype.idProperty] = id;
        
        
        return Ext.merge({}, data);
    },
    
    setRecord: function(record, id) {
        if (id) {
            record.set('id', id, {
                commit: true
            });
        } else {
            id = record.getId();
        }
        var me = this,
            rawData = record.getData(),
            data = {},
            model = me.getModel(),
            fields = model.getFields(),
            length = fields.length,
            i = 0,
            field, name, obj, key;
        for (; i < length; i++) {
            field = fields[i];
            name = field.name;
            if (field.persist) {
                data[name] = rawData[name];
            }
        }
        
        delete data[model.prototype.idProperty];
        
        if (record.isNode && record.get('depth') === 1) {
            delete data.parentId;
        }
        obj = me.getStorageObject();
        key = me.getRecordKey(id);
        
        me.cache[id] = data;
        
        obj.removeItem(key);
        obj.setItem(key, Ext.encode(data));
    },
    
    removeRecord: function(record) {
        var me = this,
            id = record.getId(),
            records = {},
            i, childNodes;
        records[id] = record;
        me.getStorageObject().removeItem(me.getRecordKey(id));
        delete me.cache[id];
        if (record.childNodes) {
            childNodes = record.childNodes;
            for (i = childNodes.length; i--; ) {
                Ext.apply(records, me.removeRecord(childNodes[i]));
            }
        }
        return records;
    },
    
    getRecordKey: function(id) {
        if (id.isModel) {
            id = id.getId();
        }
        return Ext.String.format("{0}-{1}", this.getId(), id);
    },
    
    getRecordCounterKey: function() {
        return Ext.String.format("{0}-counter", this.getId());
    },
    
    getTreeKey: function() {
        return Ext.String.format("{0}-tree", this.getId());
    },
    
    getIds: function() {
        var me = this,
            ids = (me.getStorageObject().getItem(me.getId()) || "").split(","),
            length = ids.length,
            isString = this.getIdField().isStringField,
            i;
        if (length === 1 && ids[0] === "") {
            ids = [];
        } else {
            for (i = 0; i < length; i++) {
                ids[i] = isString ? ids[i] : +ids[i];
            }
        }
        return ids;
    },
    getIdField: function() {
        return this.getModel().prototype.idField;
    },
    
    setIds: function(ids) {
        var obj = this.getStorageObject(),
            str = ids.join(","),
            id = this.getId();
        obj.removeItem(id);
        if (!Ext.isEmpty(str)) {
            obj.setItem(id, str);
        }
    },
    
    getNextId: function() {
        var me = this,
            obj = me.getStorageObject(),
            key = me.getRecordCounterKey(),
            isString = me.getIdField().isStringField,
            id;
        id = me.idGenerator.generate();
        obj.setItem(key, id);
        if (isString) {
            id = id + '';
        }
        return id;
    },
    
    getTreeData: function() {
        var me = this,
            ids = me.getIds(),
            length = ids.length,
            records = [],
            recordHash = {},
            root = [],
            i = 0,
            Model = me.getModel(),
            idProperty = Model.prototype.idProperty,
            rootLength, record, parent, parentId, children, id;
        for (; i < length; i++) {
            id = ids[i];
            
            record = me.getRecord(id);
            
            records.push(record);
            
            recordHash[id] = record;
            if (!record.parentId) {
                
                root.push(record);
            }
        }
        rootLength = root.length;
        
        Ext.Array.sort(records, me.sortByParentId);
        
        for (i = rootLength; i < length; i++) {
            record = records[i];
            parentId = record.parentId;
            if (!parent || parent[idProperty] !== parentId) {
                
                parent = recordHash[parentId];
                parent.children = children = [];
            }
            
            children.push(record);
        }
        for (i = length; i--; ) {
            record = records[i];
            if (!record.children && !record.leaf) {
                
                record.loaded = true;
            }
        }
        
        for (i = rootLength; i--; ) {
            record = root[i];
            root[i] = new Model(record);
        }
        return root;
    },
    
    sortByParentId: function(node1, node2) {
        return (node1.parentId || 0) - (node2.parentId || 0);
    },
    
    initialize: function() {
        var me = this,
            storageObject = me.getStorageObject(),
            lastId = +storageObject.getItem(me.getRecordCounterKey()),
            id = me.getId();
        storageObject.setItem(id, storageObject.getItem(id) || "");
        if (storageObject.getItem(me.getTreeKey())) {
            me.isHierarchical = true;
        }
        me.idGenerator = new Ext.data.identifier.Sequential({
            seed: lastId ? lastId + 1 : 1
        });
    },
    
    clear: function() {
        var me = this,
            obj = me.getStorageObject(),
            ids = me.getIds(),
            len = ids.length,
            i;
        
        for (i = 0; i < len; i++) {
            obj.removeItem(me.getRecordKey(ids[i]));
        }
        
        obj.removeItem(me.getRecordCounterKey());
        obj.removeItem(me.getTreeKey());
        obj.removeItem(me.getId());
        
        me.cache = {};
    },
    
    getStorageObject: function() {
        Ext.Error.raise("The getStorageObject function has not been defined in your Ext.data.proxy.WebStorage subclass");
    }
});


Ext.define('Ext.data.proxy.LocalStorage', {
    extend: 'Ext.data.proxy.WebStorage',
    alias: 'proxy.localstorage',
    alternateClassName: 'Ext.data.LocalStorageProxy',
    
    getStorageObject: function() {
        return window.localStorage;
    }
});


Ext.define('Ext.data.proxy.Rest', {
    extend: 'Ext.data.proxy.Ajax',
    alternateClassName: 'Ext.data.RestProxy',
    alias: 'proxy.rest',
    
    defaultActionMethods: {
        create: 'POST',
        read: 'GET',
        update: 'PUT',
        destroy: 'DELETE'
    },
    slashRe: /\/$/,
    periodRe: /\.$/,
    config: {
        
        appendId: true,
        
        format: null,
        
        batchActions: false,
        
        actionMethods: {
            create: 'POST',
            read: 'GET',
            update: 'PUT',
            destroy: 'DELETE'
        }
    },
    
    buildUrl: function(request) {
        var me = this,
            operation = request.getOperation(),
            records = operation.getRecords(),
            record = records ? records[0] : null,
            format = me.getFormat(),
            url = me.getUrl(request),
            id, params;
        if (record && !record.phantom) {
            id = record.getId();
        } else {
            id = operation.getId();
        }
        if (me.getAppendId() && me.isValidId(id)) {
            if (!url.match(me.slashRe)) {
                url += '/';
            }
            url += encodeURIComponent(id);
            params = request.getParams();
            if (params) {
                delete params[me.getIdParam()];
            }
        }
        if (format) {
            if (!url.match(me.periodRe)) {
                url += '.';
            }
            url += format;
        }
        request.setUrl(url);
        return me.callParent([
            request
        ]);
    },
    isValidId: function(id) {
        return id || id === 0;
    }
});


Ext.define('Ext.data.proxy.SessionStorage', {
    extend: 'Ext.data.proxy.WebStorage',
    alias: 'proxy.sessionstorage',
    alternateClassName: 'Ext.data.SessionStorageProxy',
    
    getStorageObject: function() {
        return window.sessionStorage;
    }
});


Ext.define('Ext.data.validator.Bound', {
    extend: 'Ext.data.validator.Validator',
    alias: 'data.validator.bound',
    type: 'bound',
    config: {
        
        min: undefined,
        
        max: undefined,
        
        emptyMessage: 'Must be present',
        
        minOnlyMessage: null,
        
        maxOnlyMessage: null,
        
        bothOnlyMessage: null
    },
    constructor: function() {
        var me = this;
        me.preventConfigure = true;
        me.callParent(arguments);
        delete me.preventConfigure;
        me.configure();
    },
    setConfig: function() {
        var me = this;
        me.preventConfigure = true;
        me.callParent(arguments);
        delete me.preventConfigure;
        me.configure();
    },
    configure: function() {
        var me = this,
            hasMin, hasMax, min, max;
        if (me.preventConfigure) {
            return;
        }
        min = me.getMin();
        max = me.getMax();
        hasMin = me.hasMin = min !== undefined;
        hasMax = me.hasMax = max !== undefined;
        if (hasMin && hasMax) {
            me._bothMsg = Ext.String.format(me.getBothMessage(), min, max);
        } else if (hasMin) {
            me._minMsg = Ext.String.format(me.getMinOnlyMessage(), min);
        } else if (hasMax) {
            me._maxMsg = Ext.String.format(me.getMaxOnlyMessage(), max);
        }
    },
    updateMin: function() {
        this.configure();
    },
    updateMax: function() {
        this.configure();
    },
    updateMinOnlyMessage: function(v) {
        this.configure();
    },
    updateMaxOnlyMessage: function() {
        this.configure();
    },
    updateBothMessage: function() {
        this.configure();
    },
    validate: function(value) {
        var me = this,
            hasMin = me.hasMin,
            hasMax = me.hasMax,
            min = me.getMin(),
            max = me.getMax(),
            msg = this.validateValue(value),
            len;
        if (msg !== true) {
            return msg;
        }
        value = me.getValue(value);
        if (hasMin && hasMax) {
            if (value < min || value > max) {
                msg = me._bothMsg;
            }
        } else if (hasMin) {
            if (value < min) {
                msg = me._minMsg;
            }
        } else if (hasMax) {
            if (value > max) {
                msg = me._maxMsg;
            }
        }
        return msg;
    },
    validateValue: function(value) {
        if (value === undefined || value === null) {
            return this.getEmptyMessage();
        }
        return true;
    },
    getValue: Ext.identityFn
});


Ext.define('Ext.data.validator.Format', {
    extend: 'Ext.data.validator.Validator',
    alias: 'data.validator.format',
    type: 'format',
    config: {
        
        message: 'Is in the wrong format',
        
        matcher: undefined
    },
    constructor: function() {
        this.callParent(arguments);
        if (!this.getMatcher()) {
            Ext.Error.raise('validator.Format must be configured with a matcher');
        }
    },
    validate: function(value) {
        var matcher = this.getMatcher(),
            result = matcher && matcher.test(value);
        return result ? result : this.getMessage();
    }
});


Ext.define('Ext.data.validator.Email', {
    extend: 'Ext.data.validator.Format',
    alias: 'data.validator.email',
    type: 'email',
    config: {
        
        message: 'Is not a valid email address',
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        matcher: /^(")?(?:[^\."])(?:(?:[\.])?(?:[\w\-!#$%&'*+\/=?\^_`{|}~]))*\1@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/
    }
});


Ext.define('Ext.data.validator.List', {
    extend: 'Ext.data.validator.Validator',
    alias: 'data.validator.list',
    type: 'list',
    config: {
        
        list: null
    },
    inclusion: null,
    validate: function(value) {
        var contains = Ext.Array.contains(this.getList(), value),
            inclusion = this.inclusion,
            exclusion = !inclusion,
            result;
        result = (inclusion && contains) || (exclusion && !contains);
        return result || this.getMessage();
    }
});


Ext.define('Ext.data.validator.Exclusion', {
    extend: 'Ext.data.validator.List',
    alias: 'data.validator.exclusion',
    type: 'exclusion',
    config: {
        
        message: 'Is a value that has been excluded'
    },
    constructor: function() {
        this.callParent(arguments);
        if (!this.getList()) {
            Ext.Error.raise('validator.Exclusion requires a list');
        }
    },
    inclusion: false
});


Ext.define('Ext.data.validator.Inclusion', {
    extend: 'Ext.data.validator.List',
    alias: 'data.validator.inclusion',
    type: 'inclusion',
    config: {
        
        message: 'Is not in the list of acceptable values'
    },
    constructor: function() {
        this.callParent(arguments);
        if (!this.getList()) {
            Ext.Error.raise('validator.Inclusion requires a list');
        }
    },
    inclusion: true
});


Ext.define('Ext.data.validator.Length', {
    extend: 'Ext.data.validator.Bound',
    alias: 'data.validator.length',
    type: 'length',
    config: {
        
        
        
        minOnlyMessage: 'Length must be at least {0}',
        
        maxOnlyMessage: 'Length must be no more than {0}',
        
        bothMessage: 'Length must be between {0} and {1}'
    },
    getValue: function(v) {
        return String(v).length;
    }
});


Ext.define('Ext.data.validator.Presence', {
    extend: 'Ext.data.validator.Validator',
    alias: 'data.validator.presence',
    type: 'presence',
    config: {
        
        message: 'Must be present',
        
        allowEmpty: false
    },
    validate: function(value) {
        var valid = !(value === undefined || value === null);
        if (valid && !this.getAllowEmpty()) {
            valid = !(value === '');
        }
        return valid ? true : this.getMessage();
    }
});


Ext.define('Ext.data.validator.Range', {
    extend: 'Ext.data.validator.Bound',
    alias: 'data.validator.range',
    type: 'range',
    config: {
        
        
        
        minOnlyMessage: 'Must be must be at least {0}',
        
        maxOnlyMessage: 'Must be no more than than {0}',
        
        bothMessage: 'Must be between {0} and {1}',
        
        nanMessage: 'Must be numeric'
    },
    validateValue: function(value) {
        var msg = this.callParent([
                value
            ]);
        if (msg === true && isNaN(value)) {
            msg = this.getNanMessage();
        }
        return msg;
    }
});


Ext.define('Ext.direct.Event', {
    alias: 'direct.event',
    status: true,
    
    constructor: function(config) {
        Ext.apply(this, config);
    },
    
    getName: function() {
        return this.name;
    },
    
    getData: function() {
        return this.data;
    }
});


Ext.define('Ext.direct.RemotingEvent', {
    extend: 'Ext.direct.Event',
    alias: 'direct.rpc',
    
    getTransaction: function() {
        var me = this;
        return me.transaction || Ext.direct.Manager.getTransaction(me.tid);
    }
});


Ext.define('Ext.direct.ExceptionEvent', {
    extend: 'Ext.direct.RemotingEvent',
    alias: 'direct.exception',
    status: false
});


Ext.define('Ext.direct.JsonProvider', {
    extend: 'Ext.direct.Provider',
    alias: 'direct.jsonprovider',
    uses: [
        'Ext.direct.ExceptionEvent',
        'Ext.direct.Manager'
    ],
    
    parseResponse: function(response) {
        var text = response && response.responseText;
        if (text) {
            if (Ext.isObject(text) || Ext.isArray(text)) {
                return text;
            }
            return Ext.decode(text);
        }
        return null;
    },
    
    createEvents: function(response) {
        var me = this,
            data = null,
            events = [],
            event, i, len;
        try {
            data = me.parseResponse(response);
        } catch (e) {
            event = new Ext.direct.ExceptionEvent({
                data: e,
                xhr: response,
                code: Ext.direct.Manager.exceptions.PARSE,
                message: 'Error parsing json response: \n\n ' + e
            });
            return [
                event
            ];
        }
        if (Ext.isArray(data)) {
            for (i = 0 , len = data.length; i < len; ++i) {
                events.push(me.createEvent(data[i]));
            }
        } else if (Ext.isObject(data)) {
            events.push(me.createEvent(data));
        }
        return events;
    },
    
    createEvent: function(response) {
        if (typeof response !== 'object' || !('type' in response)) {
            return new Ext.direct.ExceptionEvent({
                data: response,
                code: Ext.direct.Manager.exceptions.DATA,
                message: 'Invalid data: event type is not specified'
            });
        }
        return Ext.create('direct.' + response.type, response);
    }
});


Ext.define('Ext.direct.PollingProvider', {
    extend: 'Ext.direct.JsonProvider',
    alias: 'direct.pollingprovider',
    requires: [
        'Ext.Ajax',
        'Ext.util.TaskRunner',
        'Ext.direct.ExceptionEvent'
    ],
    type: 'polling',
    
    interval: 3000,
    
    
    
    
    
    constructor: function(config) {
        var me = this;
        me.callParent([
            config
        ]);
        me.pollTask = Ext.TaskManager.newTask({
            run: me.runPoll,
            interval: me.interval,
            scope: me
        });
    },
    destroy: function() {
        this.pollTask = null;
        this.callParent();
    },
    doConnect: function() {
        var me = this,
            url = me.url,
            pollFn = me.pollFn;
        
        
        
        
        if (pollFn && Ext.isString(pollFn)) {
            var fnName = pollFn;
            me.pollFn = pollFn = Ext.direct.Manager.parseMethod(pollFn);
            if (!Ext.isFunction(pollFn)) {
                Ext.Error.raise("Cannot resolve Ext.Direct API method " + fnName + " for PollingProvider");
            }
        } else if (Ext.isFunction(url)) {
            Ext.log.warn('Using a function for url is deprecated, use pollFn instead.');
            me.pollFn = pollFn = url;
            me.url = url = null;
        }
        if (url || pollFn) {
            me.setInterval(me.interval);
            me.pollTask.start();
        }
    },
    doDisconnect: function() {
        this.pollTask.stop();
    },
    getInterval: function() {
        return this.pollTask.interval;
    },
    setInterval: function(interval) {
        var me = this,
            pollTask = me.pollTask;
        if (interval < 100) {
            Ext.Error.raise("Attempting to configure PollProvider " + me.id + " with interval that is less than 100ms.");
        }
        me.interval = pollTask.interval = interval;
        if (me.isConnected()) {
            pollTask.restart(interval);
        }
    },
    
    runPoll: function() {
        var me = this,
            url = me.url,
            pollFn = me.pollFn,
            baseParams = me.baseParams,
            args;
        if (me.fireEvent('beforepoll', me) !== false) {
            if (pollFn) {
                args = pollFn.directCfg.method.getArgs({
                    params: baseParams !== undefined ? baseParams : {},
                    callback: me.onPollFn,
                    scope: me
                });
                pollFn.apply(window, args);
            } else {
                Ext.Ajax.request({
                    url: url,
                    callback: me.onData,
                    scope: me,
                    params: baseParams
                });
            }
            me.fireEvent('poll', me);
        }
    },
    
    onData: function(opt, success, response) {
        var me = this,
            i, len, events;
        if (success) {
            events = me.createEvents(response);
            for (i = 0 , len = events.length; i < len; ++i) {
                me.fireEvent('data', me, events[i]);
            }
        } else {
            events = new Ext.direct.ExceptionEvent({
                data: null,
                code: Ext.direct.Manager.exceptions.TRANSPORT,
                message: 'Unable to connect to the server.',
                xhr: response
            });
            me.fireEvent('data', me, events);
        }
    },
    
    onPollFn: function(result, event, success, options) {
        this.onData(null, success, {
            responseText: result
        });
    },
    inheritableStatics: {
        checkConfig: function(config) {
            
            return config && config.type === 'polling' && (config.url || config.pollFn);
        }
    }
});


Ext.define('Ext.direct.RemotingMethod', {
    constructor: function(config) {
        var me = this,
            params = config.params,
            len = config.len,
            metadataCfg = config.metadata,
            metadata = {},
            name, pLen, p, param;
        me.name = config.name;
        if (config.formHandler) {
            me.formHandler = config.formHandler;
        } else if (Ext.isNumeric(len)) {
            
            me.len = len;
            me.ordered = true;
        } else {
            
            me.named = true;
            me.strict = config.strict !== undefined ? config.strict : true;
            me.params = {};
            
            
            pLen = params && params.length;
            for (p = 0; p < pLen; p++) {
                param = params[p];
                name = Ext.isObject(param) ? param.name : param;
                me.params[name] = true;
            }
        }
        if (metadataCfg) {
            params = metadataCfg.params;
            len = metadataCfg.len;
            if (Ext.isNumeric(len)) {
                if (len === 0) {
                    Ext.Error.raise('metadata.len cannot be 0 ' + 'for Ext.Direct method ' + me.name);
                }
                metadata.ordered = true;
                metadata.len = len;
            } else if (Ext.isArray(params)) {
                metadata.named = true;
                metadata.params = {};
                for (p = 0 , pLen = params.length; p < pLen; p++) {
                    param = params[p];
                    metadata.params[param] = true;
                }
                metadata.strict = metadataCfg.strict !== undefined ? metadataCfg.strict : true;
            } else {
                Ext.Error.raise('metadata is neither named nor ordered ' + 'for Ext.Direct method ' + me.name);
            }
            me.metadata = metadata;
        }
    },
    
    getArgs: function(config) {
        var me = this,
            params = config.params,
            paramOrder = config.paramOrder,
            paramsAsHash = config.paramsAsHash,
            metadata = config.metadata,
            options = config.options,
            args = [],
            i, len;
        if (me.ordered) {
            if (me.len > 0) {
                
                if (paramOrder) {
                    for (i = 0 , len = paramOrder.length; i < len; i++) {
                        args.push(params[paramOrder[i]]);
                    }
                } else if (paramsAsHash) {
                    
                    args.push(params);
                }
            }
        } else {
            args.push(params);
        }
        args.push(config.callback, config.scope || window);
        if (options || metadata) {
            options = Ext.apply({}, options);
            if (metadata) {
                
                
                options.metadata = metadata;
            }
            args.push(options);
        }
        return args;
    },
    
    getCallData: function(args) {
        var me = this,
            data = null,
            len = me.len,
            params = me.params,
            strict = me.strict,
            form, callback, scope, name, options, metadata;
        
        
        
        if (me.ordered) {
            callback = args[len];
            scope = args[len + 1];
            options = args[len + 2];
            if (len !== 0) {
                data = args.slice(0, len);
            }
        } else if (me.formHandler) {
            form = args[0];
            callback = args[1];
            scope = args[2];
            options = args[3];
        } else {
            data = Ext.apply({}, args[0]);
            callback = args[1];
            scope = args[2];
            options = args[3];
            
            if (strict) {
                for (name in data) {
                    if (data.hasOwnProperty(name) && !params[name]) {
                        delete data[name];
                    }
                }
            }
        }
        if (me.metadata && options && options.metadata) {
            if (me.metadata.ordered) {
                if (!Ext.isArray(options.metadata)) {
                    Ext.Error.raise('options.metadata is not an Array ' + 'for Ext.Direct method ' + me.name);
                } else if (options.metadata.length < me.metadata.len) {
                    Ext.Error.raise('Not enough parameters in options.metadata ' + 'for Ext.Direct method ' + me.name);
                }
                metadata = options.metadata.slice(0, me.metadata.len);
            } else {
                if (!Ext.isObject(options.metadata)) {
                    Ext.Error.raise('options.metadata is not an Object ' + 'for Ext.Direct method ' + me.name);
                }
                metadata = Ext.apply({}, options.metadata);
                if (me.metadata.strict) {
                    for (name in metadata) {
                        if (metadata.hasOwnProperty(name) && !me.metadata.params[name]) {
                            delete metadata[name];
                        }
                    }
                }
                for (name in me.metadata.params) {
                    if (!metadata.hasOwnProperty(name)) {
                        Ext.Error.raise('Named parameter ' + name + ' is missing ' + 'in options.metadata for Ext.Direct method ' + me.name);
                    }
                }
            }
            delete options.metadata;
        }
        return {
            form: form,
            data: data,
            metadata: metadata,
            callback: callback,
            scope: scope,
            options: options
        };
    }
});


Ext.define('Ext.direct.Transaction', {
    alias: 'direct.transaction',
    alternateClassName: 'Ext.Direct.Transaction',
    statics: {
        TRANSACTION_ID: 0
    },
    
    
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);
        me.id = me.tid = ++me.self.TRANSACTION_ID;
        me.retryCount = 0;
    },
    send: function() {
        var me = this;
        me.provider.queueTransaction(me);
    },
    retry: function() {
        var me = this;
        me.retryCount++;
        me.send();
    },
    getProvider: function() {
        return this.provider;
    }
});


Ext.define('Ext.direct.RemotingProvider', {
    extend: 'Ext.direct.JsonProvider',
    alias: 'direct.remotingprovider',
    requires: [
        'Ext.util.MixedCollection',
        'Ext.util.DelayedTask',
        'Ext.direct.Transaction',
        'Ext.direct.RemotingMethod',
        'Ext.direct.Manager'
    ],
    type: 'remoting',
    
    
    
    
    
    
    enableBuffer: 10,
    
    maxRetries: 1,
    
    
    
    
    constructor: function(config) {
        var me = this;
        me.callParent(arguments);
        me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || Ext.global;
        me.transactions = new Ext.util.MixedCollection();
        me.callBuffer = [];
    },
    doConnect: function() {
        if (!this.apiCreated) {
            this.initAPI();
            this.apiCreated = true;
        }
    },
    
    getNamespace: function(root, action) {
        var parts, ns, i, len;
        root = root || Ext.global;
        parts = action.toString().split('.');
        for (i = 0 , len = parts.length; i < len; i++) {
            ns = parts[i];
            root = root[ns];
            if (typeof root === 'undefined') {
                return root;
            }
        }
        return root;
    },
    
    createNamespaces: function(root, action) {
        var parts, ns, i, len;
        root = root || Ext.global;
        parts = action.toString().split('.');
        for (i = 0 , len = parts.length; i < len; i++) {
            ns = parts[i];
            root[ns] = root[ns] || {};
            root = root[ns];
        }
        return root;
    },
    
    initAPI: function() {
        var me = this,
            actions = me.actions,
            namespace = me.namespace,
            Manager = Ext.direct.Manager,
            action, cls, methods, i, len, method, handler;
        for (action in actions) {
            if (actions.hasOwnProperty(action)) {
                if (me.disableNestedActions) {
                    cls = namespace[action];
                    if (!cls) {
                        cls = namespace[action] = {};
                    }
                } else {
                    cls = me.getNamespace(namespace, action);
                    if (!cls) {
                        cls = me.createNamespaces(namespace, action);
                    }
                }
                methods = actions[action];
                for (i = 0 , len = methods.length; i < len; ++i) {
                    method = new Ext.direct.RemotingMethod(methods[i]);
                    cls[method.name] = handler = me.createHandler(action, method);
                    Manager.registerMethod(handler.$name, handler);
                }
            }
        }
    },
    
    createHandler: function(action, method) {
        var me = this,
            slice = Array.prototype.slice,
            handler;
        if (!method.formHandler) {
            handler = function() {
                me.configureRequest(action, method, slice.call(arguments, 0));
            };
        } else {
            handler = function() {
                me.configureFormRequest(action, method, slice.call(arguments, 0));
            };
        }
        handler.name = handler.$name = action + '.' + method.name;
        handler.$directFn = true;
        handler.directCfg = handler.$directCfg = {
            action: action,
            method: method
        };
        return handler;
    },
    
    connect: function() {
        var me = this;
        if (!me.url) {
            Ext.Error.raise('Error initializing RemotingProvider "' + me.id + '", no url configured.');
        }
        me.callParent();
    },
    
    runCallback: function(transaction, event) {
        var success = !!event.status,
            funcName = success ? 'success' : 'failure',
            callback, options, result;
        if (transaction && transaction.callback) {
            callback = transaction.callback;
            options = transaction.callbackOptions;
            result = typeof event.result !== 'undefined' ? event.result : event.data;
            if (Ext.isFunction(callback)) {
                callback(result, event, success, options);
            } else {
                Ext.callback(callback[funcName], callback.scope, [
                    result,
                    event,
                    success,
                    options
                ]);
                Ext.callback(callback.callback, callback.scope, [
                    result,
                    event,
                    success,
                    options
                ]);
            }
        }
    },
    
    onData: function(options, success, response) {
        var me = this,
            i, len, events, event, transaction, transactions;
        if (success) {
            events = me.createEvents(response);
            for (i = 0 , len = events.length; i < len; ++i) {
                event = events[i];
                transaction = me.getTransaction(event);
                me.fireEvent('data', me, event);
                if (transaction && me.fireEvent('beforecallback', me, event, transaction) !== false) {
                    me.runCallback(transaction, event, true);
                    Ext.direct.Manager.removeTransaction(transaction);
                }
            }
        } else {
            transactions = [].concat(options.transaction);
            for (i = 0 , len = transactions.length; i < len; ++i) {
                transaction = me.getTransaction(transactions[i]);
                if (transaction && transaction.retryCount < me.maxRetries) {
                    transaction.retry();
                } else {
                    event = new Ext.direct.ExceptionEvent({
                        data: null,
                        transaction: transaction,
                        code: Ext.direct.Manager.exceptions.TRANSPORT,
                        message: 'Unable to connect to the server.',
                        xhr: response
                    });
                    me.fireEvent('data', me, event);
                    if (transaction && me.fireEvent('beforecallback', me, transaction) !== false) {
                        me.runCallback(transaction, event, false);
                        Ext.direct.Manager.removeTransaction(transaction);
                    }
                }
            }
        }
    },
    
    getTransaction: function(options) {
        return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
    },
    
    getPayload: function(transaction) {
        var result = {
                action: transaction.action,
                method: transaction.method,
                data: transaction.data,
                type: 'rpc',
                tid: transaction.id
            };
        if (transaction.metadata) {
            result.metadata = transaction.metadata;
        }
        return result;
    },
    
    sendRequest: function(transaction) {
        var me = this,
            request, callData, params,
            enableUrlEncode = me.enableUrlEncode,
            payload, i, len;
        request = {
            url: me.url,
            callback: me.onData,
            scope: me,
            transaction: transaction,
            timeout: me.timeout
        };
        
        if (transaction.timeout) {
            request.timeout = transaction.timeout;
        }
        if (Ext.isArray(transaction)) {
            callData = [];
            for (i = 0 , len = transaction.length; i < len; ++i) {
                payload = me.getPayload(transaction[i]);
                callData.push(payload);
            }
        } else {
            callData = me.getPayload(transaction);
        }
        if (enableUrlEncode) {
            params = {};
            params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData);
            request.params = params;
        } else {
            request.jsonData = callData;
        }
        Ext.Ajax.request(request);
    },
    
    queueTransaction: function(transaction) {
        var me = this,
            enableBuffer = me.enableBuffer;
        if (transaction.form) {
            me.sendFormRequest(transaction);
            return;
        }
        if (enableBuffer === false || typeof transaction.timeout !== 'undefined') {
            me.sendRequest(transaction);
            return;
        }
        me.callBuffer.push(transaction);
        if (enableBuffer) {
            if (!me.callTask) {
                me.callTask = new Ext.util.DelayedTask(me.combineAndSend,me);
            }
            me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
        } else {
            me.combineAndSend();
        }
    },
    
    combineAndSend: function() {
        var me = this,
            buffer = me.callBuffer,
            len = buffer.length;
        if (len > 0) {
            me.sendRequest(len == 1 ? buffer[0] : buffer);
            me.callBuffer = [];
        }
    },
    
    configureTransaction: function(action, method, args, isForm) {
        var data, cb, scope, options, params;
        data = method.getCallData(args);
        cb = data.callback;
        scope = data.scope;
        options = data.options;
        if (cb && !Ext.isFunction(cb)) {
            Ext.Error.raise("Callback argument is not a function " + "for Ext.Direct method " + action + "." + method.name);
        }
        
        
        cb = cb && scope ? Ext.Function.bind(cb, scope) : cb;
        params = Ext.apply({}, {
            provider: this,
            args: args,
            action: action,
            method: method.name,
            form: data.form,
            data: data.data,
            metadata: data.metadata,
            callbackOptions: options,
            callback: cb,
            isForm: isForm
        });
        if (options && options.timeout != null) {
            params.timeout = options.timeout;
        }
        return new Ext.direct.Transaction(params);
    },
    
    configureRequest: function(action, method, args) {
        var me = this,
            transaction;
        transaction = me.configureTransaction(action, method, args);
        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            me.queueTransaction(transaction);
            me.fireEvent('call', me, transaction, method);
        }
    },
    
    configureFormRequest: function(action, method, args) {
        var me = this,
            transaction, form, isUpload, postParams;
        transaction = me.configureTransaction(action, method, args, true);
        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            form = transaction.form;
            isUpload = String(form.getAttribute("enctype")).toLowerCase() === 'multipart/form-data';
            postParams = {
                extTID: transaction.id,
                extAction: action,
                extMethod: method.name,
                extType: 'rpc',
                extUpload: String(isUpload)
            };
            if (transaction.metadata) {
                postParams.extMetadata = Ext.JSON.encode(transaction.metadata);
            }
            Ext.apply(transaction, {
                form: form,
                isUpload: isUpload,
                params: postParams
            });
            me.sendFormRequest(transaction);
            me.fireEvent('call', me, transaction, method);
        }
    },
    
    sendFormRequest: function(transaction) {
        var me = this;
        Ext.Ajax.request({
            url: me.url,
            params: transaction.params,
            callback: me.onData,
            scope: me,
            form: transaction.form,
            isUpload: transaction.isUpload,
            transaction: transaction
        });
    },
    inheritableStatics: {
        checkConfig: function(config) {
            
            
            return config && config.type === 'remoting' && config.url && Ext.isArray(config.actions);
        }
    }
});


Ext.define('Ext.dom.GarbageCollector', {
    singleton: true,
    
    interval: 30000,
    constructor: function() {
        var me = this;
        me.collect = Ext.Function.bind(me.collect, me);
        me.lastTime = Ext.now();
        me.resume();
    },
    
    collect: function() {
        var me = this,
            cache = Ext.cache,
            eid, dom, el, t, isGarbage, tagName;
        var collectedIds = [];
        for (eid in cache) {
            if (!cache.hasOwnProperty(eid)) {
                
                continue;
            }
            el = cache[eid];
            if (el.skipGarbageCollection) {
                
                continue;
            }
            dom = el.dom;
            
            if (!dom) {
                Ext.Error.raise('Missing DOM node in element garbage collection: ' + eid);
            }
            try {
                
                
                
                
                
                isGarbage = Ext.isGarbage(dom);
            } catch (e) {
                
                
                
                
                delete cache[eid];
                collectedIds.push('#' + el.id);
                
                continue;
            }
            if (isGarbage) {
                if (el && el.dom) {
                    tagName = el.dom.tagName;
                    el.collect();
                    collectedIds.push((tagName ? tagName : '') + '#' + el.id);
                }
            }
        }
        
        if (Ext.isIE9m) {
            t = {};
            for (eid in cache) {
                if (cache.hasOwnProperty(eid)) {
                    t[eid] = cache[eid];
                }
            }
            Ext.cache = t;
        }
        me.lastTime = Ext.now();
        return collectedIds;
    },
    
    pause: function() {
        clearTimeout(this.timerId);
    },
    
    resume: function() {
        var me = this,
            lastTime = me.lastTime;
        if (Ext.enableGarbageCollector && (Ext.now() - lastTime > me.interval)) {
            me.collect();
        }
        me.timerId = Ext.interval(me.collect, me.interval);
    }
});


Ext.define('Ext.event.gesture.Recognizer', {
    requires: [
        'Ext.event.publisher.Gesture'
    ],
    mixins: [
        'Ext.mixin.Identifiable'
    ],
    
    priority: 0,
    handledEvents: [],
    config: {
        onRecognized: Ext.emptyFn,
        callbackScope: null
    },
    constructor: function(config) {
        this.initConfig(config);
        Ext.event.publisher.Gesture.instance.registerRecognizer(this);
    },
    onStart: Ext.emptyFn,
    onEnd: Ext.emptyFn,
    onTouchStart: Ext.emptyFn,
    onTouchMove: Ext.emptyFn,
    onTouchEnd: Ext.emptyFn,
    onTouchCancel: Ext.emptyFn,
    fail: function() {
        return false;
    },
    fire: function() {
        this.getOnRecognized().apply(this.getCallbackScope(), arguments);
    },
    reset: Ext.emptyFn,
    debugHooks: {
        $enabled: false,
        
        fail: function(msg) {
            Ext.log.info(this.$className + ' Gesture Failed: ' + msg);
            return false;
        }
    }
});


Ext.define('Ext.event.gesture.SingleTouch', {
    extend: 'Ext.event.gesture.Recognizer',
    inheritableStatics: {
        NOT_SINGLE_TOUCH: "Not Single Touch",
        TOUCH_MOVED: "Touch Moved",
        EVENT_CANCELED: "Event Canceled"
    },
    onTouchStart: function(e) {
        if (e.touches.length > 1) {
            return this.fail(this.self.NOT_SINGLE_TOUCH);
        }
    },
    onTouchCancel: function() {
        return false;
    }
});


Ext.define('Ext.event.gesture.DoubleTap', {
    extend: 'Ext.event.gesture.SingleTouch',
    priority: 300,
    inheritableStatics: {
        DIFFERENT_TARGET: 'Different Target'
    },
    config: {
        
        moveDistance: 8,
        
        tapDistance: 24,
        maxDuration: 300
    },
    handledEvents: [
        'singletap',
        'doubletap'
    ],
    
    
    singleTapTimer: null,
    startTime: 0,
    lastTapTime: 0,
    onTouchStart: function(e) {
        var me = this,
            lastStartPoint;
        if (me.callParent(arguments) === false) {
            return false;
        }
        
        lastStartPoint = me.lastStartPoint = e.changedTouches[0].point;
        
        me.startPoint = me.startPoint || lastStartPoint;
        me.startTime = e.time;
        clearTimeout(me.singleTapTimer);
    },
    onTouchMove: function(e) {
        var me = this,
            point = e.changedTouches[0].point;
        if (Math.abs(point.getDistanceTo(me.lastStartPoint)) >= me.getMoveDistance()) {
            me.startPoint = null;
            return me.fail(me.self.TOUCH_MOVED);
        }
    },
    onTouchEnd: function(e) {
        var me = this,
            maxDuration = me.getMaxDuration(),
            time = e.time,
            target = e.target,
            lastTapTime = me.lastTapTime,
            lastTarget = me.lastTarget,
            point = e.changedTouches[0].point,
            duration;
        me.lastTapTime = time;
        me.lastTarget = target;
        if (lastTapTime) {
            duration = time - lastTapTime;
            if (duration <= maxDuration && Math.abs(point.getDistanceTo(me.startPoint)) <= me.getTapDistance()) {
                if (target !== lastTarget) {
                    return me.fail(me.self.DIFFERENT_TARGET);
                }
                me.lastTarget = null;
                me.lastTapTime = 0;
                me.fire('doubletap', e, {
                    touch: e.changedTouches[0],
                    duration: duration
                });
                me.startPoint = null;
                return;
            }
        }
        if (time - me.startTime > maxDuration) {
            me.fireSingleTap(e);
        } else {
            me.setSingleTapTimer(e);
        }
    },
    setSingleTapTimer: function(e) {
        var me = this;
        me.singleTapTimer = Ext.defer(function() {
            me.fireSingleTap(e);
        }, me.getMaxDuration());
    },
    fireSingleTap: function(e, touch) {
        this.fire('singletap', e, {
            touch: touch
        });
        this.startPoint = null;
    },
    reset: function() {
        var me = this;
        me.startTime = me.lastTapTime = 0;
        me.lastStartPoint = me.startPoint = me.singleTapTimer = null;
    }
}, function(DoubleTap) {
    var gestures = Ext.manifest.gestures;
    DoubleTap.instance = new DoubleTap(gestures && gestures.doubleTap);
});


Ext.define('Ext.event.gesture.Drag', {
    extend: 'Ext.event.gesture.SingleTouch',
    priority: 100,
    isStarted: false,
    startPoint: null,
    previousPoint: null,
    lastPoint: null,
    handledEvents: [
        'dragstart',
        'drag',
        'dragend',
        'dragcancel'
    ],
    config: {
        
        minDistance: 8
    },
    constructor: function() {
        this.callParent(arguments);
        this.initInfo();
    },
    initInfo: function() {
        this.info = {
            touch: null,
            previous: {
                x: 0,
                y: 0
            },
            x: 0,
            y: 0,
            delta: {
                x: 0,
                y: 0
            },
            absDelta: {
                x: 0,
                y: 0
            },
            flick: {
                velocity: {
                    x: 0,
                    y: 0
                }
            },
            direction: {
                x: 0,
                y: 0
            },
            time: 0,
            previousTime: {
                x: 0,
                y: 0
            }
        };
    },
    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            if (this.isStarted && this.lastMoveEvent !== null) {
                this.lastMoveEvent.isStopped = false;
                this.onTouchEnd(this.lastMoveEvent);
            }
            return false;
        }
        this.startTime = e.time;
        this.startPoint = e.changedTouches[0].point;
    },
    tryDragStart: function(e) {
        var startPoint = this.startPoint,
            touch = e.changedTouches[0],
            point = touch.point,
            minDistance = this.getMinDistance(),
            info = this.info;
        if (Math.abs(point.getDistanceTo(startPoint)) >= minDistance) {
            this.isStarted = true;
            this.previousPoint = this.lastPoint = point;
            this.resetInfo('x', e, touch);
            this.resetInfo('y', e, touch);
            info.time = e.time;
            this.fire('dragstart', e, info);
        }
    },
    onTouchMove: function(e) {
        if (!this.isStarted) {
            this.tryDragStart(e);
        }
        if (!this.isStarted) {
            return;
        }
        var touch = e.changedTouches[0],
            point = touch.point;
        if (this.lastPoint) {
            this.previousPoint = this.lastPoint;
        }
        this.lastPoint = point;
        this.lastMoveEvent = e;
        this.updateInfo('x', e, touch);
        this.updateInfo('y', e, touch);
        this.info.time = e.time;
        this.fire('drag', e, this.info);
    },
    onAxisDragEnd: function(axis, info) {
        var duration = info.time - info.previousTime[axis];
        if (duration > 0) {
            info.flick.velocity[axis] = (info[axis] - info.previous[axis]) / duration;
        }
    },
    resetInfo: function(axis, e, touch) {
        var value = this.lastPoint[axis],
            startValue = this.startPoint[axis],
            delta = value - startValue,
            capAxis = axis.toUpperCase(),
            info = this.info;
        info.touch = touch;
        info.delta[axis] = delta;
        info.absDelta[axis] = Math.abs(delta);
        info.previousTime[axis] = this.startTime;
        info.previous[axis] = startValue;
        info[axis] = value;
        info.direction[axis] = 0;
        info['start' + capAxis] = this.startPoint[axis];
        info['previous' + capAxis] = info.previous[axis];
        info['page' + capAxis] = info[axis];
        info['delta' + capAxis] = info.delta[axis];
        info['absDelta' + capAxis] = info.absDelta[axis];
        info['previousDelta' + capAxis] = 0;
        info.startTime = this.startTime;
    },
    updateInfo: function(axis, e, touch) {
        var me = this,
            value = me.lastPoint[axis],
            previousValue = me.previousPoint[axis],
            startValue = me.startPoint[axis],
            delta = value - startValue,
            info = me.info,
            direction = info.direction,
            capAxis = axis.toUpperCase(),
            previousFlick = info.previous[axis];
        info.touch = touch;
        info.delta[axis] = delta;
        info.absDelta[axis] = Math.abs(delta);
        if (value !== previousFlick && value !== info[axis]) {
            info.previous[axis] = info[axis];
            info.previousTime[axis] = info.time;
        }
        info[axis] = value;
        if (value > previousValue) {
            direction[axis] = 1;
        } else if (value < previousValue) {
            direction[axis] = -1;
        }
        info['start' + capAxis] = startValue;
        info['previous' + capAxis] = info.previous[axis];
        info['page' + capAxis] = info[axis];
        info['delta' + capAxis] = info.delta[axis];
        info['absDelta' + capAxis] = info.absDelta[axis];
        info['previousDelta' + capAxis] = info.previous[axis] - startValue;
        info.startTime = me.startTime;
    },
    onTouchEnd: function(e) {
        this.doEnd(e);
    },
    onTouchCancel: function(e) {
        this.doEnd(e, true);
        return false;
    },
    doEnd: function(e, isCancel) {
        if (!this.isStarted) {
            this.tryDragStart(e);
        }
        if (this.isStarted) {
            var touch = e.changedTouches[0],
                point = touch.point,
                info = this.info;
            this.isStarted = false;
            this.lastPoint = point;
            this.updateInfo('x', e, touch);
            this.updateInfo('y', e, touch);
            info.time = e.time;
            this.onAxisDragEnd('x', info);
            this.onAxisDragEnd('y', info);
            this.fire(isCancel ? 'dragcancel' : 'dragend', e, info);
            this.startPoint = null;
            this.previousPoint = null;
            this.lastPoint = null;
            this.lastMoveEvent = null;
        }
    },
    reset: function() {
        var me = this;
        me.isStarted = me.lastPoint = me.startPoint = me.previousPoint = me.lastPoint = me.lastMoveEvent = null;
        me.initInfo();
    }
}, function(Drag) {
    var gestures = Ext.manifest.gestures;
    Drag.instance = new Drag(gestures && gestures.drag);
});


Ext.define('Ext.event.gesture.Swipe', {
    extend: 'Ext.event.gesture.SingleTouch',
    priority: 500,
    handledEvents: [
        'swipestart',
        'swipe',
        'swipecancel'
    ],
    
    
    
    inheritableStatics: {
        MAX_OFFSET_EXCEEDED: 'Max Offset Exceeded',
        MAX_DURATION_EXCEEDED: 'Max Duration Exceeded',
        DISTANCE_NOT_ENOUGH: 'Distance Not Enough'
    },
    config: {
        minDistance: 80,
        maxOffset: 35,
        maxDuration: 1000
    },
    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }
        var touch = e.changedTouches[0];
        this.startTime = e.time;
        this.isHorizontal = true;
        this.isVertical = true;
        this.startX = touch.pageX;
        this.startY = touch.pageY;
    },
    onTouchMove: function(e) {
        var touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - this.startX,
            deltaY = y - this.startY,
            absDeltaX = Math.abs(x - this.startX),
            absDeltaY = Math.abs(y - this.startY),
            duration = e.time - this.startTime,
            minDistance = this.getMinDistance(),
            time = e.time,
            direction, distance;
        if (time - this.startTime > this.getMaxDuration()) {
            return this.fail(this.self.MAX_DURATION_EXCEEDED);
        }
        if (this.isHorizontal && absDeltaY > this.getMaxOffset()) {
            this.isHorizontal = false;
        }
        if (this.isVertical && absDeltaX > this.getMaxOffset()) {
            this.isVertical = false;
        }
        if (!this.isVertical || !this.isHorizontal) {
            if (this.isHorizontal && absDeltaX < minDistance) {
                direction = (deltaX < 0) ? 'left' : 'right';
                distance = absDeltaX;
            } else if (this.isVertical && absDeltaY < minDistance) {
                direction = (deltaY < 0) ? 'up' : 'down';
                distance = absDeltaY;
            }
        }
        if (direction && !this.started) {
            this.started = true;
            this.fire('swipestart', e, {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        }
        if (!this.isHorizontal && !this.isVertical) {
            return this.fail(this.self.MAX_OFFSET_EXCEEDED);
        }
    },
    onTouchEnd: function(e) {
        if (this.onTouchMove(e) === false) {
            return false;
        }
        var touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - this.startX,
            deltaY = y - this.startY,
            absDeltaX = Math.abs(deltaX),
            absDeltaY = Math.abs(deltaY),
            minDistance = this.getMinDistance(),
            duration = e.time - this.startTime,
            direction, distance;
        if (this.isVertical && absDeltaY < minDistance) {
            this.isVertical = false;
        }
        if (this.isHorizontal && absDeltaX < minDistance) {
            this.isHorizontal = false;
        }
        if (this.isHorizontal) {
            direction = (deltaX < 0) ? 'left' : 'right';
            distance = absDeltaX;
        } else if (this.isVertical) {
            direction = (deltaY < 0) ? 'up' : 'down';
            distance = absDeltaY;
        } else {
            return this.fail(this.self.DISTANCE_NOT_ENOUGH);
        }
        this.started = false;
        this.fire('swipe', e, {
            touch: touch,
            direction: direction,
            distance: distance,
            duration: duration
        });
    },
    onTouchCancel: function(e) {
        this.fire('swipecancel', e);
        return false;
    },
    reset: function() {
        var me = this;
        me.startTime = me.isHorizontal = me.isVertical = me.startX = me.startY = null;
    }
}, function(Swipe) {
    var gestures = Ext.manifest.gestures;
    Swipe.instance = new Swipe(gestures && gestures.swipe);
});


Ext.define('Ext.event.gesture.EdgeSwipe', {
    extend: 'Ext.event.gesture.Swipe',
    priority: 800,
    handledEvents: [
        'edgeswipe',
        'edgeswipestart',
        'edgeswipeend',
        'edgeswipecancel'
    ],
    inheritableStatics: {
        NOT_NEAR_EDGE: 'Not Near Edge'
    },
    config: {
        minDistance: 60
    },
    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }
        var touch = e.changedTouches[0];
        this.started = false;
        this.direction = null;
        this.isHorizontal = true;
        this.isVertical = true;
        this.startX = touch.pageX;
        this.startY = touch.pageY;
    },
    onTouchMove: function(e) {
        var touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - this.startX,
            deltaY = y - this.startY,
            absDeltaY = Math.abs(y - this.startY),
            absDeltaX = Math.abs(x - this.startX),
            minDistance = this.getMinDistance(),
            maxOffset = this.getMaxOffset(),
            duration = e.time - this.startTime,
            elementWidth = Ext.Viewport && Ext.Element.getViewportWidth(),
            elementHeight = Ext.Viewport && Ext.Element.getViewportHeight(),
            direction, distance;
        
        if (this.isVertical && absDeltaX > maxOffset) {
            this.isVertical = false;
        }
        
        if (this.isHorizontal && absDeltaY > maxOffset) {
            this.isHorizontal = false;
        }
        
        if (this.isVertical && this.isHorizontal) {
            if (absDeltaY > absDeltaX) {
                this.isHorizontal = false;
            } else {
                this.isVertical = false;
            }
        }
        
        if (this.isHorizontal) {
            direction = (deltaX < 0) ? 'left' : 'right';
            distance = deltaX;
        } else if (this.isVertical) {
            direction = (deltaY < 0) ? 'up' : 'down';
            distance = deltaY;
        }
        this.direction = this.direction || direction;
        
        if (this.direction == 'up') {
            distance = deltaY * -1;
        } else if (this.direction == 'left') {
            distance = deltaX * -1;
        }
        this.distance = distance;
        if (distance == 0) {
            return this.fail(this.self.DISTANCE_NOT_ENOUGH);
        }
        if (!this.started) {
            
            if (this.direction == 'right' && this.startX > minDistance) {
                return this.fail(this.self.NOT_NEAR_EDGE);
            } else if (this.direction == 'down' && this.startY > minDistance) {
                return this.fail(this.self.NOT_NEAR_EDGE);
            } else if (this.direction == 'left' && (elementWidth - this.startX) > minDistance) {
                return this.fail(this.self.NOT_NEAR_EDGE);
            } else if (this.direction == 'up' && (elementHeight - this.startY) > minDistance) {
                return this.fail(this.self.NOT_NEAR_EDGE);
            }
            
            this.started = true;
            this.startTime = e.time;
            this.fire('edgeswipestart', e, {
                touch: touch,
                direction: this.direction,
                distance: this.distance,
                duration: duration
            });
        } else {
            this.fire('edgeswipe', e, {
                touch: touch,
                direction: this.direction,
                distance: this.distance,
                duration: duration
            });
        }
    },
    onTouchEnd: function(e) {
        var duration;
        if (this.onTouchMove(e) !== false) {
            duration = e.time - this.startTime;
            this.fire('edgeswipeend', e, {
                touch: e.changedTouches[0],
                direction: this.direction,
                distance: this.distance,
                duration: duration
            });
        }
    },
    onTouchCancel: function(e) {
        this.fire('edgeswipecancel', e, {
            touch: e.changedTouches[0]
        });
        return false;
    },
    reset: function() {
        var me = this;
        me.started = me.direction = me.isHorizontal = me.isVertical = me.startX = me.startY = me.startTime = me.distance = null;
    }
}, function(EdgeSwipe) {
    var gestures = Ext.manifest.gestures;
    EdgeSwipe.instance = new EdgeSwipe(gestures && gestures.edgeSwipe);
});


Ext.define('Ext.event.gesture.LongPress', {
    extend: 'Ext.event.gesture.SingleTouch',
    priority: 400,
    inheritableStatics: {
        DURATION_NOT_ENOUGH: 'Duration Not Enough'
    },
    config: {
        moveDistance: 8,
        minDuration: 1000
    },
    handledEvents: [
        'longpress',
        'taphold'
    ],
    
    
    fireLongPress: function(e) {
        this.fire('longpress', e, {
            touch: e.changedTouches[0],
            duration: this.getMinDuration()
        });
        this.isLongPress = true;
    },
    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }
        this.startPoint = e.changedTouches[0].point;
        this.isLongPress = false;
        this.setLongPressTimer(e);
    },
    setLongPressTimer: function(e) {
        var me = this;
        me.timer = Ext.defer(function() {
            me.fireLongPress(e);
        }, me.getMinDuration());
    },
    onTouchMove: function(e) {
        var point = e.changedTouches[0].point;
        if (Math.abs(point.getDistanceTo(this.startPoint)) >= this.getMoveDistance()) {
            return this.fail(this.self.TOUCH_MOVED);
        }
    },
    onTouchEnd: function() {
        if (!this.isLongPress) {
            return this.fail(this.self.DURATION_NOT_ENOUGH);
        }
    },
    fail: function() {
        clearTimeout(this.timer);
        return this.callParent(arguments);
    },
    reset: function() {
        this.isLongPress = this.startPoint = null;
    },
    fire: function(eventName) {
        if (eventName === 'longpress') {
            var args = Array.prototype.slice.call(arguments);
            args[0] = 'taphold';
            this.fire.apply(this, args);
        }
        return this.callParent(arguments);
    }
}, function(LongPress) {
    var gestures = Ext.manifest.gestures;
    LongPress.instance = new LongPress(gestures && gestures.longPress);
});


Ext.define('Ext.event.gesture.MultiTouch', {
    extend: 'Ext.event.gesture.Recognizer',
    requiredTouchesCount: 2,
    isTracking: false,
    isStarted: false,
    onTouchStart: function(e) {
        var requiredTouchesCount = this.requiredTouchesCount,
            touches = e.touches,
            touchesCount = touches.length;
        if (touchesCount === requiredTouchesCount) {
            this.start(e);
        } else if (touchesCount > requiredTouchesCount) {
            this.end(e);
        }
    },
    onTouchEnd: function(e) {
        this.end(e);
    },
    onTouchCancel: function(e) {
        this.end(e, true);
        return false;
    },
    start: function() {
        if (!this.isTracking) {
            this.isTracking = true;
            this.isStarted = false;
        }
    },
    end: function(e, isCancel) {
        if (this.isTracking) {
            this.isTracking = false;
            if (this.isStarted) {
                this.isStarted = false;
                this[isCancel ? 'fireCancel' : 'fireEnd'](e);
            }
        }
    },
    reset: function() {
        this.isTracking = this.isStarted = false;
    }
});


Ext.define('Ext.event.gesture.Pinch', {
    extend: 'Ext.event.gesture.MultiTouch',
    priority: 600,
    handledEvents: [
        'pinchstart',
        'pinch',
        'pinchend',
        'pinchcancel'
    ],
    
    
    
    
    startDistance: 0,
    lastTouches: null,
    onTouchMove: function(e) {
        if (!this.isTracking) {
            return;
        }
        var touches = e.touches,
            firstPoint, secondPoint, distance;
        firstPoint = touches[0].point;
        secondPoint = touches[1].point;
        distance = firstPoint.getDistanceTo(secondPoint);
        if (distance === 0) {
            return;
        }
        if (!this.isStarted) {
            this.isStarted = true;
            this.startDistance = distance;
            this.fire('pinchstart', e, {
                touches: touches,
                distance: distance,
                scale: 1
            });
        } else {
            this.fire('pinch', e, {
                touches: touches,
                distance: distance,
                scale: distance / this.startDistance
            });
        }
    },
    fireEnd: function(e) {
        this.fire('pinchend', e);
    },
    fireCancel: function(e) {
        this.fire('pinchcancel', e);
    },
    fail: function() {
        return this.callParent(arguments);
    },
    reset: function() {
        this.lastTouches = null;
        this.startDistance = 0;
        this.callParent();
    }
}, function(Pinch) {
    var gestures = Ext.manifest.gestures;
    Pinch.instance = new Pinch(gestures && gestures.pinch);
});


Ext.define('Ext.event.gesture.Rotate', {
    extend: 'Ext.event.gesture.MultiTouch',
    priority: 700,
    handledEvents: [
        'rotatestart',
        'rotate',
        'rotateend',
        'rotatecancel'
    ],
    
    
    
    
    
    startAngle: 0,
    lastTouches: null,
    lastAngle: null,
    onTouchMove: function(e) {
        if (!this.isTracking) {
            return;
        }
        var touches = e.touches,
            lastAngle = this.lastAngle,
            firstPoint, secondPoint, angle, nextAngle, previousAngle, diff;
        firstPoint = touches[0].point;
        secondPoint = touches[1].point;
        angle = firstPoint.getAngleTo(secondPoint);
        if (lastAngle !== null) {
            diff = Math.abs(lastAngle - angle);
            nextAngle = angle + 360;
            previousAngle = angle - 360;
            if (Math.abs(nextAngle - lastAngle) < diff) {
                angle = nextAngle;
            } else if (Math.abs(previousAngle - lastAngle) < diff) {
                angle = previousAngle;
            }
        }
        this.lastAngle = angle;
        if (!this.isStarted) {
            this.isStarted = true;
            this.startAngle = angle;
            this.fire('rotatestart', e, {
                touches: touches,
                angle: angle,
                rotation: 0
            });
        } else {
            this.fire('rotate', e, {
                touches: touches,
                angle: angle,
                rotation: angle - this.startAngle
            });
        }
        this.lastTouches = Ext.Array.clone(touches);
    },
    fireEnd: function(e) {
        this.lastAngle = null;
        this.fire('rotateend', e);
    },
    fireCancel: function(e) {
        this.lastAngle = null;
        this.fire('rotatecancel', e);
    },
    reset: function() {
        var me = this;
        me.lastTouches = me.lastAngle = me.startAngle = null;
        this.callParent();
    }
}, function(Rotate) {
    var gestures = Ext.manifest.gestures;
    Rotate.instance = new Rotate(gestures && gestures.rotate);
});


Ext.define('Ext.event.gesture.Tap', {
    extend: 'Ext.event.gesture.SingleTouch',
    priority: 200,
    handledEvents: [
        'tap',
        'tapcancel'
    ],
    config: {
        
        moveDistance: 8
    },
    onTouchStart: function(e) {
        if (this.callParent([
            e
        ]) === false) {
            return false;
        }
        this.startPoint = e.changedTouches[0].point;
    },
    onTouchMove: function(e) {
        var touch = e.changedTouches[0],
            point = touch.point;
        if (Math.abs(point.getDistanceTo(this.startPoint)) >= this.getMoveDistance()) {
            this.fire('tapcancel', e, {
                touch: touch
            });
            return this.fail(this.self.TOUCH_MOVED);
        }
    },
    onTouchEnd: function(e) {
        this.fire('tap', e, {
            touch: e.changedTouches[0]
        });
    },
    onTouchCancel: function(e) {
        this.fire('tapcancel', e, {
            touch: e.changedTouches[0]
        });
        return false;
    },
    reset: function() {
        this.startPoint = null;
    }
}, function(Tap) {
    var gestures = Ext.manifest.gestures;
    Tap.instance = new Tap(gestures && gestures.tap);
});


Ext.define('Ext.fx.State', {
    isAnimatable: {
        'background-color': true,
        'background-image': true,
        'background-position': true,
        'border-bottom-color': true,
        'border-bottom-width': true,
        'border-color': true,
        'border-left-color': true,
        'border-left-width': true,
        'border-right-color': true,
        'border-right-width': true,
        'border-spacing': true,
        'border-top-color': true,
        'border-top-width': true,
        'border-width': true,
        'bottom': true,
        'color': true,
        'crop': true,
        'font-size': true,
        'font-weight': true,
        'height': true,
        'left': true,
        'letter-spacing': true,
        'line-height': true,
        'margin-bottom': true,
        'margin-left': true,
        'margin-right': true,
        'margin-top': true,
        'max-height': true,
        'max-width': true,
        'min-height': true,
        'min-width': true,
        'opacity': true,
        'outline-color': true,
        'outline-offset': true,
        'outline-width': true,
        'padding-bottom': true,
        'padding-left': true,
        'padding-right': true,
        'padding-top': true,
        'right': true,
        'text-indent': true,
        'text-shadow': true,
        'top': true,
        'vertical-align': true,
        'visibility': true,
        'width': true,
        'word-spacing': true,
        'z-index': true,
        'zoom': true,
        'transform': true
    },
    constructor: function(data) {
        this.data = {};
        this.set(data);
    },
    setConfig: function(data) {
        this.set(data);
        return this;
    },
    setRaw: function(data) {
        this.data = data;
        return this;
    },
    clear: function() {
        return this.setRaw({});
    },
    setTransform: function(name, value) {
        var data = this.data,
            isArray = Ext.isArray(value),
            transform = data.transform,
            ln, key;
        if (!transform) {
            transform = data.transform = {
                translateX: 0,
                translateY: 0,
                translateZ: 0,
                scaleX: 1,
                scaleY: 1,
                scaleZ: 1,
                rotate: 0,
                rotateX: 0,
                rotateY: 0,
                rotateZ: 0,
                skewX: 0,
                skewY: 0
            };
        }
        if (typeof name == 'string') {
            switch (name) {
                case 'translate':
                    if (isArray) {
                        ln = value.length;
                        if (ln == 0) {
                            break;
                        }
                        transform.translateX = value[0];
                        if (ln == 1) {
                            break;
                        }
                        transform.translateY = value[1];
                        if (ln == 2) {
                            break;
                        }
                        transform.translateZ = value[2];
                    } else {
                        transform.translateX = value;
                    };
                    break;
                case 'rotate':
                    if (isArray) {
                        ln = value.length;
                        if (ln == 0) {
                            break;
                        }
                        transform.rotateX = value[0];
                        if (ln == 1) {
                            break;
                        }
                        transform.rotateY = value[1];
                        if (ln == 2) {
                            break;
                        }
                        transform.rotateZ = value[2];
                    } else {
                        transform.rotate = value;
                    };
                    break;
                case 'scale':
                    if (isArray) {
                        ln = value.length;
                        if (ln == 0) {
                            break;
                        }
                        transform.scaleX = value[0];
                        if (ln == 1) {
                            break;
                        }
                        transform.scaleY = value[1];
                        if (ln == 2) {
                            break;
                        }
                        transform.scaleZ = value[2];
                    } else {
                        transform.scaleX = value;
                        transform.scaleY = value;
                    };
                    break;
                case 'skew':
                    if (isArray) {
                        ln = value.length;
                        if (ln == 0) {
                            break;
                        }
                        transform.skewX = value[0];
                        if (ln == 1) {
                            break;
                        }
                        transform.skewY = value[1];
                    } else {
                        transform.skewX = value;
                    };
                    break;
                default:
                    transform[name] = value;
            }
        } else {
            for (key in name) {
                if (name.hasOwnProperty(key)) {
                    value = name[key];
                    this.setTransform(key, value);
                }
            }
        }
    },
    set: function(name, value) {
        var data = this.data,
            key;
        if (typeof name != 'string') {
            for (key in name) {
                value = name[key];
                if (key === 'transform') {
                    this.setTransform(value);
                } else {
                    data[key] = value;
                }
            }
        } else {
            if (name === 'transform') {
                this.setTransform(value);
            } else {
                data[name] = value;
            }
        }
        return this;
    },
    unset: function(name) {
        var data = this.data;
        if (data.hasOwnProperty(name)) {
            delete data[name];
        }
        return this;
    },
    getData: function() {
        return this.data;
    }
});


Ext.define('Ext.fx.animation.Abstract', {
    extend: 'Ext.Evented',
    isAnimation: true,
    requires: [
        'Ext.fx.State'
    ],
    config: {
        name: '',
        element: null,
        
        before: null,
        from: {},
        to: {},
        after: null,
        states: {},
        duration: 300,
        
        easing: 'linear',
        iteration: 1,
        direction: 'normal',
        delay: 0,
        onBeforeStart: null,
        onEnd: null,
        onBeforeEnd: null,
        scope: null,
        reverse: null,
        preserveEndState: false,
        replacePrevious: true
    },
    STATE_FROM: '0%',
    STATE_TO: '100%',
    DIRECTION_UP: 'up',
    DIRECTION_DOWN: 'down',
    DIRECTION_LEFT: 'left',
    DIRECTION_RIGHT: 'right',
    stateNameRegex: /^(?:[\d\.]+)%$/,
    constructor: function() {
        this.states = {};
        this.callParent(arguments);
        return this;
    },
    applyElement: function(element) {
        return Ext.get(element);
    },
    applyBefore: function(before, current) {
        if (before) {
            return Ext.factory(before, Ext.fx.State, current);
        }
    },
    applyAfter: function(after, current) {
        if (after) {
            return Ext.factory(after, Ext.fx.State, current);
        }
    },
    setFrom: function(from) {
        return this.setState(this.STATE_FROM, from);
    },
    setTo: function(to) {
        return this.setState(this.STATE_TO, to);
    },
    getFrom: function() {
        return this.getState(this.STATE_FROM);
    },
    getTo: function() {
        return this.getState(this.STATE_TO);
    },
    setStates: function(states) {
        var validNameRegex = this.stateNameRegex,
            name;
        for (name in states) {
            if (validNameRegex.test(name)) {
                this.setState(name, states[name]);
            }
        }
        return this;
    },
    getStates: function() {
        return this.states;
    },
    end: function() {
        
        
        this.stop();
    },
    stop: function() {
        this.fireEvent('stop', this);
    },
    destroy: function() {
        this.stop();
        this.callParent();
    },
    setState: function(name, state) {
        var states = this.getStates(),
            stateInstance;
        stateInstance = Ext.factory(state, Ext.fx.State, states[name]);
        if (stateInstance) {
            states[name] = stateInstance;
        } else if (name === this.STATE_TO) {
            Ext.Logger.error("Setting and invalid '100%' / 'to' state of: " + state);
        }
        return this;
    },
    getState: function(name) {
        return this.getStates()[name];
    },
    getData: function() {
        var states = this.getStates(),
            statesData = {},
            before = this.getBefore(),
            after = this.getAfter(),
            from = states[this.STATE_FROM],
            to = states[this.STATE_TO],
            fromData = from.getData(),
            toData = to.getData(),
            data, name, state;
        for (name in states) {
            if (states.hasOwnProperty(name)) {
                state = states[name];
                data = state.getData();
                statesData[name] = data;
            }
        }
        if (Ext.browser.is.AndroidStock2) {
            statesData['0.0001%'] = fromData;
        }
        return {
            before: before ? before.getData() : {},
            after: after ? after.getData() : {},
            states: statesData,
            from: fromData,
            to: toData,
            duration: this.getDuration(),
            iteration: this.getIteration(),
            direction: this.getDirection(),
            easing: this.getEasing(),
            delay: this.getDelay(),
            onEnd: this.getOnEnd(),
            onBeforeEnd: this.getOnBeforeEnd(),
            onBeforeStart: this.getOnBeforeStart(),
            scope: this.getScope(),
            preserveEndState: this.getPreserveEndState(),
            replacePrevious: this.getReplacePrevious()
        };
    }
});


Ext.define('Ext.fx.animation.Slide', {
    extend: 'Ext.fx.animation.Abstract',
    alternateClassName: 'Ext.fx.animation.SlideIn',
    alias: [
        'animation.slide',
        'animation.slideIn'
    ],
    config: {
        
        direction: 'left',
        
        out: false,
        
        offset: 0,
        
        easing: 'auto',
        containerBox: 'auto',
        elementBox: 'auto',
        isElementBoxFit: true,
        useCssTransform: true
    },
    reverseDirectionMap: {
        up: 'down',
        down: 'up',
        left: 'right',
        right: 'left'
    },
    applyEasing: function(easing) {
        if (easing === 'auto') {
            return 'ease-' + ((this.getOut()) ? 'in' : 'out');
        }
        return easing;
    },
    getContainerBox: function() {
        var box = this._containerBox;
        if (box === 'auto') {
            box = this.getElement().getParent().getBox();
        }
        return box;
    },
    getElementBox: function() {
        var box = this._elementBox;
        if (this.getIsElementBoxFit()) {
            return this.getContainerBox();
        }
        if (box === 'auto') {
            box = this.getElement().getBox();
        }
        return box;
    },
    getData: function() {
        var elementBox = this.getElementBox(),
            containerBox = this.getContainerBox(),
            box = elementBox ? elementBox : containerBox,
            from = this.getFrom(),
            to = this.getTo(),
            out = this.getOut(),
            offset = this.getOffset(),
            direction = this.getDirection(),
            useCssTransform = this.getUseCssTransform(),
            reverse = this.getReverse(),
            translateX = 0,
            translateY = 0,
            fromX, fromY, toX, toY;
        if (reverse) {
            direction = this.reverseDirectionMap[direction];
        }
        switch (direction) {
            case this.DIRECTION_UP:
                if (out) {
                    translateY = containerBox.top - box.top - box.height - offset;
                } else {
                    translateY = containerBox.bottom - box.bottom + box.height + offset;
                };
                break;
            case this.DIRECTION_DOWN:
                if (out) {
                    translateY = containerBox.bottom - box.bottom + box.height + offset;
                } else {
                    translateY = containerBox.top - box.height - box.top - offset;
                };
                break;
            case this.DIRECTION_RIGHT:
                if (out) {
                    translateX = containerBox.right - box.right + box.width + offset;
                } else {
                    translateX = containerBox.left - box.left - box.width - offset;
                };
                break;
            case this.DIRECTION_LEFT:
                if (out) {
                    translateX = containerBox.left - box.left - box.width - offset;
                } else {
                    translateX = containerBox.right - box.right + box.width + offset;
                };
                break;
        }
        fromX = (out) ? 0 : translateX;
        fromY = (out) ? 0 : translateY;
        if (useCssTransform) {
            from.setTransform({
                translateX: fromX,
                translateY: fromY
            });
        } else {
            from.set('left', fromX);
            from.set('top', fromY);
        }
        toX = (out) ? translateX : 0;
        toY = (out) ? translateY : 0;
        if (useCssTransform) {
            to.setTransform({
                translateX: toX,
                translateY: toY
            });
        } else {
            to.set('left', toX);
            to.set('top', toY);
        }
        return this.callParent(arguments);
    }
});


Ext.define('Ext.fx.animation.SlideOut', {
    extend: 'Ext.fx.animation.Slide',
    alias: [
        'animation.slideOut'
    ],
    config: {
        
        out: true
    }
});


Ext.define('Ext.fx.animation.Fade', {
    extend: 'Ext.fx.animation.Abstract',
    alternateClassName: 'Ext.fx.animation.FadeIn',
    alias: [
        'animation.fade',
        'animation.fadeIn'
    ],
    config: {
        
        out: false,
        before: {
            display: null,
            opacity: 0
        },
        after: {
            opacity: null
        },
        reverse: null
    },
    updateOut: function(newOut) {
        var to = this.getTo(),
            from = this.getFrom();
        if (newOut) {
            from.set('opacity', 1);
            to.set('opacity', 0);
        } else {
            from.set('opacity', 0);
            to.set('opacity', 1);
        }
    }
});


Ext.define('Ext.fx.animation.FadeOut', {
    extend: 'Ext.fx.animation.Fade',
    alias: 'animation.fadeOut',
    config: {
        
        out: true,
        before: {}
    }
});


Ext.define('Ext.fx.animation.Flip', {
    extend: 'Ext.fx.animation.Abstract',
    alias: 'animation.flip',
    config: {
        easing: 'ease-in',
        
        direction: 'right',
        half: false,
        out: null
    },
    getData: function() {
        var from = this.getFrom(),
            to = this.getTo(),
            direction = this.getDirection(),
            out = this.getOut(),
            half = this.getHalf(),
            rotate = (half) ? 90 : 180,
            fromScale = 1,
            toScale = 1,
            fromRotateX = 0,
            fromRotateY = 0,
            toRotateX = 0,
            toRotateY = 0;
        if (out) {
            toScale = 0.8;
        } else {
            fromScale = 0.8;
        }
        switch (direction) {
            case this.DIRECTION_UP:
                if (out) {
                    toRotateX = rotate;
                } else {
                    fromRotateX = -rotate;
                };
                break;
            case this.DIRECTION_DOWN:
                if (out) {
                    toRotateX = -rotate;
                } else {
                    fromRotateX = rotate;
                };
                break;
            case this.DIRECTION_RIGHT:
                if (out) {
                    toRotateY = rotate;
                } else {
                    fromRotateY = -rotate;
                };
                break;
            case this.DIRECTION_LEFT:
                if (out) {
                    toRotateY = -rotate;
                } else {
                    fromRotateY = rotate;
                };
                break;
        }
        from.setTransform({
            rotateX: fromRotateX,
            rotateY: fromRotateY,
            scale: fromScale
        });
        to.setTransform({
            rotateX: toRotateX,
            rotateY: toRotateY,
            scale: toScale
        });
        return this.callParent(arguments);
    }
});


Ext.define('Ext.fx.animation.Pop', {
    extend: 'Ext.fx.animation.Abstract',
    alias: [
        'animation.pop',
        'animation.popIn'
    ],
    alternateClassName: 'Ext.fx.animation.PopIn',
    config: {
        
        out: false,
        before: {
            display: null,
            opacity: 0
        },
        after: {
            opacity: null
        }
    },
    getData: function() {
        var to = this.getTo(),
            from = this.getFrom(),
            out = this.getOut();
        if (out) {
            from.set('opacity', 1);
            from.setTransform({
                scale: 1
            });
            to.set('opacity', 0);
            to.setTransform({
                scale: 0
            });
        } else {
            from.set('opacity', 0);
            from.setTransform({
                scale: 0
            });
            to.set('opacity', 1);
            to.setTransform({
                scale: 1
            });
        }
        return this.callParent(arguments);
    }
});


Ext.define('Ext.fx.animation.PopOut', {
    extend: 'Ext.fx.animation.Pop',
    alias: 'animation.popOut',
    config: {
        
        out: true,
        before: {}
    }
});


Ext.define('Ext.fx.Animation', {
    requires: [
        'Ext.fx.animation.Slide',
        'Ext.fx.animation.SlideOut',
        'Ext.fx.animation.Fade',
        'Ext.fx.animation.FadeOut',
        'Ext.fx.animation.Flip',
        'Ext.fx.animation.Pop',
        'Ext.fx.animation.PopOut'
    ],
    
    constructor: function(config) {
        var defaultClass = Ext.fx.animation.Abstract,
            type;
        if (typeof config == 'string') {
            type = config;
            config = {};
        } else if (config && config.type) {
            type = config.type;
        }
        if (type) {
            if (Ext.browser.is.AndroidStock2) {
                if (type == 'pop') {
                    type = 'fade';
                }
                if (type == 'popIn') {
                    type = 'fadeIn';
                }
                if (type == 'popOut') {
                    type = 'fadeOut';
                }
            }
            defaultClass = Ext.ClassManager.getByAlias('animation.' + type);
            if (!defaultClass) {
                Ext.Logger.error("Invalid animation type of: '" + type + "'");
            }
        }
        return Ext.factory(config, defaultClass);
    }
});


Ext.define('Ext.fx.runner.Css', {
    extend: 'Ext.Evented',
    requires: [
        'Ext.fx.Animation'
    ],
    prefixedProperties: {
        'transform': true,
        'transform-origin': true,
        'perspective': true,
        'transform-style': true,
        'transition': true,
        'transition-property': true,
        'transition-duration': true,
        'transition-timing-function': true,
        'transition-delay': true,
        'animation': true,
        'animation-name': true,
        'animation-duration': true,
        'animation-iteration-count': true,
        'animation-direction': true,
        'animation-timing-function': true,
        'animation-delay': true
    },
    lengthProperties: {
        'top': true,
        'right': true,
        'bottom': true,
        'left': true,
        'width': true,
        'height': true,
        'max-height': true,
        'max-width': true,
        'min-height': true,
        'min-width': true,
        'margin-bottom': true,
        'margin-left': true,
        'margin-right': true,
        'margin-top': true,
        'padding-bottom': true,
        'padding-left': true,
        'padding-right': true,
        'padding-top': true,
        'border-bottom-width': true,
        'border-left-width': true,
        'border-right-width': true,
        'border-spacing': true,
        'border-top-width': true,
        'border-width': true,
        'outline-width': true,
        'letter-spacing': true,
        'line-height': true,
        'text-indent': true,
        'word-spacing': true,
        'font-size': true,
        'translate': true,
        'translateX': true,
        'translateY': true,
        'translateZ': true,
        'translate3d': true
    },
    durationProperties: {
        'transition-duration': true,
        'transition-delay': true,
        'animation-duration': true,
        'animation-delay': true
    },
    angleProperties: {
        rotate: true,
        rotateX: true,
        rotateY: true,
        rotateZ: true,
        skew: true,
        skewX: true,
        skewY: true
    },
    lengthUnitRegex: /([a-z%]*)$/,
    DEFAULT_UNIT_LENGTH: 'px',
    DEFAULT_UNIT_ANGLE: 'deg',
    DEFAULT_UNIT_DURATION: 'ms',
    formattedNameCache: {},
    constructor: function() {
        var supports3dTransform = Ext.feature.has.Css3dTransforms;
        if (supports3dTransform) {
            this.transformMethods = [
                'translateX',
                'translateY',
                'translateZ',
                'rotate',
                'rotateX',
                'rotateY',
                'rotateZ',
                'skewX',
                'skewY',
                'scaleX',
                'scaleY',
                'scaleZ'
            ];
        } else {
            this.transformMethods = [
                'translateX',
                'translateY',
                'rotate',
                'skewX',
                'skewY',
                'scaleX',
                'scaleY'
            ];
        }
        this.vendorPrefix = Ext.browser.getStyleDashPrefix();
        this.ruleStylesCache = {};
        this.callParent();
    },
    getStyleSheet: function() {
        var styleSheet = this.styleSheet,
            styleElement, styleSheets;
        if (!styleSheet) {
            styleElement = document.createElement('style');
            styleElement.type = 'text/css';
            (document.head || document.getElementsByTagName('head')[0]).appendChild(styleElement);
            styleSheets = document.styleSheets;
            this.styleSheet = styleSheet = styleSheets[styleSheets.length - 1];
        }
        return styleSheet;
    },
    applyRules: function(selectors) {
        var styleSheet = this.getStyleSheet(),
            ruleStylesCache = this.ruleStylesCache,
            rules = styleSheet.cssRules,
            selector, properties, ruleStyle, ruleStyleCache, rulesLength, name, value;
        for (selector in selectors) {
            properties = selectors[selector];
            ruleStyle = ruleStylesCache[selector];
            if (ruleStyle === undefined) {
                rulesLength = rules.length;
                styleSheet.insertRule(selector + '{}', rulesLength);
                ruleStyle = ruleStylesCache[selector] = rules.item(rulesLength).style;
            }
            ruleStyleCache = ruleStyle.$cache;
            if (!ruleStyleCache) {
                ruleStyleCache = ruleStyle.$cache = {};
            }
            for (name in properties) {
                value = this.formatValue(properties[name], name);
                name = this.formatName(name);
                if (ruleStyleCache[name] !== value) {
                    ruleStyleCache[name] = value;
                    if (value === null) {
                        ruleStyle.removeProperty(name);
                    } else {
                        ruleStyle.setProperty(name, value, 'important');
                    }
                }
            }
        }
        return this;
    },
    applyStyles: function(styles) {
        var id, element, elementStyle, properties, name, value;
        for (id in styles) {
            if (styles.hasOwnProperty(id)) {
                element = document.getElementById(id);
                if (!element) {
                    return this;
                }
                elementStyle = element.style;
                properties = styles[id];
                for (name in properties) {
                    if (properties.hasOwnProperty(name)) {
                        value = this.formatValue(properties[name], name);
                        name = this.formatName(name);
                        if (value === null) {
                            elementStyle.removeProperty(name);
                        } else {
                            elementStyle.setProperty(name, value, 'important');
                        }
                    }
                }
            }
        }
        return this;
    },
    formatName: function(name) {
        var cache = this.formattedNameCache,
            formattedName = cache[name];
        if (!formattedName) {
            if ((Ext.os.is.Tizen || !Ext.feature.has.CssTransformNoPrefix) && this.prefixedProperties[name]) {
                formattedName = this.vendorPrefix + name;
            } else {
                formattedName = name;
            }
            cache[name] = formattedName;
        }
        return formattedName;
    },
    formatValue: function(value, name) {
        var type = typeof value,
            lengthUnit = this.DEFAULT_UNIT_LENGTH,
            transformMethods, method, i, ln, transformValues, values, unit;
        if (value === null) {
            return '';
        }
        if (type == 'string') {
            if (this.lengthProperties[name]) {
                unit = value.match(this.lengthUnitRegex)[1];
                if (unit.length > 0) {
                    if (unit !== lengthUnit) {
                        Ext.Logger.error("Length unit: '" + unit + "' in value: '" + value + "' of property: '" + name + "' is not " + "valid for animation. Only 'px' is allowed");
                    }
                } else {
                    return value + lengthUnit;
                }
            }
            return value;
        } else if (type == 'number') {
            if (value == 0) {
                return '0';
            }
            if (this.lengthProperties[name]) {
                return value + lengthUnit;
            }
            if (this.angleProperties[name]) {
                return value + this.DEFAULT_UNIT_ANGLE;
            }
            if (this.durationProperties[name]) {
                return value + this.DEFAULT_UNIT_DURATION;
            }
        } else if (name === 'transform') {
            transformMethods = this.transformMethods;
            transformValues = [];
            for (i = 0 , ln = transformMethods.length; i < ln; i++) {
                method = transformMethods[i];
                transformValues.push(method + '(' + this.formatValue(value[method], method) + ')');
            }
            return transformValues.join(' ');
        } else if (Ext.isArray(value)) {
            values = [];
            for (i = 0 , ln = value.length; i < ln; i++) {
                values.push(this.formatValue(value[i], name));
            }
            return (values.length > 0) ? values.join(', ') : 'none';
        }
        return value;
    }
});


Ext.define('Ext.fx.runner.CssTransition', {
    extend: 'Ext.fx.runner.Css',
    requires: [
        'Ext.AnimationQueue'
    ],
    alternateClassName: 'Ext.Animator',
    singleton: true,
    listenersAttached: false,
    constructor: function() {
        this.runningAnimationsData = {};
        return this.callParent(arguments);
    },
    attachListeners: function() {
        this.listenersAttached = true;
        Ext.getWin().on('transitionend', 'onTransitionEnd', this);
    },
    onTransitionEnd: function(e) {
        var target = e.target,
            id = target.id;
        if (id && this.runningAnimationsData.hasOwnProperty(id)) {
            this.refreshRunningAnimationsData(Ext.get(target), [
                e.browserEvent.propertyName
            ]);
        }
    },
    onAnimationEnd: function(element, data, animation, isInterrupted, isReplaced) {
        var id = element.getId(),
            runningData = this.runningAnimationsData[id],
            endRules = {},
            endData = {},
            runningNameMap, toPropertyNames, i, ln, name;
        animation.un('stop', 'onAnimationStop', this);
        if (runningData) {
            runningNameMap = runningData.nameMap;
        }
        endRules[id] = endData;
        if (data.onBeforeEnd) {
            data.onBeforeEnd.call(data.scope || this, element, isInterrupted);
        }
        animation.fireEvent('animationbeforeend', animation, element, isInterrupted);
        this.fireEvent('animationbeforeend', this, animation, element, isInterrupted);
        if (isReplaced || (!isInterrupted && !data.preserveEndState)) {
            toPropertyNames = data.toPropertyNames;
            for (i = 0 , ln = toPropertyNames.length; i < ln; i++) {
                name = toPropertyNames[i];
                if (runningNameMap && !runningNameMap.hasOwnProperty(name)) {
                    endData[name] = null;
                }
            }
        }
        if (data.after) {
            Ext.merge(endData, data.after);
        }
        this.applyStyles(endRules);
        if (data.onEnd) {
            data.onEnd.call(data.scope || this, element, isInterrupted);
        }
        animation.fireEvent('animationend', animation, element, isInterrupted);
        this.fireEvent('animationend', this, animation, element, isInterrupted);
        Ext.AnimationQueue.stop(Ext.emptyFn, animation);
    },
    onAllAnimationsEnd: function(element) {
        var id = element.getId(),
            endRules = {};
        delete this.runningAnimationsData[id];
        endRules[id] = {
            'transition-property': null,
            'transition-duration': null,
            'transition-timing-function': null,
            'transition-delay': null
        };
        this.applyStyles(endRules);
        this.fireEvent('animationallend', this, element);
    },
    hasRunningAnimations: function(element) {
        var id = element.getId(),
            runningAnimationsData = this.runningAnimationsData;
        return runningAnimationsData.hasOwnProperty(id) && runningAnimationsData[id].sessions.length > 0;
    },
    refreshRunningAnimationsData: function(element, propertyNames, interrupt, replace) {
        var id = element.getId(),
            runningAnimationsData = this.runningAnimationsData,
            runningData = runningAnimationsData[id];
        if (!runningData) {
            return;
        }
        var nameMap = runningData.nameMap,
            nameList = runningData.nameList,
            sessions = runningData.sessions,
            ln, j, subLn, name, i, session, map, list,
            hasCompletedSession = false;
        interrupt = Boolean(interrupt);
        replace = Boolean(replace);
        if (!sessions) {
            return this;
        }
        ln = sessions.length;
        if (ln === 0) {
            return this;
        }
        if (replace) {
            runningData.nameMap = {};
            nameList.length = 0;
            for (i = 0; i < ln; i++) {
                session = sessions[i];
                this.onAnimationEnd(element, session.data, session.animation, interrupt, replace);
            }
            sessions.length = 0;
        } else {
            for (i = 0; i < ln; i++) {
                session = sessions[i];
                map = session.map;
                list = session.list;
                for (j = 0 , subLn = propertyNames.length; j < subLn; j++) {
                    name = propertyNames[j];
                    if (map[name]) {
                        delete map[name];
                        Ext.Array.remove(list, name);
                        session.length--;
                        if (--nameMap[name] == 0) {
                            delete nameMap[name];
                            Ext.Array.remove(nameList, name);
                        }
                    }
                }
                if (session.length == 0) {
                    sessions.splice(i, 1);
                    i--;
                    ln--;
                    hasCompletedSession = true;
                    this.onAnimationEnd(element, session.data, session.animation, interrupt);
                }
            }
        }
        if (!replace && !interrupt && sessions.length == 0 && hasCompletedSession) {
            this.onAllAnimationsEnd(element);
        }
    },
    getRunningData: function(id) {
        var runningAnimationsData = this.runningAnimationsData;
        if (!runningAnimationsData.hasOwnProperty(id)) {
            runningAnimationsData[id] = {
                nameMap: {},
                nameList: [],
                sessions: []
            };
        }
        return runningAnimationsData[id];
    },
    getTestElement: function() {
        var testElement = this.testElement,
            iframe, iframeDocument, iframeStyle;
        if (!testElement) {
            iframe = document.createElement('iframe');
            iframe.setAttribute('tabindex', -1);
            iframeStyle = iframe.style;
            iframeStyle.setProperty('visibility', 'hidden', 'important');
            iframeStyle.setProperty('width', '0px', 'important');
            iframeStyle.setProperty('height', '0px', 'important');
            iframeStyle.setProperty('position', 'absolute', 'important');
            iframeStyle.setProperty('border', '0px', 'important');
            iframeStyle.setProperty('zIndex', '-1000', 'important');
            document.body.appendChild(iframe);
            iframeDocument = iframe.contentDocument;
            iframeDocument.open();
            iframeDocument.writeln('</body>');
            iframeDocument.close();
            this.testElement = testElement = iframeDocument.createElement('div');
            testElement.style.setProperty('position', 'absolute', 'important');
            iframeDocument.body.appendChild(testElement);
            this.testElementComputedStyle = window.getComputedStyle(testElement);
        }
        return testElement;
    },
    getCssStyleValue: function(name, value) {
        var testElement = this.getTestElement(),
            computedStyle = this.testElementComputedStyle,
            style = testElement.style;
        style.setProperty(name, value);
        if (Ext.browser.is.Firefox) {
            
            testElement.offsetHeight;
        }
        value = computedStyle.getPropertyValue(name);
        style.removeProperty(name);
        return value;
    },
    run: function(animations) {
        var me = this,
            isLengthPropertyMap = this.lengthProperties,
            fromData = {},
            toData = {},
            data = {},
            element, elementId, from, to, before, fromPropertyNames, toPropertyNames, doApplyTo, message, runningData, elementData, i, j, ln, animation, propertiesLength, sessionNameMap, computedStyle, formattedName, name, toFormattedValue, computedValue, fromFormattedValue, isLengthProperty, runningNameMap, runningNameList, runningSessions, runningSession;
        if (!this.listenersAttached) {
            this.attachListeners();
        }
        animations = Ext.Array.from(animations);
        for (i = 0 , ln = animations.length; i < ln; i++) {
            animation = animations[i];
            animation = Ext.factory(animation, Ext.fx.Animation);
            element = animation.getElement();
            
            Ext.AnimationQueue.start(Ext.emptyFn, animation);
            computedStyle = window.getComputedStyle(element.dom);
            elementId = element.getId();
            data = Ext.merge({}, animation.getData());
            if (animation.onBeforeStart) {
                animation.onBeforeStart.call(animation.scope || this, element);
            }
            animation.fireEvent('animationstart', animation);
            this.fireEvent('animationstart', this, animation);
            data[elementId] = data;
            before = data.before;
            from = data.from;
            to = data.to;
            data.fromPropertyNames = fromPropertyNames = [];
            data.toPropertyNames = toPropertyNames = [];
            for (name in to) {
                if (to.hasOwnProperty(name)) {
                    to[name] = toFormattedValue = this.formatValue(to[name], name);
                    formattedName = this.formatName(name);
                    isLengthProperty = isLengthPropertyMap.hasOwnProperty(name);
                    if (!isLengthProperty) {
                        toFormattedValue = this.getCssStyleValue(formattedName, toFormattedValue);
                    }
                    if (from.hasOwnProperty(name)) {
                        from[name] = fromFormattedValue = this.formatValue(from[name], name);
                        if (!isLengthProperty) {
                            fromFormattedValue = this.getCssStyleValue(formattedName, fromFormattedValue);
                        }
                        if (toFormattedValue !== fromFormattedValue) {
                            fromPropertyNames.push(formattedName);
                            toPropertyNames.push(formattedName);
                        }
                    } else {
                        computedValue = computedStyle.getPropertyValue(formattedName);
                        if (toFormattedValue !== computedValue) {
                            toPropertyNames.push(formattedName);
                        }
                    }
                }
            }
            propertiesLength = toPropertyNames.length;
            if (propertiesLength === 0) {
                this.onAnimationEnd(element, data, animation);
                
                continue;
            }
            runningData = this.getRunningData(elementId);
            runningSessions = runningData.sessions;
            if (runningSessions.length > 0) {
                this.refreshRunningAnimationsData(element, Ext.Array.merge(fromPropertyNames, toPropertyNames), true, data.replacePrevious);
            }
            runningNameMap = runningData.nameMap;
            runningNameList = runningData.nameList;
            sessionNameMap = {};
            for (j = 0; j < propertiesLength; j++) {
                name = toPropertyNames[j];
                sessionNameMap[name] = true;
                if (!runningNameMap.hasOwnProperty(name)) {
                    runningNameMap[name] = 1;
                    runningNameList.push(name);
                } else {
                    runningNameMap[name]++;
                }
            }
            runningSession = {
                element: element,
                map: sessionNameMap,
                list: toPropertyNames.slice(),
                length: propertiesLength,
                data: data,
                animation: animation
            };
            runningSessions.push(runningSession);
            animation.on('stop', 'onAnimationStop', this);
            elementData = Ext.apply({}, before);
            Ext.apply(elementData, from);
            if (runningNameList.length > 0) {
                fromPropertyNames = Ext.Array.difference(runningNameList, fromPropertyNames);
                toPropertyNames = Ext.Array.merge(fromPropertyNames, toPropertyNames);
                elementData['transition-property'] = fromPropertyNames;
            }
            fromData[elementId] = elementData;
            toData[elementId] = Ext.apply({}, to);
            toData[elementId]['transition-property'] = toPropertyNames;
            toData[elementId]['transition-duration'] = data.duration;
            toData[elementId]['transition-timing-function'] = data.easing;
            toData[elementId]['transition-delay'] = data.delay;
            animation.startTime = Date.now();
        }
        message = this.$className;
        this.applyStyles(fromData);
        doApplyTo = function(e) {
            if (e.data === message && e.source === window) {
                window.removeEventListener('message', doApplyTo, false);
                me.applyStyles(toData);
            }
        };
        if (Ext.browser.is.IE) {
            Ext.Function.requestAnimationFrame(function() {
                window.addEventListener('message', doApplyTo, false);
                window.postMessage(message, '*');
            });
        } else {
            window.addEventListener('message', doApplyTo, false);
            window.postMessage(message, '*');
        }
    },
    onAnimationStop: function(animation) {
        var runningAnimationsData = this.runningAnimationsData,
            id, runningData, sessions, i, ln, session;
        for (id in runningAnimationsData) {
            if (runningAnimationsData.hasOwnProperty(id)) {
                runningData = runningAnimationsData[id];
                sessions = runningData.sessions;
                for (i = 0 , ln = sessions.length; i < ln; i++) {
                    session = sessions[i];
                    if (session.animation === animation) {
                        this.refreshRunningAnimationsData(session.element, session.list.slice(), false);
                    }
                }
            }
        }
    }
});


Ext.define('Ext.fx.Runner', {
    requires: [
        'Ext.fx.runner.CssTransition'
    ],
    
    constructor: function() {
        return new Ext.fx.runner.CssTransition();
    }
});


Ext.define('Ext.fx.animation.Cube', {
    extend: 'Ext.fx.animation.Abstract',
    alias: 'animation.cube',
    config: {
        
        before: {},
        
        after: {},
        
        direction: 'right',
        out: false
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    getData: function() {
        var to = this.getTo(),
            from = this.getFrom(),
            before = this.getBefore(),
            after = this.getAfter(),
            out = this.getOut(),
            direction = this.getDirection(),
            el = this.getElement(),
            elW = el.getWidth(),
            elH = el.getHeight(),
            origin = out ? '100% 100%' : '0% 0%',
            fromOpacity = 1,
            toOpacity = 1,
            transformFrom = {
                rotateY: 0,
                translateZ: 0
            },
            transformTo = {
                rotateY: 0,
                translateZ: 0
            };
        if (direction == "left" || direction == "right") {
            if (out) {
                toOpacity = 0.5;
                transformTo.translateZ = elW;
                transformTo.rotateY = -90;
            } else {
                fromOpacity = 0.5;
                transformFrom.translateZ = elW;
                transformFrom.rotateY = 90;
            }
        }
        before['transform-origin'] = origin;
        after['transform-origin'] = null;
        to.set('transform', transformTo);
        from.set('transform', transformFrom);
        from.set('opacity', fromOpacity);
        to.set('opacity', toOpacity);
        return this.callParent(arguments);
    }
});


Ext.define('Ext.fx.animation.Wipe', {
    extend: 'Ext.fx.Animation',
    alternateClassName: 'Ext.fx.animation.WipeIn',
    config: {
        
        easing: 'ease-out',
        
        direction: 'right',
        
        out: false
    },
    refresh: function() {
        var me = this,
            el = me.getElement(),
            elBox = el.dom.getBoundingClientRect(),
            elWidth = elBox.width,
            elHeight = elBox.height,
            from = me.getFrom(),
            to = me.getTo(),
            out = me.getOut(),
            direction = me.getDirection(),
            maskFromX = 0,
            maskFromY = 0,
            maskToX = 0,
            maskToY = 0,
            mask, tmp;
        switch (direction) {
            case 'up':
                if (out) {
                    mask = '-webkit-gradient(linear, left top, left bottom, from(#000), to(transparent), color-stop(33%, #000), color-stop(66%, transparent))';
                    maskFromY = elHeight * 3 + 'px';
                    maskToY = elHeight + 'px';
                } else {
                    mask = '-webkit-gradient(linear, left top, left bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    maskFromY = -elHeight * 2 + 'px';
                    maskToY = 0;
                };
                break;
            case 'down':
                if (out) {
                    mask = '-webkit-gradient(linear, left top, left bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    maskFromY = -elHeight * 2 + 'px';
                    maskToY = 0;
                } else {
                    mask = '-webkit-gradient(linear, left top, left bottom, from(#000), to(transparent), color-stop(33%, #000), color-stop(66%, transparent))';
                    maskFromY = elHeight * 3 + 'px';
                    maskToY = elHeight + 'px';
                };
                break;
            case 'right':
                if (out) {
                    mask = '-webkit-gradient(linear, right top, left top, from(#000), to(transparent), color-stop(33%, #000), color-stop(66%, transparent))';
                    maskFromX = -elWidth * 2 + 'px';
                    maskToX = 0;
                } else {
                    mask = '-webkit-gradient(linear, right top, left top, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    maskToX = -elWidth * 2 + 'px';
                };
                break;
            case 'left':
                if (out) {
                    mask = '-webkit-gradient(linear, right top, left top, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    maskToX = -elWidth * 2 + 'px';
                } else {
                    mask = '-webkit-gradient(linear, right top, left top, from(#000), to(transparent), color-stop(33%, #000), color-stop(66%, transparent))';
                    maskFromX = -elWidth * 2 + 'px';
                    maskToX = 0;
                };
                break;
        }
        if (!out) {
            tmp = maskFromY;
            maskFromY = maskToY;
            maskToY = tmp;
            tmp = maskFromX;
            maskFromX = maskToX;
            maskToX = tmp;
        }
        from.set('mask-image', mask);
        from.set('mask-size', elWidth * 3 + 'px ' + elHeight * 3 + 'px');
        from.set('mask-position-x', maskFromX);
        from.set('mask-position-y', maskFromY);
        to.set('mask-position-x', maskToX);
        to.set('mask-position-y', maskToY);
    }
});


Ext.define('Ext.fx.animation.WipeOut', {
    extend: 'Ext.fx.animation.Wipe',
    config: {
        
        out: true
    }
});


Ext.define('Ext.fx.easing.EaseIn', {
    extend: 'Ext.fx.easing.Linear',
    alias: 'easing.ease-in',
    config: {
        exponent: 4,
        duration: 1500
    },
    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            duration = this.getDuration(),
            startValue = this.getStartValue(),
            endValue = this.getEndValue(),
            distance = this.distance,
            theta = deltaTime / duration,
            thetaEnd = Math.pow(theta, this.getExponent()),
            currentValue = startValue + (thetaEnd * distance);
        if (deltaTime >= duration) {
            this.isEnded = true;
            return endValue;
        }
        return currentValue;
    }
});


Ext.define('Ext.fx.easing.Easing', {
    requires: [
        'Ext.fx.easing.Linear'
    ],
    constructor: function(easing) {
        return Ext.factory(easing, Ext.fx.easing.Linear, null, 'easing');
    }
});


Ext.define('Ext.fx.layout.card.Abstract', {
    extend: 'Ext.Evented',
    isAnimation: true,
    config: {
        direction: 'left',
        duration: null,
        reverse: null,
        layout: null
    },
    updateLayout: function() {
        this.enable();
    },
    enable: function() {
        var layout = this.getLayout();
        if (layout) {
            layout.onBefore('activeitemchange', 'onActiveItemChange', this);
        }
    },
    disable: function() {
        var layout = this.getLayout();
        if (this.isAnimating) {
            this.stopAnimation();
        }
        if (layout) {
            layout.unBefore('activeitemchange', 'onActiveItemChange', this);
        }
    },
    onActiveItemChange: Ext.emptyFn,
    destroy: function() {
        var layout = this.getLayout();
        if (this.isAnimating) {
            this.stopAnimation();
        }
        if (layout) {
            layout.unBefore('activeitemchange', 'onActiveItemChange', this);
        }
        this.setLayout(null);
        if (this.observableId) {
            this.fireEvent('destroy', this);
            this.clearListeners();
            this.clearManagedListeners();
        }
    }
});


Ext.define('Ext.fx.layout.card.Style', {
    extend: 'Ext.fx.layout.card.Abstract',
    requires: [
        'Ext.fx.Animation'
    ],
    config: {
        inAnimation: {
            before: {
                visibility: null
            },
            preserveEndState: false,
            replacePrevious: true
        },
        outAnimation: {
            preserveEndState: false,
            replacePrevious: true
        }
    },
    constructor: function(config) {
        var inAnimation, outAnimation;
        this.callParent([
            config
        ]);
        this.endAnimationCounter = 0;
        inAnimation = this.getInAnimation();
        outAnimation = this.getOutAnimation();
        inAnimation.on('animationend', 'incrementEnd', this);
        outAnimation.on('animationend', 'incrementEnd', this);
    },
    updateDirection: function(direction) {
        this.getInAnimation().setDirection(direction);
        this.getOutAnimation().setDirection(direction);
    },
    updateDuration: function(duration) {
        this.getInAnimation().setDuration(duration);
        this.getOutAnimation().setDuration(duration);
    },
    updateReverse: function(reverse) {
        this.getInAnimation().setReverse(reverse);
        this.getOutAnimation().setReverse(reverse);
    },
    incrementEnd: function() {
        this.endAnimationCounter++;
        if (this.endAnimationCounter > 1) {
            this.endAnimationCounter = 0;
            this.fireEvent('animationend', this);
        }
    },
    applyInAnimation: function(animation, inAnimation) {
        return Ext.factory(animation, Ext.fx.Animation, inAnimation);
    },
    applyOutAnimation: function(animation, outAnimation) {
        return Ext.factory(animation, Ext.fx.Animation, outAnimation);
    },
    updateInAnimation: function(animation) {
        animation.setScope(this);
    },
    updateOutAnimation: function(animation) {
        animation.setScope(this);
    },
    onActiveItemChange: function(cardLayout, newItem, oldItem, options, controller) {
        var inAnimation = this.getInAnimation(),
            outAnimation = this.getOutAnimation(),
            inElement, outElement;
        if (newItem && oldItem && oldItem.isPainted()) {
            inElement = newItem.renderElement;
            outElement = oldItem.renderElement;
            inAnimation.setElement(inElement);
            outAnimation.setElement(outElement);
            outAnimation.setOnBeforeEnd(function(element, interrupted) {
                if (interrupted || Ext.Animator.hasRunningAnimations(element)) {
                    controller.firingArguments[1] = null;
                    controller.firingArguments[2] = null;
                }
            });
            outAnimation.setOnEnd(function() {
                controller.resume();
            });
            inElement.dom.style.setProperty('visibility', 'hidden', 'important');
            newItem.show();
            Ext.Animator.run([
                outAnimation,
                inAnimation
            ]);
            controller.pause();
        }
    },
    destroy: function() {
        Ext.destroy(this.getInAnimation(), this.getOutAnimation());
        this.callParent(arguments);
    }
});


Ext.define('Ext.fx.layout.card.Slide', {
    extend: 'Ext.fx.layout.card.Style',
    alias: 'fx.layout.card.slide',
    config: {
        inAnimation: {
            type: 'slide',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'slide',
            easing: 'ease-out',
            out: true
        }
    },
    updateReverse: function(reverse) {
        this.getInAnimation().setReverse(reverse);
        this.getOutAnimation().setReverse(reverse);
    }
});


Ext.define('Ext.fx.layout.card.Cover', {
    extend: 'Ext.fx.layout.card.Style',
    alias: 'fx.layout.card.cover',
    config: {
        reverse: null,
        inAnimation: {
            before: {
                'z-index': 100
            },
            after: {
                'z-index': 0
            },
            type: 'slide',
            easing: 'ease-out'
        },
        outAnimation: {
            easing: 'ease-out',
            from: {
                opacity: 0.99
            },
            to: {
                opacity: 1
            },
            out: true
        }
    },
    updateReverse: function(reverse) {
        this.getInAnimation().setReverse(reverse);
        this.getOutAnimation().setReverse(reverse);
    }
});


Ext.define('Ext.fx.layout.card.Reveal', {
    extend: 'Ext.fx.layout.card.Style',
    alias: 'fx.layout.card.reveal',
    config: {
        inAnimation: {
            easing: 'ease-out',
            from: {
                opacity: 0.99
            },
            to: {
                opacity: 1
            }
        },
        outAnimation: {
            before: {
                'z-index': 100
            },
            after: {
                'z-index': 0
            },
            type: 'slide',
            easing: 'ease-out',
            out: true
        }
    },
    updateReverse: function(reverse) {
        this.getInAnimation().setReverse(reverse);
        this.getOutAnimation().setReverse(reverse);
    }
});


Ext.define('Ext.fx.layout.card.Fade', {
    extend: 'Ext.fx.layout.card.Style',
    alias: 'fx.layout.card.fade',
    config: {
        reverse: null,
        inAnimation: {
            type: 'fade',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'fade',
            easing: 'ease-out',
            out: true
        }
    }
});


Ext.define('Ext.fx.layout.card.Flip', {
    extend: 'Ext.fx.layout.card.Style',
    alias: 'fx.layout.card.flip',
    config: {
        duration: 500,
        inAnimation: {
            type: 'flip',
            half: true,
            easing: 'ease-out',
            before: {
                'backface-visibility': 'hidden'
            },
            after: {
                'backface-visibility': null
            }
        },
        outAnimation: {
            type: 'flip',
            half: true,
            easing: 'ease-in',
            before: {
                'backface-visibility': 'hidden'
            },
            after: {
                'backface-visibility': null
            },
            out: true
        }
    },
    onActiveItemChange: function(cardLayout, newItem, oldItem, options, controller) {
        var parent = newItem.element.getParent();
        parent.addCls(Ext.baseCSSPrefix + 'layout-card-perspective');
        this.on('animationend', function() {
            parent.removeCls(Ext.baseCSSPrefix + 'layout-card-perspective');
        }, this, {
            single: true
        });
        this.callParent(arguments);
    },
    updateDuration: function(duration) {
        var halfDuration = duration / 2,
            inAnimation = this.getInAnimation(),
            outAnimation = this.getOutAnimation();
        inAnimation.setDelay(halfDuration);
        inAnimation.setDuration(halfDuration);
        outAnimation.setDuration(halfDuration);
    }
});


Ext.define('Ext.fx.layout.card.Pop', {
    extend: 'Ext.fx.layout.card.Style',
    alias: 'fx.layout.card.pop',
    config: {
        duration: 500,
        inAnimation: {
            type: 'pop',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'pop',
            easing: 'ease-in',
            out: true
        }
    },
    updateDuration: function(duration) {
        var halfDuration = duration / 2,
            inAnimation = this.getInAnimation(),
            outAnimation = this.getOutAnimation();
        inAnimation.setDelay(halfDuration);
        inAnimation.setDuration(halfDuration);
        outAnimation.setDuration(halfDuration);
    }
});


Ext.define('Ext.fx.layout.card.Scroll', {
    extend: 'Ext.fx.layout.card.Abstract',
    requires: [
        'Ext.fx.easing.Linear'
    ],
    alias: 'fx.layout.card.scroll',
    config: {
        duration: 150
    },
    constructor: function(config) {
        this.initConfig(config);
    },
    getEasing: function() {
        var easing = this.easing;
        if (!easing) {
            this.easing = easing = new Ext.fx.easing.Linear();
        }
        return easing;
    },
    updateDuration: function(duration) {
        this.getEasing().setDuration(duration);
    },
    onActiveItemChange: function(cardLayout, newItem, oldItem, options, controller) {
        var direction = this.getDirection(),
            easing = this.getEasing(),
            containerElement, inElement, outElement, containerWidth, containerHeight, reverse;
        if (newItem && oldItem) {
            if (this.isAnimating) {
                this.stopAnimation();
            }
            newItem.setWidth('100%');
            newItem.setHeight('100%');
            containerElement = this.getLayout().container.innerElement;
            containerWidth = containerElement.getWidth();
            containerHeight = containerElement.getHeight();
            inElement = newItem.renderElement;
            outElement = oldItem.renderElement;
            this.oldItem = oldItem;
            this.newItem = newItem;
            this.currentEventController = controller;
            this.containerElement = containerElement;
            this.isReverse = reverse = this.getReverse();
            newItem.show();
            if (direction == 'right') {
                direction = 'left';
                this.isReverse = reverse = !reverse;
            } else if (direction == 'down') {
                direction = 'up';
                this.isReverse = reverse = !reverse;
            }
            if (direction == 'left') {
                if (reverse) {
                    easing.setConfig({
                        startValue: containerWidth,
                        endValue: 0
                    });
                    containerElement.dom.scrollLeft = containerWidth;
                    outElement.setLeft(containerWidth);
                } else {
                    easing.setConfig({
                        startValue: 0,
                        endValue: containerWidth
                    });
                    inElement.setLeft(containerWidth);
                }
            } else {
                if (reverse) {
                    easing.setConfig({
                        startValue: containerHeight,
                        endValue: 0
                    });
                    containerElement.dom.scrollTop = containerHeight;
                    outElement.setTop(containerHeight);
                } else {
                    easing.setConfig({
                        startValue: 0,
                        endValue: containerHeight
                    });
                    inElement.setTop(containerHeight);
                }
            }
            this.startAnimation();
            controller.pause();
        }
    },
    startAnimation: function() {
        this.isAnimating = true;
        this.getEasing().setStartTime(Date.now());
        Ext.AnimationQueue.start(this.doAnimationFrame, this);
    },
    doAnimationFrame: function() {
        var easing = this.getEasing(),
            direction = this.getDirection(),
            scroll = 'scrollTop',
            value;
        if (direction == 'left' || direction == 'right') {
            scroll = 'scrollLeft';
        }
        if (easing.isEnded) {
            this.stopAnimation();
        } else {
            value = easing.getValue();
            this.containerElement.dom[scroll] = value;
        }
    },
    stopAnimation: function() {
        var me = this,
            direction = me.getDirection(),
            scroll = 'setTop',
            oldItem = me.oldItem,
            newItem = me.newItem;
        if (direction == 'left' || direction == 'right') {
            scroll = 'setLeft';
        }
        me.currentEventController.resume();
        if (me.isReverse && oldItem && oldItem.renderElement && oldItem.renderElement.dom) {
            oldItem.renderElement[scroll](null);
        } else if (newItem && newItem.renderElement && newItem.renderElement.dom) {
            newItem.renderElement[scroll](null);
        }
        Ext.AnimationQueue.stop(this.doAnimationFrame, this);
        me.isAnimating = false;
        me.fireEvent('animationend', me);
    }
});


Ext.define('Ext.fx.layout.Card', {
    requires: [
        'Ext.fx.layout.card.Slide',
        'Ext.fx.layout.card.Cover',
        'Ext.fx.layout.card.Reveal',
        'Ext.fx.layout.card.Fade',
        'Ext.fx.layout.card.Flip',
        'Ext.fx.layout.card.Pop',
        
        'Ext.fx.layout.card.Scroll'
    ],
    constructor: function(config) {
        var defaultClass = Ext.fx.layout.card.Abstract,
            type;
        if (!config) {
            return null;
        }
        if (typeof config == 'string') {
            type = config;
            config = {};
        } else if (config.type) {
            type = config.type;
        }
        config.elementBox = false;
        if (type) {
            if (Ext.browser.is.AndroidStock2) {
                
                if (type != 'fade') {
                    type = 'scroll';
                }
            }
            defaultClass = Ext.ClassManager.getByAlias('fx.layout.card.' + type);
            if (!defaultClass) {
                Ext.Logger.error("Unknown card animation type: '" + type + "'");
            }
        }
        return Ext.factory(config, defaultClass);
    }
});


Ext.define('Ext.fx.layout.card.Cube', {
    extend: 'Ext.fx.layout.card.Style',
    alias: 'fx.layout.card.cube',
    config: {
        reverse: null,
        inAnimation: {
            type: 'cube'
        },
        outAnimation: {
            type: 'cube',
            out: true
        }
    }
});


Ext.define('Ext.fx.layout.card.ScrollCover', {
    extend: 'Ext.fx.layout.card.Scroll',
    alias: 'fx.layout.card.scrollcover',
    onActiveItemChange: function(cardLayout, inItem, outItem, options, controller) {
        var containerElement, containerSize, xy, animConfig, inTranslate, outTranslate;
        this.lastController = controller;
        this.inItem = inItem;
        if (inItem && outItem) {
            containerElement = this.getLayout().container.innerElement;
            containerSize = containerElement.getSize();
            xy = this.calculateXY(containerSize);
            animConfig = {
                easing: this.getEasing(),
                duration: this.getDuration()
            };
            inItem.renderElement.dom.style.setProperty('visibility', 'hidden', 'important');
            inTranslate = inItem.setTranslatable(true).getTranslatable();
            outTranslate = outItem.setTranslatable(true).getTranslatable();
            outTranslate.translate({
                x: 0,
                y: 0
            });
            
            inTranslate.translate({
                x: xy.left,
                y: xy.top
            });
            inTranslate.getWrapper().dom.style.setProperty('z-index', '100', 'important');
            inItem.show();
            inTranslate.on({
                animationstart: 'onInAnimationStart',
                animationend: 'onInAnimationEnd',
                scope: this
            });
            inTranslate.translateAnimated({
                x: 0,
                y: 0
            }, animConfig);
            controller.pause();
        }
    },
    onInAnimationStart: function() {
        this.inItem.renderElement.dom.style.removeProperty('visibility');
    },
    onInAnimationEnd: function() {
        this.inItem.getTranslatable().getWrapper().dom.style.removeProperty('z-index');
        
        
        this.lastController.resume();
    }
});


Ext.define('Ext.fx.layout.card.ScrollReveal', {
    extend: 'Ext.fx.layout.card.Scroll',
    alias: 'fx.layout.card.scrollreveal',
    onActiveItemChange: function(cardLayout, inItem, outItem, options, controller) {
        var containerElement, containerSize, xy, animConfig, outTranslate, inTranslate;
        this.lastController = controller;
        this.outItem = outItem;
        this.inItem = inItem;
        if (inItem && outItem) {
            containerElement = this.getLayout().container.innerElement;
            containerSize = containerElement.getSize();
            xy = this.calculateXY(containerSize);
            animConfig = {
                easing: this.getEasing(),
                duration: this.getDuration()
            };
            outTranslate = outItem.setTranslatable(true).getTranslatable();
            inTranslate = inItem.setTranslatable(true).getTranslatable();
            outTranslate.getWrapper().dom.style.setProperty('z-index', '100', 'important');
            outTranslate.translate({
                x: 0,
                y: 0
            });
            inTranslate.translate({
                x: 0,
                y: 0
            });
            inItem.show();
            outTranslate.on({
                animationend: 'onOutAnimationEnd',
                scope: this
            });
            outTranslate.translateAnimated({
                x: xy.x,
                y: xy.y
            }, animConfig);
            controller.pause();
        }
    },
    onOutAnimationEnd: function() {
        this.outItem.getTranslatable().getWrapper().dom.style.removeProperty('z-index');
        
        
        this.lastController.resume();
    }
});


Ext.define('Ext.fx.runner.CssAnimation', {
    extend: 'Ext.fx.runner.Css',
    constructor: function() {
        this.runningAnimationsMap = {};
        this.elementEndStates = {};
        this.animationElementMap = {};
        this.keyframesRulesCache = {};
        this.uniqueId = 0;
        return this.callParent(arguments);
    },
    attachListeners: function() {
        this.listenersAttached = true;
        Ext.getWin().on({
            animationstart: 'onAnimationStart',
            animationend: 'onAnimationEnd',
            scope: this
        });
    },
    onAnimationStart: function(e) {
        var name = e.browserEvent.animationName,
            elementId = this.animationElementMap[name],
            animation = this.runningAnimationsMap[elementId][name],
            elementEndStates = this.elementEndStates,
            elementEndState = elementEndStates[elementId],
            data = {};
        console.log("START============= " + name);
        if (elementEndState) {
            delete elementEndStates[elementId];
            data[elementId] = elementEndState;
            this.applyStyles(data);
        }
        if (animation.before) {
            data[elementId] = animation.before;
            this.applyStyles(data);
        }
    },
    onAnimationEnd: function(e) {
        var element = e.target,
            name = e.browserEvent.animationName,
            animationElementMap = this.animationElementMap,
            elementId = animationElementMap[name],
            runningAnimationsMap = this.runningAnimationsMap,
            runningAnimations = runningAnimationsMap[elementId],
            animation = runningAnimations[name];
        console.log("END============= " + name);
        if (animation.onBeforeEnd) {
            animation.onBeforeEnd.call(animation.scope || this, element);
        }
        if (animation.onEnd) {
            animation.onEnd.call(animation.scope || this, element);
        }
        delete animationElementMap[name];
        delete runningAnimations[name];
        this.removeKeyframesRule(name);
    },
    generateAnimationId: function() {
        return 'animation-' + (++this.uniqueId);
    },
    run: function(animations) {
        var data = {},
            elementEndStates = this.elementEndStates,
            animationElementMap = this.animationElementMap,
            runningAnimationsMap = this.runningAnimationsMap,
            runningAnimations, states, elementId, animationId, i, ln, animation, name, runningAnimation, names, durations, easings, delays, directions, iterations;
        if (!this.listenersAttached) {
            this.attachListeners();
        }
        animations = Ext.Array.from(animations);
        for (i = 0 , ln = animations.length; i < ln; i++) {
            animation = animations[i];
            animation = Ext.factory(animation, Ext.fx.Animation);
            elementId = animation.getElement().getId();
            animationId = animation.getName() || this.generateAnimationId();
            animationElementMap[animationId] = elementId;
            animation = animation.getData();
            states = animation.states;
            this.addKeyframesRule(animationId, states);
            runningAnimations = runningAnimationsMap[elementId];
            if (!runningAnimations) {
                runningAnimations = runningAnimationsMap[elementId] = {};
            }
            runningAnimations[animationId] = animation;
            names = [];
            durations = [];
            easings = [];
            delays = [];
            directions = [];
            iterations = [];
            for (name in runningAnimations) {
                if (runningAnimations.hasOwnProperty(name)) {
                    runningAnimation = runningAnimations[name];
                    names.push(name);
                    durations.push(runningAnimation.duration);
                    easings.push(runningAnimation.easing);
                    delays.push(runningAnimation.delay);
                    directions.push(runningAnimation.direction);
                    iterations.push(runningAnimation.iteration);
                }
            }
            data[elementId] = {
                'animation-name': names,
                'animation-duration': durations,
                'animation-timing-function': easings,
                'animation-delay': delays,
                'animation-direction': directions,
                'animation-iteration-count': iterations
            };
            
            if (animation.preserveEndState) {
                elementEndStates[elementId] = states['100%'];
            }
        }
        this.applyStyles(data);
    },
    addKeyframesRule: function(name, keyframes) {
        var percentage, properties, keyframesRule, styleSheet, rules, styles, rulesLength, key, value;
        styleSheet = this.getStyleSheet();
        rules = styleSheet.cssRules;
        rulesLength = rules.length;
        styleSheet.insertRule('@' + this.vendorPrefix + 'keyframes ' + name + '{}', rulesLength);
        keyframesRule = rules[rulesLength];
        for (percentage in keyframes) {
            properties = keyframes[percentage];
            rules = keyframesRule.cssRules;
            rulesLength = rules.length;
            styles = [];
            for (key in properties) {
                value = this.formatValue(properties[key], key);
                key = this.formatName(key);
                styles.push(key + ':' + value);
            }
            keyframesRule.insertRule(percentage + '{' + styles.join(';') + '}', rulesLength);
        }
        return this;
    },
    removeKeyframesRule: function(name) {
        var styleSheet = this.getStyleSheet(),
            rules = styleSheet.cssRules,
            i, ln, rule;
        for (i = 0 , ln = rules.length; i < ln; i++) {
            rule = rules[i];
            if (rule.name === name) {
                styleSheet.removeRule(i);
                break;
            }
        }
        return this;
    }
});


Ext.define('Ext.mixin.Hookable', {
    extend: 'Ext.Mixin',
    mixinConfig: {
        id: 'hookable'
    },
    bind: function(instance, boundMethod, bindingMethod, preventDefault, extraArgs) {
        if (!bindingMethod) {
            bindingMethod = boundMethod;
        }
        var boundFn = instance[boundMethod],
            fn, binding;
        if (boundFn && boundFn.hasOwnProperty('$binding')) {
            binding = boundFn.$binding;
            if (binding.bindingMethod === bindingMethod && binding.bindingScope === this) {
                return this;
            }
        }
        instance[boundMethod] = fn = function() {
            var binding = fn.$binding,
                scope = binding.bindingScope,
                args = Array.prototype.slice.call(arguments);
            args.push(arguments);
            if (extraArgs) {
                args.push.apply(args, extraArgs);
            }
            if (!binding.preventDefault && scope[binding.bindingMethod].apply(scope, args) !== false) {
                return binding.boundFn.apply(this, arguments);
            }
        };
        fn.$binding = {
            preventDefault: !!preventDefault,
            boundFn: boundFn,
            bindingMethod: bindingMethod,
            bindingScope: this
        };
        return this;
    },
    unbind: function(instance, boundMethod, bindingMethod) {
        if (!bindingMethod) {
            bindingMethod = boundMethod;
        }
        var fn = instance[boundMethod],
            binding = fn.$binding,
            boundFn, currentBinding;
        while (binding) {
            boundFn = binding.boundFn;
            if (binding.bindingMethod === bindingMethod && binding.bindingScope === this) {
                if (currentBinding) {
                    currentBinding.boundFn = boundFn;
                } else {
                    instance[boundMethod] = boundFn;
                }
                return this;
            }
            currentBinding = binding;
            binding = boundFn.$binding;
        }
        return this;
    }
});


Ext.define('Ext.mixin.Mashup', function(Mashup) {
    return {
        extend: 'Ext.Mixin',
        mixinConfig: {
            id: 'mashup',
            extended: function(baseClass, derivedClass) {
                Mashup.process(derivedClass);
            }
        },
        statics: {
            process: function(targetClass) {
                var body = targetClass.prototype,
                    requiredScripts = body.requiredScripts,
                    hooks = targetClass._classHooks,
                    onCreated = hooks.onCreated;
                if (requiredScripts) {
                    delete body.requiredScripts;
                    hooks.onCreated = function() {
                        var me = this,
                            args = Ext.Array.slice(arguments);
                        Ext.Loader.loadScripts({
                            url: requiredScripts,
                            cache: true,
                            
                            onLoad: function() {
                                hooks.onCreated = onCreated;
                                hooks.onCreated.call(me, args);
                            }
                        });
                    };
                }
            }
        },
        onClassMixedIn: function(targetClass) {
            Mashup.process(targetClass);
        }
    };
});


Ext.define('Ext.mixin.Responsive', function(Responsive) {
    return {
        extend: 'Ext.Mixin',
        requires: [
            'Ext.GlobalEvents'
        ],
        mixinConfig: {
            id: 'responsive',
            after: {
                destroy: 'destroy'
            }
        },
        config: {
            
            responsiveConfig: {
                $value: undefined,
                merge: function(newValue, oldValue, target, mixinClass) {
                    if (!newValue) {
                        return oldValue;
                    }
                    var ret = oldValue ? Ext.Object.chain(oldValue) : {},
                        rule;
                    for (rule in newValue) {
                        if (!mixinClass || !(rule in ret)) {
                            ret[rule] = {
                                fn: null,
                                
                                config: newValue[rule]
                            };
                        }
                    }
                    return ret;
                }
            },
            
            responsiveFormulas: {
                $value: 0,
                merge: function(newValue, oldValue, target, mixinClass) {
                    return this.mergeNew(newValue, oldValue, target, mixinClass);
                }
            }
        },
        
        destroy: function() {
            Responsive.unregister(this);
            this.callParent();
        },
        privates: {
            statics: {
                
                active: false,
                
                all: {},
                
                context: Ext.Object.chain(Ext.platformTags),
                
                count: 0,
                
                nextId: 0,
                
                activate: function() {
                    Responsive.active = true;
                    Responsive.updateContext();
                    Ext.on('resize', Responsive.onResize, Responsive);
                },
                
                deactivate: function() {
                    Responsive.active = false;
                    Ext.un('resize', Responsive.onResize, Responsive);
                },
                
                notify: function() {
                    var all = Responsive.all,
                        context = Responsive.context,
                        globalEvents = Ext.GlobalEvents,
                        timer = Responsive.timer,
                        id;
                    if (timer) {
                        Responsive.timer = null;
                        Ext.Function.cancelAnimationFrame(timer);
                    }
                    Responsive.updateContext();
                    Ext.suspendLayouts();
                    globalEvents.fireEvent('beforeresponsiveupdate', context);
                    for (id in all) {
                        all[id].setupResponsiveContext();
                    }
                    globalEvents.fireEvent('beginresponsiveupdate', context);
                    for (id in all) {
                        all[id].updateResponsiveState();
                    }
                    globalEvents.fireEvent('responsiveupdate', context);
                    Ext.resumeLayouts(true);
                },
                
                onResize: function() {
                    if (!Responsive.timer) {
                        Responsive.timer = Ext.Function.requestAnimationFrame(Responsive.onTimer);
                    }
                },
                
                onTimer: function() {
                    Responsive.timer = null;
                    Responsive.notify();
                },
                
                processConfig: function(instance, instanceConfig, name) {
                    var value = instanceConfig && instanceConfig[name],
                        config = instance.config,
                        cfg, configurator;
                    
                    
                    if (value) {
                        configurator = instance.getConfigurator();
                        cfg = configurator.configs[name];
                        
                        
                        config[name] = cfg.merge(value, config[name], instance);
                    }
                },
                register: function(responder) {
                    var id = responder.$responsiveId;
                    if (!id) {
                        responder.$responsiveId = id = ++Responsive.nextId;
                        Responsive.all[id] = responder;
                        if (++Responsive.count === 1) {
                            Responsive.activate();
                        }
                    }
                },
                unregister: function(responder) {
                    var id = responder.$responsiveId;
                    if (id in Responsive.all) {
                        responder.$responsiveId = null;
                        delete Responsive.all[id];
                        if (--Responsive.count === 0) {
                            Responsive.deactivate();
                        }
                    }
                },
                
                updateContext: function() {
                    var El = Ext.Element,
                        width = El.getViewportWidth(),
                        height = El.getViewportHeight(),
                        context = Responsive.context;
                    context.width = width;
                    context.height = height;
                    context.tall = width < height;
                    context.wide = !context.tall;
                    context.landscape = context.portrait = false;
                    if (!context.platform) {
                        context.platform = Ext.platformTags;
                    }
                    context[Ext.dom.Element.getOrientation()] = true;
                }
            },
            
            
            
            afterClassMixedIn: function(targetClass) {
                var proto = targetClass.prototype,
                    responsiveConfig = proto.responsiveConfig,
                    responsiveFormulas = proto.responsiveFormulas,
                    config;
                if (responsiveConfig || responsiveFormulas) {
                    config = {};
                    if (responsiveConfig) {
                        delete proto.responsiveConfig;
                        config.responsiveConfig = responsiveConfig;
                    }
                    if (responsiveFormulas) {
                        delete proto.responsiveFormulas;
                        config.responsiveFormulas = responsiveFormulas;
                    }
                    targetClass.getConfigurator().add(config);
                }
            },
            
            
            
            
            applyResponsiveConfig: function(rules) {
                for (var rule in rules) {
                    rules[rule].fn = Ext.createRuleFn(rule);
                }
                return rules;
            },
            applyResponsiveFormulas: function(formulas) {
                var ret = {},
                    fn, name;
                if (formulas) {
                    for (name in formulas) {
                        if (Ext.isString(fn = formulas[name])) {
                            fn = Ext.createRuleFn(fn);
                        }
                        ret[name] = fn;
                    }
                }
                return ret;
            },
            
            getResponsiveState: function() {
                var context = Responsive.context,
                    rules = this.getResponsiveConfig(),
                    ret = {},
                    entry, rule;
                if (rules) {
                    for (rule in rules) {
                        entry = rules[rule];
                        if (entry.fn.call(this, context)) {
                            Ext.merge(ret, entry.config);
                        }
                    }
                }
                return ret;
            },
            setupResponsiveContext: function() {
                var formulas = this.getResponsiveFormulas(),
                    context = Responsive.context,
                    name;
                if (formulas) {
                    for (name in formulas) {
                        context[name] = formulas[name].call(this, context);
                    }
                }
            },
            
            transformInstanceConfig: function(instanceConfig) {
                var me = this,
                    ret;
                Responsive.register(me);
                
                
                
                
                if (instanceConfig) {
                    Responsive.processConfig(me, instanceConfig, 'responsiveConfig');
                    Responsive.processConfig(me, instanceConfig, 'responsiveFormulas');
                }
                
                
                me.setupResponsiveContext();
                
                
                ret = me.getResponsiveState();
                if (instanceConfig) {
                    ret = Ext.merge({}, instanceConfig, ret);
                    
                    delete ret.responsiveConfig;
                    delete ret.responsiveFormulas;
                }
                return ret;
            },
            
            updateResponsiveState: function() {
                var config = this.getResponsiveState();
                this.setConfig(config);
            }
        }
    };
});


Ext.define('Ext.mixin.Selectable', {
    extend: 'Ext.Mixin',
    mixinConfig: {
        id: 'selectable',
        after: {
            updateStore: 'updateStore'
        }
    },
    
    
    config: {
        
        disableSelection: null,
        
        mode: 'SINGLE',
        
        allowDeselect: false,
        
        lastSelected: null,
        
        lastFocused: null,
        
        deselectOnContainerClick: true
    },
    modes: {
        SINGLE: true,
        SIMPLE: true,
        MULTI: true
    },
    selectableEventHooks: {
        addrecords: 'onSelectionStoreAdd',
        removerecords: 'onSelectionStoreRemove',
        updaterecord: 'onSelectionStoreUpdate',
        load: 'refreshSelection',
        refresh: 'refreshSelection'
    },
    constructor: function() {
        this.selected = new Ext.util.MixedCollection();
        this.callParent(arguments);
    },
    
    applyMode: function(mode) {
        mode = mode ? mode.toUpperCase() : 'SINGLE';
        
        
        return this.modes[mode] ? mode : 'SINGLE';
    },
    
    updateStore: function(newStore, oldStore) {
        var me = this,
            bindEvents = Ext.apply({}, me.selectableEventHooks, {
                scope: me
            });
        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            if (oldStore.autoDestroy) {
                oldStore.destroy();
            } else {
                oldStore.un(bindEvents);
                if (newStore) {
                    newStore.un('clear', 'onSelectionStoreClear', this);
                }
            }
        }
        if (newStore) {
            newStore.on(bindEvents);
            newStore.onBefore('clear', 'onSelectionStoreClear', this);
            me.refreshSelection();
        }
    },
    
    selectAll: function(silent) {
        var me = this,
            selections = me.getStore().getRange();
        me.select(selections, true, silent);
    },
    
    deselectAll: function(supress) {
        var me = this,
            selections = me.getStore().getRange();
        me.deselect(selections, supress);
        me.selected.clear();
        me.setLastSelected(null);
        me.setLastFocused(null);
    },
    
    
    selectWithEvent: function(record) {
        var me = this,
            isSelected = me.isSelected(record);
        switch (me.getMode()) {
            case 'MULTI':
            case 'SIMPLE':
                if (isSelected) {
                    me.deselect(record);
                } else {
                    me.select(record, true);
                };
                break;
            case 'SINGLE':
                if (me.getAllowDeselect() && isSelected) {
                    
                    me.deselect(record);
                } else {
                    
                    me.select(record, false);
                };
                break;
        }
    },
    
    selectRange: function(startRecord, endRecord, keepExisting) {
        var me = this,
            store = me.getStore(),
            records = [],
            tmp, i;
        if (me.getDisableSelection()) {
            return;
        }
        
        if (startRecord > endRecord) {
            tmp = endRecord;
            endRecord = startRecord;
            startRecord = tmp;
        }
        for (i = startRecord; i <= endRecord; i++) {
            records.push(store.getAt(i));
        }
        this.doMultiSelect(records, keepExisting);
    },
    
    select: function(records, keepExisting, suppressEvent) {
        var me = this,
            record;
        if (me.getDisableSelection()) {
            return;
        }
        if (typeof records === "number") {
            records = [
                me.getStore().getAt(records)
            ];
        }
        if (!records) {
            return;
        }
        if (me.getMode() == "SINGLE" && records) {
            record = records.length ? records[0] : records;
            me.doSingleSelect(record, suppressEvent);
        } else {
            me.doMultiSelect(records, keepExisting, suppressEvent);
        }
    },
    
    doSingleSelect: function(record, suppressEvent) {
        var me = this,
            selected = me.selected;
        if (me.getDisableSelection()) {
            return;
        }
        
        
        if (me.isSelected(record)) {
            return;
        }
        if (selected.getCount() > 0) {
            me.deselect(me.getLastSelected(), suppressEvent);
        }
        selected.add(record);
        me.setLastSelected(record);
        me.onItemSelect(record, suppressEvent);
        me.setLastFocused(record);
        if (!suppressEvent) {
            me.fireSelectionChange([
                record
            ]);
        }
    },
    
    doMultiSelect: function(records, keepExisting, suppressEvent) {
        if (records === null || this.getDisableSelection()) {
            return;
        }
        records = !Ext.isArray(records) ? [
            records
        ] : records;
        var me = this,
            selected = me.selected,
            ln = records.length,
            change = false,
            i = 0,
            record;
        if (!keepExisting && selected.getCount() > 0) {
            change = true;
            me.deselect(me.getSelection(), true);
        }
        for (; i < ln; i++) {
            record = records[i];
            if (keepExisting && me.isSelected(record)) {
                
                continue;
            }
            change = true;
            me.setLastSelected(record);
            selected.add(record);
            if (!suppressEvent) {
                me.setLastFocused(record);
            }
            me.onItemSelect(record, suppressEvent);
        }
        if (change && !suppressEvent) {
            this.fireSelectionChange(records);
        }
    },
    
    deselect: function(records, suppressEvent) {
        var me = this;
        if (me.getDisableSelection()) {
            return;
        }
        records = Ext.isArray(records) ? records : [
            records
        ];
        var selected = me.selected,
            change = false,
            i = 0,
            store = me.getStore(),
            ln = records.length,
            record;
        for (; i < ln; i++) {
            record = records[i];
            if (typeof record === 'number') {
                record = store.getAt(record);
            }
            if (selected.remove(record)) {
                if (me.getLastSelected() == record) {
                    me.setLastSelected(selected.last());
                }
                change = true;
            }
            if (record) {
                me.onItemDeselect(record, suppressEvent);
            }
        }
        if (change && !suppressEvent) {
            me.fireSelectionChange(records);
        }
    },
    
    updateLastFocused: function(newRecord, oldRecord) {
        this.onLastFocusChanged(oldRecord, newRecord);
    },
    fireSelectionChange: function(records) {
        var me = this;
        me.fireAction('beforeselectionchange', [
            me
        ], function() {
            me.fireAction('selectionchange', [
                me,
                records
            ], 'getSelection');
        });
    },
    
    getSelection: function() {
        return this.selected.getRange();
    },
    
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.getStore().getAt(record) : record;
        return this.selected.indexOf(record) !== -1;
    },
    
    hasSelection: function() {
        return this.selected.getCount() > 0;
    },
    
    refreshSelection: function() {
        var me = this,
            selections = me.getSelection();
        me.deselectAll(true);
        if (selections.length) {
            me.select(selections, false, true);
        }
    },
    
    
    
    onSelectionStoreRemove: function(store, records) {
        var me = this,
            selected = me.selected,
            ln = records.length,
            record, i;
        if (me.getDisableSelection()) {
            return;
        }
        for (i = 0; i < ln; i++) {
            record = records[i];
            if (selected.remove(record)) {
                if (me.getLastSelected() == record) {
                    me.setLastSelected(null);
                }
                if (me.getLastFocused() == record) {
                    me.setLastFocused(null);
                }
                me.fireSelectionChange([
                    record
                ]);
            }
        }
    },
    onSelectionStoreClear: function(store) {
        var records = store.getData().items;
        this.onSelectionStoreRemove(store, records);
    },
    
    getSelectionCount: function() {
        return this.selected.getCount();
    },
    onSelectionStoreAdd: Ext.emptyFn,
    onSelectionStoreUpdate: Ext.emptyFn,
    onItemSelect: Ext.emptyFn,
    onItemDeselect: Ext.emptyFn,
    onLastFocusChanged: Ext.emptyFn,
    onEditorKey: Ext.emptyFn
}, function() {});


Ext.define('Ext.mixin.Traversable', {
    extend: 'Ext.Mixin',
    mixinConfig: {
        id: 'traversable'
    },
    setParent: function(parent) {
        this.parent = parent;
        return this;
    },
    
    hasParent: function() {
        return Boolean(this.parent);
    },
    
    getParent: function() {
        return this.parent;
    },
    getAncestors: function() {
        var ancestors = [],
            parent = this.getParent();
        while (parent) {
            ancestors.push(parent);
            parent = parent.getParent();
        }
        return ancestors;
    },
    getAncestorIds: function() {
        var ancestorIds = [],
            parent = this.getParent();
        while (parent) {
            ancestorIds.push(parent.getId());
            parent = parent.getParent();
        }
        return ancestorIds;
    }
});


Ext.define('Ext.perf.Accumulator', function() {
    var currentFrame = null,
        khrome = Ext.global['chrome'],
        formatTpl,
        
        
        getTimestamp = function() {
            getTimestamp = Ext.now;
            var interval, toolbox;
            
            if (Ext.isChrome && khrome && khrome.Interval) {
                interval = new khrome.Interval();
                interval.start();
                getTimestamp = function() {
                    return interval.microseconds() / 1000;
                };
            } else if (window.ActiveXObject) {
                try {
                    
                    toolbox = new ActiveXObject('SenchaToolbox.Toolbox');
                    Ext.senchaToolbox = toolbox;
                    
                    getTimestamp = function() {
                        return toolbox.milliseconds;
                    };
                } catch (e) {}
            }
            
            Ext.perf.getTimestamp = Ext.perf.Accumulator.getTimestamp = getTimestamp;
            return getTimestamp();
        };
    function adjustSet(set, time) {
        set.sum += time;
        set.min = Math.min(set.min, time);
        set.max = Math.max(set.max, time);
    }
    function leaveFrame(time) {
        var totalTime = time ? time : (getTimestamp() - this.time),
            
            me = this,
            
            accum = me.accum;
        ++accum.count;
        if (!--accum.depth) {
            adjustSet(accum.total, totalTime);
        }
        adjustSet(accum.pure, totalTime - me.childTime);
        currentFrame = me.parent;
        if (currentFrame) {
            ++currentFrame.accum.childCount;
            currentFrame.childTime += totalTime;
        }
    }
    function makeSet() {
        return {
            min: Number.MAX_VALUE,
            max: 0,
            sum: 0
        };
    }
    function makeTap(me, fn) {
        return function() {
            var frame = me.enter(),
                ret = fn.apply(this, arguments);
            frame.leave();
            return ret;
        };
    }
    function round(x) {
        return Math.round(x * 100) / 100;
    }
    function setToJSON(count, childCount, calibration, set) {
        var data = {
                avg: 0,
                min: set.min,
                max: set.max,
                sum: 0
            };
        if (count) {
            calibration = calibration || 0;
            data.sum = set.sum - childCount * calibration;
            data.avg = data.sum / count;
        }
        
        
        return data;
    }
    return {
        requires: [
            'Ext.XTemplate',
            'Ext.ClassManager'
        ],
        constructor: function(name) {
            var me = this;
            me.count = me.childCount = me.depth = me.maxDepth = 0;
            me.pure = makeSet();
            me.total = makeSet();
            me.name = name;
        },
        statics: {
            getTimestamp: getTimestamp
        },
        format: function(calibration) {
            if (!formatTpl) {
                formatTpl = new Ext.XTemplate([
                    '{name} - {count} call(s)',
                    '<tpl if="count">',
                    '<tpl if="childCount">',
                    ' ({childCount} children)',
                    '</tpl>',
                    '<tpl if="depth - 1">',
                    ' ({depth} deep)',
                    '</tpl>',
                    '<tpl for="times">',
                    ', {type}: {[this.time(values.sum)]} msec (',
                    
                    'avg={[this.time(values.sum / parent.count)]}',
                    
                    ')',
                    '</tpl>',
                    '</tpl>'
                ].join(''),{
                    time: function(t) {
                        return Math.round(t * 100) / 100;
                    }
                });
            }
            var data = this.getData(calibration);
            data.name = this.name;
            data.pure.type = 'Pure';
            data.total.type = 'Total';
            data.times = [
                data.pure,
                data.total
            ];
            return formatTpl.apply(data);
        },
        getData: function(calibration) {
            var me = this;
            return {
                count: me.count,
                childCount: me.childCount,
                depth: me.maxDepth,
                pure: setToJSON(me.count, me.childCount, calibration, me.pure),
                total: setToJSON(me.count, me.childCount, calibration, me.total)
            };
        },
        enter: function() {
            var me = this,
                frame = {
                    accum: me,
                    leave: leaveFrame,
                    childTime: 0,
                    parent: currentFrame
                };
            ++me.depth;
            if (me.maxDepth < me.depth) {
                me.maxDepth = me.depth;
            }
            currentFrame = frame;
            frame.time = getTimestamp();
            
            return frame;
        },
        monitor: function(fn, scope, args) {
            var frame = this.enter();
            if (args) {
                fn.apply(scope, args);
            } else {
                fn.call(scope);
            }
            frame.leave();
        },
        report: function() {
            Ext.log(this.format());
        },
        tap: function(className, methodName) {
            var me = this,
                methods = typeof methodName == 'string' ? [
                    methodName
                ] : methodName,
                klass, statik, i, parts, length, name, src, tapFunc;
            tapFunc = function() {
                if (typeof className == 'string') {
                    klass = Ext.global;
                    parts = className.split('.');
                    for (i = 0 , length = parts.length; i < length; ++i) {
                        klass = klass[parts[i]];
                    }
                } else {
                    klass = className;
                }
                for (i = 0 , length = methods.length; i < length; ++i) {
                    name = methods[i];
                    statik = name.charAt(0) == '!';
                    if (statik) {
                        name = name.substring(1);
                    } else {
                        statik = !(name in klass.prototype);
                    }
                    src = statik ? klass : klass.prototype;
                    src[name] = makeTap(me, src[name]);
                }
            };
            Ext.ClassManager.onCreated(tapFunc, me, className);
            return me;
        }
    };
}, function() {
    Ext.perf.getTimestamp = this.getTimestamp;
});


Ext.define('Ext.perf.Monitor', {
    singleton: true,
    alternateClassName: 'Ext.Perf',
    requires: [
        'Ext.perf.Accumulator'
    ],
    constructor: function() {
        this.accumulators = [];
        this.accumulatorsByName = {};
    },
    calibrate: function() {
        var accum = new Ext.perf.Accumulator('$'),
            total = accum.total,
            getTimestamp = Ext.perf.Accumulator.getTimestamp,
            count = 0,
            frame, endTime, startTime;
        startTime = getTimestamp();
        do {
            frame = accum.enter();
            frame.leave();
            ++count;
        } while (total.sum < 100);
        endTime = getTimestamp();
        return (endTime - startTime) / count;
    },
    get: function(name) {
        var me = this,
            accum = me.accumulatorsByName[name];
        if (!accum) {
            me.accumulatorsByName[name] = accum = new Ext.perf.Accumulator(name);
            me.accumulators.push(accum);
        }
        return accum;
    },
    enter: function(name) {
        return this.get(name).enter();
    },
    monitor: function(name, fn, scope) {
        this.get(name).monitor(fn, scope);
    },
    report: function() {
        var me = this,
            accumulators = me.accumulators,
            calibration = me.calibrate();
        accumulators.sort(function(a, b) {
            return (a.name < b.name) ? -1 : ((b.name < a.name) ? 1 : 0);
        });
        me.updateGC();
        Ext.log('Calibration: ' + Math.round(calibration * 100) / 100 + ' msec/sample');
        Ext.each(accumulators, function(accum) {
            Ext.log(accum.format(calibration));
        });
    },
    getData: function(all) {
        var ret = {},
            accumulators = this.accumulators;
        Ext.each(accumulators, function(accum) {
            if (all || accum.count) {
                ret[accum.name] = accum.getData();
            }
        });
        return ret;
    },
    reset: function() {
        Ext.each(this.accumulators, function(accum) {
            var me = accum;
            me.count = me.childCount = me.depth = me.maxDepth = 0;
            me.pure = {
                min: Number.MAX_VALUE,
                max: 0,
                sum: 0
            };
            me.total = {
                min: Number.MAX_VALUE,
                max: 0,
                sum: 0
            };
        });
    },
    updateGC: function() {
        var accumGC = this.accumulatorsByName.GC,
            toolbox = Ext.senchaToolbox,
            bucket;
        if (accumGC) {
            accumGC.count = toolbox.garbageCollectionCounter || 0;
            if (accumGC.count) {
                bucket = accumGC.pure;
                accumGC.total.sum = bucket.sum = toolbox.garbageCollectionMilliseconds;
                bucket.min = bucket.max = bucket.sum / accumGC.count;
                bucket = accumGC.total;
                bucket.min = bucket.max = bucket.sum / accumGC.count;
            }
        }
    },
    watchGC: function() {
        Ext.perf.getTimestamp();
        
        var toolbox = Ext.senchaToolbox;
        if (toolbox) {
            this.get("GC");
            toolbox.watchGarbageCollector(false);
        }
    },
    
    setup: function(config) {
        if (!config) {
            config = {
                
                
                
                
                
                
                
                
                render: {
                    'Ext.Component': 'render'
                },
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                layout: {
                    'Ext.layout.Context': 'run'
                }
            };
        }
        this.currentConfig = config;
        var key, prop, accum, className, methods;
        for (key in config) {
            if (config.hasOwnProperty(key)) {
                prop = config[key];
                accum = Ext.Perf.get(key);
                for (className in prop) {
                    if (prop.hasOwnProperty(className)) {
                        methods = prop[className];
                        accum.tap(className, methods);
                    }
                }
            }
        }
        this.watchGC();
    }
});


Ext.define('Ext.plugin.Abstract', {
    alternateClassName: 'Ext.AbstractPlugin',
    
    isPlugin: true,
    
    constructor: function(config) {
        if (config) {
            this.pluginConfig = config;
            this.initConfig(config);
        }
    },
    
    clonePlugin: function(overrideCfg) {
        return new this.self(Ext.apply({}, overrideCfg, this.pluginConfig));
    },
    
    setCmp: function(cmp) {
        this.cmp = cmp;
    },
    
    getCmp: function() {
        return this.cmp;
    },
    
    
    init: Ext.emptyFn,
    
    
    
    onClassExtended: function(cls, data, hooks) {
        var alias = data.alias;
        
        if (alias && !data.ptype) {
            if (Ext.isArray(alias)) {
                alias = alias[0];
            }
            cls.prototype.ptype = alias.split('plugin.')[1];
        }
    }
});

Ext.define('Ext.overrides.plugin.Abstract', {
    override: 'Ext.plugin.Abstract',
    $configStrict: false,
    $configPrefixed: false,
    disabled: false,
    
    
    getState: null,
    
    applyState: null,
    
    enable: function() {
        this.disabled = false;
    },
    
    disable: function() {
        this.disabled = true;
    }
});


Ext.define('Ext.util.Base64', {
    singleton: true,
    
    _str: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
    
    encode: function(input) {
        var me = this;
        var output = '',
            chr1, chr2, chr3, enc1, enc2, enc3, enc4,
            i = 0;
        input = me._utf8_encode(input);
        var len = input.length;
        while (i < len) {
            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);
            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;
            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }
            output = output + me._str.charAt(enc1) + me._str.charAt(enc2) + me._str.charAt(enc3) + me._str.charAt(enc4);
        }
        return output;
    },
    
    decode: function(input) {
        var me = this;
        var output = '',
            chr1, chr2, chr3, enc1, enc2, enc3, enc4,
            i = 0;
        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
        var len = input.length;
        while (i < len) {
            enc1 = me._str.indexOf(input.charAt(i++));
            enc2 = me._str.indexOf(input.charAt(i++));
            enc3 = me._str.indexOf(input.charAt(i++));
            enc4 = me._str.indexOf(input.charAt(i++));
            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
            output = output + String.fromCharCode(chr1);
            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }
        }
        output = me._utf8_decode(output);
        return output;
    },
    
    _utf8_encode: function(string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = '',
            n = 0,
            len = string.length;
        for (; n < len; n++) {
            var c = string.charCodeAt(n);
            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    },
    
    _utf8_decode: function(utftext) {
        var string = '',
            i = 0,
            c = 0,
            c3 = 0,
            c2 = 0,
            len = utftext.length;
        while (i < len) {
            c = utftext.charCodeAt(i);
            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            } else if ((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            } else {
                c2 = utftext.charCodeAt(i + 1);
                c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }
});


Ext.define('Ext.util.DelimitedValue', {
    
    dateFormat: 'C',
    
    delimiter: '\t',
    
    lineBreak: '\n',
    
    quote: '"',
    parseREs: {},
    quoteREs: {},
    constructor: function(config) {
        if (config) {
            Ext.apply(this, config);
        }
    },
    
    decode: function(input, delimiter) {
        var me = this,
            
            
            delim = (delimiter || me.delimiter),
            row = [],
            result = [
                row
            ],
            quote = me.quote,
            quoteREs = me.quoteREs,
            parseREs = me.parseREs,
            
            
            parseRE = parseREs[delim] || (parseREs[delim] = new RegExp(
            "(\\" + delim + "|\\r?\\n|\\r|^)" + 
            "(?:\\" + quote + "([^\\" + quote + "]*(?:\\" + quote + "\\" + quote + "[^\\" + quote + "]*)*)\\" + quote + "|" + 
            "([^\"\\" + delim + "\\r\\n]*))","gi")),
            dblQuoteRE = quoteREs[quote] || (quoteREs[quote] = new RegExp('\\' + quote + '\\' + quote,'g')),
            arrMatches, strMatchedDelimiter, strMatchedValue;
        
        
        while (arrMatches = parseRE.exec(input)) {
            strMatchedDelimiter = arrMatches[1];
            
            
            
            
            if (strMatchedDelimiter.length && strMatchedDelimiter !== delim) {
                
                
                result.push(row = []);
            }
            
            
            
            if (arrMatches[2]) {
                
                
                strMatchedValue = arrMatches[2].replace(dblQuoteRE, '"');
            } else {
                
                strMatchedValue = arrMatches[3];
            }
            row.push(strMatchedValue);
        }
        return result;
    },
    
    encode: function(input, delimiter) {
        var me = this,
            delim = delimiter || me.delimiter,
            dateFormat = me.dateFormat,
            quote = me.quote,
            rowIndex = input.length,
            result = [],
            outputRow = [],
            col, columnIndex, inputRow, twoQuotes;
        while (rowIndex-- > 0) {
            inputRow = input[rowIndex];
            outputRow.length = columnIndex = inputRow.length;
            while (columnIndex-- > 0) {
                col = inputRow[columnIndex];
                if (col == null) {
                    
                    col = '';
                } else if (typeof col === 'string') {
                    
                    if (col && col.indexOf(quote) > -1) {
                        if (!twoQuotes) {
                            twoQuotes = quote + quote;
                        }
                        col = quote + col.split(quote).join(twoQuotes) + quote;
                    }
                } else if (Ext.isDate(col)) {
                    col = Ext.Date.format(col, dateFormat);
                } else if (col && (isNaN(col) || Ext.isArray(col))) {
                    Ext.Error.raise('Cannot serialize ' + Ext.typeOf(col) + ' into CSV');
                }
                outputRow[columnIndex] = col;
            }
            result[rowIndex] = outputRow.join(delim);
        }
        return result.join(me.lineBreak);
    }
});


Ext.define('Ext.util.CSV', {
    extend: 'Ext.util.DelimitedValue',
    singleton: true,
    delimiter: ','
});


Ext.define('Ext.util.LocalStorage', {
    
    id: null,
    
    destroyed: false,
    
    lazyKeys: true,
    
    prefix: '',
    
    session: false,
    
    _keys: null,
    
    _store: null,
    
    _users: 0,
    statics: {
        cache: {},
        
        get: function(id) {
            var me = this,
                cache = me.cache,
                config = {
                    _users: 1
                },
                
                instance;
            if (Ext.isString(id)) {
                config.id = id;
            } else {
                Ext.apply(config, id);
            }
            if (!(instance = cache[config.id])) {
                instance = new me(config);
            } else {
                if (instance === true) {
                    Ext.Error.raise('Creating a shared instance of private local store "' + me.id + '".');
                }
                ++instance._users;
            }
            return instance;
        },
        
        supported: true
    },
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);
        if (!me.hasOwnProperty('id')) {
            Ext.Error.raise("No id was provided to the local store.");
        }
        if (me._users) {
            
            
            
            Ext.util.LocalStorage.cache[me.id] = me;
        } else {
            
            
            if (Ext.util.LocalStorage.cache[me.id]) {
                Ext.Error.raise('Cannot create duplicate instance of local store "' + me.id + '". Use Ext.util.LocalStorage.get() to share instances.');
            }
            
            
            Ext.util.LocalStorage.cache[me.id] = true;
        }
        me.init();
    },
    
    init: function() {
        var me = this,
            id = me.id;
        if (!me.prefix && id) {
            me.prefix = id + '-';
        }
        me._store = (me.session ? window.sessionStorage : window.localStorage);
    },
    
    destroy: function() {
        var me = this;
        if (me._users) {
            Ext.log.warn('LocalStorage(id=' + me.id + ') destroyed while in use');
        }
        delete Ext.util.LocalStorage.cache[me.id];
        me._store = me._keys = null;
        me.destroyed = true;
        me.destroy = Ext.emptyFn;
    },
    
    getKeys: function() {
        var me = this,
            store = me._store,
            prefix = me.prefix,
            keys = me._keys,
            n = prefix.length,
            i, key;
        if (!keys) {
            me._keys = keys = [];
            for (i = store.length; i--; ) {
                key = store.key(i);
                if (key.length > n) {
                    if (prefix === key.substring(0, n)) {
                        keys.push(key.substring(n));
                    }
                }
            }
        }
        return keys;
    },
    
    release: function() {
        if (!--this._users) {
            this.destroy();
        }
    },
    
    save: Ext.emptyFn,
    
    clear: function() {
        var me = this,
            store = me._store,
            prefix = me.prefix,
            keys = me._keys || me.getKeys(),
            i;
        for (i = keys.length; i--; ) {
            store.removeItem(prefix + keys[i]);
        }
        keys.length = 0;
    },
    
    key: function(index) {
        var keys = this._keys || this.getKeys();
        return (0 <= index && index < keys.length) ? keys[index] : null;
    },
    
    getItem: function(key) {
        var k = this.prefix + key;
        return this._store.getItem(k);
    },
    
    removeItem: function(key) {
        var me = this,
            k = me.prefix + key,
            store = me._store,
            keys = me._keys,
            length = store.length;
        store.removeItem(k);
        if (keys && length !== store.length) {
            if (me.lazyKeys) {
                me._keys = null;
            } else {
                Ext.Array.remove(keys, key);
            }
        }
    },
    
    setItem: function(key, value) {
        var me = this,
            k = me.prefix + key,
            store = me._store,
            length = store.length,
            keys = me._keys;
        store.setItem(k, value);
        if (keys && length !== store.length) {
            
            keys.push(key);
        }
    }
}, function() {
    var LocalStorage = this;
    if ('localStorage' in window) {
        return;
    }
    if (!Ext.isIE) {
        LocalStorage.supported = false;
        LocalStorage.prototype.init = function() {
            Ext.Error.raise("Local storage is not supported on this browser");
        };
        return;
    }
    
    
    
    
    LocalStorage.override({
        
        data: null,
        
        
        flushDelay: 1,
        init: function() {
            var me = this,
                data = me.data,
                el;
            me.el = el = document.createElement('div');
            el.id = (me.id || (me.id = 'extjs-localstore'));
            el.addBehavior('#default#userdata');
            
            Ext.getHead().dom.appendChild(el);
            el.load(me.id);
            data = el.getAttribute('xdata');
            me.data = data = (data ? Ext.decode(data) : {});
            me._flushFn = function() {
                me._timer = null;
                me.save(0);
            };
        },
        destroy: function() {
            var me = this,
                el = me.el;
            if (el) {
                
                if (me._timer) {
                    me.save();
                }
                el.parentNode.removeChild(el);
                me.data = me.el = null;
                me.callParent();
            }
        },
        getKeys: function() {
            var me = this,
                keys = me._keys;
            if (!keys) {
                me._keys = keys = Ext.Object.getKeys(me.data);
            }
            return keys;
        },
        
        save: function(delay) {
            var me = this;
            if (!delay) {
                if (me._timer) {
                    clearTimeout(me._timer);
                    me._timer = null;
                }
                me.el.setAttribute('xdata', Ext.encode(me.data));
                me.el.save(me.id);
            } else if (!me._timer) {
                me._timer = Ext.defer(me._flushFn, delay);
            }
        },
        clear: function() {
            var me = this;
            me.data = {};
            me._keys = null;
            me.save(me.flushDelay);
        },
        getItem: function(key) {
            var data = this.data;
            return (key in data) ? data[key] : null;
        },
        removeItem: function(key) {
            var me = this,
                keys = me._keys,
                data = me.data;
            if (key in data) {
                delete data[key];
                if (keys) {
                    if (me.lazyKeys) {
                        me._keys = null;
                    } else {
                        Ext.Array.remove(keys, key);
                    }
                }
                me.save(me.flushDelay);
            }
        },
        setItem: function(key, value) {
            var me = this,
                data = me.data,
                keys = me._keys;
            if (keys && !(key in data)) {
                keys.push(key);
            }
            data[key] = value;
            me.save(me.flushDelay);
        }
    });
});


Ext.define('Ext.util.TSV', {
    extend: 'Ext.util.DelimitedValue',
    singleton: true,
    delimiter: '\t'
});



Ext.define('Ext.util.TaskManager', {
    extend: 'Ext.util.TaskRunner',
    alternateClassName: [
        'Ext.TaskManager'
    ],
    singleton: true
});


Ext.define('Ext.util.TextMetrics', {
    requires: [
        'Ext.dom.Element'
    ],
    statics: {
        shared: null,
        
        measure: function(el, text, fixedWidth) {
            var me = this,
                shared = me.shared;
            if (!shared) {
                shared = me.shared = new me(el,fixedWidth);
            }
            shared.bind(el);
            shared.setFixedWidth(fixedWidth || 'auto');
            return shared.getSize(text);
        },
        
        destroy: function() {
            var me = this;
            Ext.destroy(me.shared);
            me.shared = null;
        }
    },
    
    constructor: function(bindTo, fixedWidth) {
        var me = this,
            measure = Ext.getBody().createChild({
                
                'data-sticky': true,
                role: 'presentation',
                cls: Ext.baseCSSPrefix + 'textmetrics'
            });
        me.measure = measure;
        if (bindTo) {
            me.bind(bindTo);
        }
        measure.position('absolute');
        measure.setLocalXY(-1000, -1000);
        measure.hide();
        if (fixedWidth) {
            measure.setWidth(fixedWidth);
        }
    },
    
    getSize: function(text) {
        var measure = this.measure,
            size;
        measure.setHtml(text);
        size = measure.getSize();
        measure.setHtml('');
        return size;
    },
    
    bind: function(el) {
        var me = this;
        me.el = Ext.get(el);
        me.measure.setStyle(me.el.getStyle([
            'font-size',
            'font-style',
            'font-weight',
            'font-family',
            'line-height',
            'text-transform',
            'letter-spacing',
            'word-break'
        ]));
    },
    
    setFixedWidth: function(width) {
        this.measure.setWidth(width);
    },
    
    getWidth: function(text) {
        this.measure.dom.style.width = 'auto';
        return this.getSize(text).width;
    },
    
    getHeight: function(text) {
        return this.getSize(text).height;
    },
    
    destroy: function() {
        var me = this;
        me.measure.destroy();
        delete me.el;
        delete me.measure;
    }
}, function() {
    Ext.Element.override({
        
        getTextWidth: function(text, min, max) {
            return Ext.Number.constrain(Ext.util.TextMetrics.measure(this.dom, Ext.valueFrom(text, this.dom.innerHTML, true)).width, min || 0, max || 1000000);
        }
    });
});


Ext.define('Ext.Action', {
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        this.initialConfig = config;
        this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
        this.items = [];
    },
    
    isAction: true,
    
    setText: function(text) {
        this.initialConfig.text = text;
        this.callEach('setText', [
            text
        ]);
    },
    
    getText: function() {
        return this.initialConfig.text;
    },
    
    setIconCls: function(cls) {
        this.initialConfig.iconCls = cls;
        this.callEach('setIconCls', [
            cls
        ]);
    },
    
    getIconCls: function() {
        return this.initialConfig.iconCls;
    },
    
    setDisabled: function(v) {
        this.initialConfig.disabled = v;
        this.callEach('setDisabled', [
            v
        ]);
    },
    
    enable: function() {
        this.setDisabled(false);
    },
    
    disable: function() {
        this.setDisabled(true);
    },
    
    isDisabled: function() {
        return this.initialConfig.disabled;
    },
    
    setHidden: function(v) {
        this.initialConfig.hidden = v;
        this.callEach('setVisible', [
            !v
        ]);
    },
    
    show: function() {
        this.setHidden(false);
    },
    
    hide: function() {
        this.setHidden(true);
    },
    
    isHidden: function() {
        return this.initialConfig.hidden;
    },
    
    setHandler: function(fn, scope) {
        this.initialConfig.handler = fn;
        this.initialConfig.scope = scope;
        this.callEach('setHandler', [
            fn,
            scope
        ]);
    },
    
    each: function(fn, scope) {
        Ext.each(this.items, fn, scope);
    },
    
    callEach: function(fnName, args) {
        var items = this.items,
            i = 0,
            len = items.length,
            item;
        Ext.suspendLayouts();
        for (; i < len; i++) {
            item = items[i];
            item[fnName].apply(item, args);
        }
        Ext.resumeLayouts(true);
    },
    
    addComponent: function(comp) {
        this.items.push(comp);
        comp.on('destroy', this.removeComponent, this);
    },
    
    removeComponent: function(comp) {
        Ext.Array.remove(this.items, comp);
    },
    
    execute: function() {
        this.initialConfig.handler.apply(this.initialConfig.scope || Ext.global, arguments);
    }
});


Ext.define('Ext.ElementLoader', {
    
    mixins: {
        observable: 'Ext.util.Observable'
    },
    uses: [
        'Ext.data.Connection',
        'Ext.Ajax'
    ],
    statics: {
        Renderer: {
            Html: function(loader, response, active) {
                loader.getTarget().setHtml(response.responseText, active.scripts === true);
                return true;
            }
        }
    },
    
    
    url: null,
    
    params: null,
    
    baseParams: null,
    
    autoLoad: false,
    
    target: null,
    
    loadMask: false,
    
    ajaxOptions: null,
    
    scripts: false,
    
    
    
    
    
    
    
    isLoader: true,
    
    
    
    constructor: function(config) {
        var me = this,
            autoLoad;
        config = config || {};
        Ext.apply(me, config);
        
        me.mixins.observable.constructor.call(me);
        me.setTarget(me.target);
        if (me.autoLoad) {
            autoLoad = me.autoLoad;
            if (autoLoad === true) {
                autoLoad = null;
            }
            me.load(autoLoad);
        }
    },
    
    setTarget: function(target) {
        var me = this;
        target = Ext.get(target);
        if (me.target && me.target != target) {
            me.abort();
        }
        me.target = target;
    },
    
    getTarget: function() {
        return this.target || null;
    },
    
    abort: function() {
        var active = this.active;
        if (active !== undefined) {
            Ext.Ajax.abort(active.request);
            if (active.mask) {
                this.removeMask();
            }
            delete this.active;
        }
    },
    
    removeMask: function() {
        this.target.unmask();
    },
    
    addMask: function(mask) {
        this.target.mask(mask === true ? null : mask);
    },
    
    load: function(options) {
        if (!this.target) {
            Ext.Error.raise('A valid target is required when loading content');
        }
        options = Ext.apply({}, options);
        var me = this,
            mask = Ext.isDefined(options.loadMask) ? options.loadMask : me.loadMask,
            params = Ext.apply({}, options.params),
            ajaxOptions = Ext.apply({}, options.ajaxOptions),
            callback = options.callback || me.callback,
            scope = options.scope || me.scope || me,
            rendererScope = options.rendererScope || me.rendererScope || me;
        Ext.applyIf(ajaxOptions, me.ajaxOptions);
        Ext.applyIf(options, ajaxOptions);
        Ext.applyIf(params, me.params);
        Ext.apply(params, me.baseParams);
        Ext.applyIf(options, {
            url: me.url
        });
        if (!options.url) {
            Ext.Error.raise('You must specify the URL from which content should be loaded');
        }
        Ext.apply(options, {
            scope: me,
            params: params,
            callback: me.onComplete
        });
        if (me.fireEvent('beforeload', me, options) === false) {
            return;
        }
        if (mask) {
            me.addMask(mask);
        }
        me.active = {
            options: options,
            mask: mask,
            scope: scope,
            rendererScope: rendererScope,
            callback: callback,
            success: options.success || me.success,
            failure: options.failure || me.failure,
            renderer: options.renderer || me.renderer,
            scripts: Ext.isDefined(options.scripts) ? options.scripts : me.scripts
        };
        me.active.request = Ext.Ajax.request(options);
        me.setOptions(me.active, options);
    },
    
    setOptions: Ext.emptyFn,
    
    onComplete: function(options, success, response) {
        var me = this,
            active = me.active,
            rendererScope, scope;
        if (active) {
            scope = active.scope;
            rendererScope = active.rendererScope;
            if (success) {
                success = me.getRenderer(active.renderer).call(rendererScope, me, response, active) !== false;
            }
            if (success) {
                Ext.callback(active.success, scope, [
                    me,
                    response,
                    options
                ]);
                me.fireEvent('load', me, response, options);
            } else {
                Ext.callback(active.failure, scope, [
                    me,
                    response,
                    options
                ]);
                me.fireEvent('exception', me, response, options);
            }
            Ext.callback(active.callback, scope, [
                me,
                success,
                response,
                options
            ]);
            if (active.mask) {
                me.removeMask();
            }
        }
        delete me.active;
    },
    
    getRenderer: function(renderer) {
        if (Ext.isFunction(renderer)) {
            return renderer;
        }
        return this.statics().Renderer.Html;
    },
    
    startAutoRefresh: function(interval, options) {
        var me = this;
        me.stopAutoRefresh();
        me.autoRefresh = Ext.interval(function() {
            me.load(options);
        }, interval);
    },
    
    stopAutoRefresh: function() {
        clearInterval(this.autoRefresh);
        delete this.autoRefresh;
    },
    
    isAutoRefreshing: function() {
        return Ext.isDefined(this.autoRefresh);
    },
    
    destroy: function() {
        var me = this;
        me.stopAutoRefresh();
        delete me.target;
        me.abort();
        me.clearListeners();
    }
});


Ext.define('Ext.ComponentLoader', {
    
    extend: 'Ext.ElementLoader',
    statics: {
        Renderer: {
            Data: function(loader, response, active) {
                var success = true;
                try {
                    loader.getTarget().update(Ext.decode(response.responseText));
                } catch (e) {
                    success = false;
                }
                return success;
            },
            Component: function(loader, response, active) {
                var success = true,
                    target = loader.getTarget(),
                    items = [];
                if (!target.isContainer) {
                    Ext.Error.raise({
                        target: target,
                        msg: 'Components can only be loaded into a container'
                    });
                }
                try {
                    items = Ext.decode(response.responseText);
                } catch (e) {
                    success = false;
                }
                if (success) {
                    target.suspendLayouts();
                    if (active.removeAll) {
                        target.removeAll();
                    }
                    target.add(items);
                    target.resumeLayouts(true);
                }
                return success;
            }
        }
    },
    
    
    target: null,
    
    loadOnRender: false,
    
    loadMask: false,
    
    
    renderer: 'html',
    
    setTarget: function(target) {
        var me = this;
        if (Ext.isString(target)) {
            target = Ext.getCmp(target);
        }
        if (me.target && me.target != target) {
            me.abort();
        }
        me.target = target;
        if (target && me.loadOnRender) {
            if (target.rendered) {
                me.doLoadOnRender();
            } else {
                me.mon(target, 'render', me.doLoadOnRender, me);
            }
        }
    },
    doLoadOnRender: function() {
        var loadOnRender = this.loadOnRender;
        this.load(Ext.isObject(loadOnRender) ? loadOnRender : null);
    },
    
    removeMask: function() {
        this.target.setLoading(false);
    },
    
    addMask: function(mask) {
        this.target.setLoading(mask);
    },
    setOptions: function(active, options) {
        active.removeAll = Ext.isDefined(options.removeAll) ? options.removeAll : this.removeAll;
    },
    
    getRenderer: function(renderer) {
        if (Ext.isFunction(renderer)) {
            return renderer;
        }
        var renderers = this.statics().Renderer;
        switch (renderer) {
            case 'component':
                return renderers.Component;
            case 'data':
                return renderers.Data;
            default:
                return Ext.ElementLoader.Renderer.Html;
        }
    }
});


Ext.define('Ext.layout.SizeModel', {
    constructor: function(config) {
        var me = this,
            SizeModel = me.self,
            sizeModelsArray = SizeModel.sizeModelsArray,
            name;
        Ext.apply(me, config);
        me[name = me.name] = true;
        
        me.fixed = !(me.auto = me.natural || me.shrinkWrap);
        
        sizeModelsArray[me.ordinal = sizeModelsArray.length] = SizeModel[name] = SizeModel.sizeModels[name] = me;
    },
    statics: {
        
        sizeModelsArray: [],
        
        sizeModels: {}
    },
    
    
    
    calculated: false,
    
    configured: false,
    
    constrainedMax: false,
    
    constrainedMin: false,
    
    
    natural: false,
    
    shrinkWrap: false,
    
    calculatedFromConfigured: false,
    
    calculatedFromNatural: false,
    
    calculatedFromShrinkWrap: false,
    
    names: null
}, function() {
    var SizeModel = this,
        sizeModelsArray = SizeModel.sizeModelsArray,
        i, j, n, pairs, sizeModel;
    
    
    new SizeModel({
        name: 'calculated'
    });
    new SizeModel({
        name: 'configured',
        names: {
            width: 'width',
            height: 'height'
        }
    });
    new SizeModel({
        name: 'natural'
    });
    new SizeModel({
        name: 'shrinkWrap'
    });
    
    
    
    new SizeModel({
        name: 'calculatedFromConfigured',
        configured: true,
        calculatedFrom: true,
        names: {
            width: 'width',
            height: 'height'
        }
    });
    new SizeModel({
        name: 'calculatedFromNatural',
        natural: true,
        calculatedFrom: true
    });
    new SizeModel({
        name: 'calculatedFromShrinkWrap',
        shrinkWrap: true,
        calculatedFrom: true
    });
    new SizeModel({
        name: 'constrainedMax',
        configured: true,
        constrained: true,
        names: {
            width: 'maxWidth',
            height: 'maxHeight'
        }
    });
    new SizeModel({
        name: 'constrainedMin',
        configured: true,
        constrained: true,
        names: {
            width: 'minWidth',
            height: 'minHeight'
        }
    });
    new SizeModel({
        name: 'constrainedDock',
        configured: true,
        constrained: true,
        constrainedByMin: true,
        names: {
            width: 'dockConstrainedWidth',
            height: 'dockConstrainedHeight'
        }
    });
    for (i = 0 , n = sizeModelsArray.length; i < n; ++i) {
        sizeModel = sizeModelsArray[i];
        
        sizeModel.pairsByHeightOrdinal = pairs = [];
        for (j = 0; j < n; ++j) {
            pairs.push({
                width: sizeModel,
                height: sizeModelsArray[j]
            });
        }
    }
});


Ext.define('Ext.layout.Layout', {
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    requires: [
        'Ext.XTemplate',
        'Ext.layout.SizeModel'
    ],
    uses: [
        'Ext.layout.Context'
    ],
    factoryConfig: {
        type: 'layout'
    },
    
    isLayout: true,
    initialized: false,
    running: false,
    
    needsItemSize: true,
    
    setsItemSize: true,
    autoSizePolicy: {
        readsWidth: 1,
        readsHeight: 1,
        setsWidth: 0,
        setsHeight: 0
    },
    $configPrefixed: false,
    $configStrict: false,
    constructor: function(config) {
        var me = this;
        me.id = Ext.id(null, me.type + '-');
        me.initConfig(config);
        
        
        delete me.type;
        me.layoutCount = 0;
    },
    
    
    beginLayout: Ext.emptyFn,
    
    beginLayoutCycle: function(ownerContext) {
        var me = this,
            context = me.context,
            changed;
        if (me.lastWidthModel != ownerContext.widthModel) {
            if (me.lastWidthModel) {
                changed = true;
            }
            me.lastWidthModel = ownerContext.widthModel;
        }
        if (me.lastHeightModel != ownerContext.heightModel) {
            if (me.lastWidthModel) {
                changed = true;
            }
            me.lastHeightModel = ownerContext.heightModel;
        }
        if (changed) {
            (context = ownerContext.context).clearTriggers(me, false);
            context.clearTriggers(me, true);
            me.triggerCount = 0;
        }
    },
    
    
    
    
    finishedLayout: function(ownerContext) {
        this.lastWidthModel = ownerContext.widthModel;
        this.lastHeightModel = ownerContext.heightModel;
        this.ownerContext = null;
    },
    
    redoLayout: Ext.emptyFn,
    undoLayout: Ext.emptyFn,
    getAnimatePolicy: function() {
        return this.animatePolicy;
    },
    
    getItemSizePolicy: function(item) {
        return this.autoSizePolicy;
    },
    
    getScrollerEl: Ext.emptyFn,
    isItemBoxParent: function(itemContext) {
        return false;
    },
    isItemLayoutRoot: function(item) {
        var sizeModel = item.getSizeModel(),
            width = sizeModel.width,
            height = sizeModel.height;
        
        
        if (!item.componentLayout.lastComponentSize && (width.calculated || height.calculated)) {
            return false;
        }
        
        return !width.shrinkWrap && !height.shrinkWrap;
    },
    isItemShrinkWrap: function(item) {
        return item.shrinkWrap;
    },
    isRunning: function() {
        return !!this.ownerContext;
    },
    
    
    
    getItemsRenderTree: function(items, renderCfgs) {
        var length = items.length,
            i, item, itemConfig, result;
        if (length) {
            result = [];
            for (i = 0; i < length; ++i) {
                item = items[i];
                
                
                if (!item.rendered) {
                    
                    
                    
                    
                    
                    if (renderCfgs && (renderCfgs[item.id] !== undefined)) {
                        itemConfig = renderCfgs[item.id];
                    } else {
                        
                        this.configureItem(item);
                        itemConfig = item.getRenderTree();
                        if (renderCfgs) {
                            renderCfgs[item.id] = itemConfig;
                        }
                    }
                    
                    if (itemConfig) {
                        result.push(itemConfig);
                    }
                }
            }
        }
        return result;
    },
    finishRender: Ext.emptyFn,
    finishRenderItems: function(target, items) {
        var length = items.length,
            i, item;
        for (i = 0; i < length; i++) {
            item = items[i];
            
            if (item.rendering) {
                
                item.finishRender(i);
            }
        }
    },
    renderChildren: function() {
        var me = this,
            items = me.getLayoutItems(),
            target = me.getRenderTarget();
        me.renderItems(items, target);
    },
    
    renderItems: function(items, target) {
        var me = this,
            ln = items.length,
            i = 0,
            item;
        if (ln) {
            Ext.suspendLayouts();
            for (; i < ln; i++) {
                item = items[i];
                if (item && !item.rendered) {
                    me.renderItem(item, target, i);
                } else if (!me.isValidParent(item, target, i)) {
                    me.moveItem(item, target, i);
                } else {
                    
                    me.configureItem(item);
                }
            }
            Ext.resumeLayouts(true);
        }
    },
    
    isValidParent: function(item, target, position) {
        var targetDom = (target && target.dom) || target,
            itemDom = this.getItemLayoutEl(item);
        
        if (itemDom && targetDom) {
            if (typeof position == 'number') {
                position = this.getPositionOffset(position);
                return itemDom === targetDom.childNodes[position];
            }
            return itemDom.parentNode === targetDom;
        }
        return false;
    },
    
    getItemLayoutEl: function(item) {
        var dom = item.el ? item.el.dom : Ext.getDom(item),
            parentNode = dom.parentNode,
            className;
        if (parentNode) {
            className = parentNode.className;
            if (className && className.indexOf(Ext.baseCSSPrefix + 'resizable-wrap') !== -1) {
                dom = dom.parentNode;
            }
        }
        return dom;
    },
    getPositionOffset: function(position) {
        return position;
    },
    
    configureItem: function(item) {
        item.ownerLayout = this;
    },
    
    renderItem: function(item, target, position) {
        var me = this;
        if (!item.rendered) {
            me.configureItem(item);
            item.render(target, position);
        }
    },
    
    moveItem: function(item, target, position) {
        target = target.dom || target;
        if (typeof position == 'number') {
            position = target.childNodes[position];
        }
        target.insertBefore(item.el.dom, position || null);
        item.container = Ext.get(target);
        this.configureItem(item);
    },
    
    onContentChange: function() {
        this.owner.updateLayout();
        return true;
    },
    
    initLayout: function() {
        this.initialized = true;
    },
    
    setOwner: function(owner) {
        this.owner = owner;
    },
    
    getLayoutItems: function() {
        return [];
    },
    onAdd: function(item) {
        item.ownerLayout = this;
    },
    onRemove: Ext.emptyFn,
    onDestroy: Ext.emptyFn,
    
    afterRemove: function(item) {
        var me = this,
            el = item.el,
            owner = me.owner,
            removeClasses;
        if (item.rendered) {
            removeClasses = [].concat(me.itemCls || []);
            if (owner.itemCls) {
                removeClasses = Ext.Array.push(removeClasses, owner.itemCls);
            }
            if (removeClasses.length) {
                el.removeCls(removeClasses);
            }
        }
        delete item.ownerLayout;
    },
    
    afterCollapse: function(owner, animated) {
        if (animated) {
            this.onContentChange(owner);
        }
    },
    
    afterExpand: function(owner, animated) {
        if (animated) {
            this.onContentChange(owner);
        }
    },
    
    destroy: function() {
        var me = this,
            target;
        if (me.targetCls) {
            target = me.getTarget();
            if (target) {
                target.removeCls(me.targetCls);
            }
        }
        me.onDestroy();
    },
    sortWeightedItems: function(items, reverseProp) {
        for (var i = 0,
            length = items.length; i < length; ++i) {
            items[i].$i = i;
        }
        Ext.Array.sort(items, function(item1, item2) {
            var ret = item2.weight - item1.weight;
            if (!ret) {
                ret = item1.$i - item2.$i;
                if (item1[reverseProp]) {
                    ret = -ret;
                }
            }
            return ret;
        });
        for (i = 0; i < length; ++i) {
            delete items[i].$i;
        }
    }
}, function() {
    var Layout = this;
    Layout.prototype.sizeModels = Layout.sizeModels = Ext.layout.SizeModel.sizeModels;
});


Ext.define('Ext.layout.container.Container', {
    extend: 'Ext.layout.Layout',
    alias: 'layout.container',
    alternateClassName: 'Ext.layout.ContainerLayout',
    mixins: [
        'Ext.util.ElementContainer'
    ],
    requires: [
        'Ext.XTemplate'
    ],
    type: 'container',
    
    
    
    beginCollapse: Ext.emptyFn,
    
    beginExpand: Ext.emptyFn,
    
    animatePolicy: null,
    
    activeItemCount: 0,
    renderTpl: [
        '{%this.renderBody(out,values)%}'
    ],
    usesContainerHeight: true,
    usesContainerWidth: true,
    usesHeight: true,
    usesWidth: true,
    constructor: function() {
        this.callParent(arguments);
        this.mixins.elementCt.constructor.call(this);
    },
    destroy: function() {
        this.callParent();
        this.mixins.elementCt.destroy.call(this);
    },
    
    beginLayout: function(ownerContext) {
        this.callParent(arguments);
        ownerContext.targetContext = ownerContext.paddingContext = ownerContext.getEl('getTarget', this);
        this.cacheChildItems(ownerContext);
    },
    beginLayoutCycle: function(ownerContext, firstCycle) {
        var me = this;
        me.callParent(arguments);
        if (firstCycle) {
            if (me.usesContainerHeight) {
                ++ownerContext.consumersContainerHeight;
            }
            if (me.usesContainerWidth) {
                ++ownerContext.consumersContainerWidth;
            }
        }
    },
    cacheChildItems: function(ownerContext) {
        var me = this,
            context, childItems, items, length, i;
        
        
        if (me.needsItemSize || me.setsItemSize) {
            context = ownerContext.context;
            childItems = ownerContext.childItems = [];
            items = ownerContext.visibleItems = me.getVisibleItems();
            length = items.length;
            for (i = 0; i < length; ++i) {
                childItems.push(context.getCmp(items[i]));
            }
        }
    },
    cacheElements: function() {
        var owner = this.owner;
        this.attachChildEls(owner.el, owner);
    },
    
    calculate: function(ownerContext) {
        var props = ownerContext.props,
            el = ownerContext.el;
        if (ownerContext.widthModel.shrinkWrap && isNaN(props.width)) {
            ownerContext.setContentWidth(el.getWidth());
        }
        if (ownerContext.heightModel.shrinkWrap && isNaN(props.height)) {
            ownerContext.setContentHeight(el.getHeight());
        }
    },
    
    configureItem: function(item) {
        var me = this,
            itemCls = me.itemCls,
            ownerItemCls = me.owner.itemCls,
            needsCopy, addClasses;
        
        item.ownerLayout = me;
        if (itemCls) {
            
            if (typeof itemCls === 'string') {
                addClasses = [
                    itemCls
                ];
            } else {
                addClasses = itemCls;
                needsCopy = !!addClasses;
            }
        }
        if (ownerItemCls) {
            
            if (needsCopy) {
                addClasses = Ext.Array.clone(addClasses);
            }
            addClasses = Ext.Array.push(addClasses || [], ownerItemCls);
        }
        if (addClasses) {
            item.addCls(addClasses);
        }
    },
    doRenderBody: function(out, renderData) {
        
        
        this.renderItems(out, renderData);
        this.renderContent(out, renderData);
    },
    doRenderContainer: function(out, renderData) {
        
        
        var me = renderData.$comp.layout,
            tpl = me.getRenderTpl(),
            data = me.getRenderData();
        tpl.applyOut(data, out);
    },
    doRenderItems: function(out, renderData) {
        
        
        var me = renderData.$layout,
            tree = me.getRenderTree();
        if (tree) {
            Ext.DomHelper.generateMarkup(tree, out);
        }
    },
    finishRender: function() {
        var me = this,
            target, items;
        me.callParent();
        me.cacheElements();
        target = me.getRenderTarget();
        items = me.getLayoutItems();
        me.finishRenderItems(target, items);
    },
    
    notifyOwner: function() {
        if (!this._hasTargetWarning && this.targetCls && !this.getTarget().hasCls(this.targetCls)) {
            this._hasTargetWarning = true;
            Ext.log.warn('targetCls is missing. This may mean that getTargetEl() is being overridden but not applyTargetCls(). ' + this.owner.id);
        }
        this.owner.afterLayout(this);
    },
    
    getContainerSize: function(ownerContext, inDom) {
        
        
        
        
        
        var targetContext = ownerContext.targetContext,
            frameInfo = targetContext.getFrameInfo(),
            padding = ownerContext.paddingContext.getPaddingInfo(),
            got = 0,
            needed = 0,
            gotWidth, gotHeight, width, height;
        
        
        
        
        
        
        if (!ownerContext.widthModel.shrinkWrap) {
            ++needed;
            width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width');
            gotWidth = (typeof width == 'number');
            if (gotWidth) {
                ++got;
                width -= frameInfo.width + padding.width;
                if (width < 0) {
                    width = 0;
                }
            }
        }
        if (!ownerContext.heightModel.shrinkWrap) {
            ++needed;
            height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height');
            gotHeight = (typeof height == 'number');
            if (gotHeight) {
                ++got;
                height -= frameInfo.height + padding.height;
                if (height < 0) {
                    height = 0;
                }
            }
        }
        return {
            width: width,
            height: height,
            needed: needed,
            got: got,
            gotAll: got == needed,
            gotWidth: gotWidth,
            gotHeight: gotHeight
        };
    },
    
    
    
    
    
    
    
    
    getPositionOffset: function(position) {
        if (!this.createsInnerCt) {
            var offset = this.owner.itemNodeOffset;
            if (offset) {
                position += offset;
            }
        }
        return position;
    },
    
    getLayoutItems: function() {
        var owner = this.owner,
            items = owner && owner.items;
        return (items && items.items) || [];
    },
    getRenderData: function() {
        var comp = this.owner;
        return {
            $comp: comp,
            $layout: this,
            ownerId: comp.id
        };
    },
    
    getRenderedItems: function() {
        var me = this,
            target = me.getRenderTarget(),
            items = me.getLayoutItems(),
            ln = items.length,
            renderedItems = [],
            i, item;
        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.rendered && me.isValidParent(item, target, i)) {
                renderedItems.push(item);
            }
        }
        return renderedItems;
    },
    
    getRenderTarget: function() {
        return this.owner.getTargetEl();
    },
    
    getElementTarget: function() {
        return this.getRenderTarget();
    },
    getRenderTpl: function() {
        var me = this,
            renderTpl = Ext.XTemplate.getTpl(this, 'renderTpl');
        
        
        if (!renderTpl.renderContent) {
            me.owner.setupRenderTpl(renderTpl);
        }
        return renderTpl;
    },
    getRenderTree: function() {
        var result,
            items = this.owner.items,
            itemsGen,
            renderCfgs = {};
        do {
            itemsGen = items.generation;
            result = this.getItemsRenderTree(this.getLayoutItems(), renderCfgs);
        } while (items.generation !== itemsGen);
        return result;
    },
    renderChildren: function() {
        var me = this,
            ownerItems = me.owner.items,
            target = me.getRenderTarget(),
            itemsGen, items;
        
        
        
        do {
            itemsGen = ownerItems.generation;
            items = me.getLayoutItems();
            me.renderItems(items, target);
        } while (ownerItems.generation !== itemsGen);
    },
    getScrollbarsNeeded: function(width, height, contentWidth, contentHeight) {
        var scrollbarSize = Ext.getScrollbarSize(),
            hasWidth = typeof width == 'number',
            hasHeight = typeof height == 'number',
            needHorz = 0,
            needVert = 0;
        
        if (!scrollbarSize.width) {
            return 0;
        }
        if (hasHeight && height < contentHeight) {
            needVert = 2;
            width -= scrollbarSize.width;
        }
        if (hasWidth && width < contentWidth) {
            needHorz = 1;
            if (!needVert && hasHeight) {
                height -= scrollbarSize.height;
                if (height < contentHeight) {
                    needVert = 2;
                }
            }
        }
        return needVert + needHorz;
    },
    
    getTarget: function() {
        return this.owner.getTargetEl();
    },
    
    getVisibleItems: function() {
        var target = this.getRenderTarget(),
            items = this.getLayoutItems(),
            ln = items.length,
            visibleItems = [],
            i, item;
        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.rendered && this.isValidParent(item, target, i) && item.hidden !== true && !item.floated) {
                visibleItems.push(item);
            }
        }
        return visibleItems;
    },
    getMoveAfterIndex: function(after) {
        return this.owner.items.indexOf(after) + 1;
    },
    moveItemBefore: function(item, before) {
        var owner = this.owner,
            items = owner.items,
            toIndex;
        if (item === before) {
            return item;
        }
        toIndex = before ? items.indexOf(before) : items.length;
        return owner.insert(toIndex, item);
    },
    setupRenderTpl: function(renderTpl) {
        renderTpl.renderBody = this.doRenderBody;
        renderTpl.renderContainer = this.doRenderContainer;
        renderTpl.renderItems = this.doRenderItems;
    },
    getContentTarget: function() {
        return this.owner.getDefaultContentTarget();
    },
    onAdd: function(item) {
        if (!item.liquidLayout) {
            ++this.activeItemCount;
        }
        this.callParent([
            item
        ]);
    },
    onRemove: function(item) {
        if (!item.liquidLayout) {
            --this.activeItemCount;
        }
        this.callParent([
            item
        ]);
    }
});


Ext.define('Ext.layout.container.Auto', {
    
    alias: [
        'layout.auto',
        'layout.autocontainer'
    ],
    extend: 'Ext.layout.container.Container',
    
    type: 'autocontainer',
    childEls: [
        'outerCt',
        'innerCt'
    ],
    
    reserveScrollbar: false,
    
    managePadding: true,
    
    manageOverflow: false,
    
    
    needsItemSize: false,
    setsItemSize: false,
    
    lastOverflowAdjust: {
        width: 0,
        height: 0
    },
    outerCtCls: Ext.baseCSSPrefix + 'autocontainer-outerCt',
    innerCtCls: Ext.baseCSSPrefix + 'autocontainer-innerCt',
    
    
    
    
    
    
    
    
    
    
    
    
    renderTpl: [
        
        
        
        '<div id="{ownerId}-outerCt" data-ref="outerCt" class="{outerCtCls}" role="presentation">',
        '<div id="{ownerId}-innerCt" data-ref="innerCt" style="{%this.renderPadding(out, values)%}" ',
        'class="{innerCtCls}" role="presentation">',
        '{%this.renderBody(out,values)%}',
        '</div>',
        '</div>'
    ],
    beginLayout: function(ownerContext) {
        this.callParent(arguments);
        this.initContextItems(ownerContext);
    },
    beforeLayoutCycle: function(ownerContext) {
        var comp = this.owner,
            inheritedState = comp.inheritedState,
            inheritedStateInner = comp.inheritedStateInner;
        if (!inheritedState || inheritedState.invalid) {
            inheritedState = comp.getInherited();
            
            inheritedStateInner = comp.inheritedStateInner;
        }
        if (ownerContext.widthModel.shrinkWrap) {
            inheritedStateInner.inShrinkWrapTable = true;
        } else {
            delete inheritedStateInner.inShrinkWrapTable;
        }
    },
    beginLayoutCycle: function(ownerContext) {
        var me = this,
            outerCt = me.outerCt,
            lastOuterCtWidth = me.lastOuterCtWidth || '',
            lastOuterCtHeight = me.lastOuterCtHeight || '',
            lastOuterCtTableLayout = me.lastOuterCtTableLayout || '',
            state = ownerContext.state,
            overflowXStyle, outerCtWidth, outerCtHeight, outerCtTableLayout, inheritedStateInner;
        me.callParent(arguments);
        
        outerCtWidth = outerCtHeight = outerCtTableLayout = '';
        if (!ownerContext.widthModel.shrinkWrap) {
            
            
            
            outerCtWidth = '100%';
            inheritedStateInner = me.owner.inheritedStateInner;
            
            
            
            overflowXStyle = me.getOverflowXStyle(ownerContext);
            outerCtTableLayout = (inheritedStateInner.inShrinkWrapTable || overflowXStyle === 'auto' || overflowXStyle === 'scroll') ? '' : 'fixed';
        }
        if (!ownerContext.heightModel.shrinkWrap && !Ext.supports.PercentageHeightOverflowBug) {
            
            
            
            
            
            outerCtHeight = '100%';
        }
        
        
        
        if ((outerCtWidth !== lastOuterCtWidth) || me.hasOuterCtPxWidth) {
            outerCt.setStyle('width', outerCtWidth);
            me.lastOuterCtWidth = outerCtWidth;
            me.hasOuterCtPxWidth = false;
        }
        
        if (outerCtTableLayout !== lastOuterCtTableLayout) {
            outerCt.setStyle('table-layout', outerCtTableLayout);
            me.lastOuterCtTableLayout = outerCtTableLayout;
        }
        
        
        
        if ((outerCtHeight !== lastOuterCtHeight) || me.hasOuterCtPxHeight) {
            outerCt.setStyle('height', outerCtHeight);
            me.lastOuterCtHeight = outerCtHeight;
            me.hasOuterCtPxHeight = false;
        }
        if (me.hasInnerCtPxHeight) {
            me.innerCt.setStyle('height', '');
            me.hasInnerCtPxHeight = false;
        }
        
        
        
        state.overflowAdjust = state.overflowAdjust || me.lastOverflowAdjust;
    },
    calculate: function(ownerContext) {
        var me = this,
            state = ownerContext.state,
            containerSize = me.getContainerSize(ownerContext, true),
            
            calculatedItems = state.calculatedItems || (state.calculatedItems = me.calculateItems ? me.calculateItems(ownerContext, containerSize) : true);
        me.setCtSizeIfNeeded(ownerContext, containerSize);
        if (calculatedItems && ownerContext.hasDomProp('containerChildrenSizeDone')) {
            me.calculateContentSize(ownerContext);
            if (containerSize.gotAll) {
                if (me.manageOverflow && !ownerContext.state.secondPass && !me.reserveScrollbar) {
                    me.calculateOverflow(ownerContext, containerSize);
                }
                return;
            }
        }
        me.done = false;
    },
    calculateContentSize: function(ownerContext) {
        var me = this,
            containerDimensions = ((ownerContext.widthModel.shrinkWrap ? 1 : 0) | (ownerContext.heightModel.shrinkWrap ? 2 : 0)),
            calcWidth = (containerDimensions & 1) || undefined,
            calcHeight = (containerDimensions & 2) || undefined,
            needed = 0,
            props = ownerContext.props;
        if (calcWidth) {
            if (isNaN(props.contentWidth)) {
                ++needed;
            } else {
                calcWidth = undefined;
            }
        }
        if (calcHeight) {
            if (isNaN(props.contentHeight)) {
                ++needed;
            } else {
                calcHeight = undefined;
            }
        }
        if (needed) {
            if (calcWidth && !ownerContext.setContentWidth(me.measureContentWidth(ownerContext))) {
                me.done = false;
            }
            if (calcHeight && !ownerContext.setContentHeight(me.measureContentHeight(ownerContext))) {
                me.done = false;
            }
        }
    },
    
    
    
    
    
    
    calculateOverflow: function(ownerContext) {
        var me = this,
            width, height, scrollbarSize, scrollbars, xauto, yauto, targetEl;
        
        
        xauto = (me.getOverflowXStyle(ownerContext) === 'auto');
        yauto = (me.getOverflowYStyle(ownerContext) === 'auto');
        if (xauto || yauto) {
            scrollbarSize = Ext.getScrollbarSize();
            targetEl = ownerContext.overflowContext.el.dom;
            scrollbars = 0;
            if (targetEl.scrollWidth > targetEl.clientWidth) {
                
                scrollbars |= 1;
            }
            if (targetEl.scrollHeight > targetEl.clientHeight) {
                
                scrollbars |= 2;
            }
            width = (yauto && (scrollbars & 2)) ? scrollbarSize.width : 0;
            height = (xauto && (scrollbars & 1)) ? scrollbarSize.height : 0;
            if (width !== me.lastOverflowAdjust.width || height !== me.lastOverflowAdjust.height) {
                me.done = false;
                
                
                ownerContext.invalidate({
                    state: {
                        overflowAdjust: {
                            width: width,
                            height: height
                        },
                        overflowState: scrollbars,
                        secondPass: true
                    }
                });
            }
        }
    },
    completeLayout: function(ownerContext) {
        this.lastOverflowAdjust = ownerContext.state.overflowAdjust;
    },
    doRenderBody: function(out, renderData) {
        
        
        var me = renderData.$layout,
            XTemplate = Ext.XTemplate,
            beforeBodyTpl = me.beforeBodyTpl,
            afterBodyTpl = me.afterBodyTpl;
        if (beforeBodyTpl) {
            XTemplate.getTpl(me, 'beforeBodyTpl').applyOut(renderData, out);
        }
        this.renderItems(out, renderData);
        this.renderContent(out, renderData);
        if (afterBodyTpl) {
            XTemplate.getTpl(me, 'afterBodyTpl').applyOut(renderData, out);
        }
    },
    doRenderPadding: function(out, renderData) {
        
        
        var me = renderData.$layout,
            owner = renderData.$layout.owner,
            padding = owner[owner.contentPaddingProperty];
        if (me.managePadding && padding) {
            out.push('padding:', owner.unitizeBox(padding));
        }
    },
    finishedLayout: function(ownerContext) {
        var innerCt = this.innerCt;
        this.callParent(arguments);
        if (Ext.isIE8) {
            
            innerCt.repaint();
        }
        if (Ext.isOpera) {
            
            
            
            innerCt.setStyle('position', 'relative');
            innerCt.dom.scrollWidth;
            innerCt.setStyle('position', '');
        }
    },
    
    getContainerSize: function(ownerContext, inDom) {
        
        
        
        
        
        var size = this.callParent(arguments),
            overflowAdjust = ownerContext.state.overflowAdjust;
        if (overflowAdjust) {
            size.width -= overflowAdjust.width;
            size.height -= overflowAdjust.height;
        }
        return size;
    },
    getRenderData: function() {
        var me = this,
            data = me.callParent();
        data.innerCtCls = me.innerCtCls;
        data.outerCtCls = me.outerCtCls;
        return data;
    },
    
    
    getRenderTarget: function() {
        return this.innerCt;
    },
    
    
    getElementTarget: function() {
        return this.innerCt;
    },
    
    getOverflowXStyle: function(ownerContext) {
        return ownerContext.overflowXStyle || (ownerContext.overflowXStyle = this.owner.scrollFlags.overflowX || ownerContext.overflowContext.getStyle('overflow-x'));
    },
    
    getOverflowYStyle: function(ownerContext) {
        return ownerContext.overflowYStyle || (ownerContext.overflowYStyle = this.owner.scrollFlags.overflowY || ownerContext.overflowContext.getStyle('overflow-y'));
    },
    initContextItems: function(ownerContext) {
        var me = this,
            target = ownerContext.target,
            overflowEl = me.owner.getOverflowEl();
        ownerContext.outerCtContext = ownerContext.getEl('outerCt', me);
        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);
        ownerContext.overflowContext = (overflowEl === ownerContext.el) ? ownerContext : ownerContext.getEl(overflowEl);
        if (target[target.contentPaddingProperty] !== undefined) {
            
            
            
            
            
            ownerContext.paddingContext = ownerContext.innerCtContext;
        }
    },
    initLayout: function() {
        var me = this,
            scrollbarWidth = Ext.getScrollbarSize().width,
            owner = me.owner;
        me.callParent();
        
        
        
        if (scrollbarWidth && me.manageOverflow && !me.hasOwnProperty('lastOverflowAdjust')) {
            if (owner.scrollable || me.reserveScrollbar) {
                me.lastOverflowAdjust = {
                    width: scrollbarWidth,
                    height: 0
                };
            }
        }
    },
    measureContentHeight: function(ownerContext) {
        
        var contentHeight = this.outerCt.getHeight(),
            target = ownerContext.target;
        if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) {
            
            
            
            
            contentHeight += ownerContext.targetContext.getPaddingInfo().height;
        }
        return contentHeight;
    },
    measureContentWidth: function(ownerContext) {
        var dom, style, old, contentWidth, target;
        
        
        
        if (this.chromeCellMeasureBug) {
            dom = this.innerCt.dom;
            style = dom.style;
            old = style.display;
            if (old == 'table-cell') {
                style.display = '';
                dom.offsetWidth;
                style.display = old;
            }
        }
        if (Ext.isSafari) {
            
            
            
            
            
            dom = this.outerCt.dom;
            style = dom.style;
            style.display = 'table-cell';
            dom.offsetWidth;
            dom.style.display = '';
        }
        
        contentWidth = this.outerCt.getWidth();
        target = ownerContext.target;
        if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) {
            
            
            
            
            contentWidth += ownerContext.targetContext.getPaddingInfo().width;
        }
        return contentWidth;
    },
    
    setCtSizeIfNeeded: function(ownerContext, containerSize) {
        var me = this,
            height = containerSize.height,
            padding = ownerContext.paddingContext.getPaddingInfo(),
            targetEl = me.getTarget(),
            overflowXStyle = me.getOverflowXStyle(ownerContext),
            canOverflowX = (overflowXStyle === 'auto' || overflowXStyle === 'scroll'),
            scrollbarSize = Ext.getScrollbarSize(),
            needsOuterHeight, needsInnerHeight;
        if (height && !ownerContext.heightModel.shrinkWrap) {
            if (Ext.supports.PercentageHeightOverflowBug) {
                
                
                needsOuterHeight = true;
            }
            if (Ext.isIE8) {
                
                
                needsInnerHeight = true;
            }
            if ((needsOuterHeight || needsInnerHeight) && canOverflowX && (targetEl.dom.scrollWidth > targetEl.dom.clientWidth)) {
                
                
                
                height = Math.max(height - scrollbarSize.height, 0);
            }
            if (needsOuterHeight) {
                ownerContext.outerCtContext.setProp('height', height + padding.height);
                me.hasOuterCtPxHeight = true;
            }
            if (needsInnerHeight) {
                ownerContext.innerCtContext.setProp('height', height);
                me.hasInnerCtPxHeight = true;
            }
        }
    },
    setupRenderTpl: function(renderTpl) {
        this.callParent(arguments);
        renderTpl.renderPadding = this.doRenderPadding;
    },
    getContentTarget: function() {
        return this.innerCt;
    },
    getScrollerEl: function() {
        return this.outerCt;
    }
}, function() {
    this.prototype.chromeCellMeasureBug = Ext.isChrome && Ext.chromeVersion >= 26;
});


Ext.define('Ext.ZIndexManager', {
    alternateClassName: 'Ext.WindowGroup',
    requires: [
        'Ext.util.SorterCollection',
        'Ext.util.FilterCollection'
    ],
    statics: {
        zBase: 9000,
        activeCounter: 0
    },
    constructor: function(container) {
        var me = this;
        me.id = Ext.id(null, 'zindex-mgr-');
        
        
        
        
        me.zIndexStack = new Ext.util.Collection({
            sorters: {
                sorterFn: function(comp1, comp2) {
                    var ret = (comp1.alwaysOnTop || 0) - (comp2.alwaysOnTop || 0);
                    if (!ret) {
                        ret = comp1.getActiveCounter() - comp2.getActiveCounter();
                    }
                    return ret;
                }
            },
            filters: {
                filterFn: function(comp) {
                    return comp.isVisible();
                }
            }
        });
        
        
        me.zIndexStack.addObserver(me);
        me.front = null;
        
        
        me.globalListeners = Ext.GlobalEvents.on({
            hide: me.onComponentShowHide,
            show: me.onComponentShowHide,
            scope: me,
            destroyable: true
        });
        if (container) {
            
            if (container.isContainer) {
                container.on('resize', me._onContainerResize, me);
                me.zseed = Ext.Number.from(me.rendered ? container.getEl().getStyle('zIndex') : undefined, me.getNextZSeed());
                
                me.targetEl = container.getTargetEl();
                me.container = container;
            } else 
            {
                Ext.on('resize', me._onContainerResize, me);
                me.zseed = me.getNextZSeed();
                me.targetEl = Ext.get(container);
            }
        } else 
        
        {
            me.zseed = me.getNextZSeed();
            Ext.onReady(function() {
                Ext.on('resize', me._onContainerResize, me);
                me.targetEl = Ext.getBody();
            });
        }
    },
    
    getId: function() {
        return this.id;
    },
    getNextZSeed: function() {
        return (Ext.ZIndexManager.zBase += 10000);
    },
    setBase: function(baseZIndex) {
        this.zseed = baseZIndex;
        return this.onCollectionSort();
    },
    
    
    
    onCollectionSort: function() {
        var me = this,
            oldFront = me.front,
            zIndex = me.zseed,
            a = me.zIndexStack.getRange(),
            len = a.length,
            i, comp, topModal, topVisible,
            doFocus = false;
        for (i = 0; i < len; i++) {
            comp = a[i];
            
            
            
            
            
            
            
            zIndex = comp.setZIndex(zIndex);
            
            
            
            if (!comp.hidden) {
                topVisible = comp;
                
                if (comp.modal) {
                    topModal = comp;
                }
            }
        }
        
        if (topVisible !== oldFront) {
            
            
            if (oldFront && !oldFront.destroying) {
                oldFront.setActive(false);
            }
            
            if (topVisible) {
                
                
                
                
                
                
                
                
                
                doFocus = topVisible.modal || ((!oldFront || oldFront.isVisible()) && ((topVisible.focusOnToFront && !topVisible.preventFocusOnActivate))) && topVisible.isFocusable(true);
                topVisible.setActive(true, null, doFocus);
            }
        }
        
        me.front = topVisible;
        
        if (topModal) {
            me._showModalMask(topModal);
        } else 
        {
            me._hideModalMask();
        }
        return zIndex;
    },
    
    onComponentUpdate: function(comp) {
        if (this.zIndexStack.contains(comp)) {
            this.zIndexStack.sort();
        }
    },
    onComponentRender: function(comp) {
        this.zIndexStack.itemChanged(comp, 'hidden');
    },
    
    onComponentShowHide: function(comp) {
        var zIndexStack = this.zIndexStack;
        
        if (comp.isFloating() && !this.hidingAll && (zIndexStack.getSource() || zIndexStack).contains(comp)) {
            zIndexStack.itemChanged(comp, 'hidden');
            zIndexStack.sort();
        }
    },
    _showModalMask: function(comp) {
        var me = this,
            compEl = comp.el,
            zIndex = compEl.getStyle('zIndex') - 4,
            maskTarget = comp.floatParent ? comp.floatParent.getTargetEl() : comp.container,
            mask = me.mask,
            shim = me.maskShim,
            viewSize, tabbableAttr, tempTabbableAttr, tabbables;
        if (!mask) {
            
            mask = me.mask = Ext.getBody().createChild({
                
                'data-sticky': true,
                role: 'presentation',
                cls: Ext.baseCSSPrefix + 'mask',
                style: 'height:0;width:0'
            });
            mask.setVisibilityMode(Ext.Element.DISPLAY);
            mask.on('click', me._onMaskClick, me);
        } else 
        
        {
            me._hideModalMask();
        }
        mask.maskTarget = maskTarget;
        viewSize = me._getMaskBox();
        if (shim) {
            shim.setStyle('zIndex', zIndex);
            shim.show();
            shim.setBox(viewSize);
        }
        mask.setStyle('zIndex', zIndex);
        tabbableAttr = 'data-savedtabindex-' + maskTarget.getId();
        tempTabbableAttr = tabbableAttr + '-temp';
        
        
        
        tabbables = compEl.findTabbableElements();
        if (tabbables.length) {
            compEl.saveTabbableState(tempTabbableAttr);
            compEl.saveChildrenTabbableState(tempTabbableAttr);
        }
        maskTarget.saveTabbableState(tabbableAttr);
        maskTarget.saveChildrenTabbableState(tabbableAttr);
        
        
        if (tabbables.length) {
            compEl.restoreChildrenTabbableState(tempTabbableAttr);
            compEl.restoreTabbableState(tempTabbableAttr);
        }
        mask.show();
        mask.setBox(viewSize);
    },
    _hideModalMask: function() {
        var mask = this.mask,
            maskShim = this.maskShim,
            maskTarget, tabbableAttr;
        if (mask && mask.isVisible()) {
            maskTarget = mask.maskTarget;
            tabbableAttr = 'data-savedtabindex-' + maskTarget.getId();
            maskTarget.restoreChildrenTabbableState(tabbableAttr);
            maskTarget.restoreTabbableState(tabbableAttr);
            mask.maskTarget = undefined;
            mask.hide();
            if (maskShim) {
                maskShim.hide();
            }
        }
    },
    _onMaskClick: function() {
        if (this.front) {
            this.front.focus();
        }
    },
    _getMaskBox: function() {
        var maskTarget = this.mask.maskTarget;
        if (maskTarget.dom === document.body) {
            return {
                height: Math.max(document.body.scrollHeight, Ext.dom.Element.getDocumentHeight()),
                width: Math.max(document.body.scrollWidth, document.documentElement.clientWidth),
                x: 0,
                y: 0
            };
        } else {
            return maskTarget.getBox();
        }
    },
    _onContainerResize: function() {
        var me = this,
            mask = me.mask,
            maskShim = me.maskShim,
            viewSize;
        if (mask && mask.isVisible()) {
            
            
            mask.hide();
            if (maskShim) {
                maskShim.hide();
            }
            viewSize = me._getMaskBox();
            if (maskShim) {
                maskShim.setSize(viewSize);
                maskShim.show();
            }
            mask.setSize(viewSize);
            mask.show();
        }
    },
    
    register: function(comp) {
        var me = this;
        if (comp.zIndexManager) {
            comp.zIndexManager.unregister(comp);
        }
        comp.zIndexManager = me;
        if (!comp.rendered) {
            
            
            
            
            comp.on('render', me.onComponentRender, me, {
                single: true
            });
        }
        me.zIndexStack.add(comp);
    },
    
    unregister: function(comp) {
        var me = this;
        delete comp.zIndexManager;
        comp.un('render', me.onComponentRender, me);
        me.zIndexStack.remove(comp);
        me.onCollectionSort();
    },
    
    get: function(id) {
        return id.isComponent ? id : this.zIndexStack.get(id);
    },
    
    bringToFront: function(comp, preventFocus) {
        var me = this,
            zIndexStack = me.zIndexStack,
            oldFront = zIndexStack.last(),
            newFront, preventFocusSetting;
        comp = me.get(comp);
        
        if (!comp || zIndexStack.find('alwaysOnTop', true)) {
            return false;
        }
        preventFocusSetting = comp.preventFocusOnActivate;
        
        
        comp.preventFocusOnActivate = preventFocus;
        comp.setActiveCounter(++Ext.ZIndexManager.activeCounter);
        comp.preventFocusOnActivate = preventFocusSetting;
        newFront = zIndexStack.last();
        
        return (newFront === comp && newFront !== oldFront);
    },
    
    sendToBack: function(comp) {
        comp = this.get(comp);
        if (comp) {
            comp.setActiveCounter(0);
        }
        return comp || null;
    },
    
    hideAll: function() {
        var all = this.zIndexStack.getRange(),
            len = all.length,
            i;
        this.hidingAll = true;
        for (i = 0; i < len; i++) {
            all[i].hide();
        }
        this.hidingAll = true;
    },
    
    hide: function() {
        var me = this,
            activeElement = Ext.Element.getActiveElement(),
            all = me.tempHidden = me.zIndexStack.getRange(),
            len = all.length,
            i, comp;
        
        me.focusRestoreElement = null;
        for (i = 0; i < len; i++) {
            comp = all[i];
            if (comp.el.contains(activeElement)) {
                me.focusRestoreElement = activeElement;
            }
            comp.el.hide();
            comp.hidden = true;
        }
    },
    
    show: function() {
        var me = this,
            i,
            tempHidden = me.tempHidden,
            len = tempHidden ? tempHidden.length : 0,
            comp;
        for (i = 0; i < len; i++) {
            comp = tempHidden[i];
            comp.el.show();
            comp.hidden = false;
            comp.setPosition(comp.x, comp.y);
        }
        me.tempHidden = null;
        if (me.focusRestoreElement) {
            me.focusRestoreElement.focus();
        }
    },
    
    getActive: function() {
        return this.zIndexStack.last();
    },
    
    getBy: function(fn, scope) {
        return this.zIndexStack.filterBy(fn, scope).getRange();
    },
    
    each: function(fn, scope) {
        this.zIndexStack.each(fn, scope);
    },
    
    eachBottomUp: function(fn, scope) {
        var stack = this.zIndexStack.getRange(),
            i,
            len = stack.length,
            comp;
        for (i = 0; i < len; i++) {
            comp = stack[i];
            if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                return;
            }
        }
    },
    
    eachTopDown: function(fn, scope) {
        var stack = this.zIndexStack.getRange(),
            i, comp;
        for (i = stack.length; i-- > 0; ) {
            comp = stack[i];
            if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                return;
            }
        }
    },
    destroy: function() {
        var me = this,
            stack = me.zIndexStack.getRange(),
            len = stack.length,
            i;
        for (i = 0; i < len; i++) {
            Ext.destroy(stack[i]);
        }
        Ext.destroy(me.mask, me.maskShim, me.zIndexStack, me.globalListeners);
        me.zIndexStack = me.container = me.targetEl = me.globalListeners = null;
    }
}, function() {
    
    Ext.WindowManager = Ext.WindowMgr = new this();
});


Ext.define('Ext.container.Container', {
    extend: 'Ext.Component',
    xtype: 'container',
    alternateClassName: [
        'Ext.Container',
        'Ext.AbstractContainer'
    ],
    requires: [
        'Ext.util.MixedCollection',
        'Ext.layout.container.Auto',
        'Ext.ZIndexManager'
    ],
    mixins: [
        'Ext.mixin.Queryable'
    ],
    renderTpl: '{%this.renderContainer(out,values)%}',
    
    isContainer: true,
    
    
    
    
    
    
    autoDestroy: true,
    
    
    
    defaultType: 'panel',
    
    detachOnRemove: true,
    
    
    items: undefined,
    
    
    
    layout: 'auto',
    
    suspendLayout: false,
    
    
    
    
    
    
    
    
    
    
    
    _applyDefaultsOptions: {
        defaults: true,
        strict: false
    },
    ariaRole: 'presentation',
    baseCls: Ext.baseCSSPrefix + 'container',
    
    layoutCounter: 0,
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    add: function() {
        var me = this,
            args = Ext.Array.slice(arguments),
            index = (typeof args[0] === 'number') ? args.shift() : -1,
            layout = me.getLayout(),
            needsLayout = false,
            addingArray, items, i, length, item, pos, ret, instanced;
        if (args.length === 1 && Ext.isArray(args[0])) {
            items = args[0];
            addingArray = true;
        } else {
            items = args;
        }
        if (me.rendered) {
            Ext.suspendLayouts();
        }
        
        ret = items = me.prepareItems(items, true);
        length = items.length;
        if (!addingArray && length === 1) {
            
            ret = items[0];
        }
        
        for (i = 0; i < length; i++) {
            item = items[i];
            if (!item) {
                Ext.Error.raise("Cannot add null item to Container with itemId/id: " + me.getItemId());
            }
            if (item.isDestroyed) {
                Ext.Error.raise("Cannot add destroyed item '" + item.getId() + "' to Container '" + me.getId() + "'");
            }
            pos = (index < 0) ? me.items.length : (index + i);
            instanced = !!item.instancedCmp;
            delete item.instancedCmp;
            
            if (item.floating) {
                me.floatingItems.add(item);
                item.onAdded(me, pos, instanced);
                delete item.initOwnerCt;
                if (me.hasListeners.add) {
                    me.fireEvent('add', me, item, pos);
                }
            } else if ((!me.hasListeners.beforeadd || me.fireEvent('beforeadd', me, item, pos) !== false) && me.onBeforeAdd(item) !== false) {
                me.items.insert(pos, item);
                item.onAdded(me, pos, instanced);
                delete item.initOwnerCt;
                me.onAdd(item, pos);
                layout.onAdd(item, pos);
                needsLayout = true;
                if (me.hasListeners.add) {
                    me.fireEvent('add', me, item, pos);
                }
            }
        }
        
        
        if (needsLayout) {
            me.updateLayout();
        }
        if (me.rendered) {
            Ext.resumeLayouts(true);
        }
        return ret;
    },
    onAdded: function(container, pos, instanced) {
        this.callParent(arguments);
        
        
        
        if (instanced) {
            Ext.ComponentManager.markReferencesDirty();
        }
    },
    onRemoved: function(destroying) {
        var refHolder;
        
        if (!destroying) {
            refHolder = this.lookupReferenceHolder();
            if (refHolder) {
                
                
                
                
                Ext.ComponentManager.markReferencesDirty();
                refHolder.clearReferences();
            }
        }
        this.callParent(arguments);
    },
    afterComponentLayout: function() {
        var floaters = this.floatingItems.items,
            floaterCount = floaters.length,
            i, floater;
        this.callParent(arguments);
        
        for (i = 0; i < floaterCount; i++) {
            floater = floaters[i];
            if (!floater.rendered && floater.autoShow) {
                floater.show();
            }
        }
    },
    
    afterLayout: function(layout) {
        var me = this,
            scroller = me.getScrollable();
        ++me.layoutCounter;
        if (scroller && me.layoutCounter > 1) {
            scroller.refresh();
        }
        if (me.hasListeners.afterlayout) {
            me.fireEvent('afterlayout', me, layout);
        }
    },
    onDestroy: function() {
        this.callParent();
        this.refs = null;
    },
    beforeDestroy: function() {
        var me = this,
            items = me.items,
            floatingItems = me.floatingItems,
            c;
        if (items) {
            while ((c = items.first())) {
                me.doRemove(c, true);
            }
        }
        if (floatingItems) {
            while ((c = floatingItems.first())) {
                me.doRemove(c, true);
            }
        }
        Ext.destroy(me.layout);
        me.callParent();
    },
    beforeRender: function() {
        var me = this,
            layout = me.getLayout(),
            targetCls;
        me.callParent();
        if (!layout.initialized) {
            layout.initLayout();
        }
        targetCls = layout.targetCls;
        if (targetCls) {
            me.applyTargetCls(targetCls);
        }
    },
    
    cascade: function(fn, scope, origArgs) {
        var me = this,
            cs = me.items ? me.items.items : [],
            len = cs.length,
            i = 0,
            c,
            args = origArgs ? origArgs.concat(me) : [
                me
            ],
            componentIndex = args.length - 1;
        if (fn.apply(scope || me, args) !== false) {
            for (; i < len; i++) {
                c = cs[i];
                if (c.cascade) {
                    c.cascade(fn, scope, origArgs);
                } else {
                    args[componentIndex] = c;
                    fn.apply(scope || c, args);
                }
            }
        }
        return this;
    },
    
    contains: function(comp, deep) {
        var result = false;
        if (deep) {
            this.cascade(function(c) {
                
                if (c.contains && c.contains(comp)) {
                    result = true;
                    return false;
                }
            });
            return result;
        } else {
            return this.items.contains(comp) || this.floatingItems.contains(comp);
        }
    },
    
    disable: function() {
        this.callParent(arguments);
        var thisIsMasking = this.maskOnDisable,
            itemsToDisable = this.getChildItemsToDisable(),
            length = itemsToDisable.length,
            item, i, modSetting;
        for (i = 0; i < length; i++) {
            item = itemsToDisable[i];
            if (item.resetDisable !== false && !item.disabled) {
                modSetting = item.maskOnDisable;
                item.maskOnDisable = !thisIsMasking;
                item.disable();
                item.maskOnDisable = modSetting;
                item.resetDisable = true;
            }
        }
        return this;
    },
    
    doLayout: function() {
        this.updateLayout();
        return this;
    },
    
    enable: function() {
        this.callParent(arguments);
        var itemsToDisable = this.getChildItemsToDisable(),
            length = itemsToDisable.length,
            item, i;
        for (i = 0; i < length; i++) {
            item = itemsToDisable[i];
            if (item.resetDisable) {
                item.enable();
            }
        }
        return this;
    },
    
    getChildByElement: function(el, deep) {
        var item, itemEl,
            i = 0,
            it = this.getRefItems(),
            ln = it.length;
        el = Ext.getDom(el);
        for (; i < ln; i++) {
            item = it[i];
            itemEl = item.getEl();
            if (itemEl && ((itemEl.dom === el) || itemEl.contains(el))) {
                return (deep && item.getChildByElement) ? item.getChildByElement(el, deep) : item;
            }
        }
        return null;
    },
    
    getComponent: function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }
        var c = this.items.get(comp);
        
        if (!c && typeof comp !== 'number') {
            c = this.floatingItems.get(comp);
        }
        return c;
    },
    
    getLayout: function() {
        var me = this,
            layout = me.layout;
        if (!layout || !layout.isLayout) {
            me.setLayout(layout);
        }
        return me.layout;
    },
    
    getRefItems: function(deep) {
        var me = this,
            items = me.items.items,
            len = items.length,
            i = 0,
            item,
            result = [];
        for (; i < len; i++) {
            item = items[i];
            result[result.length] = item;
            if (deep && item.getRefItems) {
                result.push.apply(result, item.getRefItems(true));
            }
        }
        
        items = me.floatingItems.items;
        len = items.length;
        for (i = 0; i < len; i++) {
            item = items[i];
            result[result.length] = item;
            if (deep && item.getRefItems) {
                result.push.apply(result, item.getRefItems(true));
            }
        }
        return result;
    },
    
    getDefaultFocus: function() {
        var defaultFocus = this.defaultFocus,
            result;
        if (defaultFocus != undefined) {
            result = this.down(defaultFocus);
        }
        
        return result;
    },
    initComponent: function() {
        var me = this;
        me.callParent();
        me.getLayout();
        
        
        me.constructing = true;
        me.initItems();
        delete me.constructing;
    },
    
    getReferences: function() {
        var ComponentManager = Ext.ComponentManager;
        if (ComponentManager.referencesDirty) {
            ComponentManager.fixReferences();
        }
        return this.refs || null;
    },
    
    lookupReference: function(key) {
        var refs = this.getReferences();
        return (refs && refs[key]) || null;
    },
    
    initItems: function() {
        var me = this,
            items = me.items;
        if (!items || !items.isMixedCollection) {
            
            
            
            me.items = new Ext.util.AbstractMixedCollection(false,me.getComponentId);
            me.floatingItems = new Ext.util.MixedCollection(false,me.getComponentId);
            if (items) {
                if (!Ext.isArray(items)) {
                    items = [
                        items
                    ];
                }
                me.add(items);
            }
        }
    },
    
    initInheritedState: function(inheritedState, inheritedStateInner) {
        var me = this,
            controller = me.controller,
            layout = me.layout,
            session = me.session,
            
            
            viewModel = me.viewModel,
            reference = me.reference,
            referenceHolder = me.referenceHolder;
        me.callParent([
            inheritedState,
            inheritedStateInner
        ]);
        if (me.collapsed) {
            inheritedState.collapsed = true;
        }
        if (controller) {
            inheritedState.referenceHolder = controller;
            referenceHolder = true;
        } else if (referenceHolder) {
            inheritedState.referenceHolder = me;
        }
        if (referenceHolder) {
            inheritedState.referencePath = '';
        } else if (reference && me.isParentReference) {
            inheritedState.referencePath = me.referenceKey + '.';
        }
        if (session) {
            inheritedState.session = session;
        }
        if (viewModel) {
            inheritedState.viewModelPath = '';
        } else if (reference && me.isParentReference) {
            inheritedState.viewModelPath = me.viewModelKey + '.';
        }
        if (layout && layout.initInheritedState) {
            layout.initInheritedState(inheritedState, inheritedStateInner);
        }
    },
    
    insert: function(index, component) {
        var compIdx;
        if (component && component.isComponent) {
            compIdx = this.items.indexOf(component);
            if (compIdx !== -1) {
                return this.move(compIdx, index);
            }
        }
        return this.add(index, component);
    },
    
    lookupComponent: function(comp) {
        if (!comp.isComponent) {
            if (typeof comp === 'string') {
                comp = Ext.ComponentManager.get(comp);
            } else {
                comp = Ext.ComponentManager.create(comp, this.defaultType);
            }
        }
        return comp;
    },
    
    move: function(fromIdx, toIdx) {
        var me = this,
            items = me.items,
            item;
        if (fromIdx.isComponent) {
            fromIdx = items.indexOf(fromIdx);
        }
        item = items.getAt(fromIdx);
        if (fromIdx !== toIdx) {
            item = items.removeAt(fromIdx);
            if (item === false) {
                return false;
            }
            toIdx = Math.min(toIdx, items.getCount());
            items.insert(toIdx, item);
            me.onMove(item, fromIdx, toIdx);
            if (me.hasListeners.childmove) {
                me.fireEvent('childmove', me, item, fromIdx, toIdx);
            }
            me.updateLayout();
        }
        return item;
    },
    
    moveBefore: function(item, before) {
        if (item !== before) {
            item = this.layout.moveItemBefore(item, before);
        }
        return item;
    },
    
    moveAfter: function(item, after) {
        var layout = this.layout,
            index;
        if (item !== after) {
            index = after ? layout.getMoveAfterIndex(after) : 0;
            item = layout.moveItemBefore(item, this.items.getAt(index));
        }
        return item;
    },
    
    nextChild: function(child, selector) {
        var me = this,
            items = me.items,
            childIndex = items.indexOf(child),
            i = 0,
            len = items.length,
            result;
        if (childIndex !== -1) {
            if (selector) {
                for (; i < len; i++) {
                    result = items.getAt(childIndex + i);
                    if (!result || Ext.ComponentQuery.is(result, selector)) {
                        break;
                    }
                }
            } else {
                result = items.getAt(childIndex + 1);
            }
            if (!result && me.ownerCt) {
                result = me.ownerCt.nextChild(me, selector);
            }
        }
        return result;
    },
    
    onAdd: Ext.emptyFn,
    
    onBeforeAdd: function(item) {
        
        var owner = item.ownerCt;
        if (owner && owner !== this) {
            owner.remove(item, false);
        }
    },
    onMove: Ext.emptyFn,
    
    onRemove: Ext.emptyFn,
    onPosition: function() {
        this.callParent(arguments);
        this.repositionFloatingItems();
    },
    onResize: function() {
        this.callParent(arguments);
        this.repositionFloatingItems();
    },
    
    prevChild: function(child, selector) {
        var me = this,
            items = me.items,
            childIndex = items.indexOf(child),
            i = 0,
            len = items.length,
            result;
        if (childIndex !== -1) {
            if (selector) {
                for (; i < len; i++) {
                    result = items.getAt(childIndex - i);
                    if (!result || Ext.ComponentQuery.is(result, selector)) {
                        break;
                    }
                }
            } else {
                result = items.getAt(childIndex - 1);
            }
            if (!result && me.ownerCt) {
                result = me.ownerCt.nextChild(me, selector);
            }
        }
        return result;
    },
    
    remove: function(component, autoDestroy) {
        var me = this,
            c = me.getComponent(component);
        if (!arguments.length) {
            Ext.log.warn("Ext.container.Container: remove takes an argument of the component to remove. cmp.remove() is incorrect usage.");
        }
        if (c && (!me.hasListeners.beforeremove || me.fireEvent('beforeremove', me, c) !== false)) {
            me.doRemove(c, autoDestroy);
            if (me.hasListeners.remove) {
                me.fireEvent('remove', me, c);
            }
            if (!me.destroying && !c.floating) {
                me.updateLayout();
            }
        }
        return c;
    },
    
    removeAll: function(autoDestroy) {
        var me = this,
            removeItems = me.items.items.slice().concat(me.floatingItems.items),
            items = [],
            i = 0,
            len = removeItems.length,
            item;
        
        Ext.suspendLayouts();
        me.removingAll = true;
        for (; i < len; i++) {
            item = removeItems[i];
            me.remove(item, autoDestroy);
            if (item.ownerCt !== me) {
                items.push(item);
            }
        }
        me.removingAll = false;
        
        Ext.resumeLayouts(!!len);
        return items;
    },
    
    setLayout: function(layout) {
        var me = this,
            currentLayout = me.layout,
            currentIsLayout = currentLayout && currentLayout.isLayout,
            protoLayout, type;
        if (typeof layout === 'string') {
            layout = {
                type: layout
            };
        }
        type = layout.type;
        if (currentIsLayout && (!type || (type === currentLayout.type))) {
            
            delete layout.type;
            currentLayout.setConfig(layout);
        } else {
            
            if (currentIsLayout) {
                currentLayout.setOwner(null);
            }
            protoLayout = me.self.prototype.layout;
            if (typeof protoLayout === 'string') {
                layout.type = type || protoLayout;
            } else {
                
                Ext.merge(Ext.merge({}, protoLayout), layout);
            }
            layout = this.layout = Ext.Factory.layout(layout);
            layout.setOwner(this);
        }
        if (me.rendered) {
            me.updateLayout();
        }
    },
    
    setActiveItem: function(item) {
        return this.getLayout().setActiveItem(item);
    },
    
    
    
    
    privates: {
        
        applyDefaults: function(config) {
            var me = this,
                defaults = me.defaults;
            if (defaults) {
                if (Ext.isFunction(defaults)) {
                    defaults = defaults.call(me, config);
                }
                if (Ext.isString(config)) {
                    config = Ext.ComponentManager.get(config);
                }
                if (config.isComponent) {
                    
                    
                    
                    config.setConfig(defaults, null, me._applyDefaultsOptions);
                } else {
                    
                    
                    
                    config = me.getConfigurator().merge(me, Ext.Object.fork(defaults), config);
                }
            }
            return config;
        },
        applyReference: function(reference) {
            var len;
            if (reference && reference.charAt(len = reference.length - 1) === '>') {
                this.isParentReference = true;
                reference = reference.substring(0, len);
            }
            if (reference && !this.validIdRe.test(reference)) {
                Ext.Error.raise('Invalid reference "' + reference + '" for ' + this.getId() + ' - not a valid identifier');
            }
            return reference;
        },
        
        
        
        
        
        applyTargetCls: function(targetCls) {
            this.addCls(targetCls);
        },
        
        attachReference: function(component) {
            var me = this,
                key, refs;
            
            if (me.destroying || me.isDestroyed) {
                return;
            }
            refs = me.refs || (me.refs = {});
            key = component.referenceKey;
            if (refs[key] && refs[key] !== component) {
                Ext.log.warn('Duplicate reference: "' + key + '" on ' + me.id);
            }
            refs[key] = component;
        },
        
        clearReference: function(component) {
            var refs = this.refs,
                key = component.referenceKey;
            if (refs && key) {
                
                
                
                component.viewModelKey = component.referenceKey = refs[key] = null;
            }
        },
        
        clearReferences: function() {
            this.refs = null;
        },
        
        detachComponent: function(component) {
            Ext.getDetachedBody().appendChild(component.getEl());
        },
        
        doRemove: function(component, doDestroy) {
            
            doDestroy = doDestroy === true || (doDestroy !== false && this.autoDestroy);
            var me = this,
                layout = me.layout,
                hasLayout = layout && me.rendered,
                
                isDestroying = component.destroying || doDestroy,
                floating = component.floating;
            if (floating) {
                me.floatingItems.remove(component);
            } else {
                me.items.remove(component);
            }
            
            if (hasLayout && !floating) {
                
                if (layout.running) {
                    Ext.Component.cancelLayout(component, isDestroying);
                }
                layout.onRemove(component, isDestroying);
            }
            component.onRemoved(isDestroying);
            me.onRemove(component, isDestroying);
            
            if (doDestroy) {
                component.destroy();
            } else 
            {
                if (hasLayout && !floating) {
                    layout.afterRemove(component);
                }
                if (me.detachOnRemove && component.rendered) {
                    me.detachComponent(component);
                }
            }
        },
        finishRenderChildren: function() {
            this.callParent();
            var layout = this.getLayout();
            if (layout) {
                layout.finishRender();
            }
        },
        
        getChildItemsToDisable: function() {
            return this.query('[isFormField],[isFocusableContainer],button');
        },
        
        getComponentId: function(comp) {
            return comp.getItemId && comp.getItemId();
        },
        
        getContentTarget: function() {
            return this.getLayout().getContentTarget();
        },
        
        getDefaultContentTarget: function() {
            return this.el;
        },
        
        getFocusEl: function() {
            var delegate = this.getDefaultFocus();
            if (delegate) {
                return delegate;
            } else if (this.focusable) {
                return this.getTargetEl();
            }
            
            return undefined;
        },
        getScrollerEl: function() {
            return this.layout.getScrollerEl() || this.callParent();
        },
        
        prepareItems: function(items, applyDefaults) {
            
            
            if (Ext.isArray(items)) {
                items = items.slice();
            } else {
                items = [
                    items
                ];
            }
            
            var me = this,
                i = 0,
                len = items.length,
                item;
            for (; i < len; i++) {
                item = items[i];
                if (item == null) {
                    Ext.Array.erase(items, i, 1);
                    --i;
                    --len;
                } else {
                    if (applyDefaults) {
                        item = this.applyDefaults(item);
                    }
                    
                    item.initOwnerCt = me;
                    if (item.isComponent) {
                        
                        
                        
                        item.instancedCmp = true;
                    }
                    items[i] = me.lookupComponent(item);
                    
                    
                    delete item.initOwnerCt;
                }
            }
            return items;
        },
        repositionFloatingItems: function() {
            var floaters = this.floatingItems.items,
                floaterCount = floaters.length,
                i, floater;
            
            for (i = 0; i < floaterCount; i++) {
                floater = floaters[i];
                if (floater.el && !floater.hidden) {
                    floater.setPosition(floater.x, floater.y);
                }
            }
        },
        _noMargin: {
            'margin-top': '',
            'margin-right': '',
            'margin-bottom': '',
            'margin-left': ''
        },
        
        
        resetItemMargins: function() {
            var items = this.items.items,
                i = items.length,
                noMargin = this._noMargin,
                item;
            while (i--) {
                item = items[i];
                item.margin$ = null;
                item.el.setStyle(noMargin);
            }
        },
        setupRenderTpl: function(renderTpl) {
            this.callParent(arguments);
            this.getLayout().setupRenderTpl(renderTpl);
        }
    }
});


Ext.define('Ext.layout.container.Editor', {
    
    alias: 'layout.editor',
    extend: 'Ext.layout.container.Container',
    
    autoSizeDefault: {
        width: 'field',
        height: 'field'
    },
    sizePolicies: {
        
        $: {
            
            $: {
                readsWidth: 1,
                readsHeight: 1,
                setsWidth: 0,
                setsHeight: 0
            },
            boundEl: {
                readsWidth: 1,
                readsHeight: 0,
                setsWidth: 0,
                setsHeight: 1
            }
        },
        boundEl: {
            
            $: {
                readsWidth: 0,
                readsHeight: 1,
                setsWidth: 1,
                setsHeight: 0
            },
            boundEl: {
                readsWidth: 0,
                readsHeight: 0,
                setsWidth: 1,
                setsHeight: 1
            }
        }
    },
    getItemSizePolicy: function(item) {
        var me = this,
            autoSize = me.owner.autoSize,
            key = autoSize && autoSize.width,
            policy = me.sizePolicies;
        policy = policy[key] || policy.$;
        key = autoSize && autoSize.height;
        policy = policy[key] || policy.$;
        return policy;
    },
    calculate: function(ownerContext) {
        var me = this,
            owner = me.owner,
            autoSize = owner.autoSize,
            fieldWidth, fieldHeight;
        if (autoSize === true) {
            autoSize = me.autoSizeDefault;
        }
        
        if (autoSize) {
            fieldWidth = me.getDimension(owner, autoSize.width, 'getWidth', owner.width);
            fieldHeight = me.getDimension(owner, autoSize.height, 'getHeight', owner.height);
        }
        
        ownerContext.childItems[0].setSize(fieldWidth, fieldHeight);
        
        ownerContext.setWidth(fieldWidth);
        ownerContext.setHeight(fieldHeight);
        
        ownerContext.setContentSize(fieldWidth || owner.field.getWidth(), fieldHeight || owner.field.getHeight());
    },
    getDimension: function(owner, type, getMethod, ownerSize) {
        switch (type) {
            
            case 'boundEl':
                return owner.boundEl[getMethod]();
            
            case 'field':
                return undefined;
            
            default:
                return ownerSize;
        }
    }
});


Ext.define('Ext.Editor', {
    extend: 'Ext.container.Container',
    xtype: 'editor',
    requires: [
        'Ext.layout.container.Editor'
    ],
    layout: 'editor',
    
    
    allowBlur: true,
    
    
    revertInvalid: true,
    
    
    
    value: '',
    
    alignment: 'c-c?',
    
    offsets: [
        0,
        0
    ],
    
    shadow: 'frame',
    
    constrain: false,
    
    swallowKeys: true,
    
    completeOnEnter: true,
    
    cancelOnEsc: true,
    
    updateEl: false,
    
    
    
    focusOnToFront: false,
    
    baseCls: Ext.baseCSSPrefix + 'editor',
    
    editing: false,
    
    
    
    
    
    
    preventDefaultAlign: true,
    specialKeyDelay: 1,
    initComponent: function() {
        var me = this,
            field = me.field = Ext.ComponentManager.create(me.field || {}, 'textfield');
        field.msgTarget = field.msgTarget || 'qtip';
        me.mon(field, {
            scope: me,
            specialkey: me.onSpecialKey
        });
        if (field.grow) {
            me.mon(field, 'autosize', me.onFieldAutosize, me, {
                delay: 1
            });
        }
        me.floating = {
            constrain: me.constrain
        };
        me.items = field;
        me.callParent(arguments);
    },
    onAdded: function(container) {
        
        
        this.ownerCmp = container;
    },
    
    onFieldAutosize: function() {
        this.updateLayout();
    },
    
    afterRender: function(ct, position) {
        var me = this,
            field = me.field,
            inputEl = field.inputEl;
        me.callParent(arguments);
        
        if (inputEl) {
            inputEl.dom.name = '';
            if (me.swallowKeys) {
                inputEl.swallowEvent([
                    'keypress',
                    
                    'keydown'
                ]);
            }
        }
    },
    
    
    onSpecialKey: function(field, event) {
        var me = this,
            key = event.getKey(),
            complete = me.completeOnEnter && key === event.ENTER,
            cancel = me.cancelOnEsc && key === event.ESC,
            task = me.specialKeyTask;
        if (complete || cancel) {
            event.stopEvent();
            if (!task) {
                me.specialKeyTask = task = new Ext.util.DelayedTask();
            }
            
            
            task.delay(me.specialKeyDelay, complete ? me.completeEdit : me.cancelEdit, me);
            
            if (me.specialKeyDelay === 0) {
                task.cancel();
                if (complete) {
                    me.completeEdit();
                } else {
                    me.cancelEdit();
                }
            }
        }
        me.fireEvent('specialkey', me, field, event);
    },
    
    startEdit: function(el, value) {
        var me = this,
            field = me.field,
            dom, ownerCt, renderTo;
        me.completeEdit();
        me.boundEl = Ext.get(el);
        dom = me.boundEl.dom;
        value = Ext.isDefined(value) ? value : Ext.String.trim(dom.textContent || dom.innerText || dom.innerHTML);
        if (me.fireEvent('beforestartedit', me, me.boundEl, value) !== false) {
            
            
            
            Ext.suspendLayouts();
            if (!me.rendered) {
                ownerCt = me.ownerCt;
                renderTo = me.renderTo || (ownerCt && ownerCt.getEl()) || Ext.getBody();
                Ext.fly(renderTo).position();
                me.renderTo = renderTo;
            }
            me.startValue = value;
            me.show();
            me.realign(true);
            
            field.suspendEvents();
            field.setValue(value);
            field.resetOriginalValue();
            field.resumeEvents();
            field.focus(field.selectOnFocus ? true : [
                Number.MAX_VALUE
            ]);
            if (field.autoSize) {
                field.autoSize();
            }
            Ext.resumeLayouts(true);
            me.toggleBoundEl(false);
            me.editing = true;
        }
    },
    
    realign: function(autoSize) {
        var me = this;
        if (autoSize === true) {
            me.updateLayout();
        }
        me.alignTo(me.boundEl, me.alignment, me.offsets);
    },
    
    completeEdit: function(remainVisible) {
        var me = this,
            field = me.field,
            startValue = me.startValue,
            value;
        if (!me.editing) {
            return;
        }
        
        if (field.assertValue) {
            field.assertValue();
        }
        value = me.getValue();
        if (!field.isValid()) {
            if (me.revertInvalid !== false) {
                me.cancelEdit(remainVisible);
            }
            return;
        }
        if (me.ignoreNoChange && !field.didValueChange(value, startValue)) {
            me.onEditComplete(remainVisible);
            return;
        }
        if (me.fireEvent('beforecomplete', me, value, startValue) !== false) {
            
            value = me.getValue();
            if (me.updateEl && me.boundEl) {
                me.boundEl.setHtml(value);
            }
            me.onEditComplete(remainVisible);
            me.fireEvent('complete', me, value, startValue);
        }
    },
    
    onShow: function() {
        var me = this;
        me.callParent(arguments);
        me.fireEvent('startedit', me, me.boundEl, me.startValue);
    },
    
    cancelEdit: function(remainVisible) {
        var me = this,
            startValue = me.startValue,
            field = me.field,
            value;
        if (me.editing) {
            if (field) {
                value = me.editedValue = me.getValue();
                
                field.suspendEvents();
                me.setValue(startValue);
                field.resumeEvents();
            }
            me.onEditComplete(remainVisible);
            me.fireEvent('canceledit', me, value, startValue);
            delete me.editedValue;
        }
    },
    
    onEditComplete: function(remainVisible) {
        this.editing = false;
        if (remainVisible !== true) {
            this.hide();
            this.toggleBoundEl(true);
        }
    },
    
    onFocusLeave: function(e) {
        var me = this;
        
        if (me.allowBlur === true && me.editing && me.selectSameEditor !== true) {
            this.completeEdit();
        }
        this.callParent([
            e
        ]);
    },
    
    onHide: function() {
        var me = this,
            field = me.field;
        if (me.editing) {
            me.completeEdit();
        } else if (field.collapse) {
            field.collapse();
        }
        me.callParent(arguments);
    },
    
    getValue: function() {
        return this.field.getValue();
    },
    
    setValue: function(value) {
        this.field.setValue(value);
    },
    toggleBoundEl: function(visible) {
        if (this.hideEl !== false) {
            this.boundEl.setVisible(visible);
        }
    },
    beforeDestroy: function() {
        var me = this,
            task = me.specialKeyTask;
        if (task) {
            task.cancel();
        }
        me.specialKeyTask = me.field = me.boundEl = Ext.destroy(me.field);
        me.callParent(arguments);
    }
});


Ext.define('Ext.EventManager', {
    singleton: true,
    mouseLeaveRe: /(mouseout|mouseleave)/,
    mouseEnterRe: /(mouseover|mouseenter)/,
    
    addListener: function(element, eventName, fn, scope, options) {
        Ext.log.warn("Ext.EventManager is deprecated. " + "Use Ext.dom.Element#addListener to attach an event listener.");
        Ext.get(element).addListener(eventName, fn, scope, options);
    },
    
    onWindowResize: function(fn, scope, options) {
        Ext.log.warn("Ext.EventManager is deprecated. " + "Use Ext.on('resize', fn) to attach a window resize listener.");
        Ext.GlobalEvents.on('resize', fn, scope, options);
    },
    
    onWindowUnload: function(fn, scope, options) {
        Ext.log.warn("Ext.EventManager is deprecated. " + "Use Ext.getWin().on('unload', fn) to attach a window unload listener.");
        Ext.getWin().on('unload', fn, scope, options);
    },
    
    purgeElement: function(element, eventName) {
        Ext.log.warn("Ext.EventManager is deprecated. " + "Call clearListeners() on a Ext.dom.Element to remove all listeners.");
        Ext.get(element).clearListeners();
    },
    
    removeAll: function(element) {
        Ext.log.warn("Ext.EventManager is deprecated. " + "Call clearListeners() on a Ext.dom.Element to remove all listeners.");
        Ext.get(element).clearListeners();
    },
    
    removeListener: function(element, eventName, fn, scope, options) {
        Ext.log.warn("Ext.EventManager is deprecated. " + "Use Ext.dom.Element#removeListener to remove an event listener.");
        Ext.get(element).removeListener(eventName, fn, scope, options);
    },
    
    removeResizeListener: function(fn, scope) {
        Ext.log.warn("Ext.EventManager is deprecated. " + "Use Ext.on('resize', fn) to detach a window resize listener.");
        Ext.GlobalEvents.un('resize', fn, scope);
    },
    
    removeUnloadListener: function(fn, scope) {
        Ext.log.warn("Ext.EventManager is deprecated. " + "Use Ext.getWin().un('unload', fn) to detach a window unload listener.");
        Ext.getWin().un('unload', fn, scope);
    },
    
    stopEvent: function(event) {
        Ext.log.warn("Ext.EventManager.stopEvent() is deprecated. " + "Call stopEvent() directly on the Ext.event.Event instance instead.");
        this.stopPropagation(event);
        this.preventDefault(event);
    },
    
    stopPropagation: function(event) {
        Ext.log.warn("Ext.EventManager.stopPropagation() is deprecated. " + "Call stopPropagation() directly on the Ext.event.Event instance instead.");
        event = event.browserEvent || event;
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    },
    
    preventDefault: function(event) {
        Ext.log.warn("Ext.EventManager.preventDefault() is deprecated. " + "Call preventDefault() directly on the Ext.event.Event instance instead.");
        event = event.browserEvent || event;
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
            
            try {
                
                if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
                    event.keyCode = -1;
                }
            } catch (e) {}
        }
    },
    
    
    getId: function(element) {
        Ext.log.warn("Ext.EventManager.getId() is deprecated. " + "Call Ext.get() to assign ids to elements.");
        element = Ext.get(element);
        return element.id;
    },
    
    getRelatedTarget: function(event) {
        Ext.log.warn("Ext.EventManager.getRelatedTarget() is deprecated. " + "Call getRelatedTarget() directly on the Ext.event.Event instance instead.");
        event = event.browserEvent || event;
        var target = event.relatedTarget;
        if (!target) {
            if (this.mouseLeaveRe.test(event.type)) {
                target = event.toElement;
            } else if (this.mouseEnterRe.test(event.type)) {
                target = event.fromElement;
            }
        }
        return this.resolveTextNode(target);
    },
    
    getPageX: function(event) {
        Ext.log.warn("Ext.EventManager.getPageX() is deprecated. " + "Call getX() directly on the Ext.event.Event instance instead.");
        return this.getPageXY(event)[0];
    },
    
    getPageXY: function(event) {
        Ext.log.warn("Ext.EventManager.getPageXY() is deprecated. " + "Call getXY() directly on the Ext.event.Event instance instead.");
        event = event.browserEvent || event;
        var x = event.pageX,
            y = event.pageY,
            docEl = doc.documentElement,
            body = doc.body;
        
        if (!x && x !== 0) {
            x = event.clientX + (docEl && docEl.scrollLeft || body && body.scrollLeft || 0) - (docEl && docEl.clientLeft || body && body.clientLeft || 0);
            y = event.clientY + (docEl && docEl.scrollTop || body && body.scrollTop || 0) - (docEl && docEl.clientTop || body && body.clientTop || 0);
        }
        return [
            x,
            y
        ];
    },
    
    getPageY: function(event) {
        Ext.log.warn("Ext.EventManager.getPageY() is deprecated. " + "Call getY() directly on the Ext.event.Event instance instead.");
        return this.getPageXY(event)[1];
    },
    
    getTarget: function(event) {
        Ext.log.warn("Ext.EventManager.getTarget() is deprecated. " + "Call getTarget() directly on the Ext.event.Event instance instead.");
        event = event.browserEvent || event;
        return EventManager.resolveTextNode(event.target || event.srcElement);
    },
    
    
    
    
    resolveTextNode: Ext.isGecko ? function(node) {
        if (node) {
            
            var s = HTMLElement.prototype.toString.call(node);
            if (s !== '[xpconnect wrapped native prototype]' && s !== '[object XULElement]') {
                return node.nodeType == 3 ? node.parentNode : node;
            }
        }
    } : function(node) {
        return node && node.nodeType == 3 ? node.parentNode : node;
    }
}, function(EventManager) {
    
    EventManager.on = EventManager.addListener;
    
    EventManager.un = EventManager.removeListener;
});


Ext.define('Ext.util.KeyMap', {
    alternateClassName: 'Ext.KeyMap',
    
    
    
    
    
    
    eventName: 'keydown',
    constructor: function(config) {
        var me = this;
        
        
        if ((arguments.length !== 1) || (typeof config === 'string') || config.dom || config.tagName || config === document || config.isComponent) {
            me.legacyConstructor.apply(me, arguments);
            return;
        }
        Ext.apply(me, config);
        me.bindings = [];
        if (!me.target.isComponent) {
            me.target = Ext.get(me.target);
        }
        if (me.binding) {
            me.addBinding(me.binding);
        } else if (config.key) {
            me.addBinding(config);
        }
        me.enable();
    },
    
    legacyConstructor: function(el, binding, eventName) {
        var me = this;
        Ext.apply(me, {
            target: Ext.get(el),
            eventName: eventName || me.eventName,
            bindings: []
        });
        if (binding) {
            me.addBinding(binding);
        }
        me.enable();
    },
    
    addBinding: function(binding) {
        var me = this,
            keyCode = binding.key,
            i, len;
        if (me.processing) {
            me.bindings = bindings.slice(0);
        }
        if (Ext.isArray(binding)) {
            for (i = 0 , len = binding.length; i < len; i++) {
                me.addBinding(binding[i]);
            }
            return;
        }
        me.bindings.push(Ext.apply({
            keyCode: me.processKeys(keyCode)
        }, binding));
    },
    
    removeBinding: function(binding) {
        var me = this,
            bindings = me.bindings,
            len = bindings.length,
            i, item, keys;
        if (me.processing) {
            me.bindings = bindings.slice(0);
        }
        keys = me.processKeys(binding.key);
        for (i = 0; i < len; ++i) {
            item = bindings[i];
            if ((item.fn || item.handler) === (binding.fn || binding.handler) && item.scope === binding.scope) {
                if (binding.alt === item.alt && binding.crtl === item.crtl && binding.shift === item.shift) {
                    if (Ext.Array.equals(item.keyCode, keys)) {
                        Ext.Array.erase(me.bindings, i, 1);
                        return;
                    }
                }
            }
        }
    },
    processKeys: function(keyCode) {
        var processed = false,
            key, keys, keyString, len, i;
        
        if (keyCode.test) {
            return keyCode;
        }
        
        if (Ext.isString(keyCode)) {
            keys = [];
            keyString = keyCode.toUpperCase();
            for (i = 0 , len = keyString.length; i < len; ++i) {
                keys.push(keyString.charCodeAt(i));
            }
            keyCode = keys;
            processed = true;
        }
        
        if (!Ext.isArray(keyCode)) {
            keyCode = [
                keyCode
            ];
        }
        if (!processed) {
            for (i = 0 , len = keyCode.length; i < len; ++i) {
                key = keyCode[i];
                if (Ext.isString(key)) {
                    keyCode[i] = key.toUpperCase().charCodeAt(0);
                }
            }
        }
        return keyCode;
    },
    
    handleTargetEvent: function(event) {
        var me = this,
            bindings, i, len;
        if (me.enabled) {
            bindings = me.bindings;
            i = 0;
            len = bindings.length;
            
            me.lastKeyEvent = event = me.processEvent.apply(me.processEventScope || me, arguments);
            
            if (me.ignoreInputFields && Ext.fly(event.target).isInputField()) {
                return;
            }
            
            
            if (!event.getKey) {
                return event;
            }
            me.processing = true;
            for (; i < len; ++i) {
                me.processBinding(bindings[i], event);
            }
            me.processing = false;
        }
    },
    
    processEvent: Ext.identityFn,
    
    processBinding: function(binding, event) {
        if (this.checkModifiers(binding, event)) {
            var key = event.getKey(),
                handler = binding.fn || binding.handler,
                scope = binding.scope || this,
                keyCode = binding.keyCode,
                defaultEventAction = binding.defaultEventAction,
                i, len;
            
            if (keyCode.test) {
                if (keyCode.test(String.fromCharCode(event.getCharCode()))) {
                    if (handler.call(scope, key, event) !== true && defaultEventAction) {
                        event[defaultEventAction]();
                    }
                }
            }
            
            else if (keyCode.length) {
                for (i = 0 , len = keyCode.length; i < len; ++i) {
                    if (key === keyCode[i]) {
                        if (handler.call(scope, key, event) !== true && defaultEventAction) {
                            event[defaultEventAction]();
                        }
                        break;
                    }
                }
            }
        }
    },
    
    checkModifiers: function(binding, event) {
        var keys = [
                'shift',
                'ctrl',
                'alt'
            ],
            i = 0,
            len = keys.length,
            val, key;
        for (; i < len; ++i) {
            key = keys[i];
            val = binding[key];
            if (!(val === undefined || (val === event[key + 'Key']))) {
                return false;
            }
        }
        return true;
    },
    
    on: function(key, fn, scope) {
        var keyCode, shift, ctrl, alt;
        if (Ext.isObject(key) && !Ext.isArray(key)) {
            keyCode = key.key;
            shift = key.shift;
            ctrl = key.ctrl;
            alt = key.alt;
        } else {
            keyCode = key;
        }
        this.addBinding({
            key: keyCode,
            shift: shift,
            ctrl: ctrl,
            alt: alt,
            fn: fn,
            scope: scope
        });
    },
    
    un: function(key, fn, scope) {
        var keyCode, shift, ctrl, alt;
        if (Ext.isObject(key) && !Ext.isArray(key)) {
            keyCode = key.key;
            shift = key.shift;
            ctrl = key.ctrl;
            alt = key.alt;
        } else {
            keyCode = key;
        }
        this.removeBinding({
            key: keyCode,
            shift: shift,
            ctrl: ctrl,
            alt: alt,
            fn: fn,
            scope: scope
        });
    },
    
    isEnabled: function() {
        return this.enabled;
    },
    
    enable: function() {
        var me = this;
        if (!me.enabled) {
            me.target.on(me.eventName, me.handleTargetEvent, me, {
                capture: me.capture
            });
            me.enabled = true;
        }
    },
    
    disable: function() {
        var me = this;
        if (me.enabled) {
            me.target.removeListener(me.eventName, me.handleTargetEvent, me);
            me.enabled = false;
        }
    },
    
    setDisabled: function(disabled) {
        if (disabled) {
            this.disable();
        } else {
            this.enable();
        }
    },
    
    destroy: function(removeTarget) {
        var me = this,
            target = me.target;
        me.bindings = [];
        me.disable();
        if (removeTarget) {
            target.destroy();
        }
        delete me.target;
    }
});


Ext.define('Ext.util.KeyNav', {
    alternateClassName: 'Ext.KeyNav',
    requires: [
        'Ext.util.KeyMap'
    ],
    
    disabled: false,
    
    defaultEventAction: false,
    
    forceKeyDown: false,
    
    
    eventName: 'keypress',
    
    
    
    
    
    statics: {
        keyOptions: {
            left: 37,
            right: 39,
            up: 38,
            down: 40,
            space: 32,
            pageUp: 33,
            pageDown: 34,
            del: 46,
            backspace: 8,
            home: 36,
            end: 35,
            enter: 13,
            esc: 27,
            tab: 9
        }
    },
    constructor: function(config) {
        var me = this;
        if (arguments.length === 2) {
            me.legacyConstructor.apply(me, arguments);
            return;
        }
        me.doConstruction(config);
    },
    
    legacyConstructor: function(el, config) {
        this.doConstruction(Ext.apply({
            target: el
        }, config));
    },
    
    doConstruction: function(config) {
        var me = this,
            keymapCfg = {
                target: config.target,
                ignoreInputFields: config.ignoreInputFields,
                eventName: me.getKeyEvent('forceKeyDown' in config ? config.forceKeyDown : me.forceKeyDown, config.eventName),
                capture: config.capture
            },
            map;
        if (me.map) {
            me.map.destroy();
        }
        
        me.initConfig(config);
        if (config.processEvent) {
            keymapCfg.processEvent = config.processEvent;
            keymapCfg.processEventScope = config.processEventScope || me;
        }
        
        if (config.keyMap) {
            map = me.map = config.keyMap;
        } else 
        {
            map = me.map = new Ext.util.KeyMap(keymapCfg);
            me.destroyKeyMap = true;
        }
        this.addBindings(config);
        map.disable();
        if (!config.disabled) {
            map.enable();
        }
    },
    addBindings: function(bindings) {
        var me = this,
            keyName, binding,
            map = me.map,
            keyCodes = Ext.util.KeyNav.keyOptions,
            defaultScope = bindings.scope || me;
        for (keyName in bindings) {
            binding = bindings[keyName];
            
            
            
            
            if (binding && (keyName.length === 1 || (keyName = keyCodes[keyName]) || (!isNaN(keyName = parseInt(keyName, 10))))) {
                if (typeof binding === 'function') {
                    binding = {
                        handler: binding,
                        defaultEventAction: (bindings.defaultEventAction !== undefined) ? bindings.defaultEventAction : me.defaultEventAction
                    };
                }
                map.addBinding({
                    key: keyName,
                    ctrl: binding.ctrl,
                    shift: binding.shift,
                    alt: binding.alt,
                    handler: Ext.Function.bind(me.handleEvent, binding.scope || defaultScope, [
                        binding.handler || binding.fn,
                        me
                    ], true),
                    defaultEventAction: (binding.defaultEventAction !== undefined) ? binding.defaultEventAction : me.defaultEventAction
                });
            }
        }
    },
    
    handleEvent: function(keyCode, event, handler, keyNav) {
        keyNav.lastKeyEvent = event;
        return handler.call(this, event);
    },
    
    destroy: function(removeEl) {
        if (this.destroyKeyMap) {
            this.map.destroy(removeEl);
        }
        delete this.map;
    },
    
    enable: function() {
        
        if (this.map) {
            this.map.enable();
            this.disabled = false;
        }
    },
    
    disable: function() {
        
        if (this.map) {
            this.map.disable();
        }
        this.disabled = true;
    },
    
    setDisabled: function(disabled) {
        this.map.setDisabled(disabled);
        this.disabled = disabled;
    },
    
    getKeyEvent: function(forceKeyDown, configuredEventName) {
        if (forceKeyDown || (Ext.supports.SpecialKeyDownRepeat && !configuredEventName)) {
            return 'keydown';
        } else {
            return configuredEventName || this.eventName;
        }
    }
});


Ext.define('Ext.FocusManager', {
    singleton: true,
    alternateClassName: [
        'Ext.FocusMgr'
    ],
    mixins: {
        observable: 'Ext.util.Observable'
    },
    requires: [
        'Ext.Component',
        'Ext.ComponentManager',
        'Ext.ComponentQuery',
        'Ext.util.HashMap',
        'Ext.util.KeyNav'
    ],
    
    enabled: false,
    
    focusElementCls: Ext.baseCSSPrefix + 'focus-element',
    focusFrameCls: Ext.baseCSSPrefix + 'focus-frame',
    
    whitelist: [
        'textfield'
    ],
    
    
    
    
    constructor: function(config) {
        var me = this,
            CQ = Ext.ComponentQuery;
        me.mixins.observable.constructor.call(me, config);
        me.focusTask = new Ext.util.DelayedTask(me.handleComponentFocus,me);
        
        Ext.override(Ext.Component, {
            onBlur: function() {
                this.callParent(arguments);
                if (me.enabled && !this.hasFocus) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentBlur.apply(me, arguments);
                }
            },
            onDestroy: function() {
                this.callParent(arguments);
                if (me.enabled) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentDestroy.apply(me, arguments);
                }
            },
            onFocus: function() {
                this.callParent(arguments);
                if (me.enabled && this.hasFocus) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentFocus.apply(me, arguments);
                }
            }
        });
        Ext.override(Ext.Component, {
            afterHide: function() {
                this.callParent(arguments);
                if (me.enabled) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentHide.apply(me, arguments);
                }
            }
        });
        me.focusData = {};
        me.subscribers = new Ext.util.HashMap();
        me.focusChain = {};
        
        Ext.apply(CQ.pseudos, {
            
            nextFocus: function(cmps, idx, step) {
                step = step || 1;
                idx = parseInt(idx, 10);
                var len = cmps.length,
                    i = idx,
                    c;
                for (; ; ) {
                    
                    if ((i += step) >= len) {
                        i = 0;
                    } else if (i < 0) {
                        i = len - 1;
                    }
                    
                    if (i === idx) {
                        return [];
                    }
                    
                    if ((c = cmps[i]).isFocusable()) {
                        return [
                            c
                        ];
                    }
                }
                return [];
            },
            prevFocus: function(cmps, idx) {
                return this.nextFocus(cmps, idx, -1);
            },
            root: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;
                for (; i < len; i++) {
                    c = cmps[i];
                    if (!c.ownerCt) {
                        results.push(c);
                    }
                }
                return results;
            }
        });
    },
    getKeyNav: function() {
        var me = this;
        me.keyNav = me.keyNav || new Ext.util.KeyNav(Ext.getDoc(),{
            disabled: true,
            scope: me,
            backspace: me.focusLast,
            enter: me.navigateIn,
            esc: me.navigateOut,
            tab: me.navigateSiblings,
            space: me.navigateIn,
            del: me.focusLast,
            left: me.navigateSiblings,
            right: me.navigateSiblings,
            down: me.navigateSiblings,
            up: me.navigateSiblings
        });
        return me.keyNav;
    },
    
    addXTypeToWhitelist: function(xtype) {
        var me = this;
        if (Ext.isArray(xtype)) {
            Ext.Array.forEach(xtype, me.addXTypeToWhitelist, me);
            return;
        }
        if (!Ext.Array.contains(me.whitelist, xtype)) {
            me.whitelist.push(xtype);
        }
    },
    clearComponent: function(cmp) {
        clearTimeout(this.cmpFocusDelay);
        if (!cmp.isDestroyed) {
            cmp.blur();
        }
    },
    
    disable: function() {
        var me = this;
        if (!me.enabled) {
            return;
        }
        delete me.options;
        me.enabled = false;
        me.removeDOM();
        
        me.getKeyNav().disable();
        me.fireEvent('disable', me);
    },
    
    enable: function(options) {
        var me = this;
        if (options === true) {
            options = {
                focusFrame: true
            };
        }
        me.options = options = options || {};
        if (me.enabled) {
            return;
        }
        
        me.enabled = Ext.enableFocusManager = true;
        me.initDOM(options);
        
        me.getKeyNav().enable();
        
        me.focusEl.focus();
        delete me.focusedCmp;
        me.fireEvent('enable', me);
    },
    focusLast: function(e) {
        var me = this;
        if (me.isWhitelisted(me.focusedCmp)) {
            return true;
        }
        
        if (me.previousFocusedCmp) {
            me.previousFocusedCmp.focus();
        }
    },
    getRootComponents: function() {
        var CQ = Ext.ComponentQuery,
            inline = CQ.query(':focusable:root:not([floating])'),
            floating = CQ.query(':focusable:root[floating]');
        
        
        floating.sort(function(a, b) {
            return a.el.getZIndex() > b.el.getZIndex();
        });
        return floating.concat(inline);
    },
    initDOM: function(options) {
        var me = this,
            cls = me.focusFrameCls,
            needListeners = Ext.ComponentQuery.query('{getFocusEl()}:not([focusListenerAdded])'),
            i = 0,
            len = needListeners.length;
        if (!Ext.isReady) {
            return Ext.onReady(me.initDOM, me);
        }
        
        
        
        for (; i < len; i++) {
            needListeners[i].addFocusListener();
        }
        
        if (!me.focusEl) {
            me.focusEl = Ext.getBody();
            me.focusEl.dom.tabIndex = -1;
        }
        
        if (!me.focusFrame && options.focusFrame) {
            me.focusFrame = Ext.getBody().createChild({
                cls: cls,
                children: [
                    {
                        cls: cls + '-top'
                    },
                    {
                        cls: cls + '-bottom'
                    },
                    {
                        cls: cls + '-left'
                    },
                    {
                        cls: cls + '-right'
                    }
                ],
                style: 'top: -100px; left: -100px;'
            });
            me.focusFrame.setVisibilityMode(Ext.Element.DISPLAY);
            me.focusFrame.hide().setLocalXY(0, 0);
        }
    },
    isWhitelisted: function(cmp) {
        return cmp && Ext.Array.some(this.whitelist, function(x) {
            return cmp.isXType(x);
        });
    },
    navigateIn: function(e) {
        var me = this,
            focusedCmp = me.focusedCmp,
            defaultRoot, firstChild;
        if (me.isWhitelisted(focusedCmp)) {
            return true;
        }
        if (!focusedCmp) {
            
            defaultRoot = me.getRootComponents()[0];
            if (defaultRoot) {
                
                
                if (defaultRoot.getFocusEl() === me.focusEl) {
                    me.focusEl.blur();
                }
                defaultRoot.focus();
            }
        } else {
            
            
            firstChild = focusedCmp.hasFocus ? Ext.ComponentQuery.query('>:focusable', focusedCmp)[0] : focusedCmp;
            if (firstChild) {
                firstChild.focus();
            } else {
                
                if (Ext.isFunction(focusedCmp.onClick)) {
                    e.button = 0;
                    focusedCmp.onClick(e);
                    if (focusedCmp.isVisible(true)) {
                        focusedCmp.focus();
                    } else {
                        me.navigateOut();
                    }
                }
            }
        }
    },
    navigateOut: function(e) {
        var me = this,
            parent;
        if (!me.focusedCmp || !(parent = me.focusedCmp.up(':focusable'))) {
            me.focusEl.focus();
        } else {
            parent.focus();
        }
        
        
        
        return true;
    },
    navigateSiblings: function(e, source, parent) {
        var me = this,
            src = source || me,
            key = e.getKey(),
            goBack = e.shiftKey || key == e.LEFT || key == e.UP,
            checkWhitelist = key === e.LEFT || key === e.RIGHT || key === e.UP || key === e.DOWN,
            nextSelector = goBack ? 'prev' : 'next',
            idx, next, focusedCmp, siblings;
        focusedCmp = (src.focusedCmp && src.focusedCmp.comp) || src.focusedCmp;
        if (!focusedCmp && !parent) {
            return true;
        }
        if (checkWhitelist && me.isWhitelisted(focusedCmp)) {
            return true;
        }
        
        if (!focusedCmp || focusedCmp.is(':root')) {
            siblings = me.getRootComponents();
        } else {
            
            parent = parent || focusedCmp.up('panel') || focusedCmp.up(':root');
            if (parent) {
                siblings = parent.getRefItems(true);
            }
        }
        
        if (siblings) {
            idx = focusedCmp ? Ext.Array.indexOf(siblings, focusedCmp) : -1;
            next = Ext.ComponentQuery.query(':' + nextSelector + 'Focus(' + idx + ')', siblings)[0];
            if (next && focusedCmp !== next) {
                next.focus();
                return next;
            }
        }
    },
    onComponentBlur: function(cmp, e) {
        var me = this;
        if (me.focusedCmp === cmp) {
            me.previousFocusedCmp = cmp;
            delete me.focusedCmp;
        }
        if (me.focusFrame) {
            me.focusFrame.hide();
        }
    },
    onComponentFocus: function(cmp, e) {
        var me = this,
            chain = me.focusChain,
            parent;
        if (!cmp.isFocusable()) {
            me.clearComponent(cmp);
            
            
            
            
            if (chain[cmp.id]) {
                return;
            }
            
            parent = cmp.up();
            if (parent) {
                
                
                
                chain[cmp.id] = true;
                parent.focus();
            }
            return;
        }
        
        me.focusChain = {};
        
        
        
        me.focusTask.delay(10, null, null, [
            cmp,
            cmp.getFocusEl()
        ]);
    },
    handleComponentFocus: function(cmp, focusEl) {
        var me = this,
            cls, ff, box, bt, bl, bw, bh, ft, fb, fl, fr;
        if (me.fireEvent('beforecomponentfocus', me, cmp, me.previousFocusedCmp) === false) {
            me.clearComponent(cmp);
            return;
        }
        me.focusedCmp = cmp;
        
        if (me.shouldShowFocusFrame(cmp)) {
            cls = '.' + me.focusFrameCls + '-';
            ff = me.focusFrame;
            
            box = (focusEl.dom ? focusEl : focusEl.el).getBox();
            
            
            
            bt = box.top;
            bl = box.left;
            bw = box.width;
            bh = box.height;
            ft = ff.child(cls + 'top');
            fb = ff.child(cls + 'bottom');
            fl = ff.child(cls + 'left');
            fr = ff.child(cls + 'right');
            ft.setWidth(bw).setLocalXY(bl, bt);
            fb.setWidth(bw).setLocalXY(bl, bt + bh - 2);
            fl.setHeight(bh - 2).setLocalXY(bl, bt + 2);
            fr.setHeight(bh - 2).setLocalXY(bl + bw - 2, bt + 2);
            ff.show();
        }
        me.fireEvent('componentfocus', me, cmp, me.previousFocusedCmp);
    },
    onComponentHide: function(cmp) {
        var me = this,
            cmpHadFocus = false,
            focusedCmp = me.focusedCmp,
            parent;
        if (focusedCmp) {
            
            
            cmpHadFocus = cmp.hasFocus || (cmp.isContainer && cmp.isAncestor(me.focusedCmp));
        }
        me.clearComponent(cmp);
        
        if (cmpHadFocus && (parent = cmp.up(':focusable'))) {
            parent.focus();
        } else {
            me.focusEl.focus();
        }
    },
    onComponentDestroy: function() {},
    removeDOM: function() {
        var me = this;
        
        
        if (me.enabled || me.subscribers.length) {
            return;
        }
        Ext.destroy(me.focusFrame);
        delete me.focusEl;
        delete me.focusFrame;
    },
    
    removeXTypeFromWhitelist: function(xtype) {
        var me = this;
        if (Ext.isArray(xtype)) {
            Ext.Array.forEach(xtype, me.removeXTypeFromWhitelist, me);
            return;
        }
        Ext.Array.remove(me.whitelist, xtype);
    },
    setupSubscriberKeys: function(container, keys) {
        var me = this,
            el = container.getFocusEl(),
            scope = keys.scope,
            handlers = {
                backspace: me.focusLast,
                enter: me.navigateIn,
                esc: me.navigateOut,
                scope: me
            },
            navSiblings = function(e) {
                if (me.focusedCmp === container) {
                    
                    
                    
                    return me.navigateSiblings(e, me, container);
                } else {
                    return me.navigateSiblings(e);
                }
            };
        Ext.iterate(keys, function(key, cb) {
            handlers[key] = function(e) {
                var ret = navSiblings(e);
                if (Ext.isFunction(cb) && cb.call(scope || container, e, ret) === true) {
                    return true;
                }
                return ret;
            };
        }, me);
        return new Ext.util.KeyNav(el,handlers);
    },
    shouldShowFocusFrame: function(cmp) {
        var me = this,
            opts = me.options || {};
        
        
        
        if (!me.focusFrame || !cmp) {
            return false;
        }
        
        if (opts.focusFrame) {
            return true;
        }
        if (me.focusData[cmp.id].focusFrame) {
            return true;
        }
        return false;
    }
});


Ext.define('Ext.Img', {
    extend: 'Ext.Component',
    alias: [
        'widget.image',
        'widget.imagecomponent'
    ],
    autoEl: 'img',
    baseCls: Ext.baseCSSPrefix + 'img',
    
    src: '',
    
    alt: '',
    
    title: '',
    
    imgCls: '',
    
    ariaRole: 'img',
    maskOnDisable: false,
    initComponent: function() {
        if (this.glyph) {
            this.autoEl = 'div';
        }
        this.callParent();
    },
    getElConfig: function() {
        var me = this,
            autoEl = me.autoEl,
            config = me.callParent(),
            glyphFontFamily = Ext._glyphFontFamily,
            glyph = me.glyph,
            img, glyphParts;
        
        
        if (autoEl === 'img' || (Ext.isObject(autoEl) && autoEl.tag === 'img')) {
            img = config;
        } else if (me.glyph) {
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                glyphFontFamily = glyphParts[1] || glyphFontFamily;
            }
            config.html = '&#' + glyph + ';';
            if (glyphFontFamily) {
                config.style = config.style || {};
                config.style.fontFamily = glyphFontFamily;
            }
        } else {
            config.cn = [
                img = {
                    tag: 'img',
                    role: me.ariaRole,
                    id: me.id + '-img'
                }
            ];
        }
        if (img) {
            if (me.imgCls) {
                img.cls = (img.cls ? img.cls + ' ' : '') + me.imgCls;
            }
            img.src = me.src || Ext.BLANK_IMAGE_URL;
        }
        if (me.alt) {
            (img || config).alt = me.alt;
        }
        if (me.title) {
            (img || config).title = me.title;
        }
        return config;
    },
    onRender: function() {
        var me = this,
            autoEl = me.autoEl,
            el;
        me.callParent(arguments);
        el = me.el;
        if (autoEl === 'img' || (Ext.isObject(autoEl) && autoEl.tag === 'img')) {
            me.imgEl = el;
        } else {
            me.imgEl = el.getById(me.id + '-img');
        }
    },
    onDestroy: function() {
        Ext.destroy(this.imgEl);
        this.imgEl = null;
        this.callParent();
    },
    
    setSrc: function(src) {
        var me = this,
            imgEl = me.imgEl;
        me.src = src;
        if (imgEl) {
            imgEl.dom.src = src || Ext.BLANK_IMAGE_URL;
        }
    },
    
    setGlyph: function(glyph) {
        var me = this,
            glyphFontFamily = Ext._glyphFontFamily,
            old = me.glyph,
            el = me.el,
            glyphParts;
        me.glyph = glyph;
        if (me.rendered && glyph != old) {
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                glyphFontFamily = glyphParts[1] || glyphFontFamily;
            }
            el.dom.innerHTML = '&#' + glyph + ';';
            if (glyphFontFamily) {
                el.setStyle('font-family', glyphFontFamily);
            }
        }
    }
});


Ext.define('Ext.util.StoreHolder', {
    requires: [
        'Ext.data.StoreManager'
    ],
    mixinId: 'storeholder',
    
    bindStore: function(store, initial, propertyName) {
        
        
        
        propertyName = propertyName || 'store';
        var me = this,
            oldStore = initial ? null : me[propertyName];
        if (store !== oldStore) {
            if (oldStore) {
                
                me.onUnbindStore(oldStore, initial, propertyName);
                
                if (me.isComponent && propertyName === 'store' && oldStore.autoDestroy) {
                    oldStore.destroy();
                } else {
                    me.unbindStoreListeners(oldStore);
                }
            }
            if (store) {
                me[propertyName] = store = Ext.data.StoreManager.lookup(store);
                me.bindStoreListeners(store);
                me.onBindStore(store, initial, propertyName, oldStore);
            } else {
                me[propertyName] = null;
            }
        }
        return me;
    },
    
    getStore: function() {
        return this.store;
    },
    
    setStore: function(store) {
        this.bindStore(store);
    },
    
    unbindStoreListeners: function(store) {
        
        var listeners = this.storeListeners;
        if (listeners) {
            store.un(listeners);
        }
    },
    
    bindStoreListeners: function(store) {
        
        var listeners = this.getStoreListeners(store);
        if (listeners) {
            listeners = Ext.apply({}, listeners);
            if (!listeners.scope) {
                listeners.scope = this;
            }
            this.storeListeners = listeners;
            store.on(listeners);
        }
    },
    
    getStoreListeners: Ext.emptyFn,
    
    onUnbindStore: Ext.emptyFn,
    
    onBindStore: Ext.emptyFn
});


Ext.define('Ext.LoadMask', {
    extend: 'Ext.Component',
    alias: 'widget.loadmask',
    
    mixins: [
        'Ext.util.StoreHolder'
    ],
    uses: [
        'Ext.data.StoreManager'
    ],
    
    
    isLoadMask: true,
    
    
    
    
    msg: 'Loading...',
    
    msgCls: Ext.baseCSSPrefix + 'mask-loading',
    msgWrapCls: Ext.baseCSSPrefix + 'mask-msg',
    
    useMsg: true,
    
    useTargetEl: false,
    
    
    cls: Ext.baseCSSPrefix + 'mask',
    componentCls: Ext.baseCSSPrefix + 'border-box',
    ariaRole: 'status',
    focusable: true,
    tabIndex: 0,
    autoEl: {
        tag: 'div',
        role: 'status'
    },
    childEls: [
        'msgWrapEl',
        'msgEl',
        'msgTextEl'
    ],
    renderTpl: [
        '<div id="{id}-msgWrapEl" data-ref="msgWrapEl" class="{[values.$comp.msgWrapCls]}">',
        '<div id="{id}-msgEl" data-ref="msgEl" class="{[values.$comp.msgCls]} ',
        Ext.baseCSSPrefix,
        'mask-msg-inner {childElCls}">',
        '<div id="{id}-msgTextEl" data-ref="msgTextEl" class="',
        Ext.baseCSSPrefix,
        'mask-msg-text',
        '{childElCls}">{msg}</div>',
        '</div>',
        '</div>'
    ],
    
    constructor: function(config) {
        var me = this,
            comp;
        if (arguments.length === 2) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.LoadMask: LoadMask now uses a standard 1 arg constructor: use the target config');
            }
            comp = me.target = config;
            config = arguments[1];
        } else {
            comp = config.target;
        }
        if (config.maskCls) {
            Ext.log.warn('Ext.LoadMask property maskCls is deprecated, use msgWrapCls instead');
            config.msgWrapCls = config.msgWrapCls || config.maskCls;
        }
        
        me.callParent([
            config
        ]);
        
        if (comp.isComponent) {
            me.ownerCt = comp;
            me.hidden = true;
            
            
            
            me.renderTo = me.getMaskTarget();
            me.external = me.renderTo === Ext.getBody();
            me.bindComponent(comp);
        } else 
        {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.LoadMask: LoadMask for elements has been deprecated, use Ext.dom.Element.mask & Ext.dom.Element.unmask');
            }
            comp = Ext.get(comp);
            me.isElement = true;
            me.renderTo = me.target;
        }
        me.render(me.renderTo);
        if (me.store) {
            me.bindStore(me.store, true);
        }
    },
    initRenderData: function() {
        var result = this.callParent(arguments);
        result.msg = this.msg || '';
        return result;
    },
    onRender: function() {
        this.callParent(arguments);
        
        
        
        this.maskEl = this.el;
    },
    bindComponent: function(comp) {
        var me = this,
            listeners = {
                scope: this,
                resize: me.sizeMask
            };
        if (me.external) {
            listeners.added = me.onComponentAdded;
            listeners.removed = me.onComponentRemoved;
            if (comp.floating) {
                listeners.move = me.sizeMask;
                me.activeOwner = comp;
            } else if (comp.ownerCt) {
                me.onComponentAdded(comp.ownerCt);
            }
        }
        me.mon(comp, listeners);
        
        
        if (me.external) {
            me.mon(Ext.GlobalEvents, {
                show: me.onContainerShow,
                hide: me.onContainerHide,
                expand: me.onContainerExpand,
                collapse: me.onContainerCollapse,
                scope: me
            });
        }
    },
    onComponentAdded: function(owner) {
        var me = this;
        delete me.activeOwner;
        me.floatParent = owner;
        if (!owner.floating) {
            owner = owner.up('[floating]');
        }
        if (owner) {
            me.activeOwner = owner;
            me.mon(owner, 'move', me.sizeMask, me);
            me.mon(owner, 'tofront', me.onOwnerToFront, me);
        } else {
            me.preventBringToFront = true;
        }
        owner = me.floatParent.ownerCt;
        if (me.rendered && me.isVisible() && owner) {
            me.floatOwner = owner;
            me.mon(owner, 'afterlayout', me.sizeMask, me, {
                single: true
            });
        }
    },
    onComponentRemoved: function(owner) {
        var me = this,
            activeOwner = me.activeOwner,
            floatOwner = me.floatOwner;
        if (activeOwner) {
            me.mun(activeOwner, 'move', me.sizeMask, me);
            me.mun(activeOwner, 'tofront', me.onOwnerToFront, me);
        }
        if (floatOwner) {
            me.mun(floatOwner, 'afterlayout', me.sizeMask, me);
        }
        delete me.activeOwner;
        delete me.floatOwner;
    },
    afterRender: function() {
        var me = this;
        me.callParent(arguments);
        
        
        
        
        
        
        if (Ext.isIE) {
            me.el.on('mousedown', me.onMouseDown, me);
        }
        
        
        this.el.skipGarbageCollection = true;
    },
    onMouseDown: function(e) {
        var el = this.el;
        if (e.within(el)) {
            e.preventDefault();
            el.focus();
        }
    },
    onOwnerToFront: function(owner, zIndex) {
        this.el.setStyle('zIndex', zIndex + 1);
    },
    
    
    onContainerShow: function(container) {
        if (!this.isHierarchicallyHidden()) {
            this.onComponentShow();
        }
    },
    
    
    onContainerHide: function(container) {
        if (this.isHierarchicallyHidden()) {
            this.onComponentHide();
        }
    },
    
    
    onContainerExpand: function(container) {
        if (!this.isHierarchicallyHidden()) {
            this.onComponentShow();
        }
    },
    
    
    onContainerCollapse: function(container) {
        if (this.isHierarchicallyHidden()) {
            this.onComponentHide();
        }
    },
    onComponentHide: function() {
        var me = this;
        if (me.rendered && me.isVisible()) {
            me.hide();
            me.showNext = true;
        }
    },
    onComponentShow: function() {
        if (this.showNext) {
            this.show();
        }
        delete this.showNext;
    },
    
    sizeMask: function() {
        var me = this,
            
            
            target = me.activeOwner || me.target,
            boxTarget = me.external ? me.getOwner().el : me.getMaskTarget();
        if (me.rendered && me.isVisible()) {
            
            
            
            if (me.external) {
                if (!me.isElement && target.floating) {
                    me.onOwnerToFront(target, target.el.getZIndex());
                }
                me.el.setSize(boxTarget.getSize()).alignTo(boxTarget, 'tl-tl');
            }
            
            me.msgWrapEl.center(me.el);
        }
    },
    
    bindStore: function(store, initial) {
        var me = this;
        me.mixins.storeholder.bindStore.apply(me, arguments);
        store = me.store;
        if (store && store.isLoading()) {
            me.onBeforeLoad();
        }
    },
    getStoreListeners: function(store) {
        var load = this.onLoad,
            beforeLoad = this.onBeforeLoad,
            result = {
                
                cachemiss: beforeLoad,
                
                cachefilled: load
            };
        
        if (!store.loadsSynchronously()) {
            result.beforeload = beforeLoad;
            result.load = load;
        }
        return result;
    },
    onDisable: function() {
        this.callParent(arguments);
        if (this.loading) {
            this.onLoad();
        }
    },
    getOwner: function() {
        return this.ownerCt || this.ownerCmp || this.floatParent;
    },
    getMaskTarget: function() {
        var owner = this.getOwner();
        if (this.isElement) {
            return this.target;
        }
        return this.useTargetEl ? owner.getTargetEl() : (owner.getMaskTarget() || Ext.getBody());
    },
    
    onBeforeLoad: function() {
        var me = this,
            owner = me.getOwner(),
            origin;
        if (!me.disabled) {
            me.loading = true;
            
            
            if (owner.componentLayoutCounter) {
                me.maybeShow();
            } else {
                
                origin = owner.afterComponentLayout;
                owner.afterComponentLayout = function() {
                    owner.afterComponentLayout = origin;
                    origin.apply(owner, arguments);
                    me.maybeShow();
                };
            }
        }
    },
    maybeShow: function() {
        var me = this,
            owner = me.getOwner();
        if (!owner.isVisible(true)) {
            me.showNext = true;
        } else if (me.loading && owner.rendered) {
            me.show();
        }
    },
    hide: function() {
        var me = this,
            ownerCt = me.ownerCt;
        
        if (me.isElement) {
            ownerCt.unmask();
            me.fireEvent('hide', this);
            return;
        }
        me.restoreFocus();
        ownerCt.enableTabbing();
        ownerCt.setMasked(false);
        delete me.showNext;
        return me.callParent(arguments);
    },
    show: function() {
        var me = this;
        
        if (me.isElement) {
            me.ownerCt.mask(this.useMsg ? this.msg : '', this.msgCls);
            me.fireEvent('show', this);
            return;
        }
        return me.callParent(arguments);
    },
    afterShow: function() {
        var me = this,
            ownerCt = me.ownerCt,
            el = me.el;
        me.loading = true;
        me.callParent(arguments);
        
        if (me.hasOwnProperty('msgWrapCls')) {
            el.dom.className = me.msgWrapCls;
        }
        if (me.useMsg) {
            me.msgTextEl.setHtml(me.msg);
        } else {
            
            me.msgEl.hide();
        }
        if (me.shim || Ext.useShims) {
            el.enableShim(null, true);
        } else {
            
            
            el.disableShim();
        }
        ownerCt.disableTabbing();
        ownerCt.setMasked(true);
        
        
        el.restoreTabbableState();
        me.saveFocus();
        me.sizeMask();
    },
    
    onLoad: function() {
        this.loading = false;
        this.hide();
    },
    beforeDestroy: function() {
        
        
        this.ownerCt = null;
        this.bindStore(null);
        this.callParent();
    },
    onDestroy: function() {
        var me = this;
        if (me.isElement) {
            me.ownerCt.unmask();
        }
        me.callParent();
    },
    privates: {
        getFocusEl: function() {
            return this.el;
        },
        saveFocus: function() {
            var me = this,
                ownerEl = me.ownerCt.el,
                activeDom = Ext.Element.getActiveElement();
            if (ownerEl.contains(activeDom)) {
                me.previouslyFocused = activeDom;
                me.el.focus();
            }
        },
        restoreFocus: function() {
            var me = this,
                ownerCt = me.ownerCt,
                prev = me.previouslyFocused,
                focusEl;
            if (me.el.dom === document.activeElement) {
                if (prev) {
                    if (Ext.fly(prev).isFocusable()) {
                        focusEl = prev;
                    }
                }
                
                
                else if (ownerCt.getDefaultFocus) {
                    focusEl = ownerCt.getDefaultFocus();
                } else if (ownerCt.canFocus()) {
                    focusEl = ownerCt.getFocusEl();
                }
                if (focusEl) {
                    focusEl.focus();
                }
            }
            delete me.previouslyFocused;
        }
    }
});


Ext.define('Ext.layout.component.Component', {
    extend: 'Ext.layout.Layout',
    type: 'component',
    isComponentLayout: true,
    nullBox: {},
    usesContentHeight: true,
    usesContentWidth: true,
    usesHeight: true,
    usesWidth: true,
    widthCache: {},
    heightCache: {},
    beginLayoutCycle: function(ownerContext, firstCycle) {
        var me = this,
            owner = me.owner,
            ownerCtContext = ownerContext.ownerCtContext,
            heightModel = ownerContext.heightModel,
            widthModel = ownerContext.widthModel,
            body = owner.el.dom === document.body,
            lastBox = owner.lastBox || me.nullBox,
            lastSize = owner.el.lastBox || me.nullBox,
            dirty = !body,
            isTopLevel = ownerContext.isTopLevel,
            ownerLayout, v, width, height;
        me.callParent(arguments);
        if (firstCycle) {
            if (me.usesContentWidth) {
                ++ownerContext.consumersContentWidth;
            }
            if (me.usesContentHeight) {
                ++ownerContext.consumersContentHeight;
            }
            if (me.usesWidth) {
                ++ownerContext.consumersWidth;
            }
            if (me.usesHeight) {
                ++ownerContext.consumersHeight;
            }
            if (ownerCtContext && !ownerCtContext.hasRawContent) {
                ownerLayout = owner.ownerLayout;
                if (ownerLayout) {
                    if (ownerLayout.usesWidth) {
                        ++ownerContext.consumersWidth;
                    }
                    if (ownerLayout.usesHeight) {
                        ++ownerContext.consumersHeight;
                    }
                }
            }
        }
        
        
        if (widthModel.configured) {
            
            
            
            
            width = owner[widthModel.names.width];
            if (isTopLevel && widthModel.calculatedFrom) {
                width = lastBox.width;
            }
            if (!body) {
                dirty = me.setWidthInDom || (firstCycle ? width !== lastSize.width : widthModel.constrained);
            }
            ownerContext.setWidth(width, dirty);
        } else if (isTopLevel) {
            if (widthModel.calculated) {
                v = lastBox.width;
                ownerContext.setWidth(v, 
                v != lastSize.width);
            }
            v = lastBox.x;
            ownerContext.setProp('x', v, 
            v != lastSize.x);
        }
        if (heightModel.configured) {
            height = owner[heightModel.names.height];
            if (isTopLevel && heightModel.calculatedFrom) {
                height = lastBox.height;
            }
            if (!body) {
                dirty = firstCycle ? height !== lastSize.height : heightModel.constrained;
            }
            ownerContext.setHeight(height, dirty);
        } else if (isTopLevel) {
            if (heightModel.calculated) {
                v = lastBox.height;
                ownerContext.setHeight(v, v != lastSize.height);
            }
            v = lastBox.y;
            ownerContext.setProp('y', v, 
            v != lastSize.y);
        }
    },
    finishedLayout: function(ownerContext) {
        var me = this,
            elementChildren = ownerContext.children,
            owner = me.owner,
            len, i, elContext, lastBox, props;
        
        
        
        if (elementChildren) {
            len = elementChildren.length;
            for (i = 0; i < len; i++) {
                elContext = elementChildren[i];
                elContext.el.lastBox = elContext.props;
            }
        }
        
        ownerContext.previousSize = me.lastComponentSize;
        
        me.lastComponentSize = owner.el.lastBox = props = ownerContext.props;
        
        
        lastBox = owner.lastBox || (owner.lastBox = {});
        lastBox.x = props.x;
        lastBox.y = props.y;
        lastBox.width = props.width;
        lastBox.height = props.height;
        lastBox.invalid = false;
        me.callParent(arguments);
    },
    notifyOwner: function(ownerContext) {
        var me = this,
            currentSize = me.lastComponentSize,
            prevSize = ownerContext.previousSize,
            args = [
                currentSize.width,
                currentSize.height
            ];
        if (prevSize) {
            args.push(prevSize.width, prevSize.height);
        }
        
        me.owner.afterComponentLayout.apply(me.owner, args);
    },
    
    getTarget: function() {
        return this.owner.el;
    },
    
    getRenderTarget: function() {
        return this.owner.el;
    },
    cacheTargetInfo: function(ownerContext) {
        var me = this,
            targetInfo = me.targetInfo,
            target;
        if (!targetInfo) {
            target = ownerContext.getEl('getTarget', me);
            me.targetInfo = targetInfo = {
                padding: target.getPaddingInfo(),
                border: target.getBorderInfo()
            };
        }
        return targetInfo;
    },
    measureAutoDimensions: function(ownerContext, dimensions) {
        
        
        
        
        
        var me = this,
            owner = me.owner,
            containerLayout = owner.layout,
            heightModel = ownerContext.heightModel,
            widthModel = ownerContext.widthModel,
            boxParent = ownerContext.boxParent,
            isBoxParent = ownerContext.isBoxParent,
            target = ownerContext.target,
            props = ownerContext.props,
            isContainer,
            ret = {
                gotWidth: false,
                gotHeight: false,
                isContainer: (isContainer = !ownerContext.hasRawContent)
            },
            hv = dimensions || 3,
            zeroWidth, zeroHeight,
            needed = 0,
            got = 0,
            ready, size, temp, key, cache;
        
        
        
        
        if (widthModel.shrinkWrap && ownerContext.consumersContentWidth) {
            ++needed;
            zeroWidth = !(hv & 1);
            if (isContainer) {
                
                
                if (zeroWidth) {
                    ret.contentWidth = 0;
                    ret.gotWidth = true;
                    ++got;
                } else if ((ret.contentWidth = ownerContext.getProp('contentWidth')) !== undefined) {
                    ret.gotWidth = true;
                    ++got;
                }
            } else {
                size = props.contentWidth;
                if (typeof size == 'number') {
                    
                    ret.contentWidth = size;
                    ret.gotWidth = true;
                    ++got;
                } else {
                    if (zeroWidth) {
                        ready = true;
                    } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {
                        ready = false;
                    } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {
                        
                        
                        
                        ready = true;
                    } else {
                        
                        
                        
                        ready = boxParent.hasDomProp('width');
                    }
                    if (ready) {
                        if (zeroWidth) {
                            temp = 0;
                        } else if (containerLayout && containerLayout.measureContentWidth) {
                            
                            
                            temp = containerLayout.measureContentWidth(ownerContext);
                        } else {
                            if (target.cacheWidth) {
                                
                                
                                key = target.xtype + '-' + target.ui;
                                cache = me.widthCache;
                                temp = cache[key] || (cache[key] = me.measureContentWidth(ownerContext));
                            } else {
                                temp = me.measureContentWidth(ownerContext);
                            }
                        }
                        if (!isNaN(ret.contentWidth = temp)) {
                            ownerContext.setContentWidth(temp, true);
                            ret.gotWidth = true;
                            ++got;
                        }
                    }
                }
            }
        } else if (widthModel.natural && ownerContext.consumersWidth) {
            ++needed;
            size = props.width;
            
            if (typeof size == 'number') {
                
                ret.width = size;
                ret.gotWidth = true;
                ++got;
            } else {
                if (isBoxParent || !boxParent) {
                    ready = true;
                } else {
                    
                    
                    
                    ready = boxParent.hasDomProp('width');
                }
                if (ready) {
                    if (!isNaN(ret.width = me.measureOwnerWidth(ownerContext))) {
                        ownerContext.setWidth(ret.width, false);
                        ret.gotWidth = true;
                        ++got;
                    }
                }
            }
        }
        
        
        if (heightModel.shrinkWrap && ownerContext.consumersContentHeight) {
            ++needed;
            zeroHeight = !(hv & 2);
            if (isContainer) {
                
                if (zeroHeight) {
                    ret.contentHeight = 0;
                    ret.gotHeight = true;
                    ++got;
                } else if ((ret.contentHeight = ownerContext.getProp('contentHeight')) !== undefined) {
                    ret.gotHeight = true;
                    ++got;
                }
            } else {
                size = props.contentHeight;
                if (typeof size == 'number') {
                    
                    ret.contentHeight = size;
                    ret.gotHeight = true;
                    ++got;
                } else {
                    if (zeroHeight) {
                        ready = true;
                    } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {
                        ready = false;
                    } else if (owner.noWrap) {
                        ready = true;
                    } else if (!widthModel.shrinkWrap) {
                        
                        ready = (ownerContext.bodyContext || ownerContext).hasDomProp('width');
                    }
                    
                    else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {
                        
                        
                        
                        ready = true;
                    } else {
                        
                        
                        
                        ready = boxParent.hasDomProp('width');
                    }
                    if (ready) {
                        if (zeroHeight) {
                            temp = 0;
                        } else if (containerLayout && containerLayout.measureContentHeight) {
                            
                            
                            temp = containerLayout.measureContentHeight(ownerContext);
                        } else {
                            if (target.cacheHeight) {
                                
                                
                                key = target.xtype + '-' + target.ui;
                                cache = me.heightCache;
                                temp = cache[key] || (cache[key] = me.measureContentHeight(ownerContext));
                            } else {
                                temp = me.measureContentHeight(ownerContext);
                            }
                        }
                        if (!isNaN(ret.contentHeight = temp)) {
                            ownerContext.setContentHeight(temp, true);
                            ret.gotHeight = true;
                            ++got;
                        }
                    }
                }
            }
        } else if (heightModel.natural && ownerContext.consumersHeight) {
            ++needed;
            size = props.height;
            
            if (typeof size == 'number') {
                
                ret.height = size;
                ret.gotHeight = true;
                ++got;
            } else {
                if (isBoxParent || !boxParent) {
                    ready = true;
                } else {
                    
                    
                    
                    ready = boxParent.hasDomProp('width');
                }
                if (ready) {
                    if (!isNaN(ret.height = me.measureOwnerHeight(ownerContext))) {
                        ownerContext.setHeight(ret.height, false);
                        ret.gotHeight = true;
                        ++got;
                    }
                }
            }
        }
        if (boxParent) {
            ownerContext.onBoxMeasured();
        }
        ret.gotAll = got == needed;
        
        return ret;
    },
    measureContentWidth: function(ownerContext) {
        
        return ownerContext.el.getWidth() - ownerContext.getFrameInfo().width;
    },
    measureContentHeight: function(ownerContext) {
        
        return ownerContext.el.getHeight() - ownerContext.getFrameInfo().height;
    },
    measureOwnerHeight: function(ownerContext) {
        return ownerContext.el.getHeight();
    },
    measureOwnerWidth: function(ownerContext) {
        return ownerContext.el.getWidth();
    }
});


Ext.define('Ext.layout.component.Auto', {
    
    alias: 'layout.autocomponent',
    extend: 'Ext.layout.component.Component',
    
    type: 'autocomponent',
    
    setHeightInDom: false,
    
    setWidthInDom: false,
    waitForOuterHeightInDom: false,
    waitForOuterWidthInDom: false,
    beginLayoutCycle: function(ownerContext, firstCycle) {
        var me = this,
            lastWidthModel = me.lastWidthModel,
            lastHeightModel = me.lastHeightModel,
            el = me.owner.el;
        me.callParent(arguments);
        if (lastWidthModel && lastWidthModel.fixed && ownerContext.widthModel.shrinkWrap) {
            el.setWidth(null);
        }
        if (lastHeightModel && lastHeightModel.fixed && ownerContext.heightModel.shrinkWrap) {
            el.setHeight(null);
        }
    },
    calculate: function(ownerContext) {
        var me = this,
            measurement = me.measureAutoDimensions(ownerContext),
            heightModel = ownerContext.heightModel,
            widthModel = ownerContext.widthModel,
            width, height;
        
        
        if (measurement.gotWidth) {
            if (widthModel.shrinkWrap) {
                me.publishOwnerWidth(ownerContext, measurement.contentWidth);
            } else if (me.publishInnerWidth) {
                me.publishInnerWidth(ownerContext, measurement.width);
            }
        } else if (!widthModel.auto && me.publishInnerWidth) {
            width = me.waitForOuterWidthInDom ? ownerContext.getDomProp('width') : ownerContext.getProp('width');
            if (width === undefined) {
                me.done = false;
            } else {
                me.publishInnerWidth(ownerContext, width);
            }
        }
        if (measurement.gotHeight) {
            if (heightModel.shrinkWrap) {
                me.publishOwnerHeight(ownerContext, measurement.contentHeight);
            } else if (me.publishInnerHeight) {
                me.publishInnerHeight(ownerContext, measurement.height);
            }
        } else if (!heightModel.auto && me.publishInnerHeight) {
            height = me.waitForOuterHeightInDom ? ownerContext.getDomProp('height') : ownerContext.getProp('height');
            if (height === undefined) {
                me.done = false;
            } else {
                me.publishInnerHeight(ownerContext, height);
            }
        }
        if (!measurement.gotAll) {
            me.done = false;
        }
    },
    calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) {
        return contentHeight + ownerContext.getFrameInfo().height;
    },
    calculateOwnerWidthFromContentWidth: function(ownerContext, contentWidth) {
        return contentWidth + ownerContext.getFrameInfo().width;
    },
    publishOwnerHeight: function(ownerContext, contentHeight) {
        var me = this,
            owner = me.owner,
            height = me.calculateOwnerHeightFromContentHeight(ownerContext, contentHeight),
            constrainedHeight, dirty, heightModel;
        if (isNaN(height)) {
            me.done = false;
        } else {
            constrainedHeight = Ext.Number.constrain(height, owner.minHeight, owner.maxHeight);
            if (constrainedHeight == height) {
                dirty = me.setHeightInDom;
            } else {
                heightModel = me.sizeModels[(constrainedHeight < height) ? 'constrainedMax' : 'constrainedMin'];
                height = constrainedHeight;
                if (ownerContext.heightModel.calculatedFromShrinkWrap) {
                    
                    
                    
                    ownerContext.heightModel = heightModel;
                } else {
                    ownerContext.invalidate({
                        heightModel: heightModel
                    });
                }
            }
            ownerContext.setHeight(height, dirty);
        }
    },
    publishOwnerWidth: function(ownerContext, contentWidth) {
        var me = this,
            owner = me.owner,
            width = me.calculateOwnerWidthFromContentWidth(ownerContext, contentWidth),
            constrainedWidth, dirty, widthModel;
        if (isNaN(width)) {
            me.done = false;
        } else {
            constrainedWidth = Ext.Number.constrain(width, owner.minWidth, owner.maxWidth);
            if (constrainedWidth == width) {
                dirty = me.setWidthInDom;
            } else {
                widthModel = me.sizeModels[(constrainedWidth < width) ? 'constrainedMax' : 'constrainedMin'];
                width = constrainedWidth;
                if (ownerContext.widthModel.calculatedFromShrinkWrap) {
                    
                    
                    
                    ownerContext.widthModel = widthModel;
                } else {
                    ownerContext.invalidate({
                        widthModel: widthModel
                    });
                }
            }
            ownerContext.setWidth(width, dirty);
        }
    }
});


Ext.define('Ext.layout.component.ProgressBar', {
    
    alias: [
        'layout.progressbar'
    ],
    extend: 'Ext.layout.component.Auto',
    
    type: 'progressbar',
    beginLayout: function(ownerContext) {
        var me = this,
            i, textEls;
        me.callParent(arguments);
        if (!ownerContext.textEls) {
            textEls = me.owner.textEl;
            
            if (textEls.isComposite) {
                ownerContext.textEls = [];
                textEls = textEls.elements;
                for (i = textEls.length; i--; ) {
                    ownerContext.textEls[i] = ownerContext.getEl(Ext.get(textEls[i]));
                }
            } else {
                ownerContext.textEls = [
                    ownerContext.getEl('textEl')
                ];
            }
        }
    },
    calculate: function(ownerContext) {
        var me = this,
            i, textEls, width;
        me.callParent(arguments);
        if (Ext.isNumber(width = ownerContext.getProp('width'))) {
            width -= ownerContext.getBorderInfo().width;
            textEls = ownerContext.textEls;
            for (i = textEls.length; i--; ) {
                textEls[i].setWidth(width);
            }
        } else {
            me.done = false;
        }
    }
});


Ext.define('Ext.ProgressBar', {
    extend: 'Ext.Component',
    alias: 'widget.progressbar',
    requires: [
        'Ext.Template',
        'Ext.CompositeElement',
        'Ext.TaskManager',
        'Ext.layout.component.ProgressBar'
    ],
    uses: [
        'Ext.fx.Anim'
    ],
    config: {
        
        value: 0,
        
        textTpl: null
    },
    
    
    
    baseCls: Ext.baseCSSPrefix + 'progress',
    
    animate: false,
    
    text: '',
    
    waitTimer: null,
    childEls: [
        'bar'
    ],
    defaultBindProperty: 'value',
    renderTpl: [
        '<tpl if="internalText">',
        '<div class="{baseCls}-text {baseCls}-text-back">{text}</div>',
        '</tpl>',
        '<div id="{id}-bar" data-ref="bar" class="{baseCls}-bar {baseCls}-bar-{ui}" role="presentation" style="width:{percentage}%">',
        '<tpl if="internalText">',
        '<div class="{baseCls}-text">',
        '<div>{text}</div>',
        '</div>',
        '</tpl>',
        '</div>'
    ],
    componentLayout: 'progressbar',
    ariaRole: 'progressbar',
    
    initRenderData: function() {
        var me = this;
        return Ext.apply(me.callParent(), {
            internalText: !me.hasOwnProperty('textEl'),
            text: me.text || '&#160;',
            percentage: me.value ? me.value * 100 : 0
        });
    },
    onRender: function() {
        var me = this;
        me.callParent(arguments);
        
        if (me.textEl) {
            me.textEl = Ext.get(me.textEl);
            me.updateText(me.text);
        } else 
        {
            
            
            me.textEl = me.el.select('.' + me.baseCls + '-text');
        }
    },
    updateValue: function(value) {
        this.updateProgress(value, Math.round(value * 100) + '%');
    },
    
    updateProgress: function(value, text, animate) {
        var me = this,
            oldValue = me.value,
            textTpl = me.getTextTpl();
        
        me.value = value || (value = 0);
        
        if (text != null) {
            me.updateText(text);
        }
        
        else if (textTpl) {
            me.updateText(textTpl.apply({
                value: value,
                percent: value * 100
            }));
        }
        if (me.rendered && !me.isDestroyed) {
            if (animate === true || (animate !== false && me.animate)) {
                me.bar.stopAnimation();
                me.bar.animate(Ext.apply({
                    from: {
                        width: (oldValue * 100) + '%'
                    },
                    to: {
                        width: (value * 100) + '%'
                    }
                }, me.animate));
            } else {
                me.bar.setStyle('width', (value * 100) + '%');
            }
        }
        me.fireEvent('update', me, value, text);
        return me;
    },
    
    updateText: function(text) {
        var me = this;
        me.text = text;
        if (me.rendered) {
            me.textEl.setHtml(me.text);
        }
        return me;
    },
    applyTextTpl: function(textTpl) {
        if (!textTpl.isTemplate) {
            textTpl = new Ext.XTemplate(textTpl);
        }
        return textTpl;
    },
    applyText: function(text) {
        this.updateText(text);
    },
    getText: function() {
        return this.text;
    },
    
    wait: function(o) {
        var me = this,
            scope;
        if (!me.waitTimer) {
            scope = me;
            o = o || {};
            me.updateText(o.text);
            me.waitTimer = Ext.TaskManager.start({
                run: function(i) {
                    var inc = o.increment || 10;
                    i -= 1;
                    me.updateProgress(((((i + inc) % inc) + 1) * (100 / inc)) * 0.01, null, o.animate);
                },
                interval: o.interval || 1000,
                duration: o.duration,
                onStop: function() {
                    if (o.fn) {
                        o.fn.apply(o.scope || me);
                    }
                    me.reset();
                },
                scope: scope
            });
        }
        return me;
    },
    
    isWaiting: function() {
        return this.waitTimer !== null;
    },
    
    reset: function(hide) {
        var me = this;
        me.updateProgress(0);
        me.clearTimer();
        if (hide === true) {
            me.hide();
        }
        return me;
    },
    
    clearTimer: function() {
        var me = this;
        if (me.waitTimer) {
            me.waitTimer.onStop = null;
            
            Ext.TaskManager.stop(me.waitTimer);
            me.waitTimer = null;
        }
    },
    onDestroy: function() {
        var me = this,
            bar = me.bar;
        me.clearTimer();
        if (me.rendered) {
            if (me.textEl.isComposite) {
                me.textEl.clear();
            }
            Ext.destroyMembers(me, 'textEl', 'progressBar');
            if (bar && me.animate) {
                bar.stopAnimation();
            }
        }
        me.callParent();
    }
});


Ext.define('Ext.ProgressBarWidget', {
    extend: 'Ext.Widget',
    alias: 'widget.progressbarwidget',
    
    requires: [
        'Ext.ProgressBar'
    ],
    config: {
        
        text: null,
        
        value: 0,
        
        animate: false,
        
        textTpl: null
    },
    cachedConfig: {
        
        baseCls: Ext.baseCSSPrefix + 'progress',
        textCls: Ext.baseCSSPrefix + 'progress-text',
        ui: 'default'
    },
    template: [
        {
            reference: 'backgroundEl'
        },
        {
            reference: 'barEl',
            children: [
                {
                    reference: 'textEl'
                }
            ]
        }
    ],
    defaultBindProperty: 'value',
    updateUi: function(ui, oldUi) {
        var baseCls = this.getBaseCls() + '-';
        if (oldUi) {
            this.element.removeCls(baseCls + oldUi);
            this.barEl.removeCls(baseCls + 'bar-' + oldUi);
        }
        this.element.addCls(baseCls + ui);
        this.barEl.addCls(baseCls + 'bar-' + ui);
    },
    updateBaseCls: function(baseCls, oldBaseCls) {
        if (oldBaseCls) {
            Ext.Error.raise('You cannot configure baseCls - use a subclass');
        }
        this.element.addCls(baseCls);
        this.barEl.addCls(baseCls + '-bar');
    },
    updateTextCls: function(textCls) {
        this.backgroundEl.addCls(textCls + ' ' + textCls + '-back');
        this.textEl.addCls(textCls);
    },
    updateValue: function(value, oldValue) {
        var me = this,
            textTpl = me.getTextTpl();
        if (textTpl) {
            me.setText(textTpl.apply({
                value: value,
                percent: value * 100
            }));
        }
        if (me.getAnimate()) {
            me.barEl.stopAnimation();
            me.barEl.animate(Ext.apply({
                from: {
                    width: (oldValue * 100) + '%'
                },
                to: {
                    width: (value * 100) + '%'
                }
            }, me.animate));
        } else {
            me.barEl.setStyle('width', (value * 100) + '%');
        }
    },
    updateText: function(text) {
        var me = this;
        me.backgroundEl.setHtml(text);
        me.textEl.setHtml(text);
    },
    applyTextTpl: function(textTpl) {
        if (!textTpl.isTemplate) {
            textTpl = new Ext.XTemplate(textTpl);
        }
        return textTpl;
    }
});


Ext.define('Ext.panel.Bar', {
    extend: 'Ext.container.Container',
    vertical: false,
    _verticalSides: {
        left: 1,
        right: 1
    },
    initComponent: function() {
        var me = this,
            vertical = me.vertical;
        me.dock = me.dock || (vertical ? 'left' : 'top');
        me.layout = Ext.apply(vertical ? {
            type: 'vbox',
            align: 'middle',
            alignRoundingMethod: 'ceil'
        } : {
            type: 'hbox',
            align: 'middle',
            alignRoundingMethod: 'floor'
        }, me.layout);
        this.callParent();
    },
    onAdded: function(container, pos, instanced) {
        this.initOrientation();
        this.callParent([
            container,
            pos,
            instanced
        ]);
    },
    onRemoved: function(destroying) {
        this.removeClsWithUI(this.uiCls);
        this.callParent([
            destroying
        ]);
    },
    beforeRender: function() {
        if (!this.ownerCt) {
            
            this.initOrientation();
        }
        this.callParent();
    },
    setDock: function(dock) {
        var me = this,
            layout, vertical;
        if (dock !== me.dock) {
            Ext.suspendLayouts();
            me.clearOrientation();
            me.callParent([
                dock
            ]);
            me.initOrientation();
            vertical = me.vertical;
            layout = me.layout;
            layout.setVertical(vertical);
            layout.setAlignRoundingMethod(vertical ? 'ceil' : 'floor');
            Ext.resumeLayouts(true);
        }
    },
    privates: {
        clearOrientation: function() {
            this.removeClsWithUI([
                this.vertical ? 'vertical' : 'horizontal',
                this.getDockName()
            ]);
        },
        getDockName: function() {
            return this.dock;
        },
        initOrientation: function() {
            var me = this,
                dock = me.dock,
                vertical = me.vertical = (dock ? dock in me._verticalSides : me.vertical);
            me.addClsWithUI([
                this.vertical ? 'vertical' : 'horizontal',
                me.getDockName()
            ]);
        }
    }
});


Ext.define('Ext.panel.Title', {
    extend: 'Ext.Component',
    xtype: 'title',
    isTitle: true,
    
    
    noWrap: true,
    
    
    
    textAlign: 'left',
    iconAlign: 'left',
    rotation: 0,
    text: '&#160;',
    beforeRenderConfig: {
        
        textAlign: null,
        
        text: null,
        
        glyph: null,
        
        icon: null,
        
        iconAlign: null,
        
        iconCls: null,
        
        rotation: null
    },
    autoEl: {
        
        unselectable: 'on'
    },
    childEls: [
        'textEl',
        'iconEl',
        'iconWrapEl'
    ],
    renderTpl: '<tpl if="iconMarkup && iconBeforeTitle">{iconMarkup}</tpl>' + 
    '<div id="{id}-textEl" data-ref="textEl" class="{textCls} {textCls}-{ui} {itemCls}{childElCls}" unselectable="on"' + '<tpl if="headerRole">' + ' role="{headerRole}"' + '</tpl>' + '>{text}</div>' + '<tpl if="iconMarkup && !iconBeforeTitle">{iconMarkup}</tpl>',
    iconTpl: '<div id="{id}-iconWrapEl" data-ref="iconWrapEl" role="presentation" ' + 'class="{iconWrapCls} {iconWrapCls}-{ui} {iconAlignCls} {itemCls}{childElCls}"' + '<tpl if="iconWrapStyle"> style="{iconWrapStyle}"</tpl>>' + '<div id="{id}-iconEl" data-ref="iconEl" role="presentation" unselectable="on" ' + 'class="{baseIconCls} {baseIconCls}-{ui} {iconCls} {glyphCls}" style="' + '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>' + '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">' + '<tpl if="glyph">&#{glyph};</tpl><tpl if="iconCls || iconUrl">&#160;</tpl>' + '</div>' + '</div>',
    _textAlignClasses: {
        left: Ext.baseCSSPrefix + 'title-align-left',
        center: Ext.baseCSSPrefix + 'title-align-center',
        right: Ext.baseCSSPrefix + 'title-align-right'
    },
    _iconAlignClasses: {
        top: Ext.baseCSSPrefix + 'title-icon-top',
        right: Ext.baseCSSPrefix + 'title-icon-right',
        bottom: Ext.baseCSSPrefix + 'title-icon-bottom',
        left: Ext.baseCSSPrefix + 'title-icon-left'
    },
    _rotationClasses: {
        0: Ext.baseCSSPrefix + 'title-rotate-none',
        1: Ext.baseCSSPrefix + 'title-rotate-right',
        2: Ext.baseCSSPrefix + 'title-rotate-left'
    },
    _rotationAngles: {
        1: 90,
        2: 270
    },
    baseCls: Ext.baseCSSPrefix + 'title',
    _titleSuffix: '-title',
    _glyphCls: Ext.baseCSSPrefix + 'title-glyph',
    _iconWrapCls: Ext.baseCSSPrefix + 'title-icon-wrap',
    _baseIconCls: Ext.baseCSSPrefix + 'title-icon',
    _itemCls: Ext.baseCSSPrefix + 'title-item',
    _textCls: Ext.baseCSSPrefix + 'title-text',
    afterComponentLayout: function() {
        var me = this,
            rotation = me.getRotation(),
            lastBox, lastX, el;
        if (rotation && !Ext.isIE8) {
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            el = me.el;
            lastBox = me.lastBox;
            lastX = lastBox.x;
            el.setStyle(me._getVerticalAdjustDirection(), (lastX + ((rotation === 1) ? lastBox.width : -lastBox.height)) + 'px');
        }
        this.callParent();
    },
    onRender: function() {
        var me = this,
            rotation = me.getRotation(),
            el = me.el;
        me.callParent();
        if (rotation) {
            el.setVertical(me._rotationAngles[rotation]);
        }
        if (Ext.supports.FixedTableWidthBug) {
            
            
            
            el._needsTableWidthFix = true;
        }
    },
    applyText: function(text) {
        if (!text) {
            text = '&#160;';
        }
        return text;
    },
    beforeRender: function() {
        var me = this;
        me.callParent();
        me.addCls(me._rotationClasses[me.getRotation()]);
        me.addCls(me._textAlignClasses[me.getTextAlign()]);
    },
    getIconMarkup: function() {
        return this.getTpl('iconTpl').apply(this.getIconRenderData());
    },
    getIconRenderData: function() {
        var me = this,
            icon = me.getIcon(),
            iconCls = me.getIconCls(),
            glyph = me.getGlyph(),
            glyphFontFamily = Ext._glyphFontFamily,
            iconAlign = me.getIconAlign(),
            glyphParts;
        if (typeof glyph === 'string') {
            glyphParts = glyph.split('@');
            glyph = glyphParts[0];
            glyphFontFamily = glyphParts[1];
        }
        return {
            id: me.id,
            ui: me.ui,
            itemCls: me._itemCls,
            iconUrl: icon,
            iconCls: iconCls,
            iconWrapCls: me._iconWrapCls,
            baseIconCls: me._baseIconCls,
            iconAlignCls: me._iconAlignClasses[iconAlign],
            glyph: glyph,
            glyphCls: glyph ? me._glyphCls : '',
            glyphFontFamily: glyphFontFamily
        };
    },
    initRenderData: function() {
        var me = this,
            iconAlign, renderData;
        renderData = Ext.apply({
            text: me.getText(),
            headerRole: me.headerRole,
            id: me.id,
            ui: me.ui,
            itemCls: me._itemCls,
            textCls: me._textCls,
            iconMarkup: null,
            iconBeforeTitle: null
        }, me.callParent());
        if (me._hasIcon()) {
            iconAlign = me.getIconAlign();
            renderData.iconMarkup = me.getIconMarkup();
            renderData.iconBeforeTitle = (iconAlign === 'top' || iconAlign === 'left');
        }
        return renderData;
    },
    onAdded: function(container, pos, instanced) {
        var me = this,
            cls = me.cls || '',
            suffix = me._titleSuffix,
            baseCls = container.baseCls;
        me.addCls([
            baseCls + suffix,
            baseCls + suffix + '-' + container.ui
        ]);
        me.callParent([
            container,
            pos,
            instanced
        ]);
    },
    updateGlyph: function(glyph, oldGlyph) {
        glyph = glyph || 0;
        var me = this,
            glyphCls = me._glyphCls,
            iconEl, fontFamily, glyphParts;
        me.glyph = glyph;
        if (me.rendered) {
            me._syncIconVisibility();
            iconEl = me.iconEl;
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                fontFamily = glyphParts[1] || Ext._glyphFontFamily;
            }
            if (!glyph) {
                iconEl.dom.innerHTML = '';
                iconEl.removeCls(glyphCls);
            } else if (oldGlyph != glyph) {
                iconEl.dom.innerHTML = '&#' + glyph + ';';
                iconEl.addCls(glyphCls);
            }
            if (fontFamily) {
                iconEl.setStyle('font-family', fontFamily);
            }
            if (me._didIconStateChange(oldGlyph, glyph)) {
                me.updateLayout();
            }
        }
    },
    updateIcon: function(icon, oldIcon) {
        icon = icon || '';
        var me = this,
            iconEl;
        if (me.rendered && icon != oldIcon) {
            me._syncIconVisibility();
            iconEl = me.iconEl;
            iconEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
            if (me._didIconStateChange(oldIcon, icon)) {
                me.updateLayout();
            }
        }
    },
    updateIconAlign: function(align, oldAlign) {
        var me = this,
            iconWrapEl = me.iconWrapEl,
            el, iconWrapEl, iconAlignClasses;
        if (me.iconWrapEl) {
            el = me.el;
            iconAlignClasses = me._iconAlignClasses;
            if (oldAlign) {
                iconWrapEl.removeCls(iconAlignClasses[oldAlign]);
            }
            iconWrapEl.addCls(iconAlignClasses[align]);
            
            
            if (align === 'top' || align === 'left') {
                el.insertFirst(iconWrapEl);
            } else {
                el.appendChild(iconWrapEl);
            }
            me.updateLayout();
        }
    },
    updateIconCls: function(cls, oldCls) {
        cls = cls || '';
        var me = this,
            iconEl;
        if (me.rendered && oldCls != cls) {
            me._syncIconVisibility();
            iconEl = me.iconEl;
            if (oldCls) {
                iconEl.removeCls(oldCls);
            }
            iconEl.addCls(cls);
            if (me._didIconStateChange(oldCls, cls)) {
                me.updateLayout();
            }
        }
    },
    updateRotation: function(rotation, oldRotation) {
        var me = this,
            clearStyles, el, rotationClasses;
        if (me.rendered) {
            el = me.el;
            rotationClasses = me._rotationClasses;
            me.removeCls(rotationClasses[oldRotation]);
            me.addCls(rotationClasses[rotation]);
            el.setHorizontal();
            if (rotation) {
                el.setVertical(me._rotationAngles[rotation]);
            }
            
            
            
            el.setStyle({
                right: '',
                left: '',
                top: '',
                height: '',
                width: ''
            });
            me.lastBox = null;
            me.updateLayout();
        }
    },
    updateText: function(text) {
        if (this.rendered) {
            this.textEl.setHtml(text);
        }
    },
    updateTextAlign: function(align, oldAlign) {
        var me = this,
            textAlignClasses = me._textAlignClasses;
        if (me.rendered) {
            if (oldAlign) {
                me.removeCls(textAlignClasses[oldAlign]);
            }
            me.addCls(textAlignClasses[align]);
            me.updateLayout();
        }
    },
    privates: {
        
        _getVerticalAdjustDirection: function() {
            return 'left';
        },
        _didIconStateChange: function(old, current) {
            var currentEmpty = Ext.isEmpty(current);
            return Ext.isEmpty(old) ? !currentEmpty : currentEmpty;
        },
        _hasIcon: function() {
            return !!(this.getIcon() || this.getIconCls() || this.getGlyph());
        },
        _syncIconVisibility: function() {
            var me = this,
                el = me.el,
                hasIcon = me._hasIcon(),
                iconWrapEl = me.iconWrapEl,
                isBefore, iconAlign;
            if (hasIcon && !iconWrapEl) {
                
                
                iconAlign = me.iconAlign;
                isBefore = (iconAlign === 'left' || iconAlign === 'top');
                el.dom.insertAdjacentHTML(isBefore ? 'afterbegin' : 'beforeend', me.getIconMarkup());
                iconWrapEl = me.iconWrapEl = el[isBefore ? 'first' : 'last']();
                me.iconEl = iconWrapEl.first();
            }
            if (iconWrapEl) {
                iconWrapEl.setDisplayed(hasIcon);
            }
        }
    }
});


Ext.define('Ext.panel.Tool', {
    extend: 'Ext.Component',
    uses: [
        'Ext.tip.QuickTipManager'
    ],
    xtype: 'tool',
    
    isTool: true,
    focusable: true,
    baseCls: Ext.baseCSSPrefix + 'tool',
    disabledCls: Ext.baseCSSPrefix + 'tool-disabled',
    
    toolPressedCls: Ext.baseCSSPrefix + 'tool-pressed',
    
    toolOverCls: Ext.baseCSSPrefix + 'tool-over',
    ariaRole: 'button',
    childEls: [
        'toolEl'
    ],
    renderTpl: [
        '<img id="{id}-toolEl" data-ref="toolEl" src="{blank}" class="{baseCls}-img {baseCls}-{type}' + '{childElCls}" role="presentation"/>'
    ],
    
    toolOwner: null,
    
    
    
    
    
    
    tooltipType: 'qtip',
    
    stopEvent: true,
    cacheHeight: true,
    cacheWidth: true,
    _toolTypes: {
        close: 1,
        collapse: 1,
        down: 1,
        expand: 1,
        gear: 1,
        help: 1,
        left: 1,
        maximize: 1,
        minimize: 1,
        minus: 1,
        
        next: 1,
        pin: 1,
        plus: 1,
        prev: 1,
        print: 1,
        refresh: 1,
        
        restore: 1,
        right: 1,
        save: 1,
        search: 1,
        toggle: 1,
        unpin: 1,
        up: 1
    },
    initComponent: function() {
        var me = this;
        if (me.id && me._toolTypes[me.id]) {
            Ext.Error.raise('When specifying a tool you should use the type option, the id can conflict now that tool is a Component');
        }
        me.type = me.type || me.id;
        Ext.applyIf(me.renderData, {
            baseCls: me.baseCls,
            blank: Ext.BLANK_IMAGE_URL,
            type: me.type
        });
        
        me.tooltip = me.tooltip || me.qtip;
        me.callParent();
    },
    
    afterRender: function() {
        var me = this,
            tip;
        me.callParent(arguments);
        me.el.on({
            click: me.onClick,
            mousedown: me.onMouseDown,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            scope: me
        });
        tip = me.tooltip;
        if (tip) {
            me.setTooltip(tip);
        }
    },
    tipAttrs: {
        qtip: 'data-qtip'
    },
    setTooltip: function(tooltip, type) {
        var me = this,
            oldTip = me.tooltip,
            oldType = me.tooltipType,
            id = me.id,
            el = me.el,
            attr;
        if (oldTip && Ext.quickTipsActive && Ext.isObject(oldTip)) {
            Ext.tip.QuickTipManager.unregister(id);
        }
        me.tooltip = tooltip;
        if (type) {
            me.tooltipType = type;
        }
        if (tooltip) {
            if (Ext.quickTipsActive && Ext.isObject(tooltip)) {
                Ext.tip.QuickTipManager.register(Ext.apply({
                    target: id
                }, tooltip));
            } else if (el) {
                if (type && oldType && type !== oldType) {
                    attr = me.tipAttrs[oldType] || 'title';
                    el.dom.removeAttribute(attr);
                }
                attr = me.tipAttrs[type || oldType] || 'title';
                el.dom.setAttribute(attr, tooltip);
            }
        }
    },
    
    setType: function(type) {
        var me = this,
            oldType = me.type;
        me.type = type;
        if (me.rendered) {
            if (oldType) {
                me.toolEl.removeCls(me.baseCls + '-' + oldType);
            }
            me.toolEl.addCls(me.baseCls + '-' + type);
        } else {
            me.renderData.type = type;
        }
        return me;
    },
    
    onDestroy: function() {
        var me = this,
            keyMap = me.keyMap;
        me.setTooltip(null);
        
        if (keyMap) {
            keyMap.destroy();
            me.keyMap = null;
        }
        delete me.toolOwner;
        me.callParent();
    },
    privates: {
        getFocusEl: function() {
            return this.el;
        },
        
        onClick: function(e, target) {
            var me = this;
            if (me.disabled) {
                return false;
            }
            
            me.el.removeCls(me.toolPressedCls + ' ' + me.toolOverCls);
            if (me.stopEvent !== false) {
                e.stopEvent();
            }
            if (me.handler) {
                Ext.callback(me.handler, me.scope, [
                    e,
                    target,
                    me.ownerCt,
                    me
                ], 0, me);
            } else if (me.callback) {
                Ext.callback(me.callback, me.scope, [
                    me.toolOwner || me.ownerCt,
                    me,
                    e
                ], 0, me);
            }
            
            me.fireEvent('click', me, e, me.toolOwner || me.ownerCt);
            return true;
        },
        
        onMouseDown: function() {
            if (this.disabled) {
                return false;
            }
            this.el.addCls(this.toolPressedCls);
        },
        
        onMouseOver: function() {
            if (this.disabled) {
                return false;
            }
            this.el.addCls(this.toolOverCls);
        },
        
        onMouseOut: function() {
            this.el.removeCls(this.toolOverCls);
        }
    }
});


Ext.define('Ext.panel.Header', {
    extend: 'Ext.panel.Bar',
    requires: [
        'Ext.panel.Title',
        'Ext.panel.Tool'
    ],
    xtype: 'header',
    
    isHeader: true,
    defaultType: 'tool',
    indicateDrag: false,
    weight: -1,
    shrinkWrap: 3,
    
    
    
    iconAlign: 'left',
    titleAlign: 'left',
    titlePosition: 0,
    titleRotation: 'default',
    beforeRenderConfig: {
        
        glyph: null,
        
        icon: null,
        
        iconCls: null,
        
        iconAlign: null,
        
        title: {
            $value: {
                ariaRole: 'presentation',
                xtype: 'title',
                flex: 1
            },
            merge: function(newValue, oldValue) {
                if (typeof newValue == 'string') {
                    newValue = {
                        text: newValue
                    };
                }
                return Ext.merge(oldValue ? Ext.Object.chain(oldValue) : {}, newValue);
            }
        },
        
        titleAlign: null,
        
        titlePosition: null,
        
        titleRotation: null
    },
    
    headerCls: Ext.baseCSSPrefix + 'header',
    
    
    initComponent: function() {
        var me = this,
            items = me.items,
            itemPosition = me.itemPosition,
            cls = [
                me.headerCls
            ];
        me.tools = me.tools || [];
        me.items = items = (items ? items.slice() : []);
        if (itemPosition !== undefined) {
            me._userItems = items.slice();
            me.items = items = [];
        }
        me.indicateDragCls = me.headerCls + '-draggable';
        if (me.indicateDrag) {
            cls.push(me.indicateDragCls);
        }
        me.addCls(cls);
        me.syncNoBorderCls();
        
        Ext.Array.push(items, me.tools);
        
        
        me.tools.length = 0;
        me.callParent();
        me.on({
            dblclick: me.onDblClick,
            click: me.onClick,
            element: 'el',
            scope: me
        });
    },
    
    addTool: function(tool) {
        
        
        this.add(Ext.ComponentManager.create(tool, 'tool'));
    },
    afterLayout: function() {
        var me = this,
            frameBR, frameTR, frameTL, xPos;
        if (me.vertical) {
            frameTR = me.frameTR;
            if (frameTR) {
                
                
                
                frameBR = me.frameBR;
                frameTL = me.frameTL;
                xPos = (me.getWidth() - frameTR.getPadding('r') - ((frameTL) ? frameTL.getPadding('l') : me.el.getBorderWidth('l'))) + 'px';
                frameBR.setStyle('background-position-x', xPos);
                frameTR.setStyle('background-position-x', xPos);
            }
        }
        this.callParent();
    },
    applyTitle: function(title, oldTitle) {
        var me = this,
            isString, configHasRotation;
        title = title || '';
        isString = typeof title === 'string';
        if (isString) {
            title = {
                text: title
            };
        }
        if (oldTitle) {
            
            
            Ext.suspendLayouts();
            oldTitle.setConfig(title);
            Ext.resumeLayouts(true);
            title = oldTitle;
        } else {
            if (isString) {
                title.xtype = 'title';
            }
            title.ui = me.ui;
            title.headerRole = me.headerRole;
            configHasRotation = ('rotation' in title);
            title = Ext.create(title);
            
            if (!configHasRotation && me.vertical && me.titleRotation === 'default') {
                title.rotation = 1;
            }
        }
        return title;
    },
    applyTitlePosition: function(position) {
        var max = this.items.getCount();
        if (this._titleInItems) {
            --max;
        }
        return Math.max(Math.min(position, max), 0);
    },
    beforeLayout: function() {
        this.callParent();
        this.syncBeforeAfterTitleClasses();
    },
    beforeRender: function() {
        var me = this,
            itemPosition = me.itemPosition;
        me.protoEl.unselectable();
        me.callParent();
        if (itemPosition !== undefined) {
            me.insert(itemPosition, me._userItems);
        }
    },
    
    getTools: function() {
        return this.tools.slice();
    },
    onAdd: function(component, index) {
        var tools = this.tools;
        this.callParent([
            component,
            index
        ]);
        if (component.isTool) {
            tools.push(component);
            tools[component.type] = component;
        }
    },
    onAdded: function(container, pos, instanced) {
        this.syncNoBorderCls();
        this.callParent([
            container,
            pos,
            instanced
        ]);
    },
    onRemoved: function(container, pos, instanced) {
        this.syncNoBorderCls();
        this.callParent([
            container,
            pos,
            instanced
        ]);
    },
    setDock: function(dock) {
        var me = this,
            title = me.getTitle(),
            rotation = me.getTitleRotation(),
            titleRotation = title.getRotation();
        Ext.suspendLayouts();
        me.callParent([
            dock
        ]);
        if (rotation === 'default') {
            rotation = (me.vertical ? 1 : 0);
            if (rotation !== titleRotation) {
                title.setRotation(rotation);
            }
            if (me.rendered) {
                
                
                me.resetItemMargins();
            }
        }
        Ext.resumeLayouts(true);
    },
    updateGlyph: function(glyph) {
        this.getTitle().setGlyph(glyph);
    },
    updateIcon: function(icon) {
        this.getTitle().setIcon(icon);
    },
    updateIconAlign: function(align, oldAlign) {
        this.getTitle().setIconAlign(align);
    },
    updateIconCls: function(cls) {
        this.getTitle().setIconCls(cls);
    },
    updateTitle: function(title, oldTitle) {
        if (!oldTitle) {
            this.insert(this.getTitlePosition(), title);
            this._titleInItems = true;
        }
        
        this.titleCmp = title;
    },
    updateTitleAlign: function(align, oldAlign) {
        this.getTitle().setTextAlign(align);
    },
    updateTitlePosition: function(position) {
        this.insert(position, this.getTitle());
    },
    updateTitleRotation: function(rotation) {
        if (rotation === 'default') {
            rotation = (this.vertical ? 1 : 0);
        }
        this.getTitle().setRotation(rotation);
    },
    privates: {
        fireClickEvent: function(type, e) {
            var toolCls = '.' + Ext.panel.Tool.prototype.baseCls;
            if (!e.getTarget(toolCls)) {
                this.fireEvent(type, this, e);
            }
        },
        getFocusEl: function() {
            return this.el;
        },
        getFramingInfoCls: function() {
            var me = this,
                cls = me.callParent(),
                owner = me.ownerCt;
            if (!me.expanding && owner && (owner.collapsed || me.isCollapsedExpander)) {
                cls += '-' + owner.collapsedCls;
            }
            return cls + '-' + me.dock;
        },
        onClick: function(e) {
            this.fireClickEvent('click', e);
        },
        onDblClick: function(e) {
            this.fireClickEvent('dblclick', e);
        },
        syncBeforeAfterTitleClasses: function(force) {
            var me = this,
                items = me.items,
                childItems = items.items,
                titlePosition = me.getTitlePosition(),
                itemCount = childItems.length,
                itemGeneration = items.generation,
                syncGen = me.syncBeforeAfterGen,
                afterCls, beforeCls, i, item;
            if (!force && (syncGen === itemGeneration)) {
                return;
            }
            me.syncBeforeAfterGen = itemGeneration;
            for (i = 0; i < itemCount; ++i) {
                item = childItems[i];
                afterCls = item.afterTitleCls || (item.afterTitleCls = item.baseCls + '-after-title');
                beforeCls = item.beforeTitleCls || (item.beforeTitleCls = item.baseCls + '-before-title');
                if (!me.title || i < titlePosition) {
                    if (syncGen) {
                        item.removeCls(afterCls);
                    }
                    
                    item.addCls(beforeCls);
                } else if (i > titlePosition) {
                    if (syncGen) {
                        item.removeCls(beforeCls);
                    }
                    item.addCls(afterCls);
                }
            }
        },
        syncNoBorderCls: function() {
            var me = this,
                ownerCt = this.ownerCt,
                noBorderCls = me.headerCls + '-noborder';
            
            if (ownerCt ? (ownerCt.border === false && !ownerCt.frame) : me.border === false) {
                me.addCls(noBorderCls);
            } else {
                me.removeCls(noBorderCls);
            }
        }
    }
});


Ext.define('Ext.layout.container.boxOverflow.None', {
    alternateClassName: 'Ext.layout.boxOverflow.None',
    alias: [
        'box.overflow.none',
        'box.overflow.None'
    ],
    
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    factoryConfig: {
        defaultType: 'none'
    },
    isBoxOverflowHandler: true,
    $configPrefixed: false,
    $configStrict: false,
    constructor: function(config) {
        this.initConfig(config);
    },
    handleOverflow: Ext.emptyFn,
    clearOverflow: Ext.emptyFn,
    beginLayout: Ext.emptyFn,
    beginLayoutCycle: Ext.emptyFn,
    calculate: function(ownerContext) {
        var me = this,
            plan = ownerContext.state.boxPlan,
            overflow;
        if (plan && plan.tooNarrow) {
            overflow = me.handleOverflow(ownerContext);
            if (overflow) {
                if (overflow.reservedSpace) {
                    me.layout.publishInnerCtSize(ownerContext, overflow.reservedSpace);
                }
            }
        } else 
        
        
        
        
        
        
        
        
        
        {
            me.clearOverflow();
        }
    },
    completeLayout: Ext.emptyFn,
    finishedLayout: function(ownerContext) {
        var me = this,
            owner = me.layout.owner,
            hiddens, hiddenCount;
        
        if (owner.hasListeners.overflowchange) {
            hiddens = owner.query('>[hidden]');
            hiddenCount = hiddens.length;
            if (hiddenCount !== me.lastHiddenCount) {
                owner.fireEvent('overflowchange', me.lastHiddenCount, hiddenCount, hiddens);
                me.lastHiddenCount = hiddenCount;
            }
        }
    },
    onRemove: Ext.emptyFn,
    
    getItem: function(item) {
        return this.layout.owner.getComponent(item);
    },
    getOwnerType: function(owner) {
        var type;
        if (owner.isToolbar) {
            type = 'toolbar';
        } else if (owner.isTabBar) {
            type = 'tab-bar';
        } else if (owner.isMenu) {
            type = 'menu';
        } else if (owner.isBreadcrumb) {
            type = 'breadcrumb';
        } else {
            type = owner.getXType();
        }
        return type;
    },
    getPrefixConfig: Ext.emptyFn,
    getSuffixConfig: Ext.emptyFn,
    getOverflowCls: function() {
        return '';
    },
    setVertical: function() {
        var me = this,
            layout = me.layout,
            innerCt = layout.innerCt;
        innerCt.removeCls(me.getOverflowCls(layout.oppositeDirection));
        innerCt.addCls(me.getOverflowCls(layout.direction));
    }
});


Ext.define('Ext.util.ClickRepeater', {
    extend: 'Ext.util.Observable',
    
    
    
    
    constructor: function(el, config) {
        var me = this;
        me.el = Ext.get(el);
        me.el.unselectable();
        Ext.apply(me, config);
        me.callParent();
        if (!me.disabled) {
            me.disabled = true;
            me.enable();
        }
        
        if (me.handler) {
            me.on("click", me.handler, me.scope || me);
        }
    },
    
    
    
    
    interval: 20,
    
    delay: 250,
    
    preventDefault: true,
    
    stopDefault: false,
    timer: 0,
    
    enable: function() {
        if (this.disabled) {
            this.el.on('mousedown', this.handleMouseDown, this);
            
            
            if (Ext.isIE8) {
                this.el.on('dblclick', this.handleDblClick, this);
            }
            if (this.preventDefault || this.stopDefault) {
                this.el.on('click', this.eventOptions, this);
            }
        }
        this.disabled = false;
    },
    
    disable: function(
    force) {
        if (force || !this.disabled) {
            clearTimeout(this.timer);
            if (this.pressedCls) {
                this.el.removeCls(this.pressedCls);
            }
            Ext.getDoc().un('mouseup', this.handleMouseUp, this);
            this.el.clearListeners();
        }
        this.disabled = true;
    },
    
    setDisabled: function(disabled) {
        this[disabled ? 'disable' : 'enable']();
    },
    eventOptions: function(e) {
        if (this.preventDefault) {
            e.preventDefault();
        }
        if (this.stopDefault) {
            e.stopEvent();
        }
    },
    
    destroy: function() {
        this.disable(true);
        this.clearListeners();
    },
    handleDblClick: function(e) {
        clearTimeout(this.timer);
        this.el.blur();
        this.fireEvent("mousedown", this, e);
        this.fireEvent("click", this, e);
    },
    
    handleMouseDown: function(e) {
        clearTimeout(this.timer);
        this.el.blur();
        if (this.pressedCls) {
            this.el.addCls(this.pressedCls);
        }
        this.mousedownTime = new Date();
        Ext.getDoc().on("mouseup", this.handleMouseUp, this);
        this.el.on("mouseout", this.handleMouseOut, this);
        this.fireEvent("mousedown", this, e);
        this.fireEvent("click", this, e);
        
        if (this.accelerate) {
            this.delay = 400;
        }
        this.timer = Ext.defer(this.click, this.delay || this.interval, this, [
            e
        ]);
    },
    
    click: function(e) {
        this.fireEvent("click", this, e);
        this.timer = Ext.defer(this.click, this.accelerate ? this.easeOutExpo(Ext.Date.getElapsed(this.mousedownTime), 400, -390, 12000) : this.interval, this, [
            e
        ]);
    },
    easeOutExpo: function(t, b, c, d) {
        return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
    },
    
    handleMouseOut: function() {
        clearTimeout(this.timer);
        if (this.pressedCls) {
            this.el.removeCls(this.pressedCls);
        }
        this.el.on("mouseover", this.handleMouseReturn, this);
    },
    
    handleMouseReturn: function(e) {
        this.el.un("mouseover", this.handleMouseReturn, this);
        if (this.pressedCls) {
            this.el.addCls(this.pressedCls);
        }
        this.click(e);
    },
    
    handleMouseUp: function(e) {
        clearTimeout(this.timer);
        this.el.un("mouseover", this.handleMouseReturn, this);
        this.el.un("mouseout", this.handleMouseOut, this);
        Ext.getDoc().un("mouseup", this.handleMouseUp, this);
        if (this.pressedCls) {
            this.el.removeCls(this.pressedCls);
        }
        this.fireEvent("mouseup", this, e);
    }
});


Ext.define('Ext.layout.container.boxOverflow.Scroller', {
    
    extend: 'Ext.layout.container.boxOverflow.None',
    requires: [
        'Ext.util.ClickRepeater',
        'Ext.Element'
    ],
    alternateClassName: 'Ext.layout.boxOverflow.Scroller',
    alias: [
        'box.overflow.scroller',
        'box.overflow.Scroller'
    ],
    
    mixins: {
        observable: 'Ext.mixin.Observable'
    },
    
    
    animateScroll: false,
    
    scrollIncrement: 20,
    
    wheelIncrement: 10,
    
    scrollRepeatInterval: 60,
    
    scrollDuration: 400,
    
    scrollerCls: Ext.baseCSSPrefix + 'box-scroller',
    beforeSuffix: '-before-scroller',
    afterSuffix: '-after-scroller',
    
    constructor: function(config) {
        var me = this;
        me.mixins.observable.constructor.call(me, config);
        me.scrollPosition = 0;
        me.scrollSize = 0;
    },
    getPrefixConfig: function() {
        return {
            role: 'presentation',
            id: this.layout.owner.id + this.beforeSuffix,
            cls: this.createScrollerCls('beforeX'),
            style: 'display:none'
        };
    },
    getSuffixConfig: function() {
        return {
            role: 'presentation',
            id: this.layout.owner.id + this.afterSuffix,
            cls: this.createScrollerCls('afterX'),
            style: 'display:none'
        };
    },
    createScrollerCls: function(xName) {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            prefix = Ext.baseCSSPrefix,
            type = me.getOwnerType(owner),
            scrollerCls = me.scrollerCls,
            cls = scrollerCls + ' ' + scrollerCls + '-' + layout.names[xName] + ' ' + scrollerCls + '-' + type + ' ' + scrollerCls + '-' + type + '-' + owner.ui;
        if (owner.plain) {
            
            cls += ' ' + scrollerCls + '-plain';
        }
        return cls;
    },
    getOverflowCls: function(direction) {
        return this.scrollerCls + '-body-' + direction;
    },
    beginLayout: function(ownerContext) {
        var layout = this.layout;
        ownerContext.innerCtScrollPos = this.getScrollPosition();
        this.callParent(arguments);
    },
    finishedLayout: function(ownerContext) {
        var me = this,
            plan = ownerContext.state.boxPlan,
            layout = me.layout,
            names = layout.names,
            scrollPos = Math.min(me.getMaxScrollPosition(), ownerContext.innerCtScrollPos),
            lastProps, lastItemContext;
        
        if (plan && plan.tooNarrow) {
            lastProps = ownerContext.childItems[ownerContext.childItems.length - 1].props;
            
            me.scrollSize = lastProps[names.x] + lastProps[names.width];
            me.updateScrollButtons();
        }
        layout.innerCt[names.setScrollLeft](scrollPos);
        this.callParent([
            ownerContext
        ]);
    },
    handleOverflow: function(ownerContext) {
        var me = this,
            names = me.layout.names,
            getWidth = names.getWidth,
            parallelMargins = names.parallelMargins,
            scrollerWidth, targetPaddingWidth, beforeScroller, afterScroller;
        me.showScrollers();
        beforeScroller = me.getBeforeScroller();
        afterScroller = me.getAfterScroller();
        scrollerWidth = beforeScroller[getWidth]() + afterScroller[getWidth]() + beforeScroller.getMargin(parallelMargins) + afterScroller.getMargin(parallelMargins);
        targetPaddingWidth = ownerContext.targetContext.getPaddingInfo()[names.width];
        return {
            reservedSpace: Math.max(scrollerWidth - targetPaddingWidth, 0)
        };
    },
    
    getBeforeScroller: function() {
        var me = this;
        return me._beforeScroller || (me._beforeScroller = me.createScroller(me.beforeSuffix, 'beforeRepeater', 'scrollLeft'));
    },
    
    getAfterScroller: function() {
        var me = this;
        return me._afterScroller || (me._afterScroller = me.createScroller(me.afterSuffix, 'afterRepeater', 'scrollRight'));
    },
    createScroller: function(suffix, repeaterName, scrollHandler) {
        var me = this,
            owner = me.layout.owner,
            scrollerCls = me.scrollerCls,
            scrollerEl;
        scrollerEl = owner.el.getById(owner.id + suffix);
        scrollerEl.addClsOnOver(scrollerCls + '-hover');
        scrollerEl.addClsOnClick(scrollerCls + '-pressed');
        scrollerEl.setVisibilityMode(Ext.Element.DISPLAY);
        me[repeaterName] = new Ext.util.ClickRepeater(scrollerEl,{
            interval: me.scrollRepeatInterval,
            handler: scrollHandler,
            scope: me
        });
        return scrollerEl;
    },
    
    createWheelListener: function() {
        var me = this;
        me.layout.innerCt.on({
            mousewheel: function(e) {
                me.scrollBy(me.getWheelDelta(e) * me.wheelIncrement * -1, false);
            },
            stopEvent: true
        });
    },
    getWheelDelta: function(e) {
        return e.getWheelDelta();
    },
    
    clearOverflow: function() {
        this.hideScrollers();
    },
    
    showScrollers: function() {
        var me = this;
        me.createWheelListener();
        me.getBeforeScroller().show();
        me.getAfterScroller().show();
        me.layout.owner.addClsWithUI(me.layout.direction === 'vertical' ? 'vertical-scroller' : 'scroller');
    },
    
    
    hideScrollers: function() {
        var me = this,
            beforeScroller = me.getBeforeScroller(),
            afterScroller = me.getAfterScroller();
        if (beforeScroller) {
            beforeScroller.hide();
            afterScroller.hide();
            me.layout.owner.removeClsWithUI(me.layout.direction === 'vertical' ? 'vertical-scroller' : 'scroller');
        }
    },
    
    
    destroy: function() {
        var me = this;
        Ext.destroy(me.beforeRepeater, me.afterRepeater, me._beforeScroller, me._afterScroller);
    },
    
    scrollBy: function(delta, animate) {
        this.scrollTo(this.getScrollPosition() + delta, animate);
    },
    
    getScrollAnim: function() {
        return {
            duration: this.scrollDuration,
            callback: this.updateScrollButtons,
            scope: this
        };
    },
    
    updateScrollButtons: function() {
        var me = this,
            beforeScroller = me.getBeforeScroller(),
            afterScroller = me.getAfterScroller(),
            disabledCls;
        if (!beforeScroller || !afterScroller) {
            return;
        }
        disabledCls = me.scrollerCls + '-disabled';
        beforeScroller[me.atExtremeBefore() ? 'addCls' : 'removeCls'](disabledCls);
        afterScroller[me.atExtremeAfter() ? 'addCls' : 'removeCls'](disabledCls);
        me.scrolling = false;
    },
    
    scrollLeft: function() {
        this.scrollBy(-this.scrollIncrement, false);
    },
    
    scrollRight: function() {
        this.scrollBy(this.scrollIncrement, false);
    },
    
    getScrollPosition: function() {
        var me = this,
            layout = me.layout,
            result;
        
        
        if (isNaN(me.scrollPosition)) {
            result = layout.innerCt[layout.names.getScrollLeft]();
        } else {
            result = me.scrollPosition;
        }
        return result;
    },
    
    getMaxScrollPosition: function() {
        var me = this,
            layout = me.layout,
            maxScrollPos = me.scrollSize - layout.innerCt[layout.names.getWidth]();
        return (maxScrollPos < 0) ? 0 : maxScrollPos;
    },
    
    atExtremeBefore: function() {
        return !this.getScrollPosition();
    },
    
    atExtremeAfter: function() {
        return this.getScrollPosition() >= this.getMaxScrollPosition();
    },
    
    setVertical: function() {
        var me = this,
            beforeScroller = me.getBeforeScroller(),
            afterScroller = me.getAfterScroller(),
            names = me.layout.names,
            scrollerCls = me.scrollerCls;
        beforeScroller.removeCls(scrollerCls + '-' + names.beforeY);
        afterScroller.removeCls(scrollerCls + '-' + names.afterY);
        beforeScroller.addCls(scrollerCls + '-' + names.beforeX);
        afterScroller.addCls(scrollerCls + '-' + names.afterX);
        this.callParent();
    },
    
    scrollTo: function(position, animate) {
        var me = this,
            layout = me.layout,
            names = layout.names,
            oldPosition = me.getScrollPosition(),
            newPosition = Ext.Number.constrain(position, 0, me.getMaxScrollPosition());
        if (newPosition != oldPosition && !me.scrolling) {
            me.scrollPosition = NaN;
            if (animate === undefined) {
                animate = me.animateScroll;
            }
            layout.innerCt[names.scrollTo](names.beforeScrollX, newPosition, animate ? me.getScrollAnim() : false);
            if (animate) {
                me.scrolling = true;
            } else {
                me.updateScrollButtons();
            }
            me.fireEvent('scroll', me, newPosition, animate ? me.getScrollAnim() : false);
        }
    },
    
    scrollToItem: function(item, animate) {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            names = layout.names,
            innerCt = layout.innerCt,
            visibility, box, newPos;
        item = me.getItem(item);
        if (item !== undefined) {
            if (item == owner.items.first()) {
                newPos = 0;
            } else if (item === owner.items.last()) {
                newPos = me.getMaxScrollPosition();
            } else {
                visibility = me.getItemVisibility(item);
                if (!visibility.fullyVisible) {
                    box = item.getBox(false, true);
                    newPos = box[names.x];
                    if (visibility.hiddenEnd) {
                        newPos -= (innerCt[names.getWidth]() - box[names.width]);
                    }
                }
            }
            if (newPos !== undefined) {
                me.scrollTo(newPos, animate);
            }
        }
    },
    
    getItemVisibility: function(item) {
        var me = this,
            box = me.getItem(item).getBox(true, true),
            layout = me.layout,
            names = layout.names,
            itemStart = box[names.x],
            itemEnd = itemStart + box[names.width],
            scrollStart = me.getScrollPosition(),
            scrollEnd = scrollStart + layout.innerCt[names.getWidth]();
        return {
            hiddenStart: itemStart < scrollStart,
            hiddenEnd: itemEnd > scrollEnd,
            fullyVisible: itemStart > scrollStart && itemEnd < scrollEnd
        };
    }
});



Ext.define('Ext.dd.DragDropManager', {
    singleton: true,
    requires: [
        'Ext.util.Region'
    ],
    uses: [
        'Ext.tip.QuickTipManager'
    ],
    
    alternateClassName: [
        'Ext.dd.DragDropMgr',
        'Ext.dd.DDM'
    ],
    
    ids: {},
    
    handleIds: {},
    
    dragCurrent: null,
    
    dragOvers: {},
    
    deltaX: 0,
    
    deltaY: 0,
    
    preventDefault: true,
    
    stopPropagation: true,
    
    initialized: false,
    
    locked: false,
    
    init: function() {
        this.initialized = true;
    },
    
    POINT: 0,
    
    INTERSECT: 1,
    
    mode: 0,
    
    notifyOccluded: false,
    
    dragCls: Ext.baseCSSPrefix + 'dd-drag-current',
    
    _execOnAll: function(sMethod, args) {
        var ids = this.ids,
            i, j, oDD, item;
        for (i in ids) {
            if (ids.hasOwnProperty(i)) {
                item = ids[i];
                for (j in item) {
                    if (item.hasOwnProperty(j)) {
                        oDD = item[j];
                        if (!this.isTypeOfDD(oDD)) {
                            
                            continue;
                        }
                        oDD[sMethod].apply(oDD, args);
                    }
                }
            }
        }
    },
    
    addListeners: function() {
        var me = this;
        me.init();
        Ext.getDoc().on({
            
            mouseup: me.handleMouseUp,
            
            
            
            mousemove: {
                fn: me.handleMouseMove,
                capture: false
            },
            dragstart: me.preventDrag,
            drag: me.preventDrag,
            dragend: me.preventDrag,
            capture: true,
            scope: me
        });
        Ext.getWin().on({
            unload: me._onUnload,
            resize: me._onResize,
            scope: me
        });
    },
    
    
    preventDrag: function(e) {
        if (this.isMouseDown) {
            e.stopPropagation();
        }
    },
    
    _onResize: function(e) {
        this._execOnAll("resetConstraints", []);
    },
    
    lock: function() {
        this.locked = true;
    },
    
    unlock: function() {
        this.locked = false;
    },
    
    isLocked: function() {
        return this.locked;
    },
    
    locationCache: {},
    
    useCache: true,
    
    clickPixelThresh: 8,
    
    dragThreshMet: false,
    
    clickTimeout: null,
    
    startX: 0,
    
    startY: 0,
    
    regDragDrop: function(oDD, sGroup) {
        if (!this.initialized) {
            this.init();
        }
        if (!this.ids[sGroup]) {
            this.ids[sGroup] = {};
        }
        this.ids[sGroup][oDD.id] = oDD;
    },
    
    removeDDFromGroup: function(oDD, sGroup) {
        if (!this.ids[sGroup]) {
            this.ids[sGroup] = {};
        }
        var obj = this.ids[sGroup];
        if (obj && obj[oDD.id]) {
            delete obj[oDD.id];
        }
    },
    
    _remove: function(oDD, clearGroup) {
        var me = this,
            ids = me.ids,
            groups = oDD.groups,
            g;
        
        
        if (me.clearingAll) {
            return;
        }
        if (me.dragCurrent === oDD) {
            me.dragCurrent = null;
        }
        for (g in groups) {
            if (groups.hasOwnProperty(g)) {
                if (clearGroup) {
                    delete ids[g];
                } else if (ids[g]) {
                    delete ids[g][oDD.id];
                }
            }
        }
        delete me.handleIds[oDD.id];
    },
    
    regHandle: function(sDDId, sHandleId) {
        if (!this.handleIds[sDDId]) {
            this.handleIds[sDDId] = {};
        }
        this.handleIds[sDDId][sHandleId] = sHandleId;
    },
    
    isDragDrop: function(id) {
        return (this.getDDById(id)) ? true : false;
    },
    
    getRelated: function(p_oDD, bTargetsOnly) {
        var oDDs = [],
            i, j, dd;
        for (i in p_oDD.groups) {
            for (j in this.ids[i]) {
                dd = this.ids[i][j];
                if (!this.isTypeOfDD(dd)) {
                    
                    continue;
                }
                if (!bTargetsOnly || dd.isTarget) {
                    oDDs[oDDs.length] = dd;
                }
            }
        }
        return oDDs;
    },
    
    isLegalTarget: function(oDD, oTargetDD) {
        var targets = this.getRelated(oDD, true),
            i, len;
        for (i = 0 , len = targets.length; i < len; ++i) {
            if (targets[i].id == oTargetDD.id) {
                return true;
            }
        }
        return false;
    },
    
    isTypeOfDD: function(oDD) {
        return (oDD && oDD.__ygDragDrop);
    },
    
    isHandle: function(sDDId, sHandleId) {
        return (this.handleIds[sDDId] && this.handleIds[sDDId][sHandleId]);
    },
    
    getDDById: function(id, force) {
        var i, dd;
        for (i in this.ids) {
            dd = this.ids[i][id];
            if (dd instanceof Ext.dd.DDTarget || force) {
                return dd;
            }
        }
        return null;
    },
    
    handleMouseDown: function(e, oDD) {
        var me = this,
            xy, el;
        me.isMouseDown = true;
        if (Ext.quickTipsActive) {
            Ext.tip.QuickTipManager.ddDisable();
        }
        me.currentPoint = e.getPoint();
        if (me.dragCurrent) {
            
            
            me.handleMouseUp(e);
        }
        me.mousedownEvent = e;
        me.currentTarget = e.getTarget();
        me.dragCurrent = oDD;
        el = oDD.getEl();
        
        
        
        
        Ext.fly(el).setCapture();
        
        xy = e.getXY();
        me.startX = xy[0];
        me.startY = xy[1];
        
        me.offsetX = me.offsetY = 0;
        me.deltaX = me.startX - el.offsetLeft;
        me.deltaY = me.startY - el.offsetTop;
        me.dragThreshMet = false;
    },
    
    startDrag: function(x, y) {
        var me = this,
            current = me.dragCurrent,
            dragEl;
        clearTimeout(me.clickTimeout);
        if (current) {
            current.b4StartDrag(x, y);
            current.startDrag(x, y);
            dragEl = current.getDragEl();
            
            if (dragEl) {
                Ext.fly(dragEl).addCls(me.dragCls);
            }
        }
        me.dragThreshMet = true;
    },
    
    handleMouseUp: function(e) {
        var me = this;
        me.isMouseDown = false;
        if (Ext.quickTipsActive) {
            Ext.tip.QuickTipManager.ddEnable();
        }
        if (!me.dragCurrent) {
            return;
        }
        
        if (Ext.isIE && document.releaseCapture) {
            document.releaseCapture();
        }
        clearTimeout(me.clickTimeout);
        if (me.dragThreshMet) {
            me.fireEvents(e, true);
        }
        me.stopDrag(e);
        me.stopEvent(e);
    },
    
    stopEvent: function(e) {
        if (this.stopPropagation) {
            e.stopPropagation();
        }
        if (this.preventDefault) {
            e.preventDefault();
        }
    },
    
    stopDrag: function(e) {
        var me = this,
            current = me.dragCurrent,
            dragEl;
        
        if (current) {
            if (me.dragThreshMet) {
                
                dragEl = current.getDragEl();
                if (dragEl) {
                    Ext.fly(dragEl).removeCls(me.dragCls);
                }
                current.b4EndDrag(e);
                current.endDrag(e);
            }
            me.dragCurrent.onMouseUp(e);
        }
        me.dragCurrent = null;
        me.dragOvers = {};
    },
    
    handleMouseMove: function(e) {
        var me = this,
            current = me.dragCurrent,
            point = me.currentPoint = e.getPoint(),
            currentX = point.x,
            currentY = point.y,
            diffX, diffY;
        me.offsetX = currentX - me.startX;
        me.offsetY = currentY - me.startY;
        if (!current) {
            return true;
        }
        if (!me.dragThreshMet) {
            diffX = Math.abs(me.offsetX);
            diffY = Math.abs(me.offsetY);
            if (diffX > me.clickPixelThresh || diffY > me.clickPixelThresh) {
                me.startDrag(me.startX, me.startY);
            }
        }
        if (me.dragThreshMet) {
            current.b4Drag(e);
            current.onDrag(e);
            if (!current.moveOnly) {
                me.fireEvents(e, false);
            }
        }
        me.stopEvent(e);
        return true;
    },
    
    fireEvents: function(e, isDrop) {
        var me = this,
            isTouch = Ext.supports.Touch,
            dragCurrent = me.dragCurrent,
            mousePoint = me.currentPoint,
            currentX = mousePoint.x,
            currentY = mousePoint.y,
            dragEl, oldDragElTop, overTarget, overTargetEl,
            allTargets = [],
            oldOvers = [],
            
            outEvts = [],
            overEvts = [],
            dropEvts = [],
            enterEvts = [],
            needsSort, i, len, sGroup,
            zoom = isTouch ? document.documentElement.clientWidth / window.innerWidth : 1,
            overDragEl;
        
        
        if (!dragCurrent || dragCurrent.isLocked()) {
            return;
        }
        
        
        
        
        
        
        
        
        
        overDragEl = !(dragCurrent.deltaX < 0 || dragCurrent.deltaY < 0);
        if (isTouch || (!me.notifyOccluded && (!Ext.supports.CSSPointerEvents || Ext.isIE10m || Ext.isOpera) && overDragEl)) {
            dragEl = dragCurrent.getDragEl();
            
            
            if (overDragEl) {
                dragEl.style.visibility = 'hidden';
            }
            e.target = document.elementFromPoint(currentX / zoom, currentY / zoom);
            if (overDragEl) {
                dragEl.style.visibility = 'visible';
            }
        }
        
        
        for (i in me.dragOvers) {
            overTarget = me.dragOvers[i];
            delete me.dragOvers[i];
            
            if (!me.isTypeOfDD(overTarget) || overTarget.isDestroyed) {
                
                continue;
            }
            
            if (me.notifyOccluded) {
                if (!this.isOverTarget(mousePoint, overTarget, me.mode)) {
                    outEvts.push(overTarget);
                }
            } else 
            {
                if (!e.within(overTarget.getEl())) {
                    outEvts.push(overTarget);
                }
            }
            oldOvers[i] = true;
        }
        
        
        
        for (sGroup in dragCurrent.groups) {
            if ("string" != typeof sGroup) {
                
                continue;
            }
            
            for (i in me.ids[sGroup]) {
                overTarget = me.ids[sGroup][i];
                
                
                
                
                
                
                if (me.isTypeOfDD(overTarget) && (overTargetEl = overTarget.getEl()) && (overTarget.isTarget) && (!overTarget.isLocked()) && (Ext.fly(overTargetEl).isVisible(true)) && ((overTarget != dragCurrent) || (dragCurrent.ignoreSelf === false))) {
                    
                    if (me.notifyOccluded) {
                        
                        if ((overTarget.zIndex = me.getZIndex(overTargetEl)) !== -1) {
                            needsSort = true;
                        }
                        allTargets.push(overTarget);
                    } else 
                    {
                        if (e.within(overTarget.getEl())) {
                            allTargets.push(overTarget);
                            break;
                        }
                    }
                }
            }
        }
        
        if (needsSort) {
            Ext.Array.sort(allTargets, me.byZIndex);
        }
        
        
        for (i = 0 , len = allTargets.length; i < len; i++) {
            overTarget = allTargets[i];
            
            if (me.isOverTarget(mousePoint, overTarget, me.mode)) {
                
                if (isDrop) {
                    dropEvts.push(overTarget);
                } else 
                {
                    
                    if (!oldOvers[overTarget.id]) {
                        enterEvts.push(overTarget);
                    } else 
                    {
                        overEvts.push(overTarget);
                    }
                    me.dragOvers[overTarget.id] = overTarget;
                }
                
                if (!me.notifyOccluded) {
                    break;
                }
            }
        }
        if (me.mode) {
            if (outEvts.length) {
                dragCurrent.b4DragOut(e, outEvts);
                dragCurrent.onDragOut(e, outEvts);
            }
            if (enterEvts.length) {
                dragCurrent.onDragEnter(e, enterEvts);
            }
            if (overEvts.length) {
                dragCurrent.b4DragOver(e, overEvts);
                dragCurrent.onDragOver(e, overEvts);
            }
            if (dropEvts.length) {
                dragCurrent.b4DragDrop(e, dropEvts);
                dragCurrent.onDragDrop(e, dropEvts);
            }
        } else {
            
            for (i = 0 , len = outEvts.length; i < len; ++i) {
                dragCurrent.b4DragOut(e, outEvts[i].id);
                dragCurrent.onDragOut(e, outEvts[i].id);
            }
            
            for (i = 0 , len = enterEvts.length; i < len; ++i) {
                
                dragCurrent.onDragEnter(e, enterEvts[i].id);
            }
            
            for (i = 0 , len = overEvts.length; i < len; ++i) {
                dragCurrent.b4DragOver(e, overEvts[i].id);
                dragCurrent.onDragOver(e, overEvts[i].id);
            }
            
            for (i = 0 , len = dropEvts.length; i < len; ++i) {
                dragCurrent.b4DragDrop(e, dropEvts[i].id);
                dragCurrent.onDragDrop(e, dropEvts[i].id);
            }
        }
        
        if (isDrop && !dropEvts.length) {
            dragCurrent.onInvalidDrop(e);
        }
    },
    
    getZIndex: function(element) {
        var body = document.body,
            z,
            zIndex = -1;
        element = Ext.getDom(element);
        while (element !== body) {
            if (!isNaN(z = Number(Ext.fly(element).getStyle('zIndex')))) {
                zIndex = z;
            }
            element = element.parentNode;
        }
        return zIndex;
    },
    
    byZIndex: function(d1, d2) {
        return d1.zIndex < d2.zIndex;
    },
    
    getBestMatch: function(dds) {
        var winner = null,
            len = dds.length,
            i, dd;
        
        
        
        
        
        if (len == 1) {
            winner = dds[0];
        } else {
            
            for (i = 0; i < len; ++i) {
                dd = dds[i];
                
                
                
                if (dd.cursorIsOver) {
                    winner = dd;
                    break;
                } else 
                {
                    if (!winner || winner.overlap.getArea() < dd.overlap.getArea()) {
                        winner = dd;
                    }
                }
            }
        }
        return winner;
    },
    
    refreshCache: function(groups) {
        var sGroup, i, oDD, loc;
        for (sGroup in groups) {
            if ("string" != typeof sGroup) {
                
                continue;
            }
            for (i in this.ids[sGroup]) {
                oDD = this.ids[sGroup][i];
                if (this.isTypeOfDD(oDD)) {
                    
                    loc = this.getLocation(oDD);
                    if (loc) {
                        this.locationCache[oDD.id] = loc;
                    } else {
                        delete this.locationCache[oDD.id];
                    }
                }
            }
        }
    },
    
    
    
    
    verifyEl: function(el) {
        if (el) {
            var parent;
            if (Ext.isIE) {
                try {
                    parent = el.offsetParent;
                } catch (e) {}
            } else {
                parent = el.offsetParent;
            }
            if (parent) {
                return true;
            }
        }
        return false;
    },
    
    getLocation: function(oDD) {
        if (!this.isTypeOfDD(oDD)) {
            return null;
        }
        
        
        if (oDD.getRegion) {
            return oDD.getRegion();
        }
        var el = oDD.getEl(),
            pos, x1, x2, y1, y2, t, r, b, l;
        try {
            pos = Ext.fly(el).getXY();
        } catch (e) {}
        if (!pos) {
            return null;
        }
        x1 = pos[0];
        x2 = x1 + el.offsetWidth;
        y1 = pos[1];
        y2 = y1 + el.offsetHeight;
        t = y1 - oDD.padding[0];
        r = x2 + oDD.padding[1];
        b = y2 + oDD.padding[2];
        l = x1 - oDD.padding[3];
        return new Ext.util.Region(t,r,b,l);
    },
    
    isOverTarget: function(pt, oTarget, intersect) {
        
        var loc = this.locationCache[oTarget.id],
            dc, pos, el, curRegion, overlap;
        if (!loc || !this.useCache) {
            loc = this.getLocation(oTarget);
            this.locationCache[oTarget.id] = loc;
        }
        if (!loc) {
            return false;
        }
        oTarget.cursorIsOver = loc.contains(pt);
        
        
        
        
        
        dc = this.dragCurrent;
        if (!dc || !dc.getTargetCoord || (!intersect && !dc.constrainX && !dc.constrainY)) {
            return oTarget.cursorIsOver;
        }
        oTarget.overlap = null;
        
        
        
        
        pos = dc.getTargetCoord(pt.x, pt.y);
        el = dc.getDragEl();
        curRegion = new Ext.util.Region(pos.y,pos.x + el.offsetWidth,pos.y + el.offsetHeight,pos.x);
        overlap = curRegion.intersect(loc);
        if (overlap) {
            oTarget.overlap = overlap;
            return (intersect) ? true : oTarget.cursorIsOver;
        } else {
            return false;
        }
    },
    
    _onUnload: function(e, me) {
        Ext.dd.DragDropManager.unregAll();
    },
    
    unregAll: function() {
        var me = this,
            cache = me.elementCache,
            i;
        if (me.dragCurrent) {
            me.stopDrag();
            me.dragCurrent = null;
        }
        me.clearingAll = true;
        me._execOnAll("unreg", []);
        delete me.clearingAll;
        for (i in cache) {
            delete cache[i];
        }
        me.elementCache = {};
        me.ids = {};
        me.handleIds = {};
    },
    
    elementCache: {},
    
    getElWrapper: function(id) {
        var oWrapper = this.elementCache[id];
        if (!oWrapper || !oWrapper.el) {
            oWrapper = this.elementCache[id] = new this.ElementWrapper(Ext.getDom(id));
        }
        return oWrapper;
    },
    
    getElement: function(id) {
        return Ext.getDom(id);
    },
    
    getCss: function(id) {
        var el = Ext.getDom(id);
        return (el) ? el.style : null;
    },
    
    ElementWrapper: function(el) {
        
        this.el = el || null;
        
        this.id = this.el && el.id;
        
        this.css = this.el && el.style;
    },
    
    
    
    getPosX: function(el) {
        return Ext.fly(el).getX();
    },
    
    getPosY: function(el) {
        return Ext.fly(el).getY();
    },
    
    swapNode: function(n1, n2) {
        if (n1.swapNode) {
            n1.swapNode(n2);
        } else {
            var p = n2.parentNode,
                s = n2.nextSibling;
            if (s == n1) {
                p.insertBefore(n1, n2);
            } else if (n2 == n1.nextSibling) {
                p.insertBefore(n2, n1);
            } else {
                n1.parentNode.replaceChild(n2, n1);
                p.insertBefore(n1, s);
            }
        }
    },
    
    getScroll: function() {
        var doc = window.document,
            docEl = doc.documentElement,
            body = doc.body,
            top = 0,
            left = 0;
        if (docEl && (docEl.scrollTop || docEl.scrollLeft)) {
            top = docEl.scrollTop;
            left = docEl.scrollLeft;
        } else if (body) {
            top = body.scrollTop;
            left = body.scrollLeft;
        }
        return {
            top: top,
            left: left
        };
    },
    
    getStyle: function(el, styleProp) {
        return Ext.fly(el).getStyle(styleProp);
    },
    
    getScrollTop: function() {
        return this.getScroll().top;
    },
    
    getScrollLeft: function() {
        return this.getScroll().left;
    },
    
    moveToEl: function(moveEl, targetEl) {
        var aCoord = Ext.fly(targetEl).getXY();
        Ext.fly(moveEl).setXY(aCoord);
    },
    
    numericSort: function(a, b) {
        return (a - b);
    },
    
    handleWasClicked: function(node, id) {
        if (this.isHandle(id, node.id)) {
            return true;
        } else {
            
            var p = node.parentNode;
            while (p) {
                if (this.isHandle(id, p.id)) {
                    return true;
                } else {
                    p = p.parentNode;
                }
            }
        }
        return false;
    }
}, function(DragDropManager) {
    Ext.onReady(function() {
        DragDropManager.addListeners();
    });
});


Ext.define('Ext.resizer.Splitter', {
    extend: 'Ext.Component',
    requires: [
        'Ext.XTemplate'
    ],
    uses: [
        'Ext.resizer.SplitterTracker'
    ],
    xtype: 'splitter',
    childEls: [
        'collapseEl'
    ],
    renderTpl: [
        '<tpl if="collapsible===true">',
        '<div id="{id}-collapseEl" data-ref="collapseEl" role="presentation" class="',
        Ext.baseCSSPrefix,
        'collapse-el ',
        Ext.baseCSSPrefix,
        'layout-split-{collapseDir}{childElCls}">&#160;',
        '</div>',
        '</tpl>'
    ],
    isSplitter: true,
    baseCls: Ext.baseCSSPrefix + 'splitter',
    collapsedClsInternal: Ext.baseCSSPrefix + 'splitter-collapsed',
    
    canResize: true,
    
    collapsible: null,
    
    
    collapseOnDblClick: true,
    
    defaultSplitMin: 40,
    
    defaultSplitMax: 1000,
    
    
    collapseTarget: 'next',
    
    horizontal: false,
    vertical: false,
    
    size: 5,
    
    tracker: null,
    ariaRole: 'separator',
    
    getTrackerConfig: function() {
        return Ext.apply({
            xclass: 'Ext.resizer.SplitterTracker',
            el: this.el,
            splitter: this
        }, this.tracker);
    },
    beforeRender: function() {
        var me = this,
            target = me.getCollapseTarget(),
            collapsible = me.collapsible;
        me.callParent();
        if (target.collapsed) {
            me.addCls(me.collapsedClsInternal);
        }
        if (!me.canResize) {
            me.addCls(me.baseCls + '-noresize');
        }
        Ext.applyIf(me.renderData, {
            collapseDir: me.getCollapseDirection(),
            collapsible: (collapsible !== null) ? collapsible : target.collapsible
        });
        me.protoEl.unselectable();
    },
    onRender: function() {
        var me = this,
            collapseEl, cfg;
        me.callParent(arguments);
        
        if (me.performCollapse !== false) {
            if (me.renderData.collapsible) {
                me.mon(me.collapseEl, 'click', me.toggleTargetCmp, me);
            }
            if (me.collapseOnDblClick) {
                me.mon(me.el, 'dblclick', me.toggleTargetCmp, me);
            }
        }
        
        me.mon(me.getCollapseTarget(), {
            collapse: me.onTargetCollapse,
            expand: me.onTargetExpand,
            beforeexpand: me.onBeforeTargetExpand,
            beforecollapse: me.onBeforeTargetCollapse,
            scope: me
        });
        if (me.canResize) {
            me.tracker = Ext.create(me.getTrackerConfig());
            
            me.relayEvents(me.tracker, [
                'beforedragstart',
                'dragstart',
                'dragend'
            ]);
        }
        collapseEl = me.collapseEl;
        if (collapseEl) {
            collapseEl.lastCollapseDirCls = me.collapseDirProps[me.collapseDirection].cls;
        }
    },
    getCollapseDirection: function() {
        var me = this,
            dir = me.collapseDirection,
            collapseTarget, idx, items, type;
        if (!dir) {
            collapseTarget = me.collapseTarget;
            if (collapseTarget.isComponent) {
                dir = collapseTarget.collapseDirection;
            }
            if (!dir) {
                
                
                
                
                
                
                
                type = me.ownerCt.layout.type;
                if (collapseTarget.isComponent) {
                    items = me.ownerCt.items;
                    idx = Number(items.indexOf(collapseTarget) === items.indexOf(me) - 1) << 1 | Number(type === 'hbox');
                } else {
                    idx = Number(me.collapseTarget === 'prev') << 1 | Number(type === 'hbox');
                }
                
                dir = [
                    'bottom',
                    'right',
                    'top',
                    'left'
                ][idx];
            }
            me.collapseDirection = dir;
        }
        me.setOrientation((dir === 'top' || dir === 'bottom') ? 'horizontal' : 'vertical');
        return dir;
    },
    getCollapseTarget: function() {
        var me = this;
        return me.collapseTarget.isComponent ? me.collapseTarget : me.collapseTarget === 'prev' ? me.previousSibling() : me.nextSibling();
    },
    setCollapseEl: function(display) {
        var el = this.collapseEl;
        if (el) {
            el.setDisplayed(display);
        }
    },
    onBeforeTargetExpand: function(target) {
        this.setCollapseEl('none');
    },
    onBeforeTargetCollapse: function() {
        this.setCollapseEl('none');
    },
    onTargetCollapse: function(target) {
        this.el.addCls(this.collapsedClsInternal + ' ' + (this.collapsedCls || ''));
        this.setCollapseEl('');
    },
    onTargetExpand: function(target) {
        this.el.removeCls(this.collapsedClsInternal + ' ' + (this.collapsedCls || ''));
        this.setCollapseEl('');
    },
    collapseDirProps: {
        top: {
            cls: Ext.baseCSSPrefix + 'layout-split-top'
        },
        right: {
            cls: Ext.baseCSSPrefix + 'layout-split-right'
        },
        bottom: {
            cls: Ext.baseCSSPrefix + 'layout-split-bottom'
        },
        left: {
            cls: Ext.baseCSSPrefix + 'layout-split-left'
        }
    },
    orientationProps: {
        horizontal: {
            opposite: 'vertical',
            fixedAxis: 'height',
            stretchedAxis: 'width'
        },
        vertical: {
            opposite: 'horizontal',
            fixedAxis: 'width',
            stretchedAxis: 'height'
        }
    },
    applyCollapseDirection: function() {
        var me = this,
            collapseEl = me.collapseEl,
            collapseDirProps = me.collapseDirProps[me.collapseDirection],
            cls;
        if (collapseEl) {
            cls = collapseEl.lastCollapseDirCls;
            if (cls) {
                collapseEl.removeCls(cls);
            }
            collapseEl.addCls(collapseEl.lastCollapseDirCls = collapseDirProps.cls);
        }
    },
    applyOrientation: function() {
        var me = this,
            orientation = me.orientation,
            orientationProps = me.orientationProps[orientation],
            defaultSize = me.size,
            fixedSizeProp = orientationProps.fixedAxis,
            stretchSizeProp = orientationProps.stretchedAxis,
            cls = me.baseCls + '-';
        me[orientation] = true;
        me[orientationProps.opposite] = false;
        if (!me.hasOwnProperty(fixedSizeProp) || me[fixedSizeProp] === '100%') {
            me[fixedSizeProp] = defaultSize;
        }
        if (!me.hasOwnProperty(stretchSizeProp) || me[stretchSizeProp] === defaultSize) {
            me[stretchSizeProp] = '100%';
        }
        me.removeCls(cls + orientationProps.opposite);
        me.addCls(cls + orientation);
    },
    setOrientation: function(orientation) {
        var me = this;
        if (me.orientation !== orientation) {
            me.orientation = orientation;
            me.applyOrientation();
        }
    },
    updateOrientation: function() {
        delete this.collapseDirection;
        
        this.getCollapseDirection();
        this.applyCollapseDirection();
    },
    toggleTargetCmp: function(e, t) {
        var cmp = this.getCollapseTarget(),
            placeholder = cmp.placeholder,
            toggle;
        
        if (Ext.isFunction(cmp.expand) && Ext.isFunction(cmp.collapse)) {
            if (placeholder && !placeholder.hidden) {
                toggle = true;
            } else {
                toggle = !cmp.hidden;
            }
            if (toggle) {
                if (cmp.collapsed) {
                    cmp.expand();
                } else if (cmp.collapseDirection) {
                    cmp.collapse();
                } else {
                    cmp.collapse(this.renderData.collapseDir);
                }
            }
        }
    },
    
    setSize: function() {
        var me = this;
        me.callParent(arguments);
        if (Ext.isIE && me.el) {
            me.el.repaint();
        }
    },
    beforeDestroy: function() {
        Ext.destroy(this.tracker);
        this.callParent();
    }
});


Ext.define('Ext.layout.container.Box', {
    extend: 'Ext.layout.container.Container',
    alias: 'layout.box',
    alternateClassName: 'Ext.layout.BoxLayout',
    requires: [
        'Ext.layout.container.boxOverflow.None',
        'Ext.layout.container.boxOverflow.Scroller',
        'Ext.util.Format',
        'Ext.dd.DragDropManager',
        'Ext.resizer.Splitter'
    ],
    type: 'box',
    config: {
        
        align: 'begin',
        
        
        constrainAlign: false,
        
        enableSplitters: true,
        
        
        overflowHandler: {
            $value: null,
            merge: function(newValue, oldValue) {
                if (typeof newValue == 'string') {
                    newValue = {
                        type: newValue
                    };
                }
                return Ext.merge(oldValue ? Ext.Object.chain(oldValue) : {}, newValue);
            }
        },
        
        padding: 0,
        
        pack: 'start',
        
        stretchMaxPartner: undefined,
        
        vertical: false,
        
        alignRoundingMethod: 'round'
    },
    
    itemCls: Ext.baseCSSPrefix + 'box-item',
    targetCls: Ext.baseCSSPrefix + 'box-layout-ct',
    targetElCls: Ext.baseCSSPrefix + 'box-target',
    innerCls: Ext.baseCSSPrefix + 'box-inner',
    manageMargins: true,
    createsInnerCt: true,
    childEls: [
        'innerCt',
        'targetEl'
    ],
    renderTpl: [
        '{%var oc,l=values.$comp.layout,oh=l.overflowHandler;' + 'if (oh && oh.getPrefixConfig!==Ext.emptyFn) {' + 'if(oc=oh.getPrefixConfig())dh.generateMarkup(oc, out)' + '}%}' + '<div id="{ownerId}-innerCt" data-ref="innerCt" role="presentation" class="{[l.innerCls]}' + '{[oh ? (" " + oh.getOverflowCls(l.direction)) : ""]}">' + '<div id="{ownerId}-targetEl" data-ref="targetEl" class="{targetElCls}" role="presentation">' + '{%this.renderBody(out, values)%}' + '</div>' + '</div>' + '{%if (oh && oh.getSuffixConfig!==Ext.emptyFn) {' + 'if(oc=oh.getSuffixConfig())dh.generateMarkup(oc, out)' + '}%}',
        {
            disableFormats: true,
            definitions: 'var dh=Ext.DomHelper;'
        }
    ],
    constructor: function(config) {
        var me = this,
            type;
        me.callParent(arguments);
        me.setVertical(me.vertical);
        
        me.flexSortFn = me.flexSort.bind(me);
        type = typeof me.padding;
        if (type === 'string' || type === 'number') {
            me.padding = Ext.util.Format.parseBox(me.padding);
            me.padding.height = me.padding.top + me.padding.bottom;
            me.padding.width = me.padding.left + me.padding.right;
        }
    },
    _beginRe: /^(?:begin|left|top)$/,
    _centerRe: /^(?:center|middle)$/,
    _endRe: /^(?:end|right|bottom)$/,
    
    
    _percentageRe: /^\s*(\d+(?:\.\d*)?)\s*[%]\s*$/,
    getItemSizePolicy: function(item, ownerSizeModel) {
        var me = this,
            policy = me.sizePolicy,
            align = me.align,
            flex = item.flex,
            key = align,
            names = me.names,
            heightName = names.height,
            widthName = names.width,
            width = item[widthName],
            height = item[heightName],
            percentageRe = me._percentageRe,
            percentageWidth = percentageRe.test(width),
            isStretch = (align === 'stretch'),
            isStretchMax = (align === 'stretchmax'),
            constrain = me.constrainAlign;
        
        if (!ownerSizeModel && (isStretch || flex || percentageWidth || (constrain && !isStretchMax))) {
            ownerSizeModel = me.owner.getSizeModel();
        }
        if (isStretch) {
            
            
            if (!percentageRe.test(height) && ownerSizeModel[heightName].shrinkWrap) {
                key = 'stretchmax';
            }
        }
        
        
        
        else if (!isStretchMax) {
            if (percentageRe.test(height)) {
                
                
                key = 'stretch';
            } else if (constrain && !ownerSizeModel[heightName].shrinkWrap) {
                
                
                key = 'stretchmax';
            } else {
                key = '';
            }
        }
        if (flex || percentageWidth) {
            
            
            if (!ownerSizeModel[widthName].shrinkWrap) {
                policy = policy.flex;
            }
        }
        
        return policy[key];
    },
    flexSort: function(a, b) {
        
        
        
        
        
        var maxWidthName = this.names.maxWidth,
            minWidthName = this.names.minWidth,
            infiniteValue = Infinity,
            aTarget = a.target,
            bTarget = b.target,
            aFlex = aTarget.flex,
            bFlex = bTarget.flex,
            result = 0,
            aMin, bMin, aMax, bMax, hasMin, hasMax;
        aMax = aTarget[maxWidthName] || infiniteValue;
        bMax = bTarget[maxWidthName] || infiniteValue;
        aMin = aTarget[minWidthName] || 0;
        bMin = bTarget[minWidthName] || 0;
        hasMin = isFinite(aMin) || isFinite(bMin);
        hasMax = isFinite(aMax) || isFinite(bMax);
        if (hasMin || hasMax) {
            if (hasMax) {
                result = aMax - bMax;
            }
            
            
            
            if (result === 0 && hasMin) {
                result = bMin - aMin;
            }
            
            if (result === 0) {
                if (hasMax) {
                    result = bFlex - aFlex;
                } else {
                    result = aFlex - bFlex;
                }
            }
        }
        return result;
    },
    isItemBoxParent: function(itemContext) {
        return true;
    },
    isItemShrinkWrap: function(item) {
        return true;
    },
    roundFlex: function(width) {
        return Math.floor(width);
    },
    
    beginCollapse: function(child) {
        var me = this;
        if (me.direction === 'vertical' && child.collapsedVertical()) {
            child.collapseMemento.capture([
                'flex'
            ]);
            delete child.flex;
        } else if (me.direction === 'horizontal' && child.collapsedHorizontal()) {
            child.collapseMemento.capture([
                'flex'
            ]);
            delete child.flex;
        }
    },
    
    beginExpand: function(child) {
        
        child.collapseMemento.restore([
            'flex'
        ]);
    },
    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            smp = owner.stretchMaxPartner,
            style = me.innerCt.dom.style,
            names = me.names,
            overflowHandler = me.overflowHandler,
            scrollable = owner.getScrollable(),
            scrollPos;
        ownerContext.boxNames = names;
        
        
        if (overflowHandler) {
            overflowHandler.beginLayout(ownerContext);
        }
        
        if (typeof smp === 'string') {
            smp = Ext.getCmp(smp) || owner.query(smp)[0];
        }
        ownerContext.stretchMaxPartner = smp && ownerContext.context.getCmp(smp);
        me.callParent(arguments);
        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);
        ownerContext.targetElContext = ownerContext.getEl('targetEl', me);
        ownerContext.ownerScrollable = scrollable = owner.getScrollable();
        if (scrollable) {
            scrollPos = scrollable.getPosition();
            ownerContext.scrollRestore = {};
            
            
            
            ownerContext.scrollRestore[names.x] = scrollPos[names.x];
            ownerContext.scrollRestore[names.y] = scrollPos[names.y];
        }
        
        style.width = '';
        style.height = '';
    },
    beginLayoutCycle: function(ownerContext, firstCycle) {
        var me = this,
            state = ownerContext.state,
            scrollable = ownerContext.ownerScrollable,
            align = me.align,
            names = ownerContext.boxNames,
            pack = me.pack,
            centerRe = me._centerRe,
            overflowHandler = me.overflowHandler,
            canScroll = ownerContext.state.canScroll,
            widthModel, heightModel;
        
        
        if (overflowHandler) {
            overflowHandler.beginLayoutCycle(ownerContext, firstCycle);
        }
        me.callParent(arguments);
        
        
        ownerContext.parallelSizeModel = widthModel = ownerContext[names.widthModel];
        ownerContext.perpendicularSizeModel = heightModel = ownerContext[names.heightModel];
        ownerContext.boxOptions = {
            align: align = {
                stretch: align === 'stretch',
                stretchmax: align === 'stretchmax',
                center: centerRe.test(align),
                bottom: me._endRe.test(align)
            },
            pack: pack = {
                center: centerRe.test(pack),
                end: pack === 'end'
            }
        };
        
        
        
        if (scrollable) {
            if (!canScroll) {
                
                
                state.canScroll = {
                    parallel: !widthModel.shrinkWrap && scrollable[names.getX](),
                    perpendicular: !heightModel.shrinkWrap && scrollable[names.getY]()
                };
            }
            if (!state.actualScroll) {
                
                state.actualScroll = {
                    parallel: false,
                    perpendicular: false
                };
            }
        }
        
        
        
        
        
        if (align.stretch && heightModel.shrinkWrap) {
            align.stretchmax = true;
            align.stretch = false;
        }
        
        align.nostretch = !(align.stretch || align.stretchmax);
        
        
        
        if (widthModel.shrinkWrap) {
            pack.center = pack.end = false;
        }
        me.cacheFlexes(ownerContext);
        
        
        
        
        
        
        me.targetEl.setWidth(20000);
    },
    
    cacheFlexes: function(ownerContext) {
        var me = this,
            names = ownerContext.boxNames,
            widthModelName = names.widthModel,
            heightModelName = names.heightModel,
            nostretch = ownerContext.boxOptions.align.nostretch,
            totalFlex = 0,
            childItems = ownerContext.childItems,
            i = childItems.length,
            flexedItems = [],
            minWidth = 0,
            smallestHeight = 0,
            smallestWidth = 0,
            minWidthName = names.minWidth,
            minHeightName = names.minHeight,
            percentageRe = me._percentageRe,
            percentageWidths = 0,
            percentageHeights = 0,
            child, childContext, flex, match, heightModel, widthModel, width, height;
        while (i--) {
            childContext = childItems[i];
            child = childContext.target;
            widthModel = childContext[widthModelName];
            
            
            
            if (widthModel.calculated) {
                childContext.flex = flex = child.flex;
                if (flex) {
                    totalFlex += flex;
                    flexedItems.push(childContext);
                    minWidth += child[minWidthName] || 0;
                } else {
                    
                    match = percentageRe.exec(child[names.width]);
                    childContext.percentageParallel = parseFloat(match[1]) / 100;
                    ++percentageWidths;
                }
            }
            
            
            if (widthModel.configured) {
                width = child[names.width];
            } else {
                width = child[minWidthName] || 0;
            }
            smallestWidth += width;
            heightModel = childContext[heightModelName];
            if (nostretch && heightModel.calculated) {
                
                
                match = percentageRe.exec(child[names.height]);
                childContext.percentagePerpendicular = parseFloat(match[1]) / 100;
                ++percentageHeights;
            }
            if (heightModel.configured) {
                height = child[names.height];
            } else {
                height = child[minHeightName] || 0;
            }
            if (height > smallestHeight) {
                smallestHeight = height;
            }
        }
        ownerContext.flexedItems = flexedItems;
        ownerContext.flexedMinWidth = minWidth;
        
        
        ownerContext.smallestWidth = smallestWidth;
        ownerContext.smallestHeight = smallestHeight;
        ownerContext.totalFlex = totalFlex;
        ownerContext.percentageWidths = percentageWidths;
        ownerContext.percentageHeights = percentageHeights;
        
        
        
        Ext.Array.sort(flexedItems, me.flexSortFn);
    },
    calculate: function(ownerContext) {
        var me = this,
            names = ownerContext.boxNames,
            state = ownerContext.state,
            actualScroll = state.actualScroll,
            needsScroll = state.needsScroll,
            canScroll = state.canScroll,
            plan = state.boxPlan || (state.boxPlan = {}),
            overflowHandler = me.overflowHandler,
            parallelShrinkWrap = ownerContext.parallelSizeModel.shrinkWrap,
            perpendicularShrinkWrap = ownerContext.perpendicularSizeModel.shrinkWrap;
        plan.targetSize = me.getContainerSize(ownerContext);
        if (canScroll && !needsScroll) {
            state.needsScroll = needsScroll = {
                
                
                parallel: canScroll.parallel && plan.targetSize[names.width] < ownerContext.smallestWidth,
                perpendicular: canScroll.perpendicular && plan.targetSize[names.height] < ownerContext.smallestHeight
            };
        }
        if (!state.parallelDone) {
            state.parallelDone = me.calculateParallel(ownerContext, names, plan);
        }
        if (!state.perpendicularDone) {
            state.perpendicularDone = me.calculatePerpendicular(ownerContext, names, plan);
        }
        if (state.parallelDone && state.perpendicularDone) {
            if (canScroll && !state.scrollPass) {
                if (needsScroll.parallel !== actualScroll.parallel || needsScroll.perpendicular !== actualScroll.perpendicular) {
                    ownerContext.invalidate({
                        state: {
                            scrollPass: true,
                            canScroll: canScroll,
                            needsScroll: actualScroll
                        }
                    });
                    me.done = false;
                    return;
                }
            }
            me.publishInnerCtSize(ownerContext);
            
            
            if (me.done && ownerContext.boxOptions.align.stretchmax && !state.stretchMaxDone) {
                me.calculateStretchMax(ownerContext, names, plan);
                state.stretchMaxDone = true;
            }
            if (overflowHandler) {
                overflowHandler.calculate(ownerContext);
            }
        } else {
            me.done = false;
        }
    },
    calculateParallel: function(ownerContext, names, plan) {
        var me = this,
            widthShrinkWrap = ownerContext.parallelSizeModel.shrinkWrap,
            widthName = names.width,
            childItems = ownerContext.childItems,
            beforeXName = names.beforeX,
            afterXName = names.afterX,
            setWidthName = names.setWidth,
            childItemsLength = childItems.length,
            flexedItems = ownerContext.flexedItems,
            flexedItemsLength = flexedItems.length,
            pack = ownerContext.boxOptions.pack,
            padding = me.padding,
            targetSize = plan.targetSize,
            containerWidth = targetSize[widthName],
            state = ownerContext.state,
            needsScroll = state.needsScroll,
            canScroll = state.canScroll,
            totalMargin = 0,
            left = padding[beforeXName],
            nonFlexWidth = left + padding[afterXName],
            scrollbarSize = Ext.getScrollbarSize(),
            scrollbarWidth = scrollbarSize[names.width],
            scrollbarHeight = scrollbarSize[names.height],
            i, childMargins, remainingWidth, remainingFlex, childContext, flex, flexedWidth, contentWidth, childWidth, percentageSpace, availableSpace;
        
        
        
        
        if (!widthShrinkWrap && !targetSize[names.gotWidth]) {
            return false;
        }
        
        for (i = 0; i < childItemsLength; ++i) {
            childContext = childItems[i];
            childMargins = childContext.marginInfo || childContext.getMarginInfo();
            totalMargin += childMargins[widthName];
            if (!childContext[names.widthModel].calculated) {
                childWidth = childContext.getProp(widthName);
                nonFlexWidth += childWidth;
                
                if (isNaN(nonFlexWidth)) {
                    return false;
                }
            }
        }
        nonFlexWidth += totalMargin;
        if (ownerContext.percentageWidths) {
            percentageSpace = containerWidth - totalMargin;
            if (isNaN(percentageSpace)) {
                return false;
            }
            for (i = 0; i < childItemsLength; ++i) {
                childContext = childItems[i];
                if (childContext.percentageParallel) {
                    childWidth = Math.ceil(percentageSpace * childContext.percentageParallel);
                    childWidth = childContext.setWidth(childWidth);
                    nonFlexWidth += childWidth;
                }
            }
        }
        
        if (widthShrinkWrap) {
            availableSpace = 0;
            plan.tooNarrow = false;
        } else {
            availableSpace = containerWidth - nonFlexWidth;
            if (needsScroll && needsScroll.perpendicular) {
                availableSpace -= scrollbarHeight;
            }
            plan.tooNarrow = availableSpace < ownerContext.flexedMinWidth;
            if (plan.tooNarrow && canScroll && canScroll.parallel) {
                state.actualScroll.parallel = true;
            }
        }
        contentWidth = nonFlexWidth;
        remainingWidth = availableSpace;
        remainingFlex = ownerContext.totalFlex;
        
        for (i = 0; i < flexedItemsLength; i++) {
            childContext = flexedItems[i];
            flex = childContext.flex;
            flexedWidth = me.roundFlex((flex / remainingFlex) * remainingWidth);
            flexedWidth = childContext[setWidthName](flexedWidth);
            
            
            contentWidth += flexedWidth;
            
            remainingWidth = Math.max(0, remainingWidth - flexedWidth);
            
            remainingFlex -= flex;
        }
        if (pack.center) {
            left += remainingWidth / 2;
            
            if (left < 0) {
                left = 0;
            }
        } else if (pack.end) {
            left += remainingWidth;
        }
        
        for (i = 0; i < childItemsLength; ++i) {
            childContext = childItems[i];
            childMargins = childContext.marginInfo;
            
            left += childMargins[beforeXName];
            childContext.setProp(names.x, left);
            
            
            
            
            left += childMargins[afterXName] + childContext.props[widthName];
        }
        contentWidth += ownerContext.targetContext.getPaddingInfo()[widthName];
        ownerContext.state.contentWidth = contentWidth;
        
        
        if (needsScroll && needsScroll.perpendicular) {
            if (widthShrinkWrap) {
                contentWidth += scrollbarWidth;
            }
            ownerContext[names.hasOverflowY] = true;
            
            ownerContext.target.componentLayout[names.setWidthInDom] = true;
            
            
            
            ownerContext[names.invalidateScrollY] = Ext.isIE8;
        }
        ownerContext[names.setContentWidth](contentWidth);
        return true;
    },
    calculatePerpendicular: function(ownerContext, names, plan) {
        var me = this,
            state = ownerContext.state,
            needsScroll = state.needsScroll,
            canScroll = state.canScroll,
            heightShrinkWrap = ownerContext.perpendicularSizeModel.shrinkWrap,
            targetSize = plan.targetSize,
            childItems = ownerContext.childItems,
            childItemsLength = childItems.length,
            mmax = Math.max,
            heightName = names.height,
            setHeightName = names.setHeight,
            beforeYName = names.beforeY,
            topPositionName = names.y,
            padding = me.padding,
            top = padding[beforeYName],
            availHeight = targetSize[heightName] - top - padding[names.afterY],
            align = ownerContext.boxOptions.align,
            isStretch = align.stretch,
            
            isStretchMax = align.stretchmax,
            isCenter = align.center,
            isBottom = align.bottom,
            constrain = me.constrainAlign,
            maxHeight = 0,
            hasPercentageSizes = 0,
            onBeforeInvalidateChild = me.onBeforeConstrainInvalidateChild,
            onAfterInvalidateChild = me.onAfterConstrainInvalidateChild,
            scrollbarHeight = Ext.getScrollbarSize().height,
            childTop, i, childHeight, childMargins, diff, height, childContext, stretchMaxPartner, stretchMaxChildren, shrinkWrapParallelOverflow, percentagePerpendicular;
        if (!heightShrinkWrap && !targetSize[names.gotHeight]) {
            return false;
        }
        if (isStretch || ((isCenter || isBottom) && !heightShrinkWrap)) {
            if (isNaN(availHeight)) {
                return false;
            }
        }
        
        
        
        
        
        
        if (needsScroll && needsScroll.parallel) {
            if (heightShrinkWrap) {
                shrinkWrapParallelOverflow = true;
            } else {
                availHeight -= scrollbarHeight;
                plan.targetSize[heightName] -= scrollbarHeight;
            }
        }
        if (isStretch) {
            height = availHeight;
            
            maxHeight = mmax(height, ownerContext.smallestHeight);
        } else {
            for (i = 0; i < childItemsLength; i++) {
                childContext = childItems[i];
                childMargins = (childContext.marginInfo || childContext.getMarginInfo())[heightName];
                if (!(percentagePerpendicular = childContext.percentagePerpendicular)) {
                    childHeight = childContext.getProp(heightName);
                } else {
                    ++hasPercentageSizes;
                    if (heightShrinkWrap) {
                        
                        
                        
                        continue;
                    } else {
                        childHeight = percentagePerpendicular * availHeight - childMargins;
                        childHeight = childContext[names.setHeight](childHeight);
                    }
                }
                
                
                
                
                
                if (!heightShrinkWrap && constrain && childContext[names.heightModel].shrinkWrap && childHeight > availHeight) {
                    childContext.invalidate({
                        before: onBeforeInvalidateChild,
                        after: onAfterInvalidateChild,
                        layout: me,
                        childHeight: availHeight,
                        names: names
                    });
                    
                    
                    ownerContext.state.parallelDone = false;
                }
                
                if (isNaN(maxHeight = mmax(maxHeight, childHeight + childMargins, childContext.target[names.minHeight] || 0))) {
                    return false;
                }
            }
        }
        
        
        
        if (shrinkWrapParallelOverflow) {
            maxHeight += scrollbarHeight;
            ownerContext[names.hasOverflowX] = true;
            
            ownerContext.target.componentLayout[names.setHeightInDom] = true;
            
            
            
            ownerContext[names.invalidateScrollX] = Ext.isIE8;
        }
        
        
        stretchMaxPartner = ownerContext.stretchMaxPartner;
        if (stretchMaxPartner) {
            
            ownerContext.setProp('maxChildHeight', maxHeight);
            stretchMaxChildren = stretchMaxPartner.childItems;
            
            if (stretchMaxChildren && stretchMaxChildren.length) {
                maxHeight = mmax(maxHeight, stretchMaxPartner.getProp('maxChildHeight'));
                if (isNaN(maxHeight)) {
                    return false;
                }
            }
        }
        ownerContext[names.setContentHeight](maxHeight + me.padding[heightName] + ownerContext.targetContext.getPaddingInfo()[heightName]);
        
        
        
        if (shrinkWrapParallelOverflow) {
            maxHeight -= scrollbarHeight;
        }
        if (maxHeight > targetSize[heightName] && canScroll && canScroll.perpendicular) {
            state.actualScroll.perpendicular = true;
        }
        plan.maxSize = maxHeight;
        if (isStretchMax) {
            height = maxHeight;
        } else if (isCenter || isBottom || hasPercentageSizes) {
            if (constrain) {
                height = heightShrinkWrap ? maxHeight : availHeight;
            } else {
                height = heightShrinkWrap ? maxHeight : mmax(availHeight, maxHeight);
            }
            
            
            
            
            height -= ownerContext.innerCtContext.getBorderInfo()[heightName];
        }
        for (i = 0; i < childItemsLength; i++) {
            childContext = childItems[i];
            childMargins = childContext.marginInfo || childContext.getMarginInfo();
            childTop = top + childMargins[beforeYName];
            if (isStretch) {
                childContext[setHeightName](height - childMargins[heightName]);
            } else {
                percentagePerpendicular = childContext.percentagePerpendicular;
                if (heightShrinkWrap && percentagePerpendicular) {
                    childMargins = childContext.marginInfo || childContext.getMarginInfo();
                    childHeight = percentagePerpendicular * height - childMargins[heightName];
                    childHeight = childContext.setHeight(childHeight);
                }
                if (isCenter) {
                    diff = height - childContext.props[heightName];
                    if (diff > 0) {
                        childTop = top + Math[me.alignRoundingMethod](diff / 2);
                    }
                } else if (isBottom) {
                    childTop = mmax(0, height - childTop - childContext.props[heightName]);
                }
            }
            childContext.setProp(topPositionName, childTop);
        }
        return true;
    },
    onBeforeConstrainInvalidateChild: function(childContext, options) {
        
        var heightModelName = options.names.heightModel;
        if (!childContext[heightModelName].constrainedMin) {
            
            
            childContext[heightModelName] = Ext.layout.SizeModel.calculated;
        }
    },
    onAfterConstrainInvalidateChild: function(childContext, options) {
        
        var names = options.names;
        
        
        
        
        
        childContext.setProp(names.beforeY, 0);
        if (childContext[names.heightModel].calculated) {
            childContext[names.setHeight](options.childHeight);
        }
    },
    calculateStretchMax: function(ownerContext, names, plan) {
        var me = this,
            heightName = names.height,
            widthName = names.width,
            childItems = ownerContext.childItems,
            length = childItems.length,
            height = plan.maxSize,
            onBeforeStretchMaxInvalidateChild = me.onBeforeStretchMaxInvalidateChild,
            onAfterStretchMaxInvalidateChild = me.onAfterStretchMaxInvalidateChild,
            childContext, props, i, childHeight;
        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            props = childContext.props;
            childHeight = height - childContext.getMarginInfo()[heightName];
            if (childHeight !== props[heightName] || 
            childContext[names.heightModel].constrained) {
                
                
                
                
                
                
                
                
                childContext.invalidate({
                    before: onBeforeStretchMaxInvalidateChild,
                    after: onAfterStretchMaxInvalidateChild,
                    layout: me,
                    
                    childWidth: props[widthName],
                    
                    childHeight: childHeight,
                    childX: props.x,
                    childY: props.y,
                    names: names
                });
            }
        }
    },
    onBeforeStretchMaxInvalidateChild: function(childContext, options) {
        
        var heightModelName = options.names.heightModel;
        
        
        
        
        if (!childContext[heightModelName].constrainedMax) {
            
            
            childContext[heightModelName] = Ext.layout.SizeModel.calculated;
        }
    },
    onAfterStretchMaxInvalidateChild: function(childContext, options) {
        
        var names = options.names,
            childHeight = options.childHeight,
            childWidth = options.childWidth;
        childContext.setProp('x', options.childX);
        childContext.setProp('y', options.childY);
        if (childContext[names.heightModel].calculated) {
            
            
            childContext[names.setHeight](childHeight);
        }
        if (childContext[names.widthModel].calculated) {
            childContext[names.setWidth](childWidth);
        }
    },
    completeLayout: function(ownerContext) {
        var me = this,
            names = ownerContext.boxNames,
            invalidateScrollX = ownerContext.invalidateScrollX,
            invalidateScrollY = ownerContext.invalidateScrollY,
            overflowHandler = me.overflowHandler,
            restoreScroll = ownerContext.restoreScroll,
            dom, el, overflowX, overflowY, styles, scroll, scrollable;
        if (overflowHandler) {
            overflowHandler.completeLayout(ownerContext);
        }
        if (invalidateScrollX || invalidateScrollY) {
            el = me.getTarget();
            dom = el.dom;
            styles = dom.style;
            if (invalidateScrollX) {
                
                overflowX = el.getStyle('overflowX');
                if (overflowX === 'auto') {
                    
                    overflowX = styles.overflowX;
                    styles.overflowX = 'scroll';
                } else 
                {
                    invalidateScrollX = false;
                }
            }
            
            if (invalidateScrollY) {
                
                overflowY = el.getStyle('overflowY');
                if (overflowY === 'auto') {
                    
                    overflowY = styles.overflowY;
                    styles.overflowY = 'scroll';
                } else 
                {
                    invalidateScrollY = false;
                }
            }
            
            if (invalidateScrollX || invalidateScrollY) {
                
                
                dom.scrollWidth;
                if (invalidateScrollX) {
                    styles.overflowX = overflowX;
                }
                
                if (invalidateScrollY) {
                    styles.overflowY = overflowY;
                }
            }
        }
        
        if (restoreScroll) {
            scrollable = ownerContext.ownerScrollable;
            scroll = restoreScroll[names.x];
            if (scroll) {
                scrollable[names.setX](scroll);
            }
            scroll = restoreScroll[names.y];
            if (scroll) {
                scrollable[names.setY](scroll);
            }
        }
    },
    finishedLayout: function(ownerContext) {
        var overflowHandler = this.overflowHandler;
        if (overflowHandler) {
            overflowHandler.finishedLayout(ownerContext);
        }
        this.callParent(arguments);
    },
    getLayoutItems: function() {
        var items = this.callParent(),
            n = items.length,
            lastVisibleItem, hide, i, item, splitAfter, splitBefore, splitter;
        for (i = 0; i < n; ++i) {
            if ((item = items[i]).isSplitter) {
                
                continue;
            }
            splitter = item.splitter;
            if (item.hidden) {
                if (splitter) {
                    
                    if (!splitter.hidden) {
                        splitter.hidden = true;
                        if (splitter.el) {
                            splitter.el.hide();
                        }
                    }
                }
                
                continue;
            }
            if (splitter) {
                splitBefore = splitter.collapseTarget === 'next';
            } else {
                
                splitBefore = false;
            }
            hide = null;
            if (lastVisibleItem && splitAfter) {
                
                
                if (splitAfter.hidden) {
                    splitAfter.hidden = false;
                    if (splitAfter.el) {
                        splitAfter.el.show();
                    }
                }
                if (splitBefore) {
                    hide = true;
                }
            } else if (splitBefore) {
                hide = !lastVisibleItem;
            }
            
            
            if (hide !== null && splitter.hidden !== hide) {
                splitter.hidden = hide;
                if (splitter.el) {
                    splitter.el.setVisible(!hide);
                }
            }
            splitAfter = !splitBefore && splitter;
            lastVisibleItem = item;
        }
        
        
        if (lastVisibleItem && splitAfter && !splitAfter.hidden) {
            splitAfter.hidden = true;
            if (splitAfter.el) {
                splitAfter.el.hide();
            }
        }
        return items;
    },
    getScrollerEl: function() {
        return this.innerCt;
    },
    
    insertSplitter: function(item, index, hidden, splitterCfg) {
        var splitter = {
                xtype: 'splitter',
                id: item.id + '-splitter',
                hidden: hidden,
                splitterFor: item,
                synthetic: true
            },
            
            at = index + ((splitterCfg.collapseTarget === 'prev') ? 1 : 0);
        splitter[this.names.height] = '100%';
        if (splitterCfg) {
            Ext.apply(splitter, splitterCfg);
        }
        item.splitter = this.owner.add(at, splitter);
    },
    publishInnerCtSize: function(ownerContext, widthOffset) {
        widthOffset = widthOffset || 0;
        var me = this,
            state = ownerContext.state,
            names = ownerContext.boxNames,
            heightName = names.height,
            widthName = names.width,
            align = ownerContext.boxOptions.align,
            padding = me.padding,
            plan = state.boxPlan,
            targetSize = plan.targetSize,
            height = plan.maxSize,
            needsScroll = state.needsScroll,
            innerCtContext = ownerContext.innerCtContext,
            innerCtWidth, innerCtHeight;
        
        
        
        if (ownerContext.parallelSizeModel.shrinkWrap || (plan.tooNarrow && state.canScroll)) {
            innerCtWidth = state.contentWidth - ownerContext.targetContext.getPaddingInfo()[widthName];
        } else {
            innerCtWidth = targetSize[widthName];
            if (needsScroll && needsScroll.perpendicular) {
                innerCtWidth -= Ext.getScrollbarSize()[widthName];
            }
        }
        innerCtWidth -= widthOffset;
        
        me.owner.tooNarrow = plan.tooNarrow;
        if (align.stretch) {
            innerCtHeight = height;
        } else {
            innerCtHeight = plan.maxSize + padding[names.beforeY] + padding[names.afterY] + innerCtContext.getBorderInfo()[heightName];
            if (!ownerContext.perpendicularSizeModel.shrinkWrap && (align.center || align.bottom)) {
                innerCtHeight = Math.max(targetSize[heightName], innerCtHeight);
            }
        }
        innerCtContext[names.setWidth](innerCtWidth);
        innerCtContext[names.setHeight](innerCtHeight);
        
        
        
        
        
        
        
        
        
        ownerContext.targetElContext.setWidth(ownerContext.innerCtContext.props.width - (me.vertical ? 0 : (widthOffset || 0)));
        
        if (isNaN(innerCtWidth + innerCtHeight)) {
            me.done = false;
        }
    },
    onAdd: function(item, index) {
        var me = this,
            
            split = me.enableSplitters && item.split && !item.isButton;
        me.callParent(arguments);
        if (split) {
            if (split === true) {
                split = {
                    collapseTarget: 'next'
                };
            } else if (Ext.isString(split)) {
                split = {
                    collapseTarget: split === 'before' ? 'next' : 'prev'
                };
            } else {
                split = Ext.apply({
                    collapseTarget: split.side === 'before' ? 'next' : 'prev'
                }, split);
            }
            me.insertSplitter(item, index, !!item.hidden, split);
        }
    },
    onRemove: function(comp, isDestroying) {
        var me = this,
            names = me.names,
            owner = me.owner,
            splitter = comp.splitter,
            overflowHandler = me.overflowHandler,
            el;
        me.callParent(arguments);
        if (splitter && owner.contains(splitter)) {
            owner.doRemove(splitter, true);
            comp.splitter = null;
        }
        if (overflowHandler) {
            overflowHandler.onRemove(comp);
        }
        if (comp.layoutMarginCap === me.id) {
            delete comp.layoutMarginCap;
        }
        if (!owner.destroying && !isDestroying && comp.rendered) {
            
            el = comp.getEl();
            if (el) {
                el.setStyle(names.beforeY, '');
                el.setStyle(names.beforeX, '');
                
                
                el.setStyle('margin', '');
            }
        }
    },
    applyOverflowHandler: function(overflowHandler, oldOverflowHandler) {
        var type;
        if (typeof overflowHandler === 'string') {
            overflowHandler = {
                type: overflowHandler
            };
        }
        type = overflowHandler.type;
        if (oldOverflowHandler && oldOverflowHandler.type === overflowHandler.type) {
            delete overflowHandler.type;
            oldOverflowHandler.setConfig(overflowHandler);
            return oldOverflowHandler;
        }
        overflowHandler.layout = this;
        return Ext.Factory.boxOverflow(overflowHandler);
    },
    
    
    getRenderTarget: function() {
        return this.targetEl;
    },
    
    
    getElementTarget: function() {
        return this.innerCt;
    },
    calculateChildBox: Ext.deprecated(),
    calculateChildBoxes: Ext.deprecated(),
    updateChildBoxes: Ext.deprecated(),
    
    destroy: function() {
        var me = this;
        Ext.destroy(me.innerCt, me.overflowHandler);
        me.flexSortFn = me.innerCt = null;
        me.callParent(arguments);
    },
    getRenderData: function() {
        var data = this.callParent();
        data.targetElCls = this.targetElCls;
        return data;
    },
    updateVertical: function(vertical) {
        var me = this,
            overflowHandler = me.overflowHandler,
            owner = me.owner,
            props = me._props;
        Ext.apply(me, vertical ? props.vbox : props.hbox);
        if (overflowHandler && owner && owner.rendered) {
            overflowHandler.setVertical(vertical);
        }
    },
    _props: {
        
        'hbox': {
            direction: 'horizontal',
            oppositeDirection: 'vertical',
            horizontal: true,
            vertical: false,
            names: {
                
                beforeX: 'left',
                beforeScrollX: 'left',
                leftCap: 'Left',
                afterX: 'right',
                width: 'width',
                contentWidth: 'contentWidth',
                minWidth: 'minWidth',
                maxWidth: 'maxWidth',
                widthCap: 'Width',
                widthModel: 'widthModel',
                widthIndex: 0,
                x: 'x',
                getX: 'getX',
                setX: 'setX',
                scrollLeft: 'scrollLeft',
                overflowX: 'overflowX',
                hasOverflowX: 'hasOverflowX',
                invalidateScrollX: 'invalidateScrollX',
                parallelMargins: 'lr',
                
                center: 'middle',
                beforeY: 'top',
                afterY: 'bottom',
                height: 'height',
                contentHeight: 'contentHeight',
                minHeight: 'minHeight',
                maxHeight: 'maxHeight',
                heightCap: 'Height',
                heightModel: 'heightModel',
                heightIndex: 1,
                y: 'y',
                getY: 'getY',
                setY: 'setY',
                overflowY: 'overflowY',
                hasOverflowY: 'hasOverflowY',
                invalidateScrollY: 'invalidateScrollY',
                perpendicularMargins: 'tb',
                
                getWidth: 'getWidth',
                getHeight: 'getHeight',
                setWidth: 'setWidth',
                setHeight: 'setHeight',
                gotWidth: 'gotWidth',
                gotHeight: 'gotHeight',
                setContentWidth: 'setContentWidth',
                setContentHeight: 'setContentHeight',
                setWidthInDom: 'setWidthInDom',
                setHeightInDom: 'setHeightInDom',
                getScrollLeft: 'getScrollLeft',
                setScrollLeft: 'setScrollLeft',
                scrollTo: 'scrollTo'
            },
            sizePolicy: {
                flex: {
                    '': {
                        readsWidth: 0,
                        readsHeight: 1,
                        setsWidth: 1,
                        setsHeight: 0
                    },
                    stretch: {
                        readsWidth: 0,
                        readsHeight: 0,
                        setsWidth: 1,
                        setsHeight: 1
                    },
                    stretchmax: {
                        readsWidth: 0,
                        readsHeight: 1,
                        setsWidth: 1,
                        setsHeight: 1
                    }
                },
                '': {
                    readsWidth: 1,
                    readsHeight: 1,
                    setsWidth: 0,
                    setsHeight: 0
                },
                stretch: {
                    readsWidth: 1,
                    readsHeight: 0,
                    setsWidth: 0,
                    setsHeight: 1
                },
                stretchmax: {
                    readsWidth: 1,
                    readsHeight: 1,
                    setsWidth: 0,
                    setsHeight: 1
                }
            }
        },
        
        'vbox': {
            direction: 'vertical',
            oppositeDirection: 'horizontal',
            horizontal: false,
            vertical: true,
            names: {
                
                beforeX: 'top',
                beforeScrollX: 'top',
                leftCap: 'Top',
                afterX: 'bottom',
                width: 'height',
                contentWidth: 'contentHeight',
                minWidth: 'minHeight',
                maxWidth: 'maxHeight',
                widthCap: 'Height',
                widthModel: 'heightModel',
                widthIndex: 1,
                x: 'y',
                getX: 'getY',
                setX: 'setY',
                scrollLeft: 'scrollTop',
                overflowX: 'overflowY',
                hasOverflowX: 'hasOverflowY',
                invalidateScrollX: 'invalidateScrollY',
                parallelMargins: 'tb',
                
                center: 'center',
                beforeY: 'left',
                afterY: 'right',
                height: 'width',
                contentHeight: 'contentWidth',
                minHeight: 'minWidth',
                maxHeight: 'maxWidth',
                heightCap: 'Width',
                heightModel: 'widthModel',
                heightIndex: 0,
                y: 'x',
                getY: 'getX',
                setY: 'setX',
                overflowY: 'overflowX',
                hasOverflowY: 'hasOverflowX',
                invalidateScrollY: 'invalidateScrollX',
                perpendicularMargins: 'lr',
                
                getWidth: 'getHeight',
                getHeight: 'getWidth',
                setWidth: 'setHeight',
                setHeight: 'setWidth',
                gotWidth: 'gotHeight',
                gotHeight: 'gotWidth',
                setContentWidth: 'setContentHeight',
                setContentHeight: 'setContentWidth',
                setWidthInDom: 'setHeightInDom',
                setHeightInDom: 'setWidthInDom',
                getScrollLeft: 'getScrollTop',
                setScrollLeft: 'setScrollTop',
                scrollTo: 'scrollTo'
            },
            sizePolicy: {
                flex: {
                    '': {
                        readsWidth: 1,
                        readsHeight: 0,
                        setsWidth: 0,
                        setsHeight: 1
                    },
                    stretch: {
                        readsWidth: 0,
                        readsHeight: 0,
                        setsWidth: 1,
                        setsHeight: 1
                    },
                    stretchmax: {
                        readsWidth: 1,
                        readsHeight: 0,
                        setsWidth: 1,
                        setsHeight: 1
                    }
                },
                '': {
                    readsWidth: 1,
                    readsHeight: 1,
                    setsWidth: 0,
                    setsHeight: 0
                },
                stretch: {
                    readsWidth: 0,
                    readsHeight: 1,
                    setsWidth: 1,
                    setsHeight: 0
                },
                stretchmax: {
                    readsWidth: 1,
                    readsHeight: 1,
                    setsWidth: 1,
                    setsHeight: 0
                }
            }
        }
    }
});


Ext.define('Ext.layout.container.HBox', {
    extend: 'Ext.layout.container.Box',
    alias: 'layout.hbox',
    alternateClassName: 'Ext.layout.HBoxLayout',
    type: 'hbox',
    vertical: false
});


Ext.define('Ext.layout.container.VBox', {
    extend: 'Ext.layout.container.Box',
    alias: 'layout.vbox',
    alternateClassName: 'Ext.layout.VBoxLayout',
    type: 'vbox',
    vertical: true
});


Ext.define('Ext.util.FocusableContainer', {
    extend: 'Ext.Mixin',
    requires: [
        'Ext.util.KeyNav'
    ],
    mixinConfig: {
        id: 'focusablecontainer',
        before: {
            onAdd: 'onFocusableChildAdd',
            onRemove: 'onFocusableChildRemove',
            destroy: 'destroyFocusableContainer',
            onFocusEnter: 'onFocusEnter'
        },
        after: {
            afterRender: 'initFocusableContainer',
            onFocusLeave: 'onFocusLeave'
        }
    },
    isFocusableContainer: true,
    
    enableFocusableContainer: true,
    
    activeChildTabIndex: 0,
    
    inactiveChildTabIndex: -1,
    privates: {
        initFocusableContainer: function() {
            
            
            if (this.enableFocusableContainer) {
                this.doInitFocusableContainer();
            }
        },
        doInitFocusableContainer: function() {
            var me = this,
                el;
            el = me.getFocusableContainerEl();
            
            
            me.activateFocusableContainerEl(el);
            me.mon(el, 'mousedown', me.onFocusableContainerMousedown, me);
            me.focusableKeyNav = me.createFocusableContainerKeyNav(el);
        },
        createFocusableContainerKeyNav: function(el) {
            var me = this;
            return new Ext.util.KeyNav(el,{
                ignoreInputFields: true,
                scope: me,
                tab: me.onFocusableContainerTabKey,
                enter: me.onFocusableContainerEnterKey,
                space: me.onFocusableContainerSpaceKey,
                up: me.onFocusableContainerUpKey,
                down: me.onFocusableContainerDownKey,
                left: me.onFocusableContainerLeftKey,
                right: me.onFocusableContainerRightKey
            });
        },
        destroyFocusableContainer: function() {
            if (this.enableFocusableContainer) {
                this.doDestroyFocusableContainer();
            }
        },
        doDestroyFocusableContainer: function() {
            var keyNav = this.focusableKeyNav;
            if (keyNav) {
                keyNav.destroy();
                delete this.focusableKeyNav;
            }
        },
        
        getFocusables: function() {
            return this.items.items;
        },
        initDefaultFocusable: function(beforeRender) {
            var me = this,
                activeIndex = me.activeChildTabIndex,
                haveFocusable = false,
                items, item, i, len, tabIdx;
            items = me.getFocusables();
            len = items.length;
            if (!len) {
                return;
            }
            
            
            
            for (i = 0; i < len; i++) {
                item = items[i];
                if (item.focusable) {
                    haveFocusable = true;
                    tabIdx = item.getTabIndex();
                    if (tabIdx != null && tabIdx >= activeIndex) {
                        return item;
                    }
                }
            }
            
            if (!haveFocusable) {
                return;
            }
            
            
            
            
            
            
            item = me.findNextFocusableChild(null, true, items, beforeRender);
            if (item) {
                me.activateFocusable(item);
            }
            return item;
        },
        clearFocusables: function() {
            var me = this,
                items = me.getFocusables(),
                len = items.length,
                item, i;
            for (i = 0; i < len; i++) {
                item = items[i];
                if (item.focusable) {
                    me.deactivateFocusable(item);
                }
            }
        },
        activateFocusable: function(child, 
        newTabIndex) {
            var activeIndex = newTabIndex != null ? newTabIndex : this.activeChildTabIndex;
            child.setTabIndex(activeIndex);
        },
        deactivateFocusable: function(child, 
        newTabIndex) {
            var inactiveIndex = newTabIndex != null ? newTabIndex : this.inactiveChildTabIndex;
            child.setTabIndex(inactiveIndex);
        },
        onFocusableContainerTabKey: function() {
            return true;
        },
        onFocusableContainerEnterKey: function() {
            return true;
        },
        onFocusableContainerSpaceKey: function() {
            return true;
        },
        onFocusableContainerUpKey: function(e) {
            return this.moveChildFocus(e, false);
        },
        onFocusableContainerLeftKey: function(e) {
            return this.moveChildFocus(e, false);
        },
        onFocusableContainerRightKey: function(e) {
            return this.moveChildFocus(e, true);
        },
        onFocusableContainerDownKey: function(e) {
            return this.moveChildFocus(e, true);
        },
        getFocusableFromEvent: function(e) {
            var child = Ext.ComponentManager.byElement(e.getTarget());
            if (!child) {
                Ext.Error.raise("No focusable child found for keyboard event!");
            }
            return child;
        },
        moveChildFocus: function(e, forward) {
            var child = this.getFocusableFromEvent(e);
            return this.focusChild(child, forward, e);
        },
        focusChild: function(child, forward) {
            var nextChild = this.findNextFocusableChild(child, forward);
            if (nextChild) {
                nextChild.focus();
            }
            return nextChild;
        },
        findNextFocusableChild: function(child, step, items, beforeRender) {
            var item, idx, i, len;
            items = items || this.getFocusables();
            
            
            
            idx = Ext.Array.indexOf(items, child);
            
            step = step === true ? 1 : step === false ? -1 : step;
            len = items.length;
            i = step > 0 ? (idx < len ? idx + step : 0) : (idx > 0 ? idx + step : len - 1);
            for (; ; i += step) {
                
                
                if (idx < 0 && (i >= len || i < 0)) {
                    return null;
                }
                
                else if (i >= len) {
                    i = -1;
                    
                    
                    continue;
                }
                
                else if (i < 0) {
                    i = len;
                    
                    continue;
                }
                
                else if (i === idx) {
                    return null;
                }
                item = items[i];
                if (!item || !item.focusable) {
                    
                    continue;
                }
                
                
                
                
                
                
                
                
                if (beforeRender || item.isFocusable()) {
                    return item;
                }
            }
            return null;
        },
        getFocusableContainerEl: function() {
            return this.el;
        },
        onFocusableChildAdd: function(child) {
            return this.doFocusableChildAdd(child);
        },
        activateFocusableContainerEl: function(el) {
            el = el || this.getFocusableContainerEl();
            el.set({
                tabindex: this.activeChildTabIndex
            });
        },
        deactivateFocusableContainerEl: function(el) {
            el = el || this.getFocusableContainerEl();
            el.set({
                tabindex: this.inactiveChildTabIndex
            });
        },
        doFocusableChildAdd: function(child) {
            if (child.focusable) {
                child.focusableContainer = this;
                this.deactivateFocusable(child);
            }
        },
        onFocusableChildRemove: function(child) {
            return this.doFocusableChildRemove(child);
        },
        doFocusableChildRemove: function(child) {
            
            
            
            if (child === this.lastFocusedChild) {
                this.lastFocusedChild = null;
                this.activateFocusableContainerEl();
            }
            delete child.focusableContainer;
        },
        onFocusableContainerMousedown: function(e, target) {
            var targetCmp = Ext.ComponentManager.byElement(target);
            
            
            
            
            
            this.mousedownTimestamp = targetCmp === this ? Ext.Date.now() : 0;
        },
        onFocusEnter: function(e) {
            var me = this,
                target = e.toComponent,
                mousedownTimestamp = me.mousedownTimestamp,
                epsilon = 50,
                child;
            me.mousedownTimestamp = 0;
            if (target === me) {
                if (!mousedownTimestamp || Ext.Date.now() - mousedownTimestamp > epsilon) {
                    child = me.initDefaultFocusable();
                    if (child) {
                        me.deactivateFocusableContainerEl();
                        child.focus();
                    }
                }
            } else {
                me.deactivateFocusableContainerEl();
            }
            return target;
        },
        onFocusLeave: function(e) {
            var me = this,
                lastFocused = me.lastFocusedChild;
            if (!me.isDestroyed) {
                me.clearFocusables();
                if (lastFocused) {
                    me.activateFocusable(lastFocused);
                } else {
                    me.activateFocusableContainerEl();
                }
            }
        },
        beforeFocusableChildBlur: Ext.privateFn,
        afterFocusableChildBlur: Ext.privateFn,
        beforeFocusableChildFocus: function(child) {
            var me = this;
            me.clearFocusables();
            me.activateFocusable(child);
            if (child.needArrowKeys) {
                me.guardFocusableChild(child);
            }
        },
        guardFocusableChild: function(child) {
            var me = this,
                index = me.activeChildTabIndex,
                guard;
            guard = me.findNextFocusableChild(child, -1);
            if (guard) {
                guard.setTabIndex(index);
            }
            guard = me.findNextFocusableChild(child, 1);
            if (guard) {
                guard.setTabIndex(index);
            }
        },
        afterFocusableChildFocus: function(child) {
            this.lastFocusedChild = child;
        },
        
        onFocusableChildShow: Ext.privateFn,
        onFocusableChildHide: Ext.privateFn,
        onFocusableChildEnable: Ext.privateFn,
        onFocusableChildDisable: Ext.privateFn,
        onFocusableChildMasked: Ext.privateFn,
        onFocusableChildDestroy: Ext.privateFn,
        onFocusableChildUpdate: Ext.privateFn
    }
});


Ext.define('Ext.toolbar.Toolbar', {
    extend: 'Ext.container.Container',
    requires: [
        'Ext.layout.container.HBox',
        'Ext.layout.container.VBox'
    ],
    uses: [
        'Ext.toolbar.Fill',
        'Ext.toolbar.Separator'
    ],
    alias: 'widget.toolbar',
    alternateClassName: 'Ext.Toolbar',
    mixins: [
        'Ext.util.FocusableContainer'
    ],
    
    isToolbar: true,
    baseCls: Ext.baseCSSPrefix + 'toolbar',
    ariaRole: 'toolbar',
    defaultType: 'button',
    
    layout: undefined,
    
    vertical: false,
    
    
    enableOverflow: false,
    
    
    overflowHandler: null,
    
    defaultButtonUI: 'default-toolbar',
    
    defaultFieldUI: 'default',
    
    defaultFooterButtonUI: 'default',
    
    defaultFooterFieldUI: 'default',
    
    trackMenus: true,
    itemCls: Ext.baseCSSPrefix + 'toolbar-item',
    
    statics: {
        shortcuts: {
            '-': 'tbseparator',
            ' ': 'tbspacer'
        },
        shortcutsHV: {
            
            0: {
                '->': {
                    xtype: 'tbfill',
                    height: 0
                }
            },
            
            1: {
                '->': {
                    xtype: 'tbfill',
                    width: 0
                }
            }
        }
    },
    initComponent: function() {
        var me = this,
            layout = me.layout;
        if (me.dock === 'right' || me.dock === 'left') {
            me.vertical = true;
        }
        me.layout = layout = Ext.applyIf(Ext.isString(layout) ? {
            type: layout
        } : layout || {}, {
            type: me.vertical ? 'vbox' : 'hbox',
            align: me.vertical ? 'stretchmax' : 'middle'
        });
        if (me.overflowHandler) {
            layout.overflowHandler = me.overflowHandler;
        } else if (me.enableOverflow) {
            layout.overflowHandler = 'menu';
        }
        if (me.vertical) {
            me.addClsWithUI('vertical');
        }
        
        if (me.ui === 'footer') {
            me.ignoreBorderManagement = true;
        }
        me.callParent();
    },
    getRefItems: function(deep) {
        var me = this,
            items = me.callParent(arguments),
            layout = me.layout,
            handler;
        if (deep && (me.enableOverflow || (me.overflowHandler === 'menu'))) {
            handler = layout.overflowHandler;
            if (handler && handler.menu) {
                items = items.concat(handler.menu.getRefItems(deep));
            }
        }
        return items;
    },
    
    
    
    lookupComponent: function(c) {
        var args = arguments;
        if (typeof c === 'string') {
            var T = Ext.toolbar.Toolbar,
                shortcut = T.shortcutsHV[this.vertical ? 1 : 0][c] || T.shortcuts[c];
            if (typeof shortcut === 'string') {
                c = {
                    xtype: shortcut
                };
            } else if (shortcut) {
                c = Ext.apply({}, shortcut);
            } else {
                c = {
                    xtype: 'tbtext',
                    text: c
                };
            }
            this.applyDefaults(c);
            
            args = [
                c
            ];
        }
        return this.callParent(args);
    },
    onBeforeAdd: function(component) {
        var me = this,
            isFooter = me.ui === 'footer',
            defaultButtonUI = isFooter ? me.defaultFooterButtonUI : me.defaultButtonUI;
        if (component.isSegmentedButton) {
            if (component.getDefaultUI() === 'default' && !component.config.hasOwnProperty('defaultUI')) {
                component.setDefaultUI(defaultButtonUI);
            }
        } else if (component.ui === 'default' && !component.hasOwnProperty('ui')) {
            if (component.isButton) {
                component.ui = defaultButtonUI;
            } else if (component.isFormField) {
                component.ui = isFooter ? me.defaultFooterFieldUI : me.defaultFieldUI;
            }
        }
        
        if (component instanceof Ext.toolbar.Separator) {
            component.setUI((me.vertical) ? 'vertical' : 'horizontal');
        }
        me.callParent(arguments);
    },
    onAdd: function(component) {
        this.callParent(arguments);
        this.trackMenu(component);
    },
    onRemove: function(c) {
        this.callParent(arguments);
        this.trackMenu(c, true);
    },
    privates: {
        
        applyDefaults: function(c) {
            if (!Ext.isString(c)) {
                c = this.callParent(arguments);
            }
            return c;
        },
        
        trackMenu: function(item, remove) {
            var me = this;
            if (me.trackMenus && item.menu) {
                item[remove ? 'un' : 'on']({
                    mouseover: me.onButtonOver,
                    menushow: me.onButtonMenuShow,
                    menuhide: me.onButtonMenuHide,
                    scope: me
                });
            }
        },
        getChildItemsToDisable: function() {
            return this.items.getRange();
        },
        
        onButtonOver: function(btn, e) {
            if (this.activeMenuBtn && this.activeMenuBtn !== btn) {
                this.activeMenuBtn.hideMenu();
                btn.focus();
                btn.showMenu(e);
                this.activeMenuBtn = btn;
            }
        },
        
        onButtonMenuShow: function(btn) {
            this.activeMenuBtn = btn;
        },
        
        onButtonMenuHide: function(btn) {
            this.activeMenuBtn = null;
        }
    }
});



Ext.define('Ext.dd.DragDrop', {
    requires: [
        'Ext.dd.DragDropManager'
    ],
    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.init(id, sGroup, config);
        }
    },
    
    
    id: null,
    
    config: null,
    
    dragElId: null,
    
    handleElId: null,
    
    invalidHandleTypes: null,
    
    invalidHandleIds: null,
    
    invalidHandleClasses: null,
    
    startPageX: 0,
    
    startPageY: 0,
    
    groups: null,
    
    locked: false,
    
    lock: function() {
        this.locked = true;
    },
    
    moveOnly: false,
    
    unlock: function() {
        this.locked = false;
    },
    
    isTarget: true,
    
    padding: null,
    
    _domRef: null,
    
    __ygDragDrop: true,
    
    constrainX: false,
    
    constrainY: false,
    
    minX: 0,
    
    maxX: 0,
    
    minY: 0,
    
    maxY: 0,
    
    maintainOffset: false,
    
    xTicks: null,
    
    yTicks: null,
    
    primaryButtonOnly: true,
    
    available: false,
    
    hasOuterHandles: false,
    triggerEvent: 'mousedown',
    
    b4StartDrag: function(x, y) {},
    
    startDrag: function(x, y) {},
    
    
    b4Drag: function(e) {},
    
    onDrag: function(e) {},
    
    
    onDragEnter: function(e, id) {},
    
    
    b4DragOver: function(e) {},
    
    onDragOver: function(e, id) {},
    
    
    b4DragOut: function(e) {},
    
    onDragOut: function(e, id) {},
    
    
    b4DragDrop: function(e) {},
    
    onDragDrop: function(e, id) {},
    
    
    onInvalidDrop: function(e) {},
    
    
    b4EndDrag: function(e) {},
    
    endDrag: function(e) {},
    
    
    b4MouseDown: function(e) {},
    
    onMouseDown: function(e) {},
    
    
    onMouseUp: function(e) {},
    
    
    onAvailable: function() {},
    
    defaultPadding: {
        left: 0,
        right: 0,
        top: 0,
        bottom: 0
    },
    
    constrainTo: function(constrainTo, pad, inContent) {
        if (Ext.isNumber(pad)) {
            pad = {
                left: pad,
                right: pad,
                top: pad,
                bottom: pad
            };
        }
        pad = pad || this.defaultPadding;
        var ddBox = Ext.get(this.getEl()).getBox(),
            constrainEl = Ext.get(constrainTo),
            s = constrainEl.getScroll(),
            c,
            constrainDom = constrainEl.dom,
            xy, topSpace, leftSpace;
        if (constrainDom == document.body) {
            c = {
                x: s.left,
                y: s.top,
                width: Ext.Element.getViewportWidth(),
                height: Ext.Element.getViewportHeight()
            };
        } else {
            xy = constrainEl.getXY();
            c = {
                x: xy[0],
                y: xy[1],
                width: constrainDom.clientWidth,
                height: constrainDom.clientHeight
            };
        }
        topSpace = ddBox.y - c.y;
        leftSpace = ddBox.x - c.x;
        this.resetConstraints();
        this.setXConstraint(leftSpace - (pad.left || 0), 
        c.width - leftSpace - ddBox.width - (pad.right || 0), 
        this.xTickSize);
        this.setYConstraint(topSpace - (pad.top || 0), 
        c.height - topSpace - ddBox.height - (pad.bottom || 0), 
        this.yTickSize);
    },
    
    getEl: function() {
        if (!this._domRef) {
            this._domRef = Ext.getDom(this.id);
        }
        return this._domRef;
    },
    
    getDragEl: function() {
        return Ext.getDom(this.dragElId);
    },
    
    init: function(id, sGroup, config) {
        var me = this;
        me.el = me.el || Ext.get(id);
        
        me.initTarget(id, sGroup, config);
        Ext.get(me.id).on(me.triggerEvent, me.handleMouseDown, me);
    },
    
    initTarget: function(id, sGroup, config) {
        
        this.config = config || {};
        
        this.DDMInstance = Ext.dd.DragDropManager;
        
        this.groups = {};
        
        
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        
        this.id = id;
        
        this.addToGroup((sGroup) ? sGroup : "default");
        
        
        this.handleElId = id;
        
        this.setDragElId(id);
        
        this.invalidHandleTypes = {
            A: "A"
        };
        this.invalidHandleIds = {};
        this.invalidHandleClasses = [];
        this.applyConfig();
        this.handleOnAvailable();
    },
    
    applyConfig: function() {
        
        
        this.padding = this.config.padding || [
            0,
            0,
            0,
            0
        ];
        this.isTarget = (this.config.isTarget !== false);
        this.maintainOffset = (this.config.maintainOffset);
        this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
    },
    
    handleOnAvailable: function() {
        this.available = true;
        this.resetConstraints();
        this.onAvailable();
    },
    
    setPadding: function(iTop, iRight, iBot, iLeft) {
        
        if (!iRight && 0 !== iRight) {
            this.padding = [
                iTop,
                iTop,
                iTop,
                iTop
            ];
        } else if (!iBot && 0 !== iBot) {
            this.padding = [
                iTop,
                iRight,
                iTop,
                iRight
            ];
        } else {
            this.padding = [
                iTop,
                iRight,
                iBot,
                iLeft
            ];
        }
    },
    
    setInitPosition: function(diffX, diffY) {
        var el = this.getEl(),
            dx, dy, p;
        if (!this.DDMInstance.verifyEl(el)) {
            return;
        }
        dx = diffX || 0;
        dy = diffY || 0;
        p = Ext.fly(el).getXY();
        this.initPageX = p[0] - dx;
        this.initPageY = p[1] - dy;
        this.lastPageX = p[0];
        this.lastPageY = p[1];
        this.setStartPosition(p);
    },
    
    setStartPosition: function(pos) {
        var p = pos || Ext.fly(this.getEl()).getXY();
        this.deltaSetXY = null;
        this.startPageX = p[0];
        this.startPageY = p[1];
    },
    
    addToGroup: function(sGroup) {
        this.groups[sGroup] = true;
        this.DDMInstance.regDragDrop(this, sGroup);
    },
    
    removeFromGroup: function(sGroup) {
        if (this.groups[sGroup]) {
            delete this.groups[sGroup];
        }
        this.DDMInstance.removeDDFromGroup(this, sGroup);
    },
    
    setDragElId: function(id) {
        this.dragElId = id;
    },
    
    setHandleElId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        this.handleElId = id;
        this.DDMInstance.regHandle(this.id, id);
    },
    
    setOuterHandleElId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        Ext.get(id).on(this.triggerEvent, this.handleMouseDown, this);
        this.setHandleElId(id);
        this.hasOuterHandles = true;
    },
    
    unreg: function() {
        var me = this,
            el;
        if (me._domRef) {
            el = Ext.fly(me.id);
            if (el) {
                el.un(me.triggerEvent, me.handleMouseDown, me);
            }
        }
        me._domRef = null;
        me.DDMInstance._remove(me, me.autoGroup);
    },
    
    destroy: function() {
        this.unreg();
        this.isDestroyed = true;
    },
    
    isLocked: function() {
        return (this.DDMInstance.isLocked() || this.locked);
    },
    
    handleMouseDown: function(e, oDD) {
        var me = this;
        if ((me.primaryButtonOnly && e.button != 0) || me.isLocked()) {
            return;
        }
        me.DDMInstance.refreshCache(me.groups);
        if (me.hasOuterHandles || me.DDMInstance.isOverTarget(e.getPoint(), me)) {
            if (me.clickValidator(e)) {
                
                me.setStartPosition();
                me.b4MouseDown(e);
                me.onMouseDown(e);
                me.DDMInstance.handleMouseDown(e, me);
                me.DDMInstance.stopEvent(e);
            }
        }
    },
    clickValidator: function(e) {
        var target = e.getTarget();
        return (this.isValidHandleChild(target) && (this.id == this.handleElId || this.DDMInstance.handleWasClicked(target, this.id)));
    },
    
    addInvalidHandleType: function(tagName) {
        var type = tagName.toUpperCase();
        this.invalidHandleTypes[type] = type;
    },
    
    addInvalidHandleId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        this.invalidHandleIds[id] = id;
    },
    
    addInvalidHandleClass: function(cssClass) {
        this.invalidHandleClasses.push(cssClass);
    },
    
    removeInvalidHandleType: function(tagName) {
        var type = tagName.toUpperCase();
        
        delete this.invalidHandleTypes[type];
    },
    
    removeInvalidHandleId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        delete this.invalidHandleIds[id];
    },
    
    removeInvalidHandleClass: function(cssClass) {
        for (var i = 0,
            len = this.invalidHandleClasses.length; i < len; ++i) {
            if (this.invalidHandleClasses[i] == cssClass) {
                delete this.invalidHandleClasses[i];
            }
        }
    },
    
    isValidHandleChild: function(node) {
        var valid = true,
            nodeName, i, len;
        
        try {
            nodeName = node.nodeName.toUpperCase();
        } catch (e) {
            nodeName = node.nodeName;
        }
        valid = valid && !this.invalidHandleTypes[nodeName];
        valid = valid && !this.invalidHandleIds[node.id];
        for (i = 0 , len = this.invalidHandleClasses.length; valid && i < len; ++i) {
            valid = !Ext.fly(node).hasCls(this.invalidHandleClasses[i]);
        }
        return valid;
    },
    
    setXTicks: function(iStartX, iTickSize) {
        this.xTicks = [];
        this.xTickSize = iTickSize;
        var tickMap = {},
            i;
        for (i = this.initPageX; i >= this.minX; i = i - iTickSize) {
            if (!tickMap[i]) {
                this.xTicks[this.xTicks.length] = i;
                tickMap[i] = true;
            }
        }
        for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
            if (!tickMap[i]) {
                this.xTicks[this.xTicks.length] = i;
                tickMap[i] = true;
            }
        }
        Ext.Array.sort(this.xTicks, this.DDMInstance.numericSort);
    },
    
    setYTicks: function(iStartY, iTickSize) {
        this.yTicks = [];
        this.yTickSize = iTickSize;
        var tickMap = {},
            i;
        for (i = this.initPageY; i >= this.minY; i = i - iTickSize) {
            if (!tickMap[i]) {
                this.yTicks[this.yTicks.length] = i;
                tickMap[i] = true;
            }
        }
        for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
            if (!tickMap[i]) {
                this.yTicks[this.yTicks.length] = i;
                tickMap[i] = true;
            }
        }
        Ext.Array.sort(this.yTicks, this.DDMInstance.numericSort);
    },
    
    setXConstraint: function(iLeft, iRight, iTickSize) {
        this.leftConstraint = iLeft;
        this.rightConstraint = iRight;
        this.minX = this.initPageX - iLeft;
        this.maxX = this.initPageX + iRight;
        if (iTickSize) {
            this.setXTicks(this.initPageX, iTickSize);
        }
        this.constrainX = true;
    },
    
    clearConstraints: function() {
        this.constrainX = false;
        this.constrainY = false;
        this.clearTicks();
    },
    
    clearTicks: function() {
        this.xTicks = null;
        this.yTicks = null;
        this.xTickSize = 0;
        this.yTickSize = 0;
    },
    
    setYConstraint: function(iUp, iDown, iTickSize) {
        this.topConstraint = iUp;
        this.bottomConstraint = iDown;
        this.minY = this.initPageY - iUp;
        this.maxY = this.initPageY + iDown;
        if (iTickSize) {
            this.setYTicks(this.initPageY, iTickSize);
        }
        this.constrainY = true;
    },
    
    resetConstraints: function() {
        
        if (this.initPageX || this.initPageX === 0) {
            
            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0,
                dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
            this.setInitPosition(dx, dy);
        } else 
        {
            this.setInitPosition();
        }
        if (this.constrainX) {
            this.setXConstraint(this.leftConstraint, this.rightConstraint, this.xTickSize);
        }
        if (this.constrainY) {
            this.setYConstraint(this.topConstraint, this.bottomConstraint, this.yTickSize);
        }
    },
    
    getTick: function(val, tickArray) {
        if (!tickArray) {
            
            
            return val;
        } else if (tickArray[0] >= val) {
            
            
            return tickArray[0];
        } else {
            var i, len, next, diff1, diff2;
            for (i = 0 , len = tickArray.length; i < len; ++i) {
                next = i + 1;
                if (tickArray[next] && tickArray[next] >= val) {
                    diff1 = val - tickArray[i];
                    diff2 = tickArray[next] - val;
                    return (diff2 > diff1) ? tickArray[i] : tickArray[next];
                }
            }
            
            
            return tickArray[tickArray.length - 1];
        }
    },
    
    toString: function() {
        return ("DragDrop " + this.id);
    }
});



Ext.define('Ext.dd.DD', {
    extend: 'Ext.dd.DragDrop',
    requires: [
        'Ext.dd.DragDropManager'
    ],
    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.init(id, sGroup, config);
        }
    },
    
    scroll: true,
    
    autoOffset: function(iPageX, iPageY) {
        var x = iPageX - this.startPageX,
            y = iPageY - this.startPageY;
        this.setDelta(x, y);
    },
    
    setDelta: function(iDeltaX, iDeltaY) {
        this.deltaX = iDeltaX;
        this.deltaY = iDeltaY;
    },
    
    setDragElPos: function(iPageX, iPageY) {
        
        
        var el = this.getDragEl();
        this.alignElWithMouse(el, iPageX, iPageY);
    },
    
    alignElWithMouse: function(el, iPageX, iPageY) {
        var oCoord = this.getTargetCoord(iPageX, iPageY),
            fly = el.dom ? el : Ext.fly(el, '_dd'),
            elSize = fly.getSize(),
            EL = Ext.Element,
            vpSize, aCoord, newLeft, newTop;
        if (!this.deltaSetXY) {
            vpSize = this.cachedViewportSize = {
                width: EL.getDocumentWidth(),
                height: EL.getDocumentHeight()
            };
            aCoord = [
                Math.max(0, Math.min(oCoord.x, vpSize.width - elSize.width)),
                Math.max(0, Math.min(oCoord.y, vpSize.height - elSize.height))
            ];
            fly.setXY(aCoord);
            newLeft = this.getLocalX(fly);
            newTop = fly.getLocalY();
            this.deltaSetXY = [
                newLeft - oCoord.x,
                newTop - oCoord.y
            ];
        } else {
            vpSize = this.cachedViewportSize;
            this.setLocalXY(fly, Math.max(0, Math.min(oCoord.x + this.deltaSetXY[0], vpSize.width - elSize.width)), Math.max(0, Math.min(oCoord.y + this.deltaSetXY[1], vpSize.height - elSize.height)));
        }
        this.cachePosition(oCoord.x, oCoord.y);
        this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
        return oCoord;
    },
    
    cachePosition: function(iPageX, iPageY) {
        if (iPageX) {
            this.lastPageX = iPageX;
            this.lastPageY = iPageY;
        } else {
            var aCoord = Ext.fly(this.getEl()).getXY();
            this.lastPageX = aCoord[0];
            this.lastPageY = aCoord[1];
        }
    },
    
    autoScroll: function(x, y, h, w) {
        if (this.scroll) {
            
            var clientH = Ext.Element.getViewportHeight(),
                
                clientW = Ext.Element.getViewportWidth(),
                
                st = this.DDMInstance.getScrollTop(),
                
                sl = this.DDMInstance.getScrollLeft(),
                
                bot = h + y,
                
                right = w + x,
                
                
                
                toBot = (clientH + st - y - this.deltaY),
                
                toRight = (clientW + sl - x - this.deltaX),
                
                
                thresh = 40,
                
                
                
                scrAmt = (document.all) ? 80 : 30;
            
            
            if (bot > clientH && toBot < thresh) {
                window.scrollTo(sl, st + scrAmt);
            }
            
            
            if (y < st && st > 0 && y - st < thresh) {
                window.scrollTo(sl, st - scrAmt);
            }
            
            
            if (right > clientW && toRight < thresh) {
                window.scrollTo(sl + scrAmt, st);
            }
            
            
            if (x < sl && sl > 0 && x - sl < thresh) {
                window.scrollTo(sl - scrAmt, st);
            }
        }
    },
    
    getTargetCoord: function(iPageX, iPageY) {
        var x = iPageX - this.deltaX,
            y = iPageY - this.deltaY;
        if (this.constrainX) {
            if (x < this.minX) {
                x = this.minX;
            }
            if (x > this.maxX) {
                x = this.maxX;
            }
        }
        if (this.constrainY) {
            if (y < this.minY) {
                y = this.minY;
            }
            if (y > this.maxY) {
                y = this.maxY;
            }
        }
        x = this.getTick(x, this.xTicks);
        y = this.getTick(y, this.yTicks);
        return {
            x: x,
            y: y
        };
    },
    
    applyConfig: function() {
        this.callParent();
        this.scroll = (this.config.scroll !== false);
    },
    
    b4MouseDown: function(e) {
        
        var xy = e.getXY();
        this.autoOffset(xy[0], xy[1]);
    },
    
    b4Drag: function(e) {
        var xy = e.getXY();
        this.setDragElPos(xy[0], xy[1]);
    },
    toString: function() {
        return ("DD " + this.id);
    },
    getLocalX: function(el) {
        return el.getLocalX();
    },
    setLocalXY: function(el, x, y) {
        el.setLocalXY(x, y);
    }
});



Ext.define('Ext.dd.DDProxy', {
    extend: 'Ext.dd.DD',
    statics: {
        
        dragElId: "ygddfdiv"
    },
    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.init(id, sGroup, config);
            this.initFrame();
        }
    },
    
    resizeFrame: true,
    
    centerFrame: false,
    
    createFrame: function() {
        var self = this,
            body = document.body,
            div, s;
        if (!body || !body.firstChild) {
            Ext.defer(function() {
                self.createFrame();
            }, 50);
            return;
        }
        div = this.getDragEl();
        if (!div) {
            div = document.createElement("div");
            div.id = this.dragElId;
            div.setAttribute('role', 'presentation');
            s = div.style;
            s.position = "absolute";
            s.visibility = "hidden";
            s.cursor = "move";
            s.border = "2px solid #aaa";
            s.zIndex = 999;
            
            
            
            body.insertBefore(div, body.firstChild);
        }
    },
    
    initFrame: function() {
        this.createFrame();
    },
    applyConfig: function() {
        this.callParent();
        this.resizeFrame = (this.config.resizeFrame !== false);
        this.centerFrame = (this.config.centerFrame);
        this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
    },
    
    showFrame: function(iPageX, iPageY) {
        var el = this.getEl(),
            dragEl = this.getDragEl(),
            s = dragEl.style;
        this._resizeProxy();
        if (this.centerFrame) {
            this.setDelta(Math.round(parseInt(s.width, 10) / 2), Math.round(parseInt(s.height, 10) / 2));
        }
        this.setDragElPos(iPageX, iPageY);
        Ext.fly(dragEl).show();
    },
    
    _resizeProxy: function() {
        if (this.resizeFrame) {
            var el = this.getEl();
            Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
        }
    },
    
    b4MouseDown: function(e) {
        var xy = e.getXY(),
            x = xy[0],
            y = xy[1];
        this.autoOffset(x, y);
        this.setDragElPos(x, y);
    },
    
    b4StartDrag: function(x, y) {
        
        this.showFrame(x, y);
    },
    
    b4EndDrag: function(e) {
        Ext.fly(this.getDragEl()).hide();
    },
    
    
    
    endDrag: function(e) {
        var lel = this.getEl(),
            del = this.getDragEl();
        
        del.style.visibility = "";
        this.beforeMove();
        
        
        lel.style.visibility = "hidden";
        Ext.dd.DDM.moveToEl(lel, del);
        del.style.visibility = "hidden";
        lel.style.visibility = "";
        this.afterDrag();
    },
    beforeMove: function() {},
    afterDrag: function() {},
    toString: function() {
        return ("DDProxy " + this.id);
    }
});


Ext.define('Ext.dd.StatusProxy', {
    extend: 'Ext.Component',
    animRepair: false,
    childEls: [
        'ghost'
    ],
    renderTpl: [
        '<div class="' + Ext.baseCSSPrefix + 'dd-drop-icon" role="presentation"></div>' + '<div id="{id}-ghost" data-ref="ghost" class="' + Ext.baseCSSPrefix + 'dd-drag-ghost" role="presentation"></div>'
    ],
    repairCls: Ext.baseCSSPrefix + 'dd-drag-repair',
    ariaRole: 'presentation',
    
    constructor: function(config) {
        var me = this;
        config = config || {};
        Ext.apply(me, {
            hideMode: 'visibility',
            hidden: true,
            floating: true,
            id: me.id || Ext.id(),
            cls: Ext.baseCSSPrefix + 'dd-drag-proxy ' + this.dropNotAllowed,
            shadow: config.shadow || false,
            renderTo: Ext.getDetachedBody()
        });
        me.callParent(arguments);
        this.dropStatus = this.dropNotAllowed;
    },
    
    dropAllowed: Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed: Ext.baseCSSPrefix + 'dd-drop-nodrop',
    
    setStatus: function(cssClass) {
        cssClass = cssClass || this.dropNotAllowed;
        if (this.dropStatus != cssClass) {
            this.el.replaceCls(this.dropStatus, cssClass);
            this.dropStatus = cssClass;
        }
    },
    
    reset: function(clearGhost) {
        var me = this,
            clsPrefix = Ext.baseCSSPrefix + 'dd-drag-proxy ';
        me.el.replaceCls(clsPrefix + me.dropAllowed, clsPrefix + me.dropNotAllowed);
        me.dropStatus = me.dropNotAllowed;
        if (clearGhost) {
            me.ghost.setHtml('');
        }
    },
    
    update: function(html) {
        if (typeof html == "string") {
            this.ghost.setHtml(html);
        } else {
            this.ghost.setHtml('');
            html.style.margin = "0";
            this.ghost.dom.appendChild(html);
        }
        var el = this.ghost.dom.firstChild;
        if (el) {
            Ext.fly(el).setStyle('float', 'none');
        }
    },
    
    getGhost: function() {
        return this.ghost;
    },
    
    hide: function(clear) {
        this.callParent();
        if (clear) {
            this.reset(true);
        }
    },
    
    stop: function() {
        if (this.anim && this.anim.isAnimated && this.anim.isAnimated()) {
            this.anim.stop();
        }
    },
    
    sync: function() {
        this.el.syncUnderlays();
    },
    
    repair: function(xy, callback, scope) {
        var me = this;
        me.callback = callback;
        me.scope = scope;
        if (xy && me.animRepair !== false) {
            me.el.addCls(me.repairCls);
            me.el.setUnderlaysVisible(false);
            me.anim = me.el.animate({
                duration: me.repairDuration || 500,
                easing: 'ease-out',
                to: {
                    x: xy[0],
                    y: xy[1]
                },
                stopAnimation: true,
                callback: me.afterRepair,
                scope: me
            });
        } else {
            me.afterRepair();
        }
    },
    
    afterRepair: function() {
        var me = this;
        me.hide(true);
        me.el.removeCls(me.repairCls);
        if (typeof me.callback == "function") {
            me.callback.call(me.scope || me);
        }
        delete me.callback;
        delete me.scope;
    }
});


Ext.define('Ext.dd.DragSource', {
    extend: 'Ext.dd.DDProxy',
    requires: [
        'Ext.dd.StatusProxy',
        'Ext.dd.DragDropManager'
    ],
    
    
    
    dropAllowed: Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed: Ext.baseCSSPrefix + 'dd-drop-nodrop',
    
    animRepair: true,
    
    repairHighlightColor: 'c3daf9',
    
    constructor: function(el, config) {
        this.el = Ext.get(el);
        if (!this.dragData) {
            this.dragData = {};
        }
        Ext.apply(this, config);
        if (!this.proxy) {
            this.proxy = new Ext.dd.StatusProxy({
                id: this.el.id + '-drag-status-proxy',
                animRepair: this.animRepair
            });
        }
        this.callParent([
            this.el.dom,
            this.ddGroup || this.group,
            {
                dragElId: this.proxy.id,
                resizeFrame: false,
                isTarget: false,
                scroll: this.scroll === true
            }
        ]);
        this.dragging = false;
    },
    
    getDragData: function(e) {
        return this.dragData;
    },
    
    onDragEnter: function(e, id) {
        var target = Ext.dd.DragDropManager.getDDById(id),
            status;
        this.cachedTarget = target;
        if (this.beforeDragEnter(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                status = target.notifyEnter(this, e, this.dragData);
                this.proxy.setStatus(status);
            } else {
                this.proxy.setStatus(this.dropAllowed);
            }
            if (this.afterDragEnter) {
                
                this.afterDragEnter(target, e, id);
            }
        }
    },
    
    beforeDragEnter: function(target, e, id) {
        return true;
    },
    
    onDragOver: function(e, id) {
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id),
            status;
        if (this.beforeDragOver(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                status = target.notifyOver(this, e, this.dragData);
                this.proxy.setStatus(status);
            }
            if (this.afterDragOver) {
                
                this.afterDragOver(target, e, id);
            }
        }
    },
    
    beforeDragOver: function(target, e, id) {
        return true;
    },
    
    onDragOut: function(e, id) {
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
        if (this.beforeDragOut(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                target.notifyOut(this, e, this.dragData);
            }
            this.proxy.reset();
            if (this.afterDragOut) {
                
                this.afterDragOut(target, e, id);
            }
        }
        this.cachedTarget = null;
    },
    
    beforeDragOut: function(target, e, id) {
        return true;
    },
    
    onDragDrop: function(e, id) {
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
        if (this.beforeDragDrop(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                if (target.notifyDrop(this, e, this.dragData) !== false) {
                    
                    this.onValidDrop(target, e, id);
                } else {
                    this.onInvalidDrop(target, e, id);
                }
            } else {
                this.onValidDrop(target, e, id);
            }
            if (this.afterDragDrop) {
                
                this.afterDragDrop(target, e, id);
            }
        }
        delete this.cachedTarget;
    },
    
    beforeDragDrop: function(target, e, id) {
        return true;
    },
    
    onValidDrop: function(target, e, id) {
        this.hideProxy();
        if (this.afterValidDrop) {
            
            this.afterValidDrop(target, e, id);
        }
    },
    
    getRepairXY: function(e, data) {
        return this.el.getXY();
    },
    
    onInvalidDrop: function(target, e, id) {
        
        
        
        var me = this;
        if (!e) {
            e = target;
            target = null;
            id = e.getTarget().id;
        }
        if (me.beforeInvalidDrop(target, e, id) !== false) {
            if (me.cachedTarget) {
                if (me.cachedTarget.isNotifyTarget) {
                    me.cachedTarget.notifyOut(me, e, me.dragData);
                }
                me.cacheTarget = null;
            }
            me.proxy.repair(me.getRepairXY(e, me.dragData), me.afterRepair, me);
            if (me.afterInvalidDrop) {
                
                me.afterInvalidDrop(e, id);
            }
        }
    },
    
    afterRepair: function() {
        var me = this;
        if (Ext.enableFx) {
            me.el.highlight(me.repairHighlightColor);
        }
        me.dragging = false;
    },
    
    beforeInvalidDrop: function(target, e, id) {
        return true;
    },
    
    handleMouseDown: function(e) {
        if (this.dragging) {
            return;
        }
        var data = this.getDragData(e);
        if (data && this.onBeforeDrag(data, e) !== false) {
            this.dragData = data;
            this.proxy.stop();
            this.callParent(arguments);
        }
    },
    
    onBeforeDrag: function(data, e) {
        return true;
    },
    
    onStartDrag: Ext.emptyFn,
    alignElWithMouse: function() {
        this.proxy.ensureAttachedToBody(true);
        return this.callParent(arguments);
    },
    
    startDrag: function(x, y) {
        this.proxy.reset();
        this.proxy.hidden = false;
        this.dragging = true;
        this.proxy.update("");
        this.onInitDrag(x, y);
        this.proxy.show();
    },
    
    onInitDrag: function(x, y) {
        var clone = this.el.dom.cloneNode(true);
        clone.id = Ext.id();
        
        this.proxy.update(clone);
        this.onStartDrag(x, y);
        return true;
    },
    
    getProxy: function() {
        return this.proxy;
    },
    
    hideProxy: function() {
        this.proxy.hide();
        this.proxy.reset(true);
        this.dragging = false;
    },
    
    triggerCacheRefresh: function() {
        Ext.dd.DDM.refreshCache(this.groups);
    },
    
    b4EndDrag: function(e) {},
    
    endDrag: function(e) {
        this.onEndDrag(this.dragData, e);
    },
    
    onEndDrag: function(data, e) {},
    
    autoOffset: function(x, y) {
        this.setDelta(-12, -20);
    },
    destroy: function() {
        this.callParent();
        Ext.destroy(this.proxy);
    }
});


Ext.define('Ext.panel.Proxy', {
    alternateClassName: 'Ext.dd.PanelProxy',
    
    moveOnDrag: true,
    
    constructor: function(panel, config) {
        var me = this;
        
        me.panel = panel;
        me.id = me.panel.id + '-ddproxy';
        Ext.apply(me, config);
    },
    
    insertProxy: true,
    
    setStatus: Ext.emptyFn,
    reset: Ext.emptyFn,
    update: Ext.emptyFn,
    stop: Ext.emptyFn,
    sync: Ext.emptyFn,
    
    getEl: function() {
        return this.ghost.el;
    },
    
    getGhost: function() {
        return this.ghost;
    },
    
    getProxy: function() {
        return this.proxy;
    },
    
    hide: function() {
        var me = this;
        if (me.ghost) {
            if (me.proxy) {
                me.proxy.destroy();
                delete me.proxy;
            }
            
            me.panel.unghost(null, me.moveOnDrag);
            delete me.ghost;
        }
    },
    
    show: function() {
        var me = this,
            panelSize;
        if (!me.ghost) {
            panelSize = me.panel.getSize();
            me.panel.el.setVisibilityMode(Ext.Element.DISPLAY);
            me.ghost = me.panel.ghost();
            if (me.insertProxy) {
                
                
                me.proxy = me.panel.el.insertSibling({
                    role: 'presentation',
                    cls: Ext.baseCSSPrefix + 'panel-dd-spacer'
                });
                me.proxy.setSize(panelSize);
            }
        }
    },
    
    repair: function(xy, callback, scope) {
        this.hide();
        Ext.callback(callback, scope || this);
    },
    
    moveProxy: function(parentNode, before) {
        if (this.proxy) {
            parentNode.insertBefore(this.proxy.dom, before);
        }
    }
});


Ext.define('Ext.panel.DD', {
    extend: 'Ext.dd.DragSource',
    requires: [
        'Ext.panel.Proxy'
    ],
    constructor: function(panel, cfg) {
        var me = this;
        me.panel = panel;
        me.dragData = {
            panel: panel
        };
        me.panelProxy = new Ext.panel.Proxy(panel,cfg);
        me.proxy = me.panelProxy.proxy;
        me.callParent([
            panel.el,
            cfg
        ]);
        me.setupEl(panel);
    },
    setupEl: function(panel) {
        var me = this,
            header = panel.header,
            el = panel.body;
        if (header) {
            me.setHandleElId(header.id);
            el = header.el;
        }
        if (el) {
            el.setStyle('cursor', 'move');
            me.scroll = false;
        } else {
            
            panel.on('boxready', me.setupEl, me, {
                single: true
            });
        }
    },
    showFrame: Ext.emptyFn,
    startDrag: Ext.emptyFn,
    b4StartDrag: function(x, y) {
        this.panelProxy.show();
    },
    b4MouseDown: function(e) {
        var xy = e.getXY(),
            x = xy[0],
            y = xy[1];
        this.autoOffset(x, y);
    },
    onInitDrag: function(x, y) {
        this.onStartDrag(x, y);
        return true;
    },
    createFrame: Ext.emptyFn,
    getDragEl: function(e) {
        var ghost = this.panelProxy.ghost;
        if (ghost) {
            return ghost.el.dom;
        }
    },
    endDrag: function(e) {
        this.panelProxy.hide();
        this.panel.saveState();
    },
    autoOffset: function(x, y) {
        x -= this.startPageX;
        y -= this.startPageY;
        this.setDelta(x, y);
    },
    
    
    onInvalidDrop: function(target, e, id) {
        var me = this;
        if (me.beforeInvalidDrop(target, e, id) !== false) {
            if (me.cachedTarget) {
                if (me.cachedTarget.isNotifyTarget) {
                    me.cachedTarget.notifyOut(me, e, me.dragData);
                }
                me.cacheTarget = null;
            }
            if (me.afterInvalidDrop) {
                
                me.afterInvalidDrop(e, id);
            }
        }
    }
});


Ext.define('Ext.layout.component.Dock', {
    
    extend: 'Ext.layout.component.Component',
    alias: 'layout.dock',
    alternateClassName: 'Ext.layout.component.AbstractDock',
    
    type: 'dock',
    horzAxisProps: {
        name: 'horz',
        oppositeName: 'vert',
        dockBegin: 'left',
        dockEnd: 'right',
        horizontal: true,
        marginBegin: 'margin-left',
        maxSize: 'maxWidth',
        minSize: 'minWidth',
        pos: 'x',
        setSize: 'setWidth',
        shrinkWrapDock: 'shrinkWrapDockWidth',
        size: 'width',
        sizeModel: 'widthModel'
    },
    vertAxisProps: {
        name: 'vert',
        oppositeName: 'horz',
        dockBegin: 'top',
        dockEnd: 'bottom',
        horizontal: false,
        marginBegin: 'margin-top',
        maxSize: 'maxHeight',
        minSize: 'minHeight',
        pos: 'y',
        setSize: 'setHeight',
        shrinkWrapDock: 'shrinkWrapDockHeight',
        size: 'height',
        sizeModel: 'heightModel'
    },
    initializedBorders: -1,
    horizontalCollapsePolicy: {
        width: true,
        x: true
    },
    verticalCollapsePolicy: {
        height: true,
        y: true
    },
    finishRender: function() {
        var me = this,
            target, items;
        me.callParent();
        target = me.getRenderTarget();
        items = me.getDockedItems();
        me.finishRenderItems(target, items);
    },
    isItemBoxParent: function(itemContext) {
        return true;
    },
    isItemShrinkWrap: function(item) {
        return true;
    },
    noBorderClasses: [
        Ext.baseCSSPrefix + 'docked-noborder-top',
        Ext.baseCSSPrefix + 'docked-noborder-right',
        Ext.baseCSSPrefix + 'docked-noborder-bottom',
        Ext.baseCSSPrefix + 'docked-noborder-left'
    ],
    noBorderClassesSides: {
        top: Ext.baseCSSPrefix + 'docked-noborder-top',
        right: Ext.baseCSSPrefix + 'docked-noborder-right',
        bottom: Ext.baseCSSPrefix + 'docked-noborder-bottom',
        left: Ext.baseCSSPrefix + 'docked-noborder-left'
    },
    borderWidthProps: {
        top: 'border-top-width',
        right: 'border-right-width',
        bottom: 'border-bottom-width',
        left: 'border-left-width'
    },
    _itemCls: Ext.baseCSSPrefix + 'docked',
    handleItemBorders: function() {
        var me = this,
            owner = me.owner,
            borders, docked,
            lastItems = me.lastDockedItems,
            oldBorders = me.borders,
            currentGeneration = owner.dockedItems.generation,
            noBorderClassesSides = me.noBorderClassesSides,
            borderWidthProps = me.borderWidthProps,
            i, ln, item, dock, side,
            collapsed = me.collapsed;
        if (me.initializedBorders == currentGeneration || (owner.border && !owner.manageBodyBorders)) {
            return;
        }
        me.initializedBorders = currentGeneration;
        
        me.collapsed = false;
        me.lastDockedItems = docked = me.getLayoutItems();
        me.collapsed = collapsed;
        borders = {
            top: [],
            right: [],
            bottom: [],
            left: []
        };
        for (i = 0 , ln = docked.length; i < ln; i++) {
            item = docked[i];
            dock = item.dock;
            if (item.ignoreBorderManagement) {
                
                continue;
            }
            if (!borders[dock].satisfied) {
                borders[dock].push(item);
                borders[dock].satisfied = true;
            }
            if (!borders.top.satisfied && dock !== 'bottom') {
                borders.top.push(item);
            }
            if (!borders.right.satisfied && dock !== 'left') {
                borders.right.push(item);
            }
            if (!borders.bottom.satisfied && dock !== 'top') {
                borders.bottom.push(item);
            }
            if (!borders.left.satisfied && dock !== 'right') {
                borders.left.push(item);
            }
        }
        if (lastItems) {
            for (i = 0 , ln = lastItems.length; i < ln; i++) {
                item = lastItems[i];
                if (!item.isDestroyed && !item.ignoreBorderManagement && !owner.manageBodyBorders) {
                    item.removeCls(me.noBorderClasses);
                }
            }
        }
        if (oldBorders) {
            for (side in oldBorders) {
                if (owner.manageBodyBorders && oldBorders[side].satisfied) {
                    owner.setBodyStyle(borderWidthProps[side], '');
                }
            }
        }
        for (side in borders) {
            ln = borders[side].length;
            if (!owner.manageBodyBorders) {
                for (i = 0; i < ln; i++) {
                    borders[side][i].addCls(noBorderClassesSides[side]);
                }
                if ((!borders[side].satisfied && !owner.bodyBorder) || owner.bodyBorder === false) {
                    owner.addBodyCls(noBorderClassesSides[side]);
                } else {
                    owner.removeBodyCls(noBorderClassesSides[side]);
                }
            } else if (borders[side].satisfied) {
                owner.setBodyStyle(borderWidthProps[side], '1px');
            }
        }
        me.borders = borders;
    },
    beforeLayoutCycle: function(ownerContext) {
        var me = this,
            owner = me.owner,
            shrinkWrap = me.sizeModels.shrinkWrap,
            shrinkWrapDock = owner.shrinkWrapDock,
            collapsedHorz, collapsedVert;
        if (owner.collapsed) {
            if (owner.collapsedVertical()) {
                collapsedVert = true;
                ownerContext.measureDimensions = 1;
            } else {
                collapsedHorz = true;
                ownerContext.measureDimensions = 2;
            }
        }
        ownerContext.collapsedVert = collapsedVert;
        ownerContext.collapsedHorz = collapsedHorz;
        
        
        
        
        if (collapsedVert) {
            ownerContext.heightModel = shrinkWrap;
        } else if (collapsedHorz) {
            ownerContext.widthModel = shrinkWrap;
        }
        shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0);
        ownerContext.shrinkWrapDockHeight = (shrinkWrapDock & 1) && ownerContext.heightModel.shrinkWrap;
        ownerContext.shrinkWrapDockWidth = (shrinkWrapDock & 2) && ownerContext.widthModel.shrinkWrap;
    },
    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            docked = me.getLayoutItems(),
            layoutContext = ownerContext.context,
            dockedItemCount = docked.length,
            lastCollapsedState = me.lastCollapsedState,
            dockedItems, i, item, itemContext, offsets, collapsed, dock;
        me.callParent(arguments);
        
        
        collapsed = owner.getCollapsed();
        if (collapsed !== lastCollapsedState && lastCollapsedState !== undefined) {
            
            if (me.owner.collapsed) {
                ownerContext.isCollapsingOrExpanding = 1;
                
                owner.addClsWithUI(owner.collapsedCls);
            } else {
                ownerContext.isCollapsingOrExpanding = 2;
                
                owner.removeClsWithUI(owner.collapsedCls);
                ownerContext.lastCollapsedState = me.lastCollapsedState;
            }
        }
        me.lastCollapsedState = collapsed;
        ownerContext.dockedItems = dockedItems = [];
        for (i = 0; i < dockedItemCount; i++) {
            item = docked[i];
            if (item.rendered) {
                dock = item.dock;
                itemContext = layoutContext.getCmp(item);
                itemContext.dockedAt = {
                    x: 0,
                    y: 0
                };
                itemContext.offsets = offsets = Ext.Element.parseBox(item.offsets || 0);
                itemContext.horizontal = dock == 'top' || dock == 'bottom';
                offsets.width = offsets.left + offsets.right;
                offsets.height = offsets.top + offsets.bottom;
                dockedItems.push(itemContext);
            }
        }
        ownerContext.bodyContext = ownerContext.getEl('body');
    },
    beginLayoutCycle: function(ownerContext) {
        var me = this,
            docked = ownerContext.dockedItems,
            len = docked.length,
            owner = me.owner,
            frameBody = owner.frameBody,
            lastHeightModel = me.lastHeightModel,
            i, item, dock;
        me.callParent(arguments);
        if (me.owner.manageHeight) {
            
            
            if (me.lastBodyDisplay) {
                owner.body.dom.style.display = me.lastBodyDisplay = '';
            }
        } else {
            
            
            
            if (me.lastBodyDisplay !== 'inline-block') {
                owner.body.dom.style.display = me.lastBodyDisplay = 'inline-block';
            }
            if (lastHeightModel && lastHeightModel.shrinkWrap && !ownerContext.heightModel.shrinkWrap) {
                owner.body.dom.style.marginBottom = '';
            }
        }
        if (ownerContext.widthModel.auto) {
            if (ownerContext.widthModel.shrinkWrap) {
                owner.el.setWidth(null);
            }
            owner.body.setWidth(null);
            if (frameBody) {
                frameBody.setWidth(null);
            }
        }
        if (ownerContext.heightModel.auto) {
            owner.body.setHeight(null);
            
            if (frameBody) {
                frameBody.setHeight(null);
            }
        }
        
        
        if (ownerContext.collapsedVert) {
            ownerContext.setContentHeight(0);
        } else if (ownerContext.collapsedHorz) {
            ownerContext.setContentWidth(0);
        }
        
        
        for (i = 0; i < len; i++) {
            item = docked[i].target;
            dock = item.dock;
            if (dock == 'right') {
                item.setLocalX(0);
            } else if (dock != 'left') {
                
                continue;
            }
        }
    },
    
    calculate: function(ownerContext) {
        var me = this,
            measure = me.measureAutoDimensions(ownerContext, ownerContext.measureDimensions),
            state = ownerContext.state,
            horzDone = state.horzDone,
            vertDone = state.vertDone,
            bodyContext = ownerContext.bodyContext,
            framing, horz, vert, forward, backward;
        
        ownerContext.borderInfo || ownerContext.getBorderInfo();
        ownerContext.paddingInfo || ownerContext.getPaddingInfo();
        ownerContext.frameInfo || ownerContext.getFrameInfo();
        bodyContext.borderInfo || bodyContext.getBorderInfo();
        bodyContext.paddingInfo || bodyContext.getPaddingInfo();
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        if (!ownerContext.frameBorder) {
            if (!(framing = ownerContext.framing)) {
                ownerContext.frameBorder = ownerContext.borderInfo;
                ownerContext.framePadding = ownerContext.paddingInfo;
            } else {
                
                ownerContext.frameBorder = framing.border;
                ownerContext.framePadding = framing.padding;
            }
        }
        
        
        horz = !horzDone && me.createAxis(ownerContext, measure.contentWidth, ownerContext.widthModel, me.horzAxisProps, ownerContext.collapsedHorz);
        vert = !vertDone && me.createAxis(ownerContext, measure.contentHeight, ownerContext.heightModel, me.vertAxisProps, ownerContext.collapsedVert);
        
        
        
        
        
        
        
        
        for (forward = 0 , backward = ownerContext.dockedItems.length; backward--; ++forward) {
            if (horz) {
                me.dockChild(ownerContext, horz, backward, forward);
            }
            if (vert) {
                me.dockChild(ownerContext, vert, backward, forward);
            }
        }
        if (horz && me.finishAxis(ownerContext, horz)) {
            state.horzDone = horzDone = horz;
        }
        if (vert && me.finishAxis(ownerContext, vert)) {
            state.vertDone = vertDone = vert;
        }
        
        
        if (horzDone && vertDone && me.finishConstraints(ownerContext, horzDone, vertDone)) {
            
            
            
            me.finishPositions(ownerContext, horzDone, vertDone);
        } else {
            me.done = false;
        }
    },
    
    createAxis: function(ownerContext, contentSize, sizeModel, axisProps, collapsedAxis) {
        var me = this,
            begin = 0,
            owner = me.owner,
            maxSize = owner[axisProps.maxSize],
            minSize = owner[axisProps.minSize] || 0,
            dockBegin = axisProps.dockBegin,
            dockEnd = axisProps.dockEnd,
            posProp = axisProps.pos,
            sizeProp = axisProps.size,
            hasMaxSize = maxSize != null,
            
            shrinkWrap = sizeModel.shrinkWrap,
            bodyContext, framing, padding, end;
        if (shrinkWrap) {
            
            
            if (collapsedAxis) {
                end = 0;
            } else {
                bodyContext = ownerContext.bodyContext;
                end = contentSize + bodyContext.borderInfo[sizeProp];
            }
        } else {
            framing = ownerContext.frameBorder;
            padding = ownerContext.framePadding;
            begin = framing[dockBegin] + padding[dockBegin];
            end = ownerContext.getProp(sizeProp) - (framing[dockEnd] + padding[dockEnd]);
        }
        return {
            shrinkWrap: sizeModel.shrinkWrap,
            sizeModel: sizeModel,
            
            initialBegin: begin,
            begin: begin,
            end: end,
            collapsed: collapsedAxis,
            horizontal: axisProps.horizontal,
            ignoreFrameBegin: null,
            ignoreFrameEnd: null,
            initialSize: end - begin,
            maxChildSize: 0,
            hasMinMaxConstraints: (minSize || hasMaxSize) && sizeModel.shrinkWrap,
            minSize: minSize,
            maxSize: hasMaxSize ? maxSize : 1000000000,
            bodyPosProp: me.owner.manageHeight ? posProp : axisProps.marginBegin,
            dockBegin: dockBegin,
            
            dockEnd: dockEnd,
            
            posProp: posProp,
            
            sizeProp: sizeProp,
            
            setSize: axisProps.setSize,
            shrinkWrapDock: ownerContext[axisProps.shrinkWrapDock],
            sizeModelName: axisProps.sizeModel,
            dockedPixelsEnd: 0
        };
    },
    
    dockChild: function(ownerContext, axis, backward, forward) {
        var me = this,
            itemContext = ownerContext.dockedItems[axis.shrinkWrap ? backward : forward],
            item = itemContext.target,
            dock = item.dock,
            
            sizeProp = axis.sizeProp,
            pos, size;
        if (item.ignoreParentFrame && ownerContext.isCollapsingOrExpanding) {
            
            
            itemContext.clearMarginCache();
        }
        itemContext.marginInfo || itemContext.getMarginInfo();
        
        if (dock == axis.dockBegin) {
            if (axis.shrinkWrap) {
                pos = me.dockOutwardBegin(ownerContext, itemContext, item, axis);
            } else {
                pos = me.dockInwardBegin(ownerContext, itemContext, item, axis);
            }
        } else if (dock == axis.dockEnd) {
            if (axis.shrinkWrap) {
                pos = me.dockOutwardEnd(ownerContext, itemContext, item, axis);
            } else {
                pos = me.dockInwardEnd(ownerContext, itemContext, item, axis);
            }
        } else {
            if (axis.shrinkWrapDock) {
                
                
                size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
                axis.maxChildSize = Math.max(axis.maxChildSize, size);
                pos = 0;
            } else {
                pos = me.dockStretch(ownerContext, itemContext, item, axis);
            }
        }
        itemContext.dockedAt[axis.posProp] = pos;
    },
    
    dockInwardBegin: function(ownerContext, itemContext, item, axis) {
        var pos = axis.begin,
            sizeProp = axis.sizeProp,
            ignoreParentFrame = item.ignoreParentFrame,
            delta, size, dock;
        if (ignoreParentFrame) {
            axis.ignoreFrameBegin = itemContext;
            dock = item.dock;
            
            delta = ownerContext.frameBorder[dock];
            
            pos -= delta + ownerContext.framePadding[dock];
        }
        if (!item.overlay) {
            size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
            axis.begin += size;
            if (ignoreParentFrame) {
                axis.begin -= delta;
            }
        }
        return pos;
    },
    
    dockInwardEnd: function(ownerContext, itemContext, item, axis) {
        var sizeProp = axis.sizeProp,
            size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp],
            pos = axis.end - size,
            frameEnd;
        if (!item.overlay) {
            axis.end = pos;
        }
        if (item.ignoreParentFrame) {
            axis.ignoreFrameEnd = itemContext;
            frameEnd = ownerContext.frameBorder[item.dock];
            pos += frameEnd + ownerContext.framePadding[item.dock];
            axis.end += frameEnd;
        }
        return pos;
    },
    
    dockOutwardBegin: function(ownerContext, itemContext, item, axis) {
        var pos = axis.begin,
            sizeProp = axis.sizeProp,
            size;
        if (axis.collapsed) {
            axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext;
        } else if (item.ignoreParentFrame) {
            axis.ignoreFrameBegin = itemContext;
        }
        
        
        if (!item.overlay) {
            size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
            pos -= size;
            axis.begin = pos;
        }
        return pos;
    },
    
    dockOutwardEnd: function(ownerContext, itemContext, item, axis) {
        var pos = axis.end,
            sizeProp = axis.sizeProp,
            size;
        size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
        if (axis.collapsed) {
            axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext;
        } else if (item.ignoreParentFrame) {
            axis.ignoreFrameEnd = itemContext;
        }
        
        
        if (!item.overlay) {
            axis.end = pos + size;
            axis.dockedPixelsEnd += size;
        }
        return pos;
    },
    
    dockStretch: function(ownerContext, itemContext, item, axis) {
        var dock = item.dock,
            
            sizeProp = axis.sizeProp,
            
            horizontal = dock == 'top' || dock == 'bottom',
            border = ownerContext.frameBorder,
            offsets = itemContext.offsets,
            padding = ownerContext.framePadding,
            endProp = horizontal ? 'right' : 'bottom',
            startProp = horizontal ? 'left' : 'top',
            pos = axis.begin + offsets[startProp],
            margin, size;
        if (item.stretch !== false) {
            size = axis.end - pos - offsets[endProp];
            if (item.ignoreParentFrame) {
                
                
                
                
                pos -= padding[startProp] + border[startProp];
                size += padding[sizeProp] + border[sizeProp];
            }
            margin = itemContext.marginInfo;
            size -= margin[sizeProp];
            itemContext[axis.setSize](size);
        }
        return pos;
    },
    
    finishAxis: function(ownerContext, axis) {
        
        
        
        if (isNaN(axis.maxChildSize)) {
            return false;
        }
        var axisBegin = axis.begin,
            size = axis.end - axisBegin,
            collapsed = axis.collapsed,
            setSizeMethod = axis.setSize,
            beginName = axis.dockBegin,
            
            endName = axis.dockEnd,
            
            padding = ownerContext.framePadding,
            border = ownerContext.frameBorder,
            borderBegin = border[beginName],
            framing = ownerContext.framing,
            framingBegin = framing && framing[beginName],
            
            paddingBegin = collapsed ? 0 : padding[beginName],
            sizeProp = axis.sizeProp,
            ignoreFrameBegin = axis.ignoreFrameBegin,
            ignoreFrameEnd = axis.ignoreFrameEnd,
            bodyContext = ownerContext.bodyContext,
            extraPaddingBegin = Math.max(borderBegin + paddingBegin - framingBegin, 0),
            bodyPos, bodySize, delta, dirty;
        if (axis.shrinkWrap) {
            
            
            
            bodySize = axis.initialSize;
            if (framing) {
                
                
                
                
                
                
                
                delta = -axisBegin + borderBegin + paddingBegin;
                bodyPos = delta - framingBegin - extraPaddingBegin;
            } else {
                bodyPos = -axisBegin;
                delta = bodyPos + paddingBegin;
            }
            if (!collapsed) {
                size += padding[sizeProp];
            }
            if (ignoreFrameBegin) {
                
                
                
                delta -= borderBegin;
                bodyPos -= borderBegin;
                
                
                
                
                
                ignoreFrameBegin.dockedAt[axis.posProp] -= paddingBegin;
            } else {
                size += borderBegin;
            }
            if (collapsed) {}
            
            
            else if (ignoreFrameEnd) {
                
                
                
                ignoreFrameEnd.dockedAt[axis.posProp] += padding[endName];
            } else {
                size += border[endName];
            }
            axis.size = size;
            
            if (!axis.horizontal && !this.owner.manageHeight) {
                
                
                dirty = false;
            }
        } else {
            
            
            if (framing) {
                
                
                delta = 0;
                bodyPos = axisBegin - framingBegin - extraPaddingBegin;
            } else {
                delta = -borderBegin;
                bodyPos = axisBegin - paddingBegin - borderBegin;
            }
            
            bodySize = size;
        }
        axis.delta = delta;
        bodyContext[setSizeMethod](bodySize, dirty);
        bodyContext.setProp(axis.bodyPosProp, bodyPos);
        return !isNaN(size);
    },
    beforeInvalidateShrinkWrapDock: function(itemContext, options) {
        var sizeModelName = options.axis.sizeModelName;
        if (!itemContext[sizeModelName].constrainedMin) {
            
            
            itemContext[sizeModelName] = Ext.layout.SizeModel.calculated;
        }
    },
    afterInvalidateShrinkWrapDock: function(itemContext, options) {
        var axis = options.axis,
            me = options.layout,
            pos;
        if (itemContext[axis.sizeModelName].calculated) {
            pos = me.dockStretch(options.ownerContext, itemContext, itemContext.target, axis);
            itemContext.setProp(axis.posProp, axis.delta + pos);
        }
    },
    
    finishConstraints: function(ownerContext, horz, vert) {
        var me = this,
            sizeModels = me.sizeModels,
            publishWidth = horz.shrinkWrap,
            publishHeight = vert.shrinkWrap,
            owner = me.owner,
            dirty, height, width, heightModel, widthModel, size, minSize, maxSize, maxChildSize, desiredSize;
        
        
        
        
        if (publishWidth) {
            size = horz.size;
            minSize = horz.collapsed ? 0 : horz.minSize;
            maxSize = horz.maxSize;
            maxChildSize = horz.maxChildSize;
            desiredSize = Math.max(size, maxChildSize);
            if (desiredSize > maxSize) {
                widthModel = sizeModels.constrainedMax;
                width = maxSize;
            } else if (desiredSize < minSize) {
                widthModel = sizeModels.constrainedMin;
                width = minSize;
            } else if (size < maxChildSize) {
                widthModel = sizeModels.constrainedDock;
                owner.dockConstrainedWidth = width = maxChildSize;
            } else {
                width = size;
            }
        }
        if (publishHeight) {
            size = vert.size;
            minSize = vert.collapsed ? 0 : vert.minSize;
            maxSize = vert.maxSize;
            maxChildSize = vert.maxChildSize;
            
            
            desiredSize = Math.max(size, maxChildSize + size - vert.initialSize);
            if (desiredSize > maxSize) {
                heightModel = sizeModels.constrainedMax;
                height = maxSize;
            } else if (desiredSize < minSize) {
                heightModel = sizeModels.constrainedMin;
                height = minSize;
            } else if (size < maxChildSize) {
                heightModel = sizeModels.constrainedDock;
                owner.dockConstrainedHeight = height = maxChildSize;
            } else {
                if (!ownerContext.collapsedVert && !owner.manageHeight) {
                    
                    
                    dirty = false;
                    
                    ownerContext.bodyContext.setProp('margin-bottom', vert.dockedPixelsEnd);
                }
                height = size;
            }
        }
        
        if (widthModel || heightModel) {
            
            
            if (widthModel && heightModel && widthModel.constrainedMax && heightModel.constrainedByMin) {
                ownerContext.invalidate({
                    widthModel: widthModel
                });
                return false;
            }
            
            
            
            if (!ownerContext.widthModel.calculatedFromShrinkWrap && !ownerContext.heightModel.calculatedFromShrinkWrap) {
                
                ownerContext.invalidate({
                    widthModel: widthModel,
                    heightModel: heightModel
                });
                return false;
            }
        } else 
        
        
        {
            
            
            me.invalidateAxes(ownerContext, horz, vert);
        }
        
        if (publishWidth) {
            ownerContext.setWidth(width);
            if (widthModel) {
                ownerContext.widthModel = widthModel;
            }
        }
        
        if (publishHeight) {
            ownerContext.setHeight(height, dirty);
            if (heightModel) {
                ownerContext.heightModel = heightModel;
            }
        }
        
        return true;
    },
    
    invalidateAxes: function(ownerContext, horz, vert) {
        var before = this.beforeInvalidateShrinkWrapDock,
            after = this.afterInvalidateShrinkWrapDock,
            horzSize = horz.end - horz.begin,
            vertSize = vert.initialSize,
            invalidateHorz = horz.shrinkWrapDock && horz.maxChildSize <= horzSize,
            invalidateVert = vert.shrinkWrapDock && vert.maxChildSize <= vertSize,
            dockedItems, len, i, itemContext, itemSize, isHorz, axis, sizeProp;
        if (invalidateHorz || invalidateVert) {
            if (invalidateVert) {
                
                
                vert.begin = vert.initialBegin;
                vert.end = vert.begin + vert.initialSize;
            }
            dockedItems = ownerContext.dockedItems;
            for (i = 0 , len = dockedItems.length; i < len; ++i) {
                itemContext = dockedItems[i];
                isHorz = itemContext.horizontal;
                axis = null;
                if (invalidateHorz && isHorz) {
                    sizeProp = horz.sizeProp;
                    itemSize = horzSize;
                    axis = horz;
                } else if (invalidateVert && !isHorz) {
                    sizeProp = vert.sizeProp;
                    itemSize = vertSize;
                    axis = vert;
                }
                if (axis) {
                    
                    itemSize -= itemContext.getMarginInfo()[sizeProp];
                    if (itemSize !== itemContext.props[sizeProp]) {
                        itemContext.invalidate({
                            before: before,
                            after: after,
                            axis: axis,
                            ownerContext: ownerContext,
                            layout: this
                        });
                    }
                }
            }
        }
    },
    
    finishPositions: function(ownerContext, horz, vert) {
        var dockedItems = ownerContext.dockedItems,
            length = dockedItems.length,
            deltaX = horz.delta,
            deltaY = vert.delta,
            index, itemContext;
        for (index = 0; index < length; ++index) {
            itemContext = dockedItems[index];
            itemContext.setProp('x', deltaX + itemContext.dockedAt.x);
            itemContext.setProp('y', deltaY + itemContext.dockedAt.y);
        }
    },
    finishedLayout: function(ownerContext) {
        var me = this,
            target = ownerContext.target;
        me.callParent(arguments);
        if (!ownerContext.animatePolicy) {
            if (ownerContext.isCollapsingOrExpanding === 1) {
                target.afterCollapse(false);
            } else if (ownerContext.isCollapsingOrExpanding === 2) {
                target.afterExpand(false);
            }
        }
    },
    getAnimatePolicy: function(ownerContext) {
        var me = this,
            lastCollapsedState, policy;
        if (ownerContext.isCollapsingOrExpanding == 1) {
            lastCollapsedState = me.lastCollapsedState;
        } else if (ownerContext.isCollapsingOrExpanding == 2) {
            lastCollapsedState = ownerContext.lastCollapsedState;
        }
        if (lastCollapsedState == 'left' || lastCollapsedState == 'right') {
            policy = me.horizontalCollapsePolicy;
        } else if (lastCollapsedState == 'top' || lastCollapsedState == 'bottom') {
            policy = me.verticalCollapsePolicy;
        }
        return policy;
    },
    
    getDockedItems: function(order, beforeBody) {
        var me = this,
            renderedOnly = (order === 'visual'),
            all = renderedOnly ? Ext.ComponentQuery.query('[rendered]', me.owner.dockedItems.items) : me.owner.dockedItems.items,
            sort = all && all.length && order !== false,
            renderOrder, dock, dockedItems, i, isBefore, length;
        if (beforeBody == null) {
            dockedItems = sort && !renderedOnly ? all.slice() : all;
        } else {
            dockedItems = [];
            for (i = 0 , length = all.length; i < length; ++i) {
                dock = all[i].dock;
                isBefore = (dock == 'top' || dock == 'left');
                if (beforeBody ? isBefore : !isBefore) {
                    dockedItems.push(all[i]);
                }
            }
            sort = sort && dockedItems.length;
        }
        if (sort) {
            renderOrder = (order = order || 'render') == 'render';
            Ext.Array.sort(dockedItems, function(a, b) {
                var aw, bw;
                
                
                if (renderOrder && ((aw = me.owner.dockOrder[a.dock]) !== (bw = me.owner.dockOrder[b.dock]))) {
                    
                    if (!(aw + bw)) {
                        return aw - bw;
                    }
                }
                aw = me.getItemWeight(a, order);
                bw = me.getItemWeight(b, order);
                if ((aw !== undefined) && (bw !== undefined)) {
                    return aw - bw;
                }
                return 0;
            });
        }
        return dockedItems || [];
    },
    getItemWeight: function(item, order) {
        var weight = item.weight || this.owner.defaultDockWeights[item.dock];
        return weight[order] || weight;
    },
    
    getLayoutItems: function() {
        var me = this,
            items, itemCount, item, i, result;
        if (me.owner.collapsed) {
            result = me.owner.getCollapsedDockedItems();
        } else {
            items = me.getDockedItems('visual');
            itemCount = items.length;
            result = [];
            for (i = 0; i < itemCount; i++) {
                item = items[i];
                if (!item.hidden) {
                    result.push(item);
                }
            }
        }
        return result;
    },
    
    measureContentWidth: function(ownerContext) {
        var bodyContext = ownerContext.bodyContext;
        return bodyContext.el.getWidth() - bodyContext.getBorderInfo().width;
    },
    measureContentHeight: function(ownerContext) {
        var bodyContext = ownerContext.bodyContext;
        return bodyContext.el.getHeight() - bodyContext.getBorderInfo().height;
    },
    redoLayout: function(ownerContext) {
        var me = this,
            owner = me.owner;
        
        if (ownerContext.isCollapsingOrExpanding == 1) {
            if (owner.reExpander) {
                owner.reExpander.el.show();
            }
            
            owner.addClsWithUI(owner.collapsedCls);
            ownerContext.redo(true);
        } else if (ownerContext.isCollapsingOrExpanding == 2) {
            
            owner.removeClsWithUI(owner.collapsedCls);
            ownerContext.bodyContext.redo();
        }
    },
    
    
    renderChildren: function() {
        var me = this,
            items = me.getDockedItems(),
            target = me.getRenderTarget();
        me.handleItemBorders();
        me.renderItems(items, target);
    },
    
    renderItems: function(items, target) {
        var me = this,
            dockedItemCount = items.length,
            itemIndex = 0,
            correctPosition = 0,
            staticNodeCount = 0,
            targetNodes = me.getRenderTarget().dom.childNodes,
            targetChildCount = targetNodes.length,
            i, j, targetChildNode, item;
        
        for (i = 0 , j = 0; i < targetChildCount; i++) {
            targetChildNode = targetNodes[i];
            if (targetChildNode.nodeType === 1 && Ext.fly(targetChildNode).hasCls(Ext.baseCSSPrefix + 'resizable-handle')) {
                break;
            }
            for (j = 0; j < dockedItemCount; j++) {
                item = items[j];
                if (item.rendered && item.el.dom === targetChildNode) {
                    break;
                }
            }
            
            
            if (j === dockedItemCount) {
                staticNodeCount++;
            }
        }
        
        for (; itemIndex < dockedItemCount; itemIndex++ , correctPosition++) {
            item = items[itemIndex];
            
            
            
            
            
            
            
            if (itemIndex === correctPosition && (item.dock === 'right' || item.dock === 'bottom')) {
                correctPosition += staticNodeCount;
            }
            
            if (item && !item.rendered) {
                me.renderItem(item, target, correctPosition);
            } else if (!me.isValidParent(item, target, correctPosition)) {
                me.moveItem(item, target, correctPosition);
            }
        }
    },
    undoLayout: function(ownerContext) {
        var me = this,
            owner = me.owner;
        
        if (ownerContext.isCollapsingOrExpanding == 1) {
            
            if (owner.reExpander) {
                owner.reExpander.el.hide();
            }
            
            owner.removeClsWithUI(owner.collapsedCls);
            ownerContext.undo(true);
        } else if (ownerContext.isCollapsingOrExpanding == 2) {
            
            owner.addClsWithUI(owner.collapsedCls);
            ownerContext.bodyContext.undo();
        }
    },
    sizePolicy: {
        nostretch: {
            setsWidth: 0,
            setsHeight: 0
        },
        horz: {
            
            shrinkWrap: {
                
                
                setsWidth: 1,
                setsHeight: 0,
                readsWidth: 1
            },
            stretch: {
                setsWidth: 1,
                setsHeight: 0
            }
        },
        vert: {
            
            shrinkWrap: {
                setsWidth: 0,
                setsHeight: 1,
                readsHeight: 1
            },
            stretch: {
                setsWidth: 0,
                setsHeight: 1
            }
        },
        stretchV: {
            setsWidth: 0,
            setsHeight: 1
        },
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        autoStretchH: {
            readsWidth: 1,
            setsWidth: 1,
            setsHeight: 0
        },
        autoStretchV: {
            readsHeight: 1,
            setsWidth: 0,
            setsHeight: 1
        }
    },
    getItemSizePolicy: function(item, ownerSizeModel) {
        var me = this,
            policy = me.sizePolicy,
            shrinkWrapDock = me.owner.shrinkWrapDock,
            dock, vertical;
        if (item.stretch === false) {
            return policy.nostretch;
        }
        dock = item.dock;
        vertical = (dock == 'left' || dock == 'right');
        shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0);
        if (vertical) {
            policy = policy.vert;
            shrinkWrapDock = shrinkWrapDock & 1;
        } else {
            policy = policy.horz;
            shrinkWrapDock = shrinkWrapDock & 2;
        }
        if (shrinkWrapDock) {
            
            if (!ownerSizeModel) {
                ownerSizeModel = me.owner.getSizeModel();
            }
            if (ownerSizeModel[vertical ? 'height' : 'width'].shrinkWrap) {
                return policy.shrinkWrap;
            }
        }
        return policy.stretch;
    },
    
    configureItem: function(item, pos) {
        this.callParent(arguments);
        item.addCls(this._itemCls);
        if (!item.ignoreBorderManagement) {
            item.addClsWithUI(this.getDockCls(item.dock));
        }
    },
    
    getDockCls: function(dock) {
        return 'docked-' + dock;
    },
    afterRemove: function(item) {
        var dom;
        this.callParent(arguments);
        item.removeCls(this._itemCls);
        if (!item.ignoreBorderManagement) {
            item.removeClsWithUI(this.getDockCls(item.dock));
        }
        dom = item.el.dom;
        if (!item.destroying && dom) {
            dom.parentNode.removeChild(dom);
        }
        this.childrenChanged = true;
    },
    
    borderCollapseMap: {},
    
    
    getBorderCollapseTable: function() {
        var me = this,
            map = me.borderCollapseMap,
            owner = me.owner,
            baseCls = owner.baseCls,
            ui = owner.ui,
            table;
        map = map[baseCls] || (map[baseCls] = {});
        table = map[ui];
        if (!table) {
            baseCls += '-' + ui + '-outer-border-';
            map[ui] = table = [
                0,
                
                baseCls + 'l',
                
                baseCls + 'b',
                
                baseCls + 'bl',
                
                baseCls + 'r',
                
                baseCls + 'rl',
                
                baseCls + 'rb',
                
                baseCls + 'rbl',
                
                baseCls + 't',
                
                baseCls + 'tl',
                
                baseCls + 'tb',
                
                baseCls + 'tbl',
                
                baseCls + 'tr',
                
                baseCls + 'trl',
                
                baseCls + 'trb',
                
                baseCls + 'trbl'
            ];
        }
        
        return table;
    }
});


Ext.define('Ext.util.Memento', (function() {
    function captureOne(src, target, prop, prefix) {
        src[prefix ? prefix + prop : prop] = target[prop];
    }
    function removeOne(src, target, prop) {
        delete src[prop];
    }
    function restoreOne(src, target, prop, prefix) {
        var name = prefix ? prefix + prop : prop,
            value = src[name];
        if (value || src.hasOwnProperty(name)) {
            restoreValue(target, prop, value);
        }
    }
    function restoreValue(target, prop, value) {
        if (Ext.isDefined(value)) {
            target[prop] = value;
        } else {
            delete target[prop];
        }
    }
    function doMany(doOne, src, target, props, prefix) {
        if (src) {
            if (Ext.isArray(props)) {
                var p,
                    pLen = props.length;
                for (p = 0; p < pLen; p++) {
                    doOne(src, target, props[p], prefix);
                }
            } else {
                doOne(src, target, props, prefix);
            }
        }
    }
    return {
        
        data: null,
        
        target: null,
        
        constructor: function(target, props) {
            this.data = {};
            if (target) {
                this.target = target;
                if (props) {
                    this.capture(props);
                }
            }
        },
        
        capture: function(props, target, prefix) {
            var me = this;
            doMany(captureOne, me.data || (me.data = {}), target || me.target, props, prefix);
        },
        
        remove: function(props) {
            doMany(removeOne, this.data, null, props);
        },
        
        restore: function(props, clear, target, prefix) {
            doMany(restoreOne, this.data, target || this.target, props, prefix);
            if (clear !== false) {
                this.remove(props);
            }
        },
        
        restoreAll: function(clear, target) {
            var me = this,
                t = target || this.target,
                data = me.data,
                prop;
            clear = clear !== false;
            for (prop in data) {
                if (data.hasOwnProperty(prop)) {
                    restoreValue(t, prop, data[prop]);
                    if (clear) {
                        delete data[prop];
                    }
                }
            }
        }
    };
}()));


Ext.define('Ext.container.DockingContainer', {
    
    requires: [
        'Ext.util.MixedCollection',
        'Ext.Element'
    ],
    
    isDockingContainer: true,
    
    
    
    defaultDockWeights: {
        top: {
            render: 1,
            visual: 1
        },
        left: {
            render: 3,
            visual: 5
        },
        right: {
            render: 5,
            visual: 7
        },
        bottom: {
            render: 7,
            visual: 3
        }
    },
    
    
    
    
    dockOrder: {
        top: -1,
        left: -1,
        right: 1,
        bottom: 1
    },
    
    horizontalDocks: 0,
    
    addDocked: function(items, pos) {
        var me = this,
            i = 0,
            instanced, item, length;
        items = me.prepareItems(items);
        length = items.length;
        for (; i < length; i++) {
            item = items[i];
            item.dock = item.dock || 'top';
            if (item.dock === 'left' || item.dock === 'right') {
                me.horizontalDocks++;
            }
            if (pos !== undefined) {
                i += pos;
                me.dockedItems.insert(i, item);
            } else {
                me.dockedItems.add(item);
            }
            instanced = !!item.instancedCmp;
            delete item.instancedCmp;
            item.onAdded(me, i, instanced);
            delete item.initOwnerCt;
            if (me.hasListeners.dockedadd) {
                me.fireEvent('dockedadd', me, item, i);
            }
            if (me.onDockedAdd !== Ext.emptyFn) {
                me.onDockedAdd(item);
            }
        }
        if (me.rendered && !me.suspendLayout) {
            me.updateLayout();
        }
        return items;
    },
    destroyDockedItems: function() {
        var dockedItems = this.dockedItems,
            c;
        if (dockedItems) {
            while ((c = dockedItems.first())) {
                this.removeDocked(c, true);
            }
        }
    },
    doRenderDockedItems: function(out, renderData, after) {
        
        
        
        
        
        
        
        var me = renderData.$comp,
            layout = me.componentLayout,
            items, tree;
        if (layout.getDockedItems && !renderData.$skipDockedItems) {
            items = layout.getDockedItems('render', !after);
            tree = items && layout.getItemsRenderTree(items);
            if (tree) {
                Ext.DomHelper.generateMarkup(tree, out);
            }
        }
    },
    
    getDockedComponent: function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }
        return this.dockedItems.get(comp);
    },
    
    getDockedItems: function(selector, beforeBody) {
        var dockedItems = this.getComponentLayout().getDockedItems('render', beforeBody);
        if (selector && dockedItems.length) {
            dockedItems = Ext.ComponentQuery.query(selector, dockedItems);
        }
        return dockedItems;
    },
    getDockingRefItems: function(deep, containerItems) {
        
        var selector = deep && '*,* *',
            
            dockedItems = this.getDockedItems(selector, true),
            items;
        
        dockedItems.push.apply(dockedItems, containerItems);
        
        items = this.getDockedItems(selector, false);
        dockedItems.push.apply(dockedItems, items);
        return dockedItems;
    },
    initDockingItems: function() {
        var me = this,
            items = me.dockedItems;
        if (!items || !items.isMixedCollection) {
            me.dockedItems = new Ext.util.AbstractMixedCollection(false,me.getComponentId);
            if (items) {
                me.addDocked(items);
            }
        }
    },
    
    insertDocked: function(pos, items) {
        this.addDocked(items, pos);
    },
    
    
    onDockedAdd: Ext.emptyFn,
    
    onDockedRemove: Ext.emptyFn,
    
    removeDocked: function(item, autoDestroy) {
        var me = this,
            layout, hasLayout;
        autoDestroy = autoDestroy === true || (autoDestroy !== false && me.autoDestroy);
        if (!me.dockedItems.contains(item)) {
            return item;
        }
        if (item.dock === 'left' || item.dock === 'right') {
            me.horizontalDocks--;
        }
        layout = me.componentLayout;
        hasLayout = layout && me.rendered;
        if (hasLayout) {
            layout.onRemove(item);
        }
        me.dockedItems.remove(item);
        
        item.onRemoved(item.destroying || autoDestroy);
        me.onDockedRemove(item);
        if (autoDestroy) {
            item.destroy();
        } else if (hasLayout) {
            
            layout.afterRemove(item);
        }
        if (me.hasListeners.dockedremove) {
            me.fireEvent('dockedremove', me, item);
        }
        if (!me.destroying && !me.suspendLayout) {
            me.updateLayout();
        }
        return item;
    },
    
    moveDocked: function(item, side) {
        if (this.rendered) {
            Ext.suspendLayouts();
        }
        this.removeDocked(item, false);
        item.dock = side;
        this.addDocked(item);
        if (this.rendered) {
            if (item.frame) {
                
                
                
                Ext.getDetachedBody().appendChild(item.el);
                item.updateFrame();
            }
            Ext.resumeLayouts(true);
        }
    },
    setupDockingRenderTpl: function(renderTpl) {
        renderTpl.renderDockedItems = this.doRenderDockedItems;
    }
});


Ext.define('Ext.panel.Panel', {
    extend: 'Ext.container.Container',
    alias: 'widget.panel',
    alternateClassName: 'Ext.Panel',
    requires: [
        'Ext.panel.Header',
        'Ext.util.MixedCollection',
        'Ext.toolbar.Toolbar',
        'Ext.fx.Anim',
        'Ext.util.KeyMap',
        'Ext.panel.DD',
        'Ext.XTemplate',
        'Ext.layout.component.Dock',
        'Ext.util.Memento'
    ],
    mixins: {
        docking: 'Ext.container.DockingContainer'
    },
    childEls: [
        'body'
    ],
    renderTpl: [
        
        '{% this.renderDockedItems(out,values,0); %}',
        '<div id="{id}-body" data-ref="body" class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl>',
        ' {baseCls}-body-{ui}<tpl if="uiCls">',
        '<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl>',
        '</tpl>{childElCls}"',
        '<tpl if="bodyRole"> role="{bodyRole}"<tpl else> role="presentation"</tpl>',
        '<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
        '{%this.renderContainer(out,values);%}',
        '</div>',
        '{% this.renderDockedItems(out,values,1); %}'
    ],
    
    
    
    
    
    
    
    headerPosition: 'top',
    iconAlign: 'left',
    titleAlign: 'left',
    titleRotation: 'default',
    beforeRenderConfig: {
        
        glyph: null,
        
        headerPosition: null,
        
        icon: null,
        
        iconAlign: null,
        
        iconCls: null,
        
        title: null,
        
        titleAlign: null,
        
        titleRotation: null
    },
    
    animCollapse: Ext.enableFx,
    
    
    
    
    
    border: true,
    
    closable: false,
    
    closeAction: 'destroy',
    
    collapsed: false,
    
    collapsedCls: 'collapsed',
    
    
    collapseFirst: true,
    
    collapsible: undefined,
    
    
    constrain: false,
    
    constrainHeader: false,
    
    
    dockedItems: null,
    
    
    
    tbar: null,
    
    
    bbar: null,
    
    
    fbar: null,
    
    
    lbar: null,
    
    
    rbar: null,
    
    buttons: null,
    
    floatable: true,
    
    frame: false,
    
    frameHeader: true,
    
    
    
    hideCollapseTool: false,
    
    manageHeight: true,
    
    maskElement: 'el',
    
    minButtonWidth: 75,
    
    
    
    
    
    preventHeader: false,
    
    shrinkWrapDock: false,
    
    
    titleCollapse: undefined,
    
    
    
    
    
    
    
    
    
    baseCls: Ext.baseCSSPrefix + 'panel',
    
    bodyPosProps: {
        x: 'x',
        y: 'y'
    },
    componentLayout: 'dock',
    
    contentPaddingProperty: 'bodyPadding',
    emptyArray: [],
    
    isPanel: true,
    defaultBindProperty: 'title',
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    addBodyCls: function(cls) {
        var me = this,
            body = me.rendered ? me.body : me.getProtoBody();
        body.addCls(cls);
        return me;
    },
    
    addTool: function(tools) {
        if (!Ext.isArray(tools)) {
            tools = [
                tools
            ];
        }
        var me = this,
            header = me.header,
            tLen = tools.length,
            curTools = me.tools,
            t, tool;
        if (!header || !header.isHeader) {
            header = null;
            if (!curTools) {
                me.tools = curTools = [];
            }
        }
        for (t = 0; t < tLen; t++) {
            tool = tools[t];
            tool.toolOwner = me;
            if (header) {
                header.addTool(tool);
            } else {
                
                
                curTools.push(tool);
            }
        }
        me.updateHeader();
    },
    
    addTools: Ext.emptyFn,
    setCollapsible: function(collapsible) {
        var me = this,
            current = me.collapsible,
            collapseTool = me.collapseTool;
        me.collapsible = collapsible;
        if (collapsible && !current) {
            me.updateCollapseTool();
            collapseTool = me.collapseTool;
            if (collapseTool) {
                collapseTool.show();
            }
        } else if (!collapsible && current) {
            if (collapseTool) {
                collapseTool.hide();
            }
        }
    },
    
    addUIClsToElement: function(cls) {
        var me = this,
            result = me.callParent(arguments);
        me.addBodyCls([
            Ext.baseCSSPrefix + cls,
            me.baseCls + '-body-' + cls,
            me.baseCls + '-body-' + me.ui + '-' + cls
        ]);
        return result;
    },
    
    afterCollapse: function(animated) {
        var me = this,
            ownerLayout = me.ownerLayout;
        me.isCollapsingOrExpanding = 0;
        me.updateCollapseTool();
        
        
        if (animated) {
            me.removeCls(Ext.baseCSSPrefix + 'animating-size');
        }
        if (ownerLayout) {
            ownerLayout.afterCollapse(me, animated);
        }
        me.setHiddenDocked();
        me.fireEvent('collapse', me);
    },
    
    afterExpand: function(animated) {
        var me = this,
            ownerLayout = me.ownerLayout;
        me.isCollapsingOrExpanding = 0;
        me.updateCollapseTool();
        
        
        if (animated) {
            me.removeCls(Ext.baseCSSPrefix + 'animating-size');
        }
        if (ownerLayout) {
            ownerLayout.afterExpand(me, animated);
        }
        me.fireEvent('expand', me);
        me.fireHierarchyEvent('expand');
    },
    beforeDestroy: function() {
        var me = this;
        Ext.destroy(me.placeholder, me.ghostPanel, me.dd);
        this.destroyDockedItems();
        me.callParent();
    },
    beforeRender: function() {
        var me = this,
            wasCollapsed;
        me.callParent();
        
        
        me.initTools();
        
        if (!(me.preventHeader || (me.header === false))) {
            me.updateHeader();
        }
        me.afterHeaderInit = true;
        
        if (me.collapsed) {
            if (me.isPlaceHolderCollapse()) {
                if (!me.hidden) {
                    me.setHiddenState(true);
                    
                    
                    
                    
                    me.preventCollapseFire = true;
                    me.placeholderCollapse();
                    delete me.preventCollapseFire;
                    wasCollapsed = me.collapsed;
                    
                    
                    
                    me.collapsed = false;
                }
            } else {
                me.beginCollapse();
                me.addClsWithUI(me.collapsedCls);
            }
        }
        
        if (wasCollapsed) {
            me.collapsed = wasCollapsed;
        }
    },
    
    getMemento: function(name) {
        var me = this;
        if (name && typeof name == 'string') {
            name += 'Memento';
            return me[name] || (me[name] = new Ext.util.Memento(me));
        }
    },
    
    beginCollapse: function() {
        var me = this,
            lastBox = me.lastBox,
            rendered = me.rendered,
            collapseMemento = me.getMemento('collapse'),
            sizeModel = me.getSizeModel(),
            header = me.header,
            reExpander;
        
        
        
        
        
        
        
        
        
        
        
        collapseMemento.capture([
            'height',
            'minHeight',
            'width',
            'minWidth'
        ]);
        if (lastBox) {
            collapseMemento.capture(me.restoreDimension(), lastBox, 'last.');
        }
        
        
        
        
        
        if (me.collapsedVertical()) {
            if (sizeModel.width.shrinkWrap) {
                me.width = rendered ? me.getWidth() : me.width || me.minWidth || 100;
            }
            delete me.height;
            me.minHeight = 0;
        } else if (me.collapsedHorizontal()) {
            if (sizeModel.height.shrinkWrap) {
                me.height = rendered ? me.getHeight() : me.height || me.minHeight || 100;
            }
            delete me.width;
            me.minWidth = 0;
        }
        if (me.ownerCt) {
            me.ownerCt.getLayout().beginCollapse(me);
        }
        
        
        if (!me.isPlaceHolderCollapse() && header !== false) {
            if (header === (reExpander = me.getReExpander())) {
                header.collapseImmune = true;
                header.getInherited().collapseImmune = true;
                header.addClsWithUI(me.getHeaderCollapsedClasses(header));
                
                if (header.rendered) {
                    header.updateFrame();
                }
            } else if (reExpander.el) {
                
                reExpander.el.show();
                reExpander.hidden = false;
            }
        }
        if (me.resizer) {
            me.resizer.disable();
        }
    },
    beginDrag: function() {
        if (this.floatingDescendants) {
            this.floatingDescendants.hide();
        }
    },
    beginExpand: function() {
        var me = this,
            lastBox = me.lastBox,
            collapseMemento = me.getMemento('collapse'),
            restoreDimension = me.restoreDimension(),
            header = me.header,
            reExpander;
        if (collapseMemento) {
            collapseMemento.restore([
                'minHeight',
                'minWidth',
                restoreDimension
            ]);
            if (lastBox) {
                collapseMemento.restore(restoreDimension, true, lastBox, 'last.');
            }
        }
        if (me.ownerCt) {
            me.ownerCt.getLayout().beginExpand(me);
        }
        if (!me.isPlaceHolderCollapse() && header !== false) {
            
            if (header === (reExpander = me.getReExpander())) {
                delete header.collapseImmune;
                delete header.getInherited().collapseImmune;
                header.removeClsWithUI(me.getHeaderCollapsedClasses(header));
                
                if (header.rendered) {
                    header.expanding = true;
                    header.updateFrame();
                    delete header.expanding;
                }
            } else {
                
                reExpander.hidden = true;
                reExpander.el.hide();
            }
        }
        if (me.resizer) {
            me.resizer.enable();
        }
    },
    bridgeToolbars: function() {
        var me = this,
            docked = [],
            minButtonWidth = me.minButtonWidth,
            fbar, fbarDefaults;
        function initToolbar(toolbar, pos, useButtonAlign) {
            if (Ext.isArray(toolbar)) {
                toolbar = {
                    xtype: 'toolbar',
                    items: toolbar
                };
            } else if (!toolbar.xtype) {
                toolbar.xtype = 'toolbar';
            }
            toolbar.dock = pos;
            if (pos == 'left' || pos == 'right') {
                toolbar.vertical = true;
            }
            
            if (useButtonAlign) {
                toolbar.layout = Ext.applyIf(toolbar.layout || {}, {
                    
                    pack: {
                        left: 'start',
                        center: 'center'
                    }[me.buttonAlign] || 'end'
                });
            }
            return toolbar;
        }
        if (me.tbar) {
            docked.push(initToolbar(me.tbar, 'top'));
            me.tbar = null;
        }
        if (me.bbar) {
            docked.push(initToolbar(me.bbar, 'bottom'));
            me.bbar = null;
        }
        if (me.buttons) {
            me.fbar = me.buttons;
            me.buttons = null;
        }
        if (me.fbar) {
            fbar = initToolbar(me.fbar, 'bottom', true);
            
            fbar.ui = 'footer';
            
            if (minButtonWidth) {
                fbarDefaults = fbar.defaults;
                fbar.defaults = function(config) {
                    var defaults = fbarDefaults || {},
                        
                        isButton = !config.xtype || config.isButton,
                        cls;
                    
                    
                    if (!isButton) {
                        cls = Ext.ClassManager.getByAlias('widget.' + config.xtype);
                        if (cls) {
                            isButton = cls.prototype.isButton;
                        }
                    }
                    if (isButton && !('minWidth' in defaults)) {
                        defaults = Ext.apply({
                            minWidth: minButtonWidth
                        }, defaults);
                    }
                    return defaults;
                };
            }
            docked.push(fbar);
            me.fbar = null;
        }
        if (me.lbar) {
            docked.push(initToolbar(me.lbar, 'left'));
            me.lbar = null;
        }
        if (me.rbar) {
            docked.push(initToolbar(me.rbar, 'right'));
            me.rbar = null;
        }
        if (me.dockedItems) {
            if (me.dockedItems.isMixedCollection) {
                me.addDocked(docked);
            } else {
                if (!Ext.isArray(me.dockedItems)) {
                    me.dockedItems = [
                        me.dockedItems
                    ];
                }
                me.dockedItems = me.dockedItems.concat(docked);
            }
        } else {
            me.dockedItems = docked;
        }
    },
    
    close: function() {
        if (this.fireEvent('beforeclose', this) !== false) {
            this.doClose();
        }
    },
    
    collapse: function(direction, animate) {
        var me = this,
            collapseDir = direction || me.collapseDirection,
            ownerCt = me.ownerCt,
            layout = me.ownerLayout,
            rendered = me.rendered;
        if (me.isCollapsingOrExpanding) {
            return me;
        }
        if (arguments.length < 2) {
            animate = me.animCollapse;
        }
        if (me.collapsed || me.fireEvent('beforecollapse', me, direction, animate) === false) {
            return me;
        }
        if (layout && layout.onBeforeComponentCollapse) {
            if (layout.onBeforeComponentCollapse(me) === false) {
                return me;
            }
        }
        if (rendered && ownerCt && me.isPlaceHolderCollapse()) {
            return me.placeholderCollapse(direction, animate);
        }
        me.collapsed = collapseDir;
        if (rendered) {
            me.beginCollapse();
        }
        me.getInherited().collapsed = true;
        me.fireHierarchyEvent('collapse');
        if (rendered) {
            me.doCollapseExpand(1, animate);
        }
        return me;
    },
    collapsedHorizontal: function() {
        var dir = this.getCollapsed();
        return dir === 'left' || dir === 'right';
    },
    collapsedVertical: function() {
        var dir = this.getCollapsed();
        return dir === 'top' || dir === 'bottom';
    },
    
    convertCollapseDir: function(collapseDir) {
        return collapseDir.substr(0, 1);
    },
    createGhost: function(cls) {
        var me = this,
            header = me.header,
            frame = me.frame && !me.alwaysFramed;
        return {
            xtype: 'panel',
            hidden: false,
            header: header ? {
                titleAlign: header.getTitleAlign()
            } : null,
            ui: frame ? me.ui.replace(/-framed$/, '') : me.ui,
            id: me.id + '-ghost',
            renderTo: Ext.getBody(),
            
            
            resizable: false,
            
            draggable: false,
            
            closable: false,
            floating: true,
            shadow: false,
            frame: frame,
            shim: me.shim,
            alwaysFramed: me.alwaysFramed,
            overlapHeader: me.overlapHeader,
            headerPosition: me.getHeaderPosition(),
            titleRotation: me.getTitleRotation(),
            baseCls: me.baseCls,
            getRefOwner: function() {
                return me.getRefOwner();
            },
            cls: me.baseCls + '-ghost ' + (cls || '')
        };
    },
    createReExpander: function(direction, defaults) {
        var me = this,
            isLeft = direction === 'left',
            isRight = direction === 'right',
            isVertical = isLeft || isRight,
            ownerCt = me.ownerCt,
            result = Ext.apply({
                hideMode: 'offsets',
                title: me.getTitle(),
                titleAlign: me.getTitleAlign(),
                vertical: isVertical,
                textCls: me.headerTextCls,
                icon: me.getIcon(),
                iconCls: me.getIconCls(),
                iconAlign: me.getIconAlign(),
                glyph: me.getGlyph(),
                baseCls: me.self.prototype.baseCls + '-header',
                ui: me.ui,
                frame: me.frame && me.frameHeader,
                ignoreParentFrame: me.frame || me.overlapHeader,
                ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement,
                indicateDrag: me.draggable,
                collapseImmune: true,
                headerRole: me.headerRole,
                ownerCt: (ownerCt && me.collapseMode === 'placeholder') ? ownerCt : me,
                ownerLayout: me.componentLayout,
                margin: me.margin
            }, defaults);
        
        
        if (me.collapseMode === 'mini') {
            if (isVertical) {
                result.width = 1;
            } else {
                result.height = 1;
            }
        }
        
        
        
        if (!me.hideCollapseTool) {
            if (isLeft || (isRight && me.isPlaceHolderCollapse())) {
                
                
                result.titlePosition = 1;
            }
            result.tools = [
                {
                    xtype: 'tool',
                    type: 'expand-' + me.getOppositeDirection(direction),
                    uiCls: [
                        'top'
                    ],
                    handler: me.toggleCollapse,
                    scope: me
                }
            ];
        }
        result = new Ext.panel.Header(result);
        result.addClsWithUI(me.getHeaderCollapsedClasses(result));
        return result;
    },
    
    doClose: function() {
        this.fireEvent('close', this);
        this[this.closeAction]();
    },
    doCollapseExpand: function(flags, animate) {
        var me = this,
            originalAnimCollapse = me.animCollapse,
            ownerLayout = me.ownerLayout;
        
        
        me.animCollapse = animate;
        
        
        me.isCollapsingOrExpanding = flags;
        
        
        if (animate) {
            me.addCls(Ext.baseCSSPrefix + 'animating-size');
        }
        if (ownerLayout && !animate) {
            ownerLayout.onContentChange(me);
        } else {
            me.updateLayout({
                isRoot: true
            });
        }
        
        me.animCollapse = originalAnimCollapse;
        return me;
    },
    endDrag: function() {
        if (this.floatingDescendants) {
            this.floatingDescendants.show();
        }
    },
    
    expand: function(animate) {
        var me = this,
            layout = me.ownerLayout,
            rendered = me.rendered;
        if (me.isCollapsingOrExpanding) {
            return me;
        }
        if (!arguments.length) {
            animate = me.animCollapse;
        }
        if (!me.collapsed && !me.floatedFromCollapse) {
            return me;
        }
        if (me.fireEvent('beforeexpand', me, animate) === false) {
            return me;
        }
        if (layout && layout.onBeforeComponentExpand) {
            if (layout.onBeforeComponentExpand(me) === false) {
                return me;
            }
        }
        delete me.getInherited().collapsed;
        if (rendered && me.isPlaceHolderCollapse()) {
            return me.placeholderExpand(animate);
        }
        me.restoreHiddenDocked();
        if (rendered) {
            me.beginExpand();
        }
        me.collapsed = false;
        if (me.rendered) {
            me.doCollapseExpand(2, animate);
        }
        return me;
    },
    findReExpander: function(direction) {
        var me = this,
            c = Ext.Component,
            dockedItems = me.dockedItems.items,
            dockedItemCount = dockedItems.length,
            comp, i;
        
        if (me.collapseMode === 'mini') {
            return;
        }
        switch (direction) {
            case c.DIRECTION_TOP:
            case c.DIRECTION_BOTTOM:
                
                
                for (i = 0; i < dockedItemCount; i++) {
                    comp = dockedItems[i];
                    if (!comp.hidden) {
                        if (comp.isHeader && (!comp.dock || comp.dock === 'top' || comp.dock === 'bottom')) {
                            return comp;
                        }
                    }
                };
                break;
            case c.DIRECTION_LEFT:
            case c.DIRECTION_RIGHT:
                
                
                for (i = 0; i < dockedItemCount; i++) {
                    comp = dockedItems[i];
                    if (!comp.hidden) {
                        if (comp.isHeader && (comp.dock === 'left' || comp.dock === 'right')) {
                            return comp;
                        }
                    }
                };
                break;
            default:
                throw ('Panel#findReExpander must be passed a valid collapseDirection');
        }
    },
    floatCollapsedPanel: function() {
        var me = this,
            placeholder = me.placeholder,
            ps = placeholder.getSize(),
            floatCls = Ext.baseCSSPrefix + 'border-region-slide-in',
            collapsed = me.collapsed,
            layoutOwner = me.ownerCt || me,
            slideDirection, onBodyMousedown, myBox;
        if (me.isSliding) {
            return;
        }
        
        if (me.el.hasCls(floatCls)) {
            me.slideOutFloatedPanel();
            return;
        }
        me.isSliding = true;
        
        placeholder.el.hide();
        placeholder.hidden = true;
        me.el.show();
        me.setHiddenState(false);
        me.collapsed = false;
        layoutOwner.updateLayout();
        
        placeholder.el.show();
        placeholder.hidden = false;
        me.el.hide();
        me.setHiddenState(true);
        me.collapsed = collapsed;
        layoutOwner.updateLayout();
        myBox = me.getBox(false, true);
        me.slideOutTask = me.slideOutTask || new Ext.util.DelayedTask(me.slideOutFloatedPanel,me);
        
        if (Ext.supports.Touch) {
            Ext.on('mousedown', onBodyMousedown = function(event) {
                if (!event.within(me.el)) {
                    Ext.un('mousedown', onBodyMousedown);
                    me.slideOutFloatedPanel();
                }
            });
        }
        if (!me.placeholderListener) {
            me.placeholderListener = placeholder.on({
                resize: me.onPlaceholderResize,
                scope: me,
                destroyable: true
            });
        }
        placeholder.el.on('mouseleave', me.onMouseLeaveFloated, me);
        me.el.on('mouseleave', me.onMouseLeaveFloated, me);
        placeholder.el.on('mouseenter', me.onMouseEnterFloated, me);
        me.el.on('mouseenter', me.onMouseEnterFloated, me);
        me.el.addCls(floatCls);
        me.floated = collapsed;
        
        if (me.collapseTool) {
            me.collapseTool.el.hide();
        }
        switch (me.collapsed) {
            case 'top':
                me.width = ps.width;
                me.setLocalXY(myBox.x, myBox.y + ps.height);
                break;
            case 'right':
                me.height = ps.height;
                me.setLocalXY(myBox.x - ps.width, myBox.y);
                break;
            case 'bottom':
                me.width = ps.width;
                me.setLocalXY(myBox.x, myBox.y - ps.height);
                break;
            case 'left':
                me.height = ps.height;
                me.setLocalXY(myBox.x + ps.width, myBox.y);
                break;
        }
        slideDirection = me.convertCollapseDir(me.collapsed);
        
        
        me.floatedFromCollapse = me.collapsed;
        me.collapsed = false;
        me.setHiddenState(false);
        me.el.slideIn(slideDirection, {
            preserveScroll: true,
            duration: Ext.Number.from(me.animCollapse, Ext.fx.Anim.prototype.duration),
            listeners: {
                afteranimate: function() {
                    me.isSliding = false;
                    me.fireEvent('float', me);
                }
            }
        });
    },
    onPlaceholderResize: function(ph, newWidth, newHeight) {
        var me = this,
            myBox = me.getBox(false, true),
            phBox = ph.getBox(false, true);
        
        switch (me.floated) {
            case 'top':
                me.width = newWidth;
                me.setLocalY(phBox.y + phBox.height);
                break;
            case 'right':
                me.height = newHeight;
                me.setLocalX(phBox.x - myBox.width);
                break;
            case 'bottom':
                me.width = newWidth;
                me.setLocalY(phBox.y - myBox.height);
                break;
            case 'left':
                me.height = newHeight;
                me.setLocalX(phBox.x + phBox.width);
                break;
        }
        me.updateLayout({
            isRoot: true
        });
    },
    getAnimationProps: function() {
        var me = this,
            animCollapse = me.animCollapse,
            props;
        props = me.callParent();
        if (typeof animCollapse === 'number') {
            props.duration = animCollapse;
        }
        return props;
    },
    
    getCollapsed: function() {
        var me = this;
        
        
        if (me.collapsed === true) {
            return me.collapseDirection;
        }
        return me.collapsed;
    },
    getCollapsedDockedItems: function() {
        var me = this;
        return me.header === false || me.collapseMode == 'placeholder' ? me.emptyArray : [
            me.getReExpander()
        ];
    },
    
    getComponent: function(comp) {
        var component = this.callParent(arguments);
        if (component === undefined && !Ext.isNumber(comp)) {
            
            component = this.getDockedComponent(comp);
        }
        return component;
    },
    
    getHeader: function() {
        return this.header;
    },
    
    
    getHeaderCollapsedClasses: function(header) {
        var me = this,
            collapsedCls = me.collapsedCls,
            collapsedClasses;
        collapsedClasses = [
            collapsedCls,
            collapsedCls + '-' + header.getDockName()
        ];
        if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
            collapsedClasses.push(collapsedCls + '-border-' + header.getDockName());
        }
        return collapsedClasses;
    },
    
    getKeyMap: function() {
        return this.keyMap || (this.keyMap = new Ext.util.KeyMap(Ext.apply({
            target: this.el
        }, this.keys)));
    },
    getOppositeDirection: function(d) {
        var c = Ext.Component;
        switch (d) {
            case c.DIRECTION_TOP:
                return c.DIRECTION_BOTTOM;
            case c.DIRECTION_RIGHT:
                return c.DIRECTION_LEFT;
            case c.DIRECTION_BOTTOM:
                return c.DIRECTION_TOP;
            case c.DIRECTION_LEFT:
                return c.DIRECTION_RIGHT;
        }
    },
    getPlaceholder: function(direction) {
        var me = this,
            collapseDir = direction || me.collapseDirection,
            listeners = null,
            placeholder = me.placeholder,
            floatable = me.floatable,
            titleCollapse = me.titleCollapse;
        if (!placeholder) {
            if (floatable || (me.collapsible && titleCollapse)) {
                listeners = {
                    click: {
                        
                        fn: (!titleCollapse && floatable) ? me.floatCollapsedPanel : me.toggleCollapse,
                        element: 'el',
                        scope: me
                    }
                };
            }
            me.placeholder = placeholder = Ext.widget(me.createReExpander(collapseDir, {
                id: me.id + '-placeholder',
                listeners: listeners
            }));
        }
        
        if (!placeholder.placeholderFor) {
            
            if (!placeholder.isComponent) {
                me.placeholder = placeholder = me.lookupComponent(placeholder);
            }
            Ext.applyIf(placeholder, {
                margin: me.margin,
                placeholderFor: me,
                synthetic: true
            });
            
            placeholder.addCls([
                Ext.baseCSSPrefix + 'region-collapsed-placeholder',
                Ext.baseCSSPrefix + 'region-collapsed-' + collapseDir + '-placeholder',
                me.collapsedCls
            ]);
        }
        return placeholder;
    },
    getProtoBody: function() {
        var me = this,
            body = me.protoBody;
        if (!body) {
            me.protoBody = body = new Ext.util.ProtoElement({
                cls: me.bodyCls,
                style: me.bodyStyle,
                clsProp: 'bodyCls',
                styleProp: 'bodyStyle',
                styleIsText: true
            });
        }
        return body;
    },
    getReExpander: function(direction) {
        var me = this,
            collapseDir = direction || me.collapseDirection,
            reExpander = me.reExpander || me.findReExpander(collapseDir);
        me.expandDirection = me.getOppositeDirection(collapseDir);
        if (!reExpander) {
            
            me.reExpander = reExpander = me.createReExpander(collapseDir, {
                dock: collapseDir,
                cls: Ext.baseCSSPrefix + 'docked ' + me.baseCls + '-' + me.ui + '-collapsed',
                isCollapsedExpander: true
            });
            me.dockedItems.insert(0, reExpander);
        }
        return reExpander;
    },
    getRefItems: function(deep) {
        var items = this.callParent(arguments);
        return this.getDockingRefItems(deep, items);
    },
    getState: function() {
        var me = this,
            state = me.callParent() || {},
            collapsed = me.collapsed,
            floated = me.floated,
            memento;
        
        if (floated) {
            me.collapsed = floated;
        }
        state = me.addPropertyToState(state, 'collapsed');
        if (floated) {
            me.collapsed = collapsed;
        }
        
        if (me.getCollapsed()) {
            memento = me.getMemento('collapse').data;
            state = me.addPropertyToState(state, 'collapsed', memento);
            if (me.collapsedVertical()) {
                delete state.height;
                if (memento) {
                    state = me.addPropertyToState(state, 'height', memento.height);
                }
            } else {
                delete state.width;
                if (memento) {
                    state = me.addPropertyToState(state, 'width', memento.width);
                }
            }
        }
        return state;
    },
    applyState: function(state) {
        var me = this,
            collapseMemento = {},
            collapsed;
        if (state) {
            collapsed = state.collapsed;
            if (collapsed) {
                collapseMemento = me.getMemento('collapse');
                Ext.Object.merge(collapseMemento.data, collapsed);
                state.collapsed = true;
            }
            me.callParent(arguments);
        }
    },
    
    
    ghost: function(cls) {
        var me = this,
            ghostPanel = me.ghostPanel,
            box = me.getBox(),
            header = me.header,
            ghostHeader, tools, icon, iconCls, glyph, i;
        if (!ghostPanel) {
            me.ghostPanel = ghostPanel = Ext.widget(me.createGhost(cls));
            ghostPanel.el.dom.removeAttribute('tabIndex');
        } else {
            ghostPanel.el.show();
        }
        ghostPanel.setHiddenState(false);
        ghostPanel.floatParent = me.floatParent;
        ghostPanel.toFront();
        if (header && !me.preventHeader) {
            ghostHeader = ghostPanel.header;
            
            ghostHeader.suspendLayouts();
            tools = ghostHeader.query('tool');
            for (i = tools.length; i--; ) {
                ghostHeader.remove(tools[i]);
            }
            
            
            ghostHeader.setTitlePosition(0);
            ghostPanel.addTool(me.ghostTools());
            ghostPanel.setTitle(me.getTitle());
            ghostHeader.setTitlePosition(header.titlePosition);
            iconCls = me.getIconCls();
            if (iconCls) {
                ghostPanel.setIconCls(iconCls);
            } else {
                icon = me.getIcon();
                if (icon) {
                    ghostPanel.setIcon(icon);
                } else {
                    glyph = me.getGlyph();
                    if (glyph) {
                        ghostPanel.setGlyph(glyph);
                    }
                }
            }
            ghostHeader.addCls(Ext.baseCSSPrefix + 'header-ghost');
            ghostHeader.resumeLayouts();
        }
        ghostPanel.setPagePosition(box.x, box.y);
        ghostPanel.setSize(box.width, box.height);
        me.el.hide();
        return ghostPanel;
    },
    
    
    ghostTools: function() {
        var tools = [],
            header = this.header,
            headerTools = header ? header.query('tool[hidden=false]') : [],
            t, tLen, tool;
        if (headerTools.length) {
            t = 0;
            tLen = headerTools.length;
            for (; t < tLen; t++) {
                tool = headerTools[t];
                
                
                
                
                tools.push({
                    type: tool.type
                });
            }
        } else {
            tools = [
                {
                    type: 'placeholder'
                }
            ];
        }
        return tools;
    },
    initBodyBorder: function() {
        var me = this;
        if (me.frame && me.bodyBorder) {
            if (!Ext.isNumber(me.bodyBorder)) {
                me.bodyBorder = 1;
            }
            me.getProtoBody().setStyle('border-width', this.unitizeBox(me.bodyBorder));
        }
    },
    
    initBodyStyles: function() {
        var me = this,
            body = me.getProtoBody();
        if (me.bodyPadding !== undefined) {
            if (me.layout.managePadding) {
                
                
                
                
                
                body.setStyle('padding', 0);
            } else {
                body.setStyle('padding', this.unitizeBox((me.bodyPadding === true) ? 5 : me.bodyPadding));
            }
        }
        me.initBodyBorder();
    },
    initBorderProps: function() {
        var me = this;
        if (me.frame && me.border && me.bodyBorder === undefined) {
            me.bodyBorder = false;
        }
        if (me.frame && me.border && (me.bodyBorder === false || me.bodyBorder === 0)) {
            me.manageBodyBorders = true;
        }
    },
    initComponent: function() {
        var me = this;
        if (me.collapsible) {
            
            me.addStateEvents([
                'expand',
                'collapse'
            ]);
        }
        if (me.unstyled) {
            me.setUI('plain');
        }
        if (me.frame) {
            me.setUI(me.ui + '-framed');
        }
        
        me.bridgeToolbars();
        me.initBorderProps();
        me.callParent();
        me.collapseDirection = me.collapseDirection || me.getHeaderPosition() || Ext.Component.DIRECTION_TOP;
        
        me.hiddenOnCollapse = new Ext.dom.CompositeElement();
    },
    initItems: function() {
        this.callParent();
        this.initDockingItems();
    },
    
    initRenderData: function() {
        var me = this,
            data = me.callParent();
        me.initBodyStyles();
        me.protoBody.writeTo(data);
        delete me.protoBody;
        return data;
    },
    
    calculateConstrainedPosition: function(constrainTo, proposedPosition, local, proposedSize) {
        var me = this,
            header = me.header,
            lastBox, fp;
        
        
        if (me.constrainHeader) {
            lastBox = header.lastBox;
            if (proposedSize) {
                if (!header.vertical) {
                    proposedSize = [
                        proposedSize[0],
                        lastBox ? lastBox.height : proposedSize[1]
                    ];
                } else {
                    proposedSize = [
                        lastBox ? lastBox.width : proposedSize[0],
                        proposedSize[1]
                    ];
                }
            } else if (lastBox) {
                proposedSize = [
                    lastBox.width,
                    lastBox.height
                ];
            }
            fp = me.floatParent;
            constrainTo = constrainTo || me.constrainTo || (fp ? fp.getTargetEl() : null) || me.container || me.el.parent();
        }
        return me.callParent([
            constrainTo,
            proposedPosition,
            local,
            proposedSize
        ]);
    },
    
    initTools: function() {
        var me = this,
            tools = me.tools,
            i, tool;
        me.tools = [];
        for (i = tools && tools.length; i; ) {
            --i;
            me.tools[i] = tool = tools[i];
            tool.toolOwner = me;
        }
        
        
        if (me.collapsible && !(me.hideCollapseTool || me.header === false || me.preventHeader)) {
            me.updateCollapseTool();
            
            if (me.collapseFirst) {
                me.tools.unshift(me.collapseTool);
            }
        }
        
        me.addTools();
        if (me.pinnable) {
            me.initPinnable();
        }
        
        if (me.closable) {
            me.addClsWithUI('closable');
            me.addTool({
                xtype: 'tool',
                type: 'close',
                scope: me,
                handler: me.close
            });
        }
        
        if (me.collapseTool && !me.collapseFirst) {
            me.addTool(me.collapseTool);
        }
    },
    isLayoutRoot: function() {
        if (this.floatedFromCollapse) {
            return true;
        }
        return this.callParent();
    },
    isPlaceHolderCollapse: function() {
        return this.collapseMode == 'placeholder';
    },
    isVisible: function(deep) {
        var me = this;
        if (me.collapsed && me.placeholder) {
            return me.placeholder.isVisible(deep);
        }
        return me.callParent(arguments);
    },
    onBoxReady: function() {
        this.callParent(arguments);
        if (this.collapsed) {
            this.setHiddenDocked();
        }
    },
    onHide: function(animateTarget, cb, scope) {
        var me = this,
            dd = me.dd;
        if (me.floatedFromCollapse) {
            me.slideOutFloatedPanel(true);
        }
        if (me.draggable && dd) {
            
            dd.endDrag();
        }
        if (me.collapsed && me.placeholder) {
            me.placeholder.hide();
        } else {
            me.callParent([
                animateTarget,
                cb,
                scope
            ]);
        }
    },
    onMouseEnterFloated: function(e) {
        this.slideOutTask.cancel();
    },
    onMouseLeaveFloated: function(e) {
        this.slideOutTask.delay(500);
    },
    onRemoved: function(destroying) {
        var me = this;
        
        
        
        if (me.placeholder && !destroying) {
            me.ownerCt.remove(me.placeholder, false);
        }
        me.callParent(arguments);
    },
    onShow: function() {
        var me = this;
        if (me.collapsed && me.isPlaceHolderCollapse()) {
            
            me.setHiddenState(true);
            me.placeholderCollapse();
        } else {
            me.callParent(arguments);
        }
    },
    placeholderCollapse: function(direction, animate) {
        var me = this,
            ownerCt = me.ownerCt,
            collapseDir = direction || me.collapseDirection,
            floatCls = Ext.baseCSSPrefix + 'border-region-slide-in',
            placeholder = me.getPlaceholder(collapseDir),
            slideInDirection;
        me.isCollapsingOrExpanding = 1;
        
        me.setHiddenState(true);
        me.collapsed = collapseDir;
        if (placeholder.rendered) {
            
            if (placeholder.el.dom.parentNode !== me.el.dom.parentNode) {
                me.el.dom.parentNode.insertBefore(placeholder.el.dom, me.el.dom);
            }
            placeholder.hidden = false;
            placeholder.setHiddenState(false);
            placeholder.el.show();
            ownerCt.updateLayout();
        } else {
            ownerCt.insert(ownerCt.items.indexOf(me), placeholder);
        }
        if (me.rendered) {
            
            me.el.setVisibilityMode(me.placeholderCollapseHideMode);
            if (animate) {
                me.el.addCls(floatCls);
                placeholder.el.hide();
                slideInDirection = me.convertCollapseDir(collapseDir);
                me.el.slideOut(slideInDirection, {
                    preserveScroll: true,
                    duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            me.el.removeCls(floatCls);
                            
                            placeholder.el.show().setStyle('display', 'none').slideIn(slideInDirection, {
                                easing: 'linear',
                                duration: 100,
                                listeners: {
                                    afteranimate: function() {
                                        placeholder.focus();
                                        placeholder.setHiddenState(false);
                                        me.isCollapsingOrExpanding = 0;
                                        me.fireEvent('collapse', me);
                                    }
                                }
                            });
                        }
                    }
                });
            } else {
                me.el.hide();
                placeholder.setHiddenState(false);
                me.isCollapsingOrExpanding = 0;
                me.fireEvent('collapse', me);
            }
        } else {
            me.isCollapsingOrExpanding = 0;
            if (!me.preventCollapseFire) {
                me.fireEvent('collapse', me);
            }
        }
        return me;
    },
    placeholderExpand: function(animate) {
        var me = this,
            collapseDir = me.collapsed,
            floatCls = Ext.baseCSSPrefix + 'border-region-slide-in',
            finalPos, floatedPos,
            center = me.ownerLayout ? me.ownerLayout.centerRegion : null;
        
        if (Ext.Component.layoutSuspendCount) {
            animate = false;
        }
        if (me.floatedFromCollapse) {
            floatedPos = me.getPosition(true);
            
            me.slideOutFloatedPanelBegin();
            me.slideOutFloatedPanelEnd();
            me.floated = false;
        }
        if (animate) {
            
            Ext.suspendLayouts();
            me.placeholder.hide();
            me.el.show();
            me.collapsed = false;
            me.setHiddenState(false);
            
            
            if (center && !floatedPos) {
                center.hidden = true;
            }
            Ext.resumeLayouts(true);
            center.hidden = false;
            me.el.addCls(floatCls);
            
            
            
            
            
            
            
            
            
            
            me.isCollapsingOrExpanding = 2;
            
            if (floatedPos) {
                finalPos = me.getXY();
                me.setLocalXY(floatedPos[0], floatedPos[1]);
                me.setXY([
                    finalPos[0],
                    finalPos[1]
                ], {
                    duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            me.el.removeCls(floatCls);
                            me.isCollapsingOrExpanding = 0;
                            me.fireEvent('expand', me);
                        }
                    }
                });
            } else 
            {
                me.el.hide();
                me.placeholder.el.show();
                me.placeholder.hidden = false;
                
                me.setHiddenState(false);
                me.el.slideIn(me.convertCollapseDir(collapseDir), {
                    preserveScroll: true,
                    duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            
                            
                            
                            
                            
                            me.el.removeCls(floatCls);
                            me.placeholder.hide();
                            
                            me.updateLayout();
                            me.isCollapsingOrExpanding = 0;
                            me.fireEvent('expand', me);
                        }
                    }
                });
            }
        } else {
            me.floated = me.collapsed = false;
            me.el.removeCls(floatCls);
            Ext.suspendLayouts();
            me.placeholder.hide();
            me.show();
            Ext.resumeLayouts(true);
            me.fireEvent('expand', me);
        }
        return me;
    },
    
    removeBodyCls: function(cls) {
        var me = this,
            body = me.rendered ? me.body : me.getProtoBody();
        body.removeCls(cls);
        return me;
    },
    removeUIClsFromElement: function(cls) {
        var me = this,
            result = me.callParent(arguments);
        me.removeBodyCls([
            Ext.baseCSSPrefix + cls,
            me.baseCls + '-body-' + cls,
            me.baseCls + '-body-' + me.ui + '-' + cls
        ]);
        return result;
    },
    restoreDimension: function() {
        var dir = this.collapseDirection;
        
        
        return (dir === 'top' || dir === 'bottom') ? 'height' : 'width';
    },
    restoreHiddenDocked: function() {
        var toShow = this.hiddenOnCollapse;
        
        toShow.setStyle('visibility', '');
        toShow.clear();
    },
    
    setBodyStyle: function(style, value) {
        var me = this,
            body = me.rendered ? me.body : me.getProtoBody();
        if (Ext.isFunction(style)) {
            style = style();
        }
        if (arguments.length == 1) {
            if (Ext.isString(style)) {
                style = Ext.Element.parseStyles(style);
            }
            body.setStyle(style);
        } else {
            body.setStyle(style, value);
        }
        return me;
    },
    
    setBorder: function(border, targetEl) {
        if (targetEl) {
            
            return;
        }
        var me = this,
            header = me.header;
        if (!border) {
            border = 0;
        } else if (border === true) {
            border = '1px';
        } else {
            border = me.unitizeBox(border);
        }
        if (header) {
            if (header.isHeader) {
                header.setBorder(border);
            } else {
                header.border = border;
            }
        }
        if (me.rendered && me.bodyBorder !== false) {
            me.body.setStyle('border-width', border);
        }
        me.updateLayout();
        me.border = border;
    },
    
    setCollapsed: function(collapsed) {
        this[collapsed ? 'collapse' : 'expand']();
    },
    setGlyph: function(glyph) {
        var me = this,
            oldGlyph = me.glyph,
            header = me.header,
            placeholder = me.placeholder;
        if (glyph !== oldGlyph) {
            me.glyph = glyph;
            if (header) {
                if (header.isHeader) {
                    header.setGlyph(glyph);
                } else {
                    header.glyph = glyph;
                }
            } else if (me.rendered || me.afterHeaderInit) {
                me.updateHeader();
            }
            if (placeholder && placeholder.setGlyph) {
                placeholder.setGlyph(glyph);
            }
            me.fireEvent('glyphchange', me, glyph, oldGlyph);
        }
    },
    setIcon: function(icon) {
        var me = this,
            oldIcon = me.icon,
            header = me.header,
            placeholder = me.placeholder;
        if (icon !== oldIcon) {
            me.icon = icon;
            if (header) {
                if (header.isHeader) {
                    header.setIcon(icon);
                } else {
                    header.icon = icon;
                }
            } else if (me.rendered || me.afterHeaderInit) {
                me.updateHeader();
            }
            if (placeholder && placeholder.setIcon) {
                placeholder.setIcon(icon);
            }
            me.fireEvent('iconchange', me, icon, oldIcon);
        }
    },
    setIconCls: function(iconCls) {
        var me = this,
            oldIconCls = me.iconCls,
            header = me.header,
            placeholder = me.placeholder;
        if (iconCls !== oldIconCls) {
            me.iconCls = iconCls;
            if (header) {
                if (header.isHeader) {
                    header.setIconCls(iconCls);
                } else {
                    header.iconCls = iconCls;
                }
            } else if (me.rendered || me.afterHeaderInit) {
                me.updateHeader();
            }
            if (placeholder && placeholder.setIconCls) {
                placeholder.setIconCls(iconCls);
            }
            me.fireEvent('iconclschange', me, iconCls, oldIconCls);
        }
    },
    setTitle: function(title) {
        var me = this,
            oldTitle = me.title,
            header = me.header,
            reExpander = me.reExpander,
            placeholder = me.placeholder;
        if (title !== oldTitle) {
            me.title = title;
            if (header) {
                if (header.isHeader) {
                    header.setTitle(title);
                }
            } else if (me.rendered || me.afterHeaderInit) {
                me.updateHeader();
            }
            if (reExpander) {
                reExpander.setTitle(title);
            }
            if (placeholder && placeholder.setTitle) {
                placeholder.setTitle(title);
            }
            me.fireEvent('titlechange', me, title, oldTitle);
        }
    },
    setHiddenDocked: function() {
        
        
        var me = this,
            toHide = me.hiddenOnCollapse,
            items = me.getDockedItems(),
            len = items.length,
            i = 0,
            item, reExpander;
        if (me.header !== false) {
            reExpander = me.getReExpander();
        }
        toHide.add(me.body);
        for (; i < len; i++) {
            item = items[i];
            if (item && item !== reExpander && item.el) {
                toHide.add(item.el);
            }
        }
        toHide.setStyle('visibility', 'hidden');
    },
    
    setUI: function(ui) {
        var me = this;
        me.callParent(arguments);
        if (me.header && me.header.rendered) {
            me.header.setUI(ui);
        }
    },
    
    toggleCollapse: function() {
        return (this.collapsed || this.floatedFromCollapse) ? this.expand() : this.collapse();
    },
    updateCollapseTool: function() {
        var me = this,
            collapseTool = me.collapseTool;
        if (!collapseTool && me.collapsible) {
            me.collapseDirection = me.collapseDirection || me.getHeaderPosition() || 'top';
            me.collapseTool = me.expandTool = collapseTool = Ext.widget({
                xtype: 'tool',
                handler: me.toggleCollapse,
                scope: me
            });
        }
        if (collapseTool) {
            if (me.collapsed && !me.isPlaceHolderCollapse()) {
                collapseTool.setType('expand-' + me.getOppositeDirection(me.collapseDirection));
            } else {
                collapseTool.setType('collapse-' + me.collapseDirection);
            }
        }
    },
    updateHeaderPosition: function(position) {
        var header = this.header;
        if (header && header.isHeader) {
            header.setDock(position);
        }
    },
    updateIconAlign: function(align) {
        var header = this.header;
        if (header && header.isHeader) {
            header.setIconAlign(align);
        }
    },
    updateTitleAlign: function(align) {
        var header = this.header;
        if (header && header.isHeader) {
            header.setTitleAlign(align);
        }
    },
    updateTitleRotation: function(rotation) {
        var header = this.header;
        if (header && header.isHeader) {
            header.setTitleRotation(rotation);
        }
    },
    
    unghost: function(show, matchPosition, focus) {
        var me = this,
            ghostPanel = me.ghostPanel;
        if (!ghostPanel) {
            return;
        }
        if (show !== false) {
            
            
            me.el.show();
            if (matchPosition !== false) {
                me.setPagePosition(ghostPanel.getXY());
                if (me.hideMode === 'offsets') {
                    
                    delete me.el.hideModeStyles;
                }
            }
            if (focus) {
                me.focus(false, 10);
            }
        }
        ghostPanel.el.hide();
        ghostPanel.setHiddenState(true);
    },
    
    updateHeader: function(force) {
        var me = this,
            header = me.header,
            title = me.getTitle(),
            tools = me.tools,
            icon = me.getIcon(),
            glyph = me.getGlyph(),
            iconCls = me.getIconCls(),
            hasIcon = glyph || icon || iconCls,
            headerPosition = me.getHeaderPosition(),
            vertical = headerPosition === 'left' || headerPosition === 'right';
        if (Ext.isObject(header) || (header !== false && (force || (title || hasIcon) || (tools && tools.length) || (me.collapsible && !me.titleCollapse)))) {
            if (header && header.isHeader) {
                header.show();
            } else {
                
                header = me.header = Ext.widget(Ext.merge({
                    xtype: 'header',
                    title: title,
                    titleAlign: me.getTitleAlign(),
                    vertical: vertical,
                    dock: me.getHeaderPosition() || 'top',
                    titleRotation: me.getTitleRotation(),
                    textCls: me.headerTextCls,
                    iconCls: iconCls,
                    iconAlign: me.getIconAlign(),
                    icon: icon,
                    glyph: glyph,
                    baseCls: me.baseCls + '-header',
                    tools: tools,
                    ui: me.ui,
                    id: me.id + '_header',
                    overCls: me.headerOverCls,
                    indicateDrag: me.draggable,
                    frame: (me.frame || me.alwaysFramed) && me.frameHeader,
                    ignoreParentFrame: me.frame || me.overlapHeader,
                    ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement,
                    headerRole: me.headerRole,
                    ownerCt: me,
                    synthetic: true,
                    
                    listeners: me.collapsible && me.titleCollapse ? {
                        click: me.toggleCollapse,
                        scope: me
                    } : null
                }, me.header));
                
                
                
                me.addDocked(header, 0);
            }
        } else if (header) {
            header.hide();
        }
    },
    
    
    
    
    privates: {
        addUIToElement: function() {
            var me = this;
            me.callParent(arguments);
            me.addBodyCls(me.baseCls + '-body-' + me.ui);
        },
        applyTargetCls: function(targetCls) {
            this.getProtoBody().addCls(targetCls);
        },
        getDefaultContentTarget: function() {
            return this.body;
        },
        getTargetEl: function() {
            var me = this;
            return me.body || me.protoBody || me.frameBody || me.el;
        },
        
        initDraggable: function() {
            var me = this;
            
            if (me.simpleDrag) {
                me.initSimpleDraggable();
            } else 
            {
                
                me.dd = new Ext.panel.DD(me,Ext.isBoolean(me.draggable) ? null : me.draggable);
            }
        },
        initResizable: function() {
            this.callParent(arguments);
            if (this.collapsed) {
                this.resizer.disable();
            }
        },
        
        initSimpleDraggable: function() {
            var me = this,
                ddConfig, dd;
            if (!me.header) {
                me.updateHeader(true);
            }
            
            if (me.header) {
                ddConfig = Ext.applyIf({
                    el: me.el,
                    delegate: '#' + me.header.id
                }, me.draggable);
                
                if (me.constrain || me.constrainHeader) {
                    ddConfig.constrain = me.constrain;
                    ddConfig.constrainDelegate = me.constrainHeader;
                    ddConfig.constrainTo = me.constrainTo || me.container;
                }
                dd = me.dd = new Ext.util.ComponentDragger(me,ddConfig);
                me.relayEvents(dd, [
                    'dragstart',
                    'drag',
                    'dragend'
                ]);
                if (me.maximized) {
                    dd.disable();
                }
            }
        },
        removeUIFromElement: function() {
            var me = this;
            me.callParent(arguments);
            me.removeBodyCls(me.baseCls + '-body-' + me.ui);
        },
        setupRenderTpl: function(renderTpl) {
            this.callParent(arguments);
            this.setupDockingRenderTpl(renderTpl);
        },
        slideOutFloatedPanel: function(preventAnimate) {
            var me = this,
                compEl = me.el,
                collapseDirection;
            if (me.isSliding || me.isDestroyed) {
                return;
            }
            me.isSliding = true;
            me.floated = false;
            me.slideOutFloatedPanelBegin();
            if (typeof me.collapsed == 'string') {
                collapseDirection = me.convertCollapseDir(me.collapsed);
            }
            compEl.slideOut(collapseDirection, {
                preserveScroll: true,
                duration: Ext.Number.from(me.animCollapse, Ext.fx.Anim.prototype.duration),
                autoEnd: preventAnimate === true,
                listeners: {
                    afteranimate: function() {
                        me.slideOutFloatedPanelEnd();
                        
                        
                        me.el.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
                    }
                }
            });
        },
        
        slideOutFloatedPanelBegin: function() {
            var me = this,
                placeholderEl = me.placeholder.el,
                el = me.el;
            me.collapsed = me.floatedFromCollapse;
            me.setHiddenState(true);
            me.floatedFromCollapse = null;
            
            placeholderEl.un('mouseleave', me.onMouseLeaveFloated, me);
            el.un('mouseleave', me.onMouseLeaveFloated, me);
            placeholderEl.un('mouseenter', me.onMouseEnterFloated, me);
            el.un('mouseenter', me.onMouseEnterFloated, me);
        },
        
        slideOutFloatedPanelEnd: function(suppressEvents) {
            var me = this;
            if (me.collapseTool) {
                me.collapseTool.el.show();
            }
            me.slideOutTask.cancel();
            me.isSliding = false;
            if (!suppressEvents) {
                me.fireEvent('unfloat', me);
            }
        }
    }
}, 
function() {
    var proto = this.prototype;
    proto.animCollapse = Ext.enableFx;
    proto.placeholderCollapseHideMode = Ext.Element.VISIBILITY;
});


Ext.define("Ext.form.Labelable", {
    extend: 'Ext.Mixin',
    requires: [
        'Ext.XTemplate',
        'Ext.overrides.dom.Element'
    ],
    isLabelable: true,
    mixinConfig: {
        id: 'labelable',
        on: {
            beforeRender: 'beforeLabelRender',
            onRender: 'onLabelRender'
        }
    },
    config: {
        childEls: [
            
            'labelEl',
            
            'bodyEl',
            
            'errorEl',
            'errorWrapEl'
        ]
    },
    
    labelableRenderTpl: [
        '{beforeLabelTpl}',
        '<label id="{id}-labelEl" data-ref="labelEl" class="{labelCls} {labelCls}-{ui} {labelClsExtra} ',
        '{unselectableCls}" style="{labelStyle}"<tpl if="inputId">',
        ' for="{inputId}"</tpl> {labelAttrTpl}>',
        '<span class="{labelInnerCls} {labelInnerCls}-{ui}" style="{labelInnerStyle}">',
        '{beforeLabelTextTpl}',
        '<tpl if="fieldLabel">{fieldLabel}',
        '<tpl if="labelSeparator">{labelSeparator}</tpl>',
        '</tpl>',
        '{afterLabelTextTpl}',
        '</span>',
        '</label>',
        '{afterLabelTpl}',
        '<div id="{id}-bodyEl" data-ref="bodyEl" class="{baseBodyCls} {baseBodyCls}-{ui}<tpl if="fieldBodyCls">',
        ' {fieldBodyCls} {fieldBodyCls}-{ui}</tpl> {growCls} {extraFieldBodyCls}"',
        '<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
        '{beforeBodyEl}',
        '{beforeSubTpl}',
        '{[values.$comp.getSubTplMarkup(values)]}',
        '{afterSubTpl}',
        '{afterBodyEl}',
        '</div>',
        '<tpl if="renderError">',
        '<div id="{id}-errorWrapEl" data-ref="errorWrapEl" class="{errorWrapCls} {errorWrapCls}-{ui}',
        ' {errorWrapExtraCls}" style="{errorWrapStyle}">',
        '<div role="alert" aria-live="polite" id="{id}-errorEl" data-ref="errorEl" ',
        'class="{errorMsgCls} {invalidMsgCls} {invalidMsgCls}-{ui}" ',
        'data-anchorTarget="{id}-inputEl">',
        '</div>',
        '</div>',
        '</tpl>',
        {
            disableFormats: true
        }
    ],
    
    activeErrorsTpl: undefined,
    htmlActiveErrorsTpl: [
        '<tpl if="errors && errors.length">',
        '<ul class="{listCls}">',
        '<tpl if="Ext.enableAria">',
        '<tpl if="fieldLabel"><div>{fieldLabel}</div></tpl>',
        '</tpl>',
        '<tpl for="errors"><li>{.}</li></tpl>',
        '</ul>',
        '</tpl>'
    ],
    plaintextActiveErrorsTpl: [
        '<tpl if="errors && errors.length">',
        '<tpl if="Ext.enableAria">',
        '<tpl if="fieldLabel">{fieldLabel}\n</tpl>',
        '</tpl>',
        '<tpl for="errors"><tpl if="xindex &gt; 1">\n</tpl>{.}</tpl>',
        '</tpl>'
    ],
    
    isFieldLabelable: true,
    
    formItemCls: Ext.baseCSSPrefix + 'form-item',
    
    labelCls: Ext.baseCSSPrefix + 'form-item-label',
    
    topLabelCls: Ext.baseCSSPrefix + 'form-item-label-top',
    rightLabelCls: Ext.baseCSSPrefix + 'form-item-label-right',
    labelInnerCls: Ext.baseCSSPrefix + 'form-item-label-inner',
    topLabelSideErrorCls: Ext.baseCSSPrefix + 'form-item-label-top-side-error',
    
    
    errorMsgCls: Ext.baseCSSPrefix + 'form-error-msg',
    errorWrapCls: Ext.baseCSSPrefix + 'form-error-wrap',
    errorWrapSideCls: Ext.baseCSSPrefix + 'form-error-wrap-side',
    errorWrapUnderCls: Ext.baseCSSPrefix + 'form-error-wrap-under',
    errorWrapUnderSideLabelCls: Ext.baseCSSPrefix + 'form-error-wrap-under-side-label',
    
    baseBodyCls: Ext.baseCSSPrefix + 'form-item-body',
    invalidIconCls: Ext.baseCSSPrefix + 'form-invalid-icon',
    invalidUnderCls: Ext.baseCSSPrefix + 'form-invalid-under',
    noLabelCls: Ext.baseCSSPrefix + 'form-item-no-label',
    
    fieldBodyCls: '',
    
    invalidCls: Ext.baseCSSPrefix + 'form-invalid',
    
    fieldLabel: undefined,
    
    labelAlign: 'left',
    
    labelWidth: 100,
    
    labelPad: 5,
    
    
    labelSeparator: ':',
    
    
    
    hideLabel: false,
    
    hideEmptyLabel: true,
    
    preventMark: false,
    
    autoFitErrors: true,
    
    msgTarget: 'qtip',
    
    
    msgTargets: {
        qtip: 1,
        title: 1,
        under: 1,
        side: 1,
        none: 1
    },
    
    
    noWrap: true,
    labelableInsertions: [
        
        'beforeBodyEl',
        
        'afterBodyEl',
        
        'beforeLabelTpl',
        
        'afterLabelTpl',
        
        'beforeSubTpl',
        
        'afterSubTpl',
        
        'beforeLabelTextTpl',
        
        'afterLabelTextTpl',
        
        'labelAttrTpl'
    ],
    statics: {
        
        initTip: function() {
            var tip = this.tip,
                cfg, copy;
            if (tip) {
                return;
            }
            cfg = {
                id: 'ext-form-error-tip',
                
                sticky: true,
                ui: 'form-invalid'
            };
            
            if (Ext.supports.Touch) {
                cfg.dismissDelay = 0;
                cfg.anchor = 'top';
                cfg.showDelay = 0;
                cfg.listeners = {
                    beforeshow: function() {
                        this.minWidth = Ext.fly(this.anchorTarget).getWidth();
                    }
                };
            }
            tip = this.tip = Ext.create('Ext.tip.QuickTip', cfg);
            copy = Ext.apply({}, tip.tagConfig);
            copy.attribute = 'errorqtip';
            tip.setTagConfig(copy);
        },
        
        destroyTip: function() {
            this.tip = Ext.destroy(this.tip);
        }
    },
    
    
    initLabelable: function() {
        var me = this,
            padding = me.padding;
        
        
        
        if (padding) {
            me.padding = undefined;
            me.extraMargins = Ext.Element.parseBox(padding);
        }
        if (!me.activeErrorsTpl) {
            if (me.msgTarget == 'title') {
                me.activeErrorsTpl = me.plaintextActiveErrorsTpl;
            } else {
                me.activeErrorsTpl = me.htmlActiveErrorsTpl;
            }
        }
        me.addCls([
            me.formItemCls,
            me.formItemCls + '-' + me.ui
        ]);
        
        me.lastActiveError = '';
        
        me.enableBubble('errorchange');
    },
    
    trimLabelSeparator: function() {
        var me = this,
            separator = me.labelSeparator,
            label = me.fieldLabel || '',
            lastChar = label.substr(label.length - 1);
        
        return lastChar === separator ? label.slice(0, -1) : label;
    },
    
    getFieldLabel: function() {
        return this.trimLabelSeparator();
    },
    
    setFieldLabel: function(label) {
        label = label || '';
        var me = this,
            separator = me.labelSeparator,
            labelEl = me.labelEl,
            errorWrapEl = me.errorWrapEl,
            sideLabel = (me.labelAlign !== 'top'),
            noLabelCls = me.noLabelCls,
            errorWrapUnderSideLabelCls = me.errorWrapUnderSideLabelCls;
        me.fieldLabel = label;
        if (me.rendered) {
            if (Ext.isEmpty(label) && me.hideEmptyLabel) {
                me.addCls(noLabelCls);
                if (sideLabel && errorWrapEl) {
                    errorWrapEl.removeCls(errorWrapUnderSideLabelCls);
                }
            } else {
                if (separator) {
                    label = me.trimLabelSeparator() + separator;
                }
                labelEl.dom.firstChild.innerHTML = label;
                me.removeCls(noLabelCls);
                if (sideLabel && errorWrapEl) {
                    errorWrapEl.addCls(errorWrapUnderSideLabelCls);
                }
            }
            me.updateLayout();
        }
    },
    setHideLabel: function(hideLabel) {
        var me = this;
        if (hideLabel !== me.hideLabel) {
            me.hideLabel = hideLabel;
            if (me.rendered) {
                me[hideLabel ? 'addCls' : 'removeCls'](me.noLabelCls);
                me.updateLayout();
            }
        }
    },
    setHideEmptyLabel: function(hideEmptyLabel) {
        var me = this,
            hide;
        if (hideEmptyLabel !== me.hideEmptyLabel) {
            me.hideEmptyLabel = hideEmptyLabel;
            if (me.rendered && !me.hideLabel) {
                hide = hideEmptyLabel && !me.getFieldLabel();
                me[hide ? 'addCls' : 'removeCls'](me.noLabelCls);
                me.updateLayout();
            }
        }
    },
    getInsertionRenderData: function(data, names) {
        var i = names.length,
            name, value;
        while (i--) {
            name = names[i];
            value = this[name];
            if (value) {
                if (typeof value != 'string') {
                    if (!value.isTemplate) {
                        value = Ext.XTemplate.getTpl(this, name);
                    }
                    value = value.apply(data);
                }
            }
            data[name] = value || '';
        }
        return data;
    },
    
    getLabelableRenderData: function() {
        var me = this,
            labelAlign = me.labelAlign,
            topLabel = (labelAlign === 'top'),
            rightLabel = (labelAlign === 'right'),
            sideError = (me.msgTarget === 'side'),
            underError = (me.msgTarget === 'under'),
            errorMsgCls = me.errorMsgCls,
            labelPad = me.labelPad,
            labelWidth = me.labelWidth,
            labelClsExtra = me.labelClsExtra || '',
            errorWrapExtraCls = sideError ? me.errorWrapSideCls : me.errorWrapUnderCls,
            labelStyle = '',
            labelInnerStyle = '',
            labelVisible = me.hasVisibleLabel(),
            autoFitErrors = me.autoFitErrors,
            defaultBodyWidth = me.defaultBodyWidth,
            bodyStyle, data;
        if (topLabel) {
            labelClsExtra += ' ' + me.topLabelCls;
            if (labelPad) {
                labelInnerStyle = 'padding-bottom:' + labelPad + 'px;';
            }
            if (sideError && !autoFitErrors) {
                labelClsExtra += ' ' + me.topLabelSideErrorCls;
            }
        } else {
            if (rightLabel) {
                labelClsExtra += ' ' + me.rightLabelCls;
            }
            if (labelPad) {
                labelStyle += me.getHorizontalPaddingStyle() + labelPad + 'px;';
            }
            labelStyle += 'width:' + (labelWidth + (labelPad ? labelPad : 0)) + 'px;';
            
            
            
            labelInnerStyle = 'width:' + labelWidth + 'px';
        }
        if (labelVisible) {
            if (!topLabel && underError) {
                errorWrapExtraCls += ' ' + me.errorWrapUnderSideLabelCls;
            }
        }
        if (defaultBodyWidth) {
            
            bodyStyle = 'min-width:' + defaultBodyWidth + 'px;max-width:' + defaultBodyWidth + 'px;';
        }
        data = {
            id: me.id,
            inputId: me.getInputId(),
            labelCls: me.labelCls,
            labelClsExtra: labelClsExtra,
            labelStyle: labelStyle + (me.labelStyle || ''),
            labelInnerStyle: labelInnerStyle,
            labelInnerCls: me.labelInnerCls,
            unselectableCls: Ext.Element.unselectableCls,
            bodyStyle: bodyStyle,
            baseBodyCls: me.baseBodyCls,
            fieldBodyCls: me.fieldBodyCls,
            extraFieldBodyCls: me.extraFieldBodyCls,
            errorWrapCls: me.errorWrapCls,
            errorWrapExtraCls: errorWrapExtraCls,
            renderError: sideError || underError,
            invalidMsgCls: sideError ? me.invalidIconCls : underError ? me.invalidUnderCls : '',
            errorMsgCls: errorMsgCls,
            growCls: me.grow ? me.growCls : '',
            errorWrapStyle: (sideError && !autoFitErrors) ? 'visibility:hidden' : 'display:none',
            fieldLabel: me.getFieldLabel(),
            labelSeparator: me.labelSeparator
        };
        me.getInsertionRenderData(data, me.labelableInsertions);
        return data;
    },
    
    getHorizontalPaddingStyle: function() {
        return 'padding-right:';
    },
    beforeLabelRender: function() {
        var me = this;
        me.setFieldDefaults(me.getInherited().fieldDefaults);
        if (me.ownerLayout) {
            me.addCls(Ext.baseCSSPrefix + me.ownerLayout.type + '-form-item');
        }
        if (!me.hasVisibleLabel()) {
            me.addCls(me.noLabelCls);
        }
    },
    onLabelRender: function() {
        var me = this,
            style = {},
            ExtElement = Ext.Element,
            errorWrapEl = me.errorWrapEl,
            margins, side;
        if (errorWrapEl) {
            errorWrapEl.setVisibilityMode((me.msgTarget === 'side' && !me.autoFitErrors) ? ExtElement.VISIBILITY : ExtElement.DISPLAY);
        }
        if (me.extraMargins) {
            margins = me.el.getMargin();
            for (side in margins) {
                if (margins.hasOwnProperty(side)) {
                    style['margin-' + side] = (margins[side] + me.extraMargins[side]) + 'px';
                }
            }
            me.el.setStyle(style);
        }
    },
    
    hasVisibleLabel: function() {
        if (this.hideLabel) {
            return false;
        }
        return !(this.hideEmptyLabel && !this.getFieldLabel());
    },
    
    getSubTplMarkup: function() {
        return '';
    },
    
    getInputId: function() {
        return '';
    },
    
    getActiveError: function() {
        return this.activeError || '';
    },
    
    hasActiveError: function() {
        return !!this.getActiveError();
    },
    
    setActiveError: function(msg) {
        this.setActiveErrors(msg);
    },
    
    getActiveErrors: function() {
        return this.activeErrors || [];
    },
    
    setActiveErrors: function(errors) {
        var me = this,
            errorWrapEl = me.errorWrapEl,
            msgTarget = me.msgTarget,
            isSide = msgTarget === 'side',
            isQtip = msgTarget === 'qtip',
            activeError, tpl, targetEl;
        errors = Ext.Array.from(errors);
        tpl = me.getTpl('activeErrorsTpl');
        me.activeErrors = errors;
        activeError = me.activeError = tpl.apply({
            fieldLabel: me.fieldLabel,
            errors: errors,
            listCls: Ext.baseCSSPrefix + 'list-plain'
        });
        me.renderActiveError();
        if (me.rendered) {
            if (isSide) {
                me.errorEl.dom.setAttribute('data-errorqtip', activeError);
            } else if (isQtip) {
                me.getActionEl().dom.setAttribute('data-errorqtip', activeError);
            } else if (msgTarget === 'title') {
                me.getActionEl().dom.setAttribute('title', activeError);
            }
            if (isSide || isQtip) {
                Ext.form.Labelable.initTip();
            }
            if (!me.msgTargets[msgTarget]) {
                targetEl = Ext.get(msgTarget);
                if (targetEl) {
                    targetEl.dom.innerHTML = activeError;
                }
            }
        }
        if (errorWrapEl) {
            errorWrapEl.setVisible(errors.length > 0);
            if (isSide && me.autoFitErrors) {
                me.labelEl.addCls(me.topLabelSideErrorCls);
            }
            me.updateLayout();
        }
    },
    
    unsetActiveError: function() {
        var me = this,
            errorWrapEl = me.errorWrapEl,
            msgTarget = me.msgTarget,
            targetEl;
        delete me.activeError;
        delete me.activeErrors;
        me.renderActiveError();
        if (me.rendered) {
            if (msgTarget === 'qtip') {
                me.getActionEl().dom.removeAttribute('data-errorqtip');
            } else if (msgTarget === 'title') {
                me.getActionEl().dom.removeAttribute('title');
            }
            if (!me.msgTargets[msgTarget]) {
                targetEl = Ext.get(msgTarget);
                if (targetEl) {
                    targetEl.dom.innerHTML = '';
                }
            }
            if (errorWrapEl) {
                errorWrapEl.hide();
                if (msgTarget === 'side' && me.autoFitErrors) {
                    me.labelEl.removeCls(me.topLabelSideErrorCls);
                }
                me.updateLayout();
            }
        }
    },
    
    renderActiveError: function() {
        var me = this,
            activeError = me.getActiveError(),
            hasError = !!activeError;
        if (activeError !== me.lastActiveError) {
            me.lastActiveError = activeError;
            me.fireEvent('errorchange', me, activeError);
        }
        if (me.rendered && !me.isDestroyed && !me.preventMark) {
            me.toggleInvalidCls(hasError);
            
            if (me.errorEl) {
                me.errorEl.dom.innerHTML = activeError;
            }
        }
    },
    
    toggleInvalidCls: function(hasError) {
        this.el[hasError ? 'addCls' : 'removeCls'](this.invalidCls);
    },
    
    setFieldDefaults: function(defaults) {
        var key;
        for (key in defaults) {
            if (!this.hasOwnProperty(key)) {
                this[key] = defaults[key];
            }
        }
    }
}, function() {
    if (Ext.supports.Touch) {
        this.prototype.msgTarget = 'side';
    }
});


Ext.define('Ext.form.field.Field', {
    mixinId: 'field',
    
    isFormField: true,
    config: {
        
        validation: null,
        
        validationField: null
    },
    
    
    
    disabled: false,
    
    submitValue: true,
    
    validateOnChange: true,
    
    valuePublishEvent: 'change',
    
    suspendCheckChange: 0,
    
    dirty: false,
    
    
    
    
    initField: function() {
        var me = this,
            valuePublishEvent = me.valuePublishEvent,
            len, i;
        me.initValue();
        var badNames = [
                'tagName',
                'nodeName',
                'children',
                'childNodes'
            ],
            name = this.name;
        if (name && Ext.Array.indexOf(badNames, name) > -1) {
            Ext.log.warn([
                'It is recommended to not use "',
                name,
                '" as a field name, because it ',
                'can cause naming collisions during form submission.'
            ].join(''));
        }
        
        if (Ext.isString(valuePublishEvent)) {
            me.on(valuePublishEvent, me.publishValue, me);
        } else {
            for (i = 0 , len = valuePublishEvent.length; i < len; ++i) {
                me.on(valuePublishEvent[i], me.publishValue, me);
            }
        }
    },
    
    initValue: function() {
        var me = this;
        
        me.suspendCheckChange++;
        me.setValue(me.value);
        me.suspendCheckChange--;
        
        me.originalValue = me.lastValue = me.getValue();
    },
    
    
    
    getFieldIdentifier: function() {
        return this.isEditorComponent ? this.dataIndex : this.name;
    },
    
    getName: function() {
        return this.name;
    },
    
    getValue: function() {
        return this.value;
    },
    
    setValue: function(value) {
        var me = this;
        me.value = value;
        me.checkChange();
        return me;
    },
    
    isEqual: function(value1, value2) {
        return String(value1) === String(value2);
    },
    
    isEqualAsString: function(value1, value2) {
        return String(Ext.valueFrom(value1, '')) === String(Ext.valueFrom(value2, ''));
    },
    
    getSubmitData: function() {
        var me = this,
            data = null;
        if (!me.disabled && me.submitValue) {
            data = {};
            data[me.getName()] = '' + me.getValue();
        }
        return data;
    },
    
    getModelData: function(includeEmptyText, 
    isSubmitting) {
        var me = this,
            data = null;
        
        
        if (!me.disabled && (me.submitValue || !isSubmitting)) {
            data = {};
            data[me.getFieldIdentifier()] = me.getValue();
        }
        return data;
    },
    
    reset: function() {
        var me = this;
        me.beforeReset();
        me.setValue(me.originalValue);
        me.clearInvalid();
        
        delete me.wasValid;
    },
    
    beforeReset: Ext.emptyFn,
    
    resetOriginalValue: function() {
        this.originalValue = this.getValue();
        this.checkDirty();
    },
    
    checkChange: function() {
        var me = this,
            newVal, oldVal;
        if (!me.suspendCheckChange) {
            newVal = me.getValue();
            oldVal = me.lastValue;
            if (!me.isDestroyed && me.didValueChange(newVal, oldVal)) {
                me.lastValue = newVal;
                me.fireEvent('change', me, newVal, oldVal);
                me.onChange(newVal, oldVal);
            }
        }
    },
    
    didValueChange: function(newVal, oldVal) {
        return !this.isEqual(newVal, oldVal);
    },
    
    onChange: function(newVal) {
        var me = this;
        if (me.validateOnChange) {
            me.validate();
        }
        me.checkDirty();
    },
    
    publishValue: function() {
        var me = this;
        if (me.rendered && !me.getErrors().length) {
            me.publishState('value', me.getValue());
        }
    },
    
    isDirty: function() {
        var me = this;
        return !me.disabled && !me.isEqual(me.getValue(), me.originalValue);
    },
    
    checkDirty: function() {
        var me = this,
            isDirty = me.isDirty();
        if (isDirty !== me.wasDirty) {
            me.dirty = isDirty;
            me.fireEvent('dirtychange', me, isDirty);
            me.onDirtyChange(isDirty);
            me.wasDirty = isDirty;
        }
    },
    
    onDirtyChange: Ext.emptyFn,
    
    getErrors: function(value) {
        var errors = [],
            validationField = this.getValidationField(),
            validation = this.getValidation(),
            result;
        if (validationField) {
            result = validationField.validate(value);
            if (result !== true) {
                errors.push(result);
            }
        }
        if (validation && validation !== true) {
            errors.push(validation);
        }
        return errors;
    },
    
    isValid: function() {
        var me = this;
        return me.disabled || Ext.isEmpty(me.getErrors());
    },
    
    validate: function() {
        var me = this,
            isValid = me.isValid();
        if (isValid !== me.wasValid) {
            me.wasValid = isValid;
            me.fireEvent('validitychange', me, isValid);
        }
        return isValid;
    },
    
    batchChanges: function(fn) {
        try {
            this.suspendCheckChange++;
            fn();
        } catch (pseudo) {
            
            throw pseudo;
        } finally {
            this.suspendCheckChange--;
        }
        this.checkChange();
    },
    
    isFileUpload: function() {
        return false;
    },
    
    extractFileInput: function() {
        return null;
    },
    
    markInvalid: Ext.emptyFn,
    
    clearInvalid: Ext.emptyFn,
    updateValidation: function(validation, oldValidation) {
        
        
        if (oldValidation) {
            this.validate();
        }
    }
});


Ext.define('Ext.form.field.Base', {
    extend: 'Ext.Component',
    mixins: [
        'Ext.form.Labelable',
        'Ext.form.field.Field'
    ],
    xtype: 'field',
    alternateClassName: [
        'Ext.form.Field',
        'Ext.form.BaseField'
    ],
    requires: [
        'Ext.util.DelayedTask',
        'Ext.XTemplate'
    ],
    focusable: true,
    shrinkWrap: true,
    
    fieldSubTpl: [
        
        '<input id="{id}" data-ref="inputEl" type="{type}" role="{role}" {inputAttrTpl}',
        ' size="1"',
        
        '<tpl if="name"> name="{name}"</tpl>',
        '<tpl if="value"> value="{[Ext.util.Format.htmlEncode(values.value)]}"</tpl>',
        '<tpl if="placeholder"> placeholder="{placeholder}"</tpl>',
        '{%if (values.maxLength !== undefined){%} maxlength="{maxLength}"{%}%}',
        '<tpl if="readOnly"> readonly="readonly"</tpl>',
        '<tpl if="disabled"> disabled="disabled"</tpl>',
        '<tpl if="tabIdx != null"> tabindex="{tabIdx}"</tpl>',
        '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
        ' class="{fieldCls} {typeCls} {typeCls}-{ui} {editableCls} {inputCls}" autocomplete="off"/>',
        {
            disableFormats: true
        }
    ],
    defaultBindProperty: 'value',
    subTplInsertions: [
        
        'inputAttrTpl'
    ],
    childEls: [
        
        'inputEl'
    ],
    
    
    inputType: 'text',
    
    isTextInput: true,
    
    
    
    invalidText: 'The value in this field is invalid',
    
    
    fieldCls: Ext.baseCSSPrefix + 'form-field',
    
    
    focusCls: 'form-focus',
    
    dirtyCls: Ext.baseCSSPrefix + 'form-dirty',
    
    checkChangeEvents: Ext.isIE && (!document.documentMode || document.documentMode <= 9) ? [
        'change',
        'propertychange',
        'keyup'
    ] : [
        'change',
        'input',
        'textInput',
        'keyup',
        'dragdrop'
    ],
    
    ignoreChangeRe: /data\-errorqtip|style\.|className/,
    
    checkChangeBuffer: 50,
    liquidLayout: true,
    
    readOnly: false,
    
    readOnlyCls: Ext.baseCSSPrefix + 'form-readonly',
    
    
    validateOnBlur: true,
    
    hasFocus: false,
    baseCls: Ext.baseCSSPrefix + 'field',
    fieldBodyCls: Ext.baseCSSPrefix + 'field-body',
    maskOnDisable: false,
    
    
    
    stretchInputElFixed: true,
    
    
    
    initComponent: function() {
        var me = this;
        me.callParent();
        me.subTplData = me.subTplData || {};
        
        me.initLabelable();
        me.initField();
        
        if (!me.name) {
            me.name = me.getInputId();
        }
        
        if (me.readOnly) {
            me.addCls(me.readOnlyCls);
        }
        me.addCls(Ext.baseCSSPrefix + 'form-type-' + me.inputType);
    },
    
    getInputId: function() {
        return this.inputId || (this.inputId = this.id + '-inputEl');
    },
    
    getSubTplData: function(fieldData) {
        var me = this,
            type = me.inputType,
            inputId = me.getInputId(),
            data;
        data = Ext.apply({
            ui: me.ui,
            id: inputId,
            cmpId: me.id,
            name: me.name || inputId,
            disabled: me.disabled,
            readOnly: me.readOnly,
            value: me.getRawValue(),
            type: type,
            fieldCls: me.fieldCls,
            fieldStyle: me.getFieldStyle(),
            childElCls: fieldData.childElCls,
            tabIdx: me.tabIndex,
            inputCls: me.inputCls,
            typeCls: Ext.baseCSSPrefix + 'form-' + (me.isTextInput ? 'text' : type),
            role: me.ariaRole
        }, me.subTplData);
        me.getInsertionRenderData(data, me.subTplInsertions);
        return data;
    },
    
    getSubTplMarkup: function(fieldData) {
        var me = this,
            data = me.getSubTplData(fieldData),
            preSubTpl = me.getTpl('preSubTpl'),
            postSubTpl = me.getTpl('postSubTpl'),
            markup = '';
        if (preSubTpl) {
            markup += preSubTpl.apply(data);
        }
        markup += me.getTpl('fieldSubTpl').apply(data);
        if (postSubTpl) {
            markup += postSubTpl.apply(data);
        }
        return markup;
    },
    initRenderData: function() {
        return Ext.applyIf(this.callParent(), this.getLabelableRenderData());
    },
    
    setFieldStyle: function(style) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl) {
            inputEl.applyStyles(style);
        }
        me.fieldStyle = style;
    },
    getFieldStyle: function() {
        var style = this.fieldStyle;
        return Ext.isObject(style) ? Ext.DomHelper.generateStyles(style, null, true) : style || '';
    },
    
    onRender: function() {
        
        
        this.callParent(arguments);
        this.mixins.labelable.self.initTip();
        this.renderActiveError();
    },
    onFocusLeave: function(e) {
        this.callParent([
            e
        ]);
        this.completeEdit();
    },
    
    completeEdit: Ext.emptyFn,
    isFileUpload: function() {
        return this.inputType === 'file';
    },
    
    getSubmitData: function() {
        var me = this,
            data = null,
            val;
        if (!me.disabled && me.submitValue) {
            val = me.getSubmitValue();
            if (val !== null) {
                data = {};
                data[me.getName()] = val;
            }
        }
        return data;
    },
    
    getSubmitValue: function() {
        return this.processRawValue(this.getRawValue());
    },
    
    getRawValue: function() {
        var me = this,
            v = (me.inputEl ? me.inputEl.getValue() : Ext.valueFrom(me.rawValue, ''));
        me.rawValue = v;
        return v;
    },
    
    setRawValue: function(value) {
        var me = this,
            rawValue = me.rawValue;
        if (!me.transformRawValue.$nullFn) {
            value = me.transformRawValue(value);
        }
        value = Ext.valueFrom(value, '');
        if (rawValue === undefined || rawValue !== value) {
            me.rawValue = value;
            
            if (me.inputEl) {
                me.bindPropertyChange(false);
                me.inputEl.dom.value = value;
                me.bindPropertyChange(true);
            }
            if (me.rendered && me.reference) {
                me.publishState('rawValue', value);
            }
        }
        return value;
    },
    
    transformRawValue: Ext.identityFn,
    
    valueToRaw: function(value) {
        return '' + Ext.valueFrom(value, '');
    },
    
    rawToValue: Ext.identityFn,
    
    processRawValue: Ext.identityFn,
    
    getValue: function() {
        var me = this,
            val = me.rawToValue(me.processRawValue(me.getRawValue()));
        me.value = val;
        return val;
    },
    
    setValue: function(value) {
        var me = this;
        me.setRawValue(me.valueToRaw(value));
        return me.mixins.field.setValue.call(me, value);
    },
    onBoxReady: function() {
        var me = this;
        me.callParent(arguments);
        if (me.setReadOnlyOnBoxReady) {
            me.setReadOnly(me.readOnly);
        }
    },
    
    onDisable: function() {
        var me = this,
            inputEl = me.inputEl;
        me.callParent();
        if (inputEl) {
            inputEl.dom.disabled = true;
            if (me.hasActiveError()) {
                
                me.clearInvalid();
                me.needsValidateOnEnable = true;
            }
        }
    },
    
    onEnable: function() {
        var me = this,
            inputEl = me.inputEl;
        me.callParent();
        if (inputEl) {
            inputEl.dom.disabled = false;
            if (me.needsValidateOnEnable) {
                delete me.needsValidateOnEnable;
                
                me.forceValidation = true;
                me.isValid();
                delete me.forceValidation;
            }
        }
    },
    
    setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
        readOnly = !!readOnly;
        me[readOnly ? 'addCls' : 'removeCls'](me.readOnlyCls);
        me.readOnly = readOnly;
        if (inputEl) {
            inputEl.dom.readOnly = readOnly;
        } else if (me.rendering) {
            me.setReadOnlyOnBoxReady = true;
        }
        me.fireEvent('writeablechange', me, readOnly);
    },
    
    fireKey: function(e) {
        if (e.isSpecialKey()) {
            this.fireEvent('specialkey', this, e);
        }
    },
    
    initEvents: function() {
        var me = this,
            inputEl = me.inputEl,
            onFieldMutation = me.onFieldMutation,
            events = me.checkChangeEvents,
            len = events.length,
            i, event;
        if (inputEl) {
            me.mon(inputEl, Ext.supports.SpecialKeyDownRepeat ? 'keydown' : 'keypress', me.fireKey, me);
            for (i = 0; i < len; ++i) {
                event = events[i];
                if (event === 'propertychange') {
                    me.usesPropertychange = true;
                }
                me.mon(inputEl, event, onFieldMutation, me);
            }
        }
        me.callParent();
    },
    
    onFieldMutation: function(e) {
        
        
        var me = this,
            task = me.checkChangeTask;
        if (!(e.type == 'propertychange' && me.ignoreChangeRe.test(e.browserEvent.propertyName))) {
            if (!task) {
                me.checkChangeTask = task = new Ext.util.DelayedTask(me.doCheckChangeTask,me);
            }
            if (!me.bindNotifyListener) {
                
                
                
                me.bindNotifyListener = Ext.on('beforebindnotify', me.onBeforeNotify, me, {
                    destroyable: true
                });
            }
            task.delay(me.checkChangeBuffer);
        }
    },
    doCheckChangeTask: function() {
        var bindNotifyListener = this.bindNotifyListener;
        if (bindNotifyListener) {
            bindNotifyListener.destroy();
            this.bindNotifyListener = null;
        }
        this.checkChange();
    },
    publishValue: function() {
        var me = this;
        if (me.rendered && !me.getErrors().length) {
            me.publishState('value', me.getValue());
        }
    },
    
    bindPropertyChange: function(active) {
        var me = this,
            usesPropertychange = me.usesPropertychange;
        if (usesPropertychange) {
            me[active ? 'mon' : 'mun'](me.inputEl, 'propertychange', me.onFieldMutation);
        }
    },
    
    onDirtyChange: function(isDirty) {
        var me = this;
        me[isDirty ? 'addCls' : 'removeCls'](me.dirtyCls);
        if (me.rendered && me.reference) {
            me.publishState('dirty', isDirty);
        }
    },
    
    isValid: function() {
        var me = this,
            disabled = me.disabled,
            validate = me.forceValidation || !disabled;
        return validate ? me.validateValue(me.processRawValue(me.getRawValue())) : disabled;
    },
    
    validateValue: function(value) {
        var me = this,
            errors = me.getErrors(value),
            isValid = Ext.isEmpty(errors);
        if (!me.preventMark) {
            if (isValid) {
                me.clearInvalid();
            } else {
                me.markInvalid(errors);
            }
        }
        return isValid;
    },
    
    markInvalid: function(errors) {
        
        var me = this,
            oldMsg = me.getActiveError(),
            active;
        me.setActiveErrors(Ext.Array.from(errors));
        active = me.getActiveError();
        if (oldMsg !== active) {
            me.setError(active);
        }
    },
    
    clearInvalid: function() {
        
        var me = this,
            hadError = me.hasActiveError();
        delete me.needsValidateOnEnable;
        me.unsetActiveError();
        if (hadError) {
            me.setError('');
        }
    },
    
    setError: function(error) {
        var me = this,
            msgTarget = me.msgTarget,
            prop;
        if (me.rendered) {
            if (msgTarget == 'title' || msgTarget == 'qtip') {
                prop = msgTarget == 'qtip' ? 'data-errorqtip' : 'title';
                me.getActionEl().dom.setAttribute(prop, error || '');
            } else {
                me.updateLayout();
            }
        }
    },
    
    renderActiveError: function() {
        var me = this,
            hasError = me.hasActiveError(),
            invalidCls = me.invalidCls + '-field';
        if (me.inputEl) {
            
            me.inputEl[hasError ? 'addCls' : 'removeCls']([
                invalidCls,
                invalidCls + '-' + me.ui
            ]);
        }
        me.mixins.labelable.renderActiveError.call(me);
    },
    beforeDestroy: function() {
        var me = this,
            task = me.checkChangeTask;
        if (task) {
            task.cancel();
        }
        me.checkChangeTask = me.bindNotifyListener = Ext.destroy(me.bindNotifyListener);
        me.callParent();
    },
    privates: {
        applyBind: function(bind, currentBindings) {
            var me = this,
                valueBinding = currentBindings && currentBindings.value,
                bindings, newValueBind;
            bindings = me.callParent([
                bind,
                currentBindings
            ]);
            newValueBind = bindings.value;
            me.hasBindingValue = !!newValueBind;
            if (newValueBind !== valueBinding && me.getInherited().modelValidation) {
                me.updateValueBinding(bindings);
            }
            return bindings;
        },
        applyRenderSelectors: function() {
            var me = this;
            me.callParent();
            
            
            if (!me.inputEl) {
                me.inputEl = me.el.getById(me.getInputId());
            }
        },
        getActionEl: function() {
            return this.inputEl || this.el;
        },
        getFocusEl: function() {
            return this.inputEl;
        },
        initRenderTpl: function() {
            var me = this;
            if (!me.hasOwnProperty('renderTpl')) {
                me.renderTpl = me.getTpl('labelableRenderTpl');
            }
            return me.callParent();
        },
        onBeforeNotify: function() {
            
            
            
            
            
            this.checkChangeTask.cancel();
            this.checkChange();
        },
        updateValueBinding: function(bindings) {
            var me = this,
                newBinding = bindings.value,
                fieldBinding = bindings.$fieldBinding;
            if (fieldBinding) {
                fieldBinding.destroy();
                bindings.$fieldBinding = null;
            }
            if (newBinding && newBinding.bindValidationField) {
                me.fieldBinding = newBinding.bindValidationField('setValidationField', me);
            }
        }
    },
    deprecated: {
        "5": {
            methods: {
                doComponentLayout: function() {
                    
                    
                    
                    this.bindPropertyChange(false);
                    this.callParent(arguments);
                    this.bindPropertyChange(true);
                }
            }
        }
    }
});


Ext.define('Ext.form.field.Display', {
    extend: 'Ext.form.field.Base',
    alias: 'widget.displayfield',
    requires: [
        'Ext.util.Format',
        'Ext.XTemplate'
    ],
    alternateClassName: [
        'Ext.form.DisplayField',
        'Ext.form.Display'
    ],
    ariaRole: 'textbox',
    fieldSubTpl: [
        '<div id="{id}" role="{role}" {inputAttrTpl}',
        '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
        ' class="{fieldCls} {fieldCls}-{ui}">{value}</div>',
        {
            compiled: true,
            disableFormats: true
        }
    ],
    focusable: false,
    
    readOnly: true,
    
    fieldCls: Ext.baseCSSPrefix + 'form-display-field',
    fieldBodyCls: Ext.baseCSSPrefix + 'form-display-field-body',
    
    htmlEncode: false,
    
    
    noWrap: false,
    
    validateOnChange: false,
    initEvents: Ext.emptyFn,
    submitValue: false,
    getValue: function() {
        return this.value;
    },
    valueToRaw: function(value) {
        if (value || value === 0 || value === false) {
            return value;
        } else {
            return '';
        }
    },
    isDirty: function() {
        return false;
    },
    isValid: Ext.returnTrue,
    validate: Ext.returnTrue,
    getRawValue: function() {
        return this.rawValue;
    },
    setRawValue: function(value) {
        var me = this;
        value = Ext.valueFrom(value, '');
        me.rawValue = value;
        if (me.rendered) {
            me.inputEl.dom.innerHTML = me.getDisplayValue();
            me.updateLayout();
        }
        return value;
    },
    
    getDisplayValue: function() {
        var me = this,
            value = this.getRawValue(),
            display;
        if (me.renderer) {
            display = me.renderer.call(me.scope || me, value, me);
        } else {
            display = me.htmlEncode ? Ext.util.Format.htmlEncode(value) : value;
        }
        return display;
    },
    getSubTplData: function(fieldData) {
        var ret = this.callParent(arguments);
        ret.value = this.getDisplayValue();
        return ret;
    }
});


Ext.define('Ext.layout.container.Fit', {
    
    extend: 'Ext.layout.container.Container',
    alternateClassName: 'Ext.layout.FitLayout',
    alias: 'layout.fit',
    
    itemCls: Ext.baseCSSPrefix + 'fit-item',
    targetCls: Ext.baseCSSPrefix + 'layout-fit',
    type: 'fit',
    manageMargins: true,
    sizePolicies: {
        0: {
            readsWidth: 1,
            readsHeight: 1,
            setsWidth: 0,
            setsHeight: 0
        },
        1: {
            readsWidth: 0,
            readsHeight: 1,
            setsWidth: 1,
            setsHeight: 0
        },
        2: {
            readsWidth: 1,
            readsHeight: 0,
            setsWidth: 0,
            setsHeight: 1
        },
        3: {
            readsWidth: 0,
            readsHeight: 0,
            setsWidth: 1,
            setsHeight: 1
        }
    },
    getItemSizePolicy: function(item, ownerSizeModel) {
        
        var sizeModel = ownerSizeModel || this.owner.getSizeModel(),
            mode = (sizeModel.width.shrinkWrap ? 0 : 1) | (sizeModel.height.shrinkWrap ? 0 : 2);
        return this.sizePolicies[mode];
    },
    beginLayoutCycle: function(ownerContext, firstCycle) {
        var me = this,
            
            resetHeight = me.lastHeightModel && me.lastHeightModel.calculated,
            resetWidth = me.lastWidthModel && me.lastWidthModel.calculated,
            resetSizes = resetWidth || resetHeight,
            maxChildMinHeight = 0,
            maxChildMinWidth = 0,
            c, childItems, i, item, length, margins, minHeight, minWidth, style, undef;
        me.callParent(arguments);
        
        
        
        
        if (resetSizes && ownerContext.targetContext.el.dom.tagName.toUpperCase() != 'TD') {
            resetSizes = resetWidth = resetHeight = false;
        }
        childItems = ownerContext.childItems;
        length = childItems.length;
        for (i = 0; i < length; ++i) {
            item = childItems[i];
            
            
            
            if (firstCycle) {
                c = item.target;
                minHeight = c.minHeight;
                minWidth = c.minWidth;
                if (minWidth || minHeight) {
                    margins = item.marginInfo || item.getMarginInfo();
                    
                    
                    minHeight += margins.height;
                    minWidth += margins.height;
                    
                    
                    if (maxChildMinHeight < minHeight) {
                        maxChildMinHeight = minHeight;
                    }
                    if (maxChildMinWidth < minWidth) {
                        maxChildMinWidth = minWidth;
                    }
                }
            }
            if (resetSizes) {
                style = item.el.dom.style;
                if (resetHeight) {
                    style.height = '';
                }
                if (resetWidth) {
                    style.width = '';
                }
            }
        }
        if (firstCycle) {
            ownerContext.maxChildMinHeight = maxChildMinHeight;
            ownerContext.maxChildMinWidth = maxChildMinWidth;
        }
        
        
        
        c = ownerContext.target;
        ownerContext.overflowX = (!ownerContext.widthModel.shrinkWrap && ownerContext.maxChildMinWidth && c.scrollFlags.x) || undef;
        ownerContext.overflowY = (!ownerContext.heightModel.shrinkWrap && ownerContext.maxChildMinHeight && c.scrollFlags.y) || undef;
    },
    calculate: function(ownerContext) {
        var me = this,
            childItems = ownerContext.childItems,
            length = childItems.length,
            containerSize = me.getContainerSize(ownerContext),
            info = {
                length: length,
                ownerContext: ownerContext,
                targetSize: containerSize
            },
            shrinkWrapWidth = ownerContext.widthModel.shrinkWrap,
            shrinkWrapHeight = ownerContext.heightModel.shrinkWrap,
            overflowX = ownerContext.overflowX,
            overflowY = ownerContext.overflowY,
            scrollbars, scrollbarSize, padding, i, contentWidth, contentHeight;
        if (overflowX || overflowY) {
            
            
            
            scrollbars = me.getScrollbarsNeeded(overflowX && containerSize.width, overflowY && containerSize.height, ownerContext.maxChildMinWidth, ownerContext.maxChildMinHeight);
            if (scrollbars) {
                scrollbarSize = Ext.getScrollbarSize();
                if (scrollbars & 1) {
                    
                    containerSize.height -= scrollbarSize.height;
                }
                if (scrollbars & 2) {
                    
                    containerSize.width -= scrollbarSize.width;
                }
            }
        }
        
        if (length > 0) {
            
            for (i = 0; i < length; ++i) {
                info.index = i;
                me.fitItem(childItems[i], info);
            }
        } else {
            info.contentWidth = info.contentHeight = 0;
        }
        if (shrinkWrapHeight || shrinkWrapWidth) {
            padding = ownerContext.targetContext.getPaddingInfo();
            if (shrinkWrapWidth) {
                if (overflowY && !containerSize.gotHeight) {
                    
                    
                    
                    me.done = false;
                } else {
                    contentWidth = info.contentWidth + padding.width;
                    
                    
                    
                    if (scrollbars & 2) {
                        
                        contentWidth += scrollbarSize.width;
                    }
                    if (!ownerContext.setContentWidth(contentWidth)) {
                        me.done = false;
                    }
                }
            }
            if (shrinkWrapHeight) {
                if (overflowX && !containerSize.gotWidth) {
                    
                    
                    
                    me.done = false;
                } else {
                    contentHeight = info.contentHeight + padding.height;
                    
                    
                    
                    if (scrollbars & 1) {
                        
                        contentHeight += scrollbarSize.height;
                    }
                    if (!ownerContext.setContentHeight(contentHeight)) {
                        me.done = false;
                    }
                }
            }
        }
    },
    fitItem: function(itemContext, info) {
        var me = this;
        if (itemContext.invalid) {
            me.done = false;
            return;
        }
        info.margins = itemContext.getMarginInfo();
        info.needed = info.got = 0;
        me.fitItemWidth(itemContext, info);
        me.fitItemHeight(itemContext, info);
        
        if (info.got != info.needed) {
            me.done = false;
        }
    },
    fitItemWidth: function(itemContext, info) {
        var contentWidth, width;
        
        if (info.ownerContext.widthModel.shrinkWrap) {
            
            width = itemContext.getProp('width') + info.margins.width;
            
            contentWidth = info.contentWidth;
            if (contentWidth === undefined) {
                info.contentWidth = width;
            } else {
                info.contentWidth = Math.max(contentWidth, width);
            }
        } else if (itemContext.widthModel.calculated) {
            ++info.needed;
            if (info.targetSize.gotWidth) {
                ++info.got;
                this.setItemWidth(itemContext, info);
            } else {
                
                return;
            }
        }
        this.positionItemX(itemContext, info);
    },
    fitItemHeight: function(itemContext, info) {
        var contentHeight, height;
        if (info.ownerContext.heightModel.shrinkWrap) {
            
            height = itemContext.getProp('height') + info.margins.height;
            
            contentHeight = info.contentHeight;
            if (contentHeight === undefined) {
                info.contentHeight = height;
            } else {
                info.contentHeight = Math.max(contentHeight, height);
            }
        } else if (itemContext.heightModel.calculated) {
            ++info.needed;
            if (info.targetSize.gotHeight) {
                ++info.got;
                this.setItemHeight(itemContext, info);
            } else {
                
                return;
            }
        }
        this.positionItemY(itemContext, info);
    },
    positionItemX: function(itemContext, info) {
        var margins = info.margins;
        
        
        if (info.index || margins.left) {
            itemContext.setProp('x', margins.left);
        }
        if (margins.width) {
            
            itemContext.setProp('margin-right', margins.width);
        }
    },
    positionItemY: function(itemContext, info) {
        var margins = info.margins;
        if (info.index || margins.top) {
            itemContext.setProp('y', margins.top);
        }
        if (margins.height) {
            
            itemContext.setProp('margin-bottom', margins.height);
        }
    },
    setItemHeight: function(itemContext, info) {
        itemContext.setHeight(info.targetSize.height - info.margins.height);
    },
    setItemWidth: function(itemContext, info) {
        itemContext.setWidth(info.targetSize.width - info.margins.width);
    }
});


Ext.define('Ext.panel.Table', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.tablepanel',
    requires: [
        'Ext.layout.container.Fit'
    ],
    uses: [
        'Ext.selection.RowModel',
        'Ext.selection.CellModel',
        'Ext.selection.CheckboxModel',
        'Ext.grid.plugin.BufferedRenderer',
        'Ext.grid.header.Container',
        'Ext.grid.locking.Lockable',
        'Ext.grid.NavigationModel'
    ],
    extraBaseCls: Ext.baseCSSPrefix + 'grid',
    extraBodyCls: Ext.baseCSSPrefix + 'grid-body',
    defaultBindProperty: 'store',
    layout: 'fit',
    ariaRole: 'grid',
    config: {
        
        selection: null
    },
    publishes: [
        'selection'
    ],
    twoWayBindable: [
        'selection'
    ],
    
    autoLoad: false,
    
    variableRowHeight: false,
    
    numFromEdge: 2,
    
    trailingBufferZone: 10,
    
    leadingBufferZone: 20,
    
    hasView: false,
    
    viewType: null,
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    deferRowRender: false,
    
    sortableColumns: true,
    
    multiColumnSort: false,
    
    enableLocking: false,
    
    
    scrollerOwner: true,
    
    enableColumnMove: true,
    
    sealedColumns: false,
    
    enableColumnResize: true,
    
    
    
    rowLines: true,
    
    
    
    
    bufferedRenderer: true,
    
    
    ownerGrid: null,
    colLinesCls: Ext.baseCSSPrefix + 'grid-with-col-lines',
    rowLinesCls: Ext.baseCSSPrefix + 'grid-with-row-lines',
    noRowLinesCls: Ext.baseCSSPrefix + 'grid-no-row-lines',
    hiddenHeaderCtCls: Ext.baseCSSPrefix + 'grid-header-ct-hidden',
    hiddenHeaderCls: Ext.baseCSSPrefix + 'grid-header-hidden',
    resizeMarkerCls: Ext.baseCSSPrefix + 'grid-resize-marker',
    emptyCls: Ext.baseCSSPrefix + 'grid-empty',
    
    
    
    focusable: true,
    
    constructor: function(config) {
        var me = this,
            store;
        me.ownerGrid = (config && config.ownerGrid) || me;
        me.callParent([
            config
        ]);
        store = this.store;
        
        store.trackStateChanges = true;
        if (me.autoLoad) {
            store.unblockLoad();
            store.load();
        }
    },
    initComponent: function() {
        if (this.verticalScroller) {
            Ext.Error.raise("The verticalScroller config is not supported.");
        }
        if (!this.viewType) {
            Ext.Error.raise("You must specify a viewType config.");
        }
        if (this.headers) {
            Ext.Error.raise("The headers config is not supported. Please specify columns instead.");
        }
        var me = this,
            headerCtCfg = me.columns || me.colModel || [],
            view, i, len, bufferedRenderer,
            
            store = me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store'),
            columns, viewScroller;
        me.enableLocking = me.enableLocking || me.hasLockedColumns(headerCtCfg);
        
        if (me.autoLoad) {
            me.store.blockLoad();
        }
        
        
        
        
        
        
        
        
        if (me.plugins) {
            me.plugins = me.constructPlugins();
        }
        
        if (me.columnLines) {
            me.addBodyCls(me.colLinesCls);
        }
        me.addBodyCls(me.rowLines ? me.rowLinesCls : me.noRowLinesCls);
        me.addBodyCls(me.extraBodyCls);
        
        
        if (me.enableLocking) {
            me.self.mixin('lockable', Ext.grid.locking.Lockable);
            me.injectLockable();
        } else 
        {
            
            if (headerCtCfg.isRootHeader) {
                if (me.hideHeaders) {
                    headerCtCfg.setHeight(0);
                    
                    headerCtCfg.hiddenHeaders = true;
                }
                me.headerCt = headerCtCfg;
                me.headerCt.grid = me;
                me.headerCt.forceFit = !!me.forceFit;
                
                me.columnManager = headerCtCfg.columnManager;
                me.visibleColumnManager = headerCtCfg.visibleColumnManager;
            } else 
            {
                if (Ext.isArray(headerCtCfg)) {
                    headerCtCfg = {
                        items: headerCtCfg
                    };
                }
                Ext.apply(headerCtCfg, {
                    grid: me,
                    forceFit: me.forceFit,
                    sortable: me.sortableColumns,
                    enableColumnMove: me.enableColumnMove,
                    enableColumnResize: me.enableColumnResize,
                    columnLines: me.columnLines,
                    sealed: me.sealedColumns
                });
                if (me.hideHeaders) {
                    headerCtCfg.height = 0;
                    
                    headerCtCfg.hiddenHeaders = true;
                }
                if (Ext.isDefined(me.enableColumnHide)) {
                    headerCtCfg.enableColumnHide = me.enableColumnHide;
                }
                me.headerCt = new Ext.grid.header.Container(headerCtCfg);
            }
            
            
            me.headerCt.setScrollable({
                x: false,
                y: false
            });
        }
        
        me.columns = columns = me.headerCt.getGridColumns();
        me.scrollTask = new Ext.util.DelayedTask(me.syncHorizontalScroll,me);
        me.cls = (me.cls || '') + (' ' + me.extraBaseCls);
        
        delete me.autoScroll;
        bufferedRenderer = me.plugins && Ext.Array.findBy(me.plugins, function(p) {
            return p.isBufferedRenderer;
        });
        
        if (bufferedRenderer) {
            me.bufferedRenderer = bufferedRenderer;
        }
        
        
        if (!me.hasView) {
            
            if (store.isBufferedStore && !store.remoteSort) {
                for (i = 0 , len = columns.length; i < len; i++) {
                    columns[i].sortable = false;
                }
            }
            if (me.hideHeaders) {
                me.headerCt.addCls(me.hiddenHeaderCtCls);
                me.addCls(me.hiddenHeaderCls);
            }
            me.relayHeaderCtEvents(me.headerCt);
            me.features = me.features || [];
            if (!Ext.isArray(me.features)) {
                me.features = [
                    me.features
                ];
            }
            me.dockedItems = [].concat(me.dockedItems || []);
            me.dockedItems.unshift(me.headerCt);
            me.viewConfig = me.viewConfig || {};
            
            
            view = me.getView();
            me.items = [
                view
            ];
            me.hasView = true;
            
            
            if (!me.hideHeaders) {
                
                viewScroller = view.getScrollable();
                if (viewScroller) {
                    me.headerCt.getScrollable().addPartner(viewScroller, 'x');
                }
            }
            
            me.bindStore(store, true);
            me.mon(view, {
                viewready: me.onViewReady,
                refresh: me.onRestoreHorzScroll,
                scope: me
            });
        }
        
        me.selModel = me.view.getSelectionModel();
        if (me.selModel.isRowModel) {
            me.selModel.on('selectionchange', me.updateBindSelection, me);
        }
        
        me.relayEvents(me.view, [
            
            'beforeitemmousedown',
            
            'beforeitemmouseup',
            
            'beforeitemmouseenter',
            
            'beforeitemmouseleave',
            
            'beforeitemclick',
            
            'beforeitemdblclick',
            
            'beforeitemcontextmenu',
            
            'itemmousedown',
            
            'itemmouseup',
            
            'itemmouseenter',
            
            'itemmouseleave',
            
            'itemclick',
            
            'itemdblclick',
            
            'itemcontextmenu',
            
            'beforecellclick',
            
            'cellclick',
            
            'beforecelldblclick',
            
            'celldblclick',
            
            'beforecellcontextmenu',
            
            'cellcontextmenu',
            
            'beforecellmousedown',
            
            'cellmousedown',
            
            'beforecellmouseup',
            
            'cellmouseup',
            
            'beforecellkeydown',
            
            'cellkeydown',
            
            'rowclick',
            
            'rowdblclick',
            
            'rowcontextmenu',
            
            'rowmousedown',
            
            'rowmouseup',
            
            'rowkeydown',
            
            'beforeitemkeydown',
            
            'itemkeydown',
            
            'beforeitemkeyup',
            
            'itemkeyup',
            
            'beforeitemkeypress',
            
            'itemkeypress',
            
            'beforecontainermousedown',
            
            'beforecontainermouseup',
            
            'beforecontainermouseover',
            
            'beforecontainermouseout',
            
            'beforecontainerclick',
            
            'beforecontainerdblclick',
            
            'beforecontainercontextmenu',
            
            'beforecontainerkeydown',
            
            'beforecontainerkeyup',
            
            'beforecontainerkeypress',
            
            'containermouseup',
            
            'containermousedown',
            
            'containermouseover',
            
            'containermouseout',
            
            'containerclick',
            
            'containerdblclick',
            
            'containercontextmenu',
            
            'containerkeydown',
            
            'containerkeyup',
            
            'containerkeypress',
            
            'selectionchange',
            
            'beforeselect',
            
            'select',
            
            'beforedeselect',
            
            'deselect'
        ]);
        me.callParent(arguments);
        me.addStateEvents([
            'columnresize',
            'columnmove',
            'columnhide',
            'columnshow',
            'sortchange',
            'filterchange',
            'groupchange'
        ]);
    },
    beforeRender: function() {
        var me = this,
            bufferedRenderer = me.bufferedRenderer;
        
        if (!me.lockable) {
            
            if (bufferedRenderer && me.getSizeModel().height.auto) {
                if (bufferedRenderer.isBufferedRenderer) {
                    Ext.Error.raise('Cannot use buffered rendering with auto height');
                }
                me.bufferedRenderer = bufferedRenderer = false;
            }
            if (bufferedRenderer && !bufferedRenderer.isBufferedRenderer) {
                
                bufferedRenderer = {
                    xclass: 'Ext.grid.plugin.BufferedRenderer'
                };
                Ext.copyTo(bufferedRenderer, me, 'variableRowHeight,numFromEdge,trailingBufferZone,leadingBufferZone,scrollToLoadBuffer');
                me.bufferedRenderer = me.addPlugin(bufferedRenderer);
            }
        }
        me.callParent(arguments);
    },
    focus: function() {
        
        this.getView().focus();
    },
    
    disableColumnHeaders: function() {
        this.headerCt.disable();
    },
    
    enableColumnHeaders: function() {
        this.headerCt.enable();
    },
    
    hasLockedColumns: function(columns) {
        var i, len, column;
        
        if (columns.isRootHeader) {
            columns = columns.items.items;
        }
        
        else if (Ext.isObject(columns)) {
            columns = columns.items;
        }
        for (i = 0 , len = columns.length; i < len; i++) {
            column = columns[i];
            if (!column.processed && column.locked) {
                return true;
            }
        }
    },
    relayHeaderCtEvents: function(headerCt) {
        this.relayEvents(headerCt, [
            
            'columnresize',
            
            'columnmove',
            
            'columnhide',
            
            'columnshow',
            
            'columnschanged',
            
            'sortchange',
            
            'headerclick',
            
            'headercontextmenu',
            
            'headertriggerclick'
        ]);
    },
    getState: function() {
        var me = this,
            state = me.callParent(),
            storeState = me.store.getState();
        state = me.addPropertyToState(state, 'columns', me.headerCt.getColumnsState());
        if (storeState) {
            state.storeState = storeState;
        }
        return state;
    },
    applyState: function(state) {
        var me = this,
            sorter = state.sort,
            storeState = state.storeState,
            store = me.store,
            columns = state.columns;
        delete state.columns;
        
        
        me.callParent(arguments);
        if (columns) {
            me.headerCt.applyColumnsState(columns);
        }
        
        if (sorter) {
            if (store.remoteSort) {
                
                store.sort({
                    property: sorter.property,
                    direction: sorter.direction,
                    root: sorter.root
                }, null, false);
            } else {
                store.sort(sorter.property, sorter.direction);
            }
        }
        
        else if (storeState) {
            store.applyState(storeState);
        }
    },
    
    getStore: function() {
        return this.store;
    },
    
    getView: function() {
        var me = this,
            scroll, scrollable, viewConfig;
        if (!me.view) {
            viewConfig = me.viewConfig;
            scroll = viewConfig.scroll || me.scroll;
            scrollable = me.scrollable;
            if (scrollable == null && viewConfig.scrollable == null && scroll !== null) {
                
                if (scroll === true || scroll === 'both') {
                    scrollable = true;
                } else if (scroll === false || scroll === 'none') {
                    scrollable = false;
                } else if (scroll === 'vertical') {
                    scrollable = {
                        x: false,
                        y: true
                    };
                } else if (scroll === 'horizontal') {
                    scrollable = {
                        x: true,
                        y: false
                    };
                }
            }
            viewConfig = Ext.apply({
                
                
                
                grid: me,
                ownerGrid: me.ownerGrid,
                deferInitialRefresh: me.deferRowRender,
                variableRowHeight: me.variableRowHeight,
                preserveScrollOnRefresh: true,
                trackOver: me.trackMouseOver !== false,
                throttledUpdate: me.throttledUpdate === true,
                xtype: me.viewType,
                store: me.store,
                headerCt: me.headerCt,
                columnLines: me.columnLines,
                rowLines: me.rowLines,
                navigationModel: 'grid',
                features: me.features,
                panel: me,
                emptyText: me.emptyText || ''
            }, me.viewConfig);
            if (scrollable != null) {
                viewConfig.scrollable = scrollable;
                me.scrollable = null;
            }
            Ext.create(viewConfig);
            
            
            
            
            if (me.view.emptyText) {
                me.view.emptyText = '<div class="' + me.emptyCls + '">' + me.view.emptyText + '</div>';
            }
            
            me.view.getComponentLayout().headerCt = me.headerCt;
            me.mon(me.view, {
                uievent: me.processEvent,
                scope: me
            });
            me.headerCt.view = me.view;
            
            if (me.hasListeners.viewcreated) {
                me.fireEvent('viewcreated', me, me.view);
            }
        }
        return me.view;
    },
    getColumnManager: function() {
        return this.columnManager;
    },
    getVisibleColumnManager: function() {
        return this.visibleColumnManager;
    },
    getTopLevelColumnManager: function() {
        return this.ownerGrid.getColumnManager();
    },
    getTopLevelVisibleColumnManager: function() {
        return this.ownerGrid.getVisibleColumnManager();
    },
    
    setAutoScroll: Ext.emptyFn,
    applyScrollable: function(scrollable) {
        if (this.view) {
            this.view.setScrollable(scrollable);
        }
        return scrollable;
    },
    getScrollable: function() {
        return null;
    },
    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {
        var header = e.position.column;
        if (header) {
            return header.processEvent.apply(header, arguments);
        }
    },
    
    ensureVisible: function(record, options) {
        this.doEnsureVisible(record, options);
    },
    scrollByDeltaY: function(yDelta, animate) {
        this.getView().scrollBy(0, yDelta, animate);
    },
    scrollByDeltaX: function(xDelta, animate) {
        this.getView().scrollBy(xDelta, 0, animate);
    },
    afterCollapse: function() {
        this.saveScrollPos();
        this.callParent(arguments);
    },
    afterExpand: function() {
        this.callParent(arguments);
        this.restoreScrollPos();
    },
    saveScrollPos: Ext.emptyFn,
    restoreScrollPos: Ext.emptyFn,
    onHeaderResize: function() {
        var scroller = this.view.getScrollable(),
            size;
        if (scroller) {
            size = scroller.getSize();
            if (size) {
                scroller.setSize({
                    x: this.headerCt.getTableWidth(),
                    y: size.y
                });
            }
        }
    },
    
    onHeaderMove: function(headerCt, header, colsToMove, fromIdx, toIdx) {
        var me = this;
        
        
        if (me.optimizedColumnMove === false) {
            me.view.refreshView();
        } else 
        {
            me.view.moveColumn(fromIdx, toIdx, colsToMove);
        }
        me.delayScroll();
    },
    
    onHeaderHide: function(headerCt, header) {
        if (this.view.refreshCounter) {
            this.view.refreshView();
        }
    },
    onHeaderShow: function(headerCt, header) {
        if (this.view.refreshCounter) {
            this.view.refreshView();
        }
    },
    
    onHeadersChanged: function(headerCt, header) {
        var me = this;
        if (me.rendered && !me.reconfiguring) {
            me.view.refreshView();
            me.delayScroll();
        }
    },
    delayScroll: function() {
        var target = this.view;
        if (target) {
            
            
            this.scrollTask.delay(10, null, null, [
                target
            ]);
        }
    },
    
    onViewReady: function() {
        this.fireEvent('viewready', this);
    },
    
    onRestoreHorzScroll: function() {
        var me = this,
            x = me.scrollXPos;
        if (x) {
            
            me.syncHorizontalScroll(me, true);
        }
    },
    getScrollerOwner: function() {
        var rootCmp = this;
        if (!this.scrollerOwner) {
            rootCmp = this.up('[scrollerOwner]');
        }
        return rootCmp;
    },
    
    getLhsMarker: function() {
        var me = this;
        return me.lhsMarker || (me.lhsMarker = Ext.DomHelper.append(me.el, {
            role: 'presentation',
            cls: me.resizeMarkerCls
        }, true));
    },
    
    getRhsMarker: function() {
        var me = this;
        return me.rhsMarker || (me.rhsMarker = Ext.DomHelper.append(me.el, {
            role: 'presentation',
            cls: me.resizeMarkerCls
        }, true));
    },
    
    getSelection: function() {
        return this.getSelectionModel().getSelection();
    },
    updateSelection: function(selection) {
        var me = this,
            sm;
        if (!me.ignoreNextSelection) {
            me.ignoreNextSelection = true;
            sm = me.getSelectionModel();
            if (selection) {
                sm.select(selection);
            } else {
                sm.deselectAll();
            }
            me.ignoreNextSelection = false;
        }
    },
    updateBindSelection: function(selModel, selection) {
        var me = this,
            selected = null;
        if (!me.ignoreNextSelection) {
            me.ignoreNextSelection = true;
            if (selection.length) {
                selected = selModel.getLastSelected();
                me.hasHadSelection = true;
            }
            if (me.hasHadSelection) {
                me.setSelection(selected);
            }
            me.ignoreNextSelection = false;
        }
    },
    getNavigationModel: function() {
        return this.getView().getNavigationModel();
    },
    
    getSelectionModel: function() {
        return this.getView().getSelectionModel();
    },
    getScrollTarget: function() {
        var items = this.getScrollerOwner().query('tableview');
        
        return items[items.length - 1];
    },
    syncHorizontalScroll: function(target, setBody) {
        var me = this,
            x = me.view.getScrollX(),
            scrollTarget;
        setBody = setBody === true;
        
        
        if (me.rendered && (setBody || x !== me.scrollXPos)) {
            
            
            if (setBody) {
                scrollTarget = me.getScrollTarget();
                scrollTarget.setScrollX(x);
            }
            me.headerCt.setScrollX(x);
            me.scrollXPos = x;
        }
    },
    
    onStoreLoad: Ext.emptyFn,
    getEditorParent: function() {
        return this.body;
    },
    bindStore: function(store, initial) {
        var me = this,
            view = me.getView();
        
        
        
        if (store) {
            
            me.store = store;
            if (view.store !== store) {
                
                
                
                
                
                view.bindStore(store, false);
            }
            me.mon(store, {
                load: me.onStoreLoad,
                scope: me
            });
            me.storeRelayers = me.relayEvents(store, [
                
                'filterchange',
                
                'groupchange'
            ]);
        } else {
            me.unbindStore();
        }
    },
    unbindStore: function() {
        var me = this,
            store = me.store,
            view;
        if (store) {
            me.store = null;
            me.mun(store, {
                load: me.onStoreLoad,
                scope: me
            });
            Ext.destroy(me.storeRelayers);
            view = me.view;
            if (view.store) {
                view.bindStore(null);
            }
        }
    },
    setColumns: function(columns) {
        
        
        
        if (columns.length || this.getColumnManager().getColumns().length) {
            this.reconfigure(undefined, columns);
        }
    },
    
    setStore: function(store) {
        this.reconfigure(store);
    },
    
    reconfigure: function(store, columns) {
        var me = this,
            oldStore = me.store,
            headerCt = me.headerCt,
            oldColumns = headerCt ? headerCt.items.getRange() : me.columns;
        
        if (arguments.length === 1 && Ext.isArray(store)) {
            columns = store;
            store = null;
        }
        
        if (columns) {
            columns = Ext.Array.slice(columns);
        }
        me.reconfiguring = true;
        me.fireEvent('beforereconfigure', me, store, columns, oldStore, oldColumns);
        Ext.suspendLayouts();
        if (me.lockable) {
            me.reconfigureLockable(store, columns);
        } else {
            if (columns) {
                
                delete me.scrollXPos;
                headerCt.removeAll();
                headerCt.add(columns);
            }
            
            
            if (store && (store = Ext.StoreManager.lookup(store)) !== oldStore) {
                me.unbindStore();
                me.bindStore(store);
            } else {
                me.getView().refreshView();
            }
        }
        Ext.resumeLayouts(true);
        me.fireEvent('reconfigure', me, store, columns, oldStore, oldColumns);
        delete me.reconfiguring;
    },
    beforeDestroy: function() {
        var me = this,
            task = me.scrollTask;
        if (task) {
            task.cancel();
            me.scrollTask = null;
        }
        Ext.destroy(me.focusEnterLeaveListeners);
        me.callParent();
    },
    onDestroy: function() {
        var me = this;
        if (me.lockable) {
            me.destroyLockable();
        }
        me.unbindStore();
        me.callParent();
        me.columns = me.storeRelayers = me.columnManager = me.visibleColumnManager = null;
    },
    destroy: function() {
        
        var me = this;
        me.callParent();
        if (me.isDestroyed) {
            me.view = me.selModel = me.headerCt = null;
        }
    },
    privates: {
        
        
        initFocusableElement: function() {},
        doEnsureVisible: function(record, options) {
            
            if (this.lockable) {
                return this.ensureLockedVisible(record, options);
            }
            
            if (typeof record !== 'number' && !record.isEntity) {
                record = this.store.getById(record);
            }
            var me = this,
                callback, scope, animate, highlight, select, doFocus,
                view = me.getView(),
                domNode = view.getNode(record);
            if (options) {
                callback = options.callback;
                scope = options.scope;
                animate = options.animate;
                highlight = options.highlight;
                select = options.select;
                doFocus = options.focus;
            }
            
            if (domNode) {
                view.getScrollable().scrollIntoView(domNode, null, animate, highlight);
                if (!record.isEntity) {
                    record = view.getRecord(domNode);
                }
                if (select) {
                    view.getSelectionModel().select(record);
                }
                if (doFocus) {
                    view.getNavigationModel().setPosition(record, 0);
                }
                Ext.callback(callback, scope || me, [
                    true,
                    record,
                    domNode
                ]);
            }
            
            else if (view.bufferedRenderer) {
                view.bufferedRenderer.scrollTo(record, {
                    animate: animate,
                    highlight: highlight,
                    select: select,
                    focus: doFocus,
                    callback: function(recordIdx, record, domNode) {
                        Ext.callback(callback, scope || me, [
                            true,
                            record,
                            domNode
                        ]);
                    }
                });
            } else {
                Ext.callback(callback, scope || me, [
                    false,
                    null
                ]);
            }
        },
        getFocusEl: function() {
            return this.getView().getFocusEl();
        }
    }
});


Ext.define('Ext.selection.Model', {
    extend: 'Ext.mixin.Observable',
    alternateClassName: 'Ext.AbstractSelectionModel',
    mixins: [
        'Ext.util.StoreHolder',
        'Ext.mixin.Factoryable'
    ],
    alias: 'selection.abstract',
    factoryConfig: {
        
        defaultType: 'dataviewmodel'
    },
    
    $configPrefixed: false,
    
    $configStrict: false,
    config: {
        
        store: null,
        
        selected: {}
    },
    
    
    isSelectionModel: true,
    
    
    allowDeselect: undefined,
    
    toggleOnClick: true,
    
    selected: null,
    
    pruneRemoved: true,
    suspendChange: 0,
    
    ignoreRightMouseSelection: false,
    
    
    constructor: function(cfg) {
        var me = this;
        me.modes = {
            SINGLE: true,
            SIMPLE: true,
            MULTI: true
        };
        me.callParent([
            cfg
        ]);
        
        me.setSelectionMode(me.mode);
        if (me.selectionMode !== 'SINGLE') {
            me.allowDeselect = true;
        }
    },
    updateStore: function(store, oldStore) {
        this.bindStore(store, !oldStore);
    },
    applySelected: function(selected) {
        if (!selected.isCollection) {
            selected = new Ext.util.Collection(Ext.apply({
                rootProperty: 'data'
            }, selected));
        }
        return selected;
    },
    
    onBindStore: function(store, initial) {
        this.mixins.storeholder.onBindStore.call(this, [
            store,
            initial
        ]);
        if (store) {
            this.refresh();
        }
    },
    getStoreListeners: function() {
        var me = this;
        return {
            add: me.onStoreAdd,
            clear: me.onStoreClear,
            remove: me.onStoreRemove,
            update: me.onStoreUpdate,
            idchanged: me.onIdChanged,
            load: me.onStoreLoad,
            refresh: me.onStoreRefresh,
            
            pageadd: me.onPageAdd,
            pageremove: me.onPageRemove
        };
    },
    suspendChanges: function() {
        ++this.suspendChange;
    },
    resumeChanges: function() {
        if (this.suspendChange) {
            --this.suspendChange;
        }
    },
    
    selectAll: function(suppressEvent) {
        var me = this,
            selections = me.store.getRange(),
            start = me.getSelection().length;
        me.suspendChanges();
        me.doSelect(selections, true, suppressEvent);
        me.resumeChanges();
        
        if (!suppressEvent) {
            me.maybeFireSelectionChange(me.getSelection().length !== start);
        }
    },
    
    deselectAll: function(suppressEvent) {
        var me = this,
            selections = me.getSelection(),
            selIndexes = {},
            store = me.store,
            start = selections.length,
            i, l, rec;
        
        
        
        
        
        
        for (i = 0 , l = selections.length; i < l; i++) {
            rec = selections[i];
            selIndexes[rec.id] = store.indexOf(rec);
        }
        
        
        selections = Ext.Array.sort(selections, function(r1, r2) {
            var idx1 = selIndexes[r1.id],
                idx2 = selIndexes[r2.id];
            
            return idx1 < idx2 ? -1 : 1;
        });
        me.suspendChanges();
        me.doDeselect(selections, suppressEvent);
        me.resumeChanges();
        
        if (!suppressEvent) {
            me.maybeFireSelectionChange(me.getSelection().length !== start);
        }
    },
    getSelectionStart: function() {
        return this.selectionStart;
    },
    setSelectionStart: function(selection) {
        this.selectionStart = selection;
    },
    
    
    
    selectWithEvent: function(record, e) {
        var me = this,
            isSelected = me.isSelected(record),
            shift = e.shiftKey;
        switch (me.selectionMode) {
            case 'MULTI':
                me.selectWithEventMulti(record, e, isSelected);
                break;
            case 'SIMPLE':
                me.selectWithEventSimple(record, e, isSelected);
                break;
            case 'SINGLE':
                me.selectWithEventSingle(record, e, isSelected);
                break;
        }
        
        
        
        
        if (!shift) {
            if (me.isSelected(record)) {
                me.selectionStart = record;
            } else {
                me.selectionStart = null;
            }
        }
    },
    
    vetoSelection: function(e) {
        if (e.type !== 'keydown' && e.button !== 0) {
            if (this.ignoreRightMouseSelection || this.isSelected(e.record)) {
                return true;
            }
        } else {
            return e.type === 'mousedown';
        }
    },
    
    
    
    onNavigate: function(e) {
        
        
        
        if (!e.record || this.vetoSelection(e.keyEvent)) {
            return;
        }
        this.onBeforeNavigate(e);
        var me = this,
            keyEvent = e.keyEvent,
            
            
            ctrlKey = keyEvent.ctrlKey || e.ctrlKey,
            recIdx = e.recordIndex,
            record = e.record,
            lastFocused = e.previousRecord,
            isSelected = me.isSelected(record),
            from = (me.selectionStart && me.isSelected(e.previousRecord)) ? me.selectionStart : (me.selectionStart = e.previousRecord),
            fromIdx = e.previousRecordIndex,
            key = keyEvent.getCharCode(),
            isSpace = key === keyEvent.SPACE,
            direction = key === keyEvent.UP || key === keyEvent.PAGE_UP ? 'up' : (key === keyEvent.DOWN || key === keyEvent.DOWN ? 'down' : null);
        switch (me.selectionMode) {
            case 'MULTI':
                if (key === keyEvent.A && ctrlKey) {
                    
                    me.selected.beginUpdate();
                    me.selectRange(0, me.store.getCount() - 1);
                    me.selected.endUpdate();
                } else if (isSpace) {
                    
                    if (keyEvent.shiftKey) {
                        me.selectRange(from, record, ctrlKey);
                    } else {
                        
                        if (isSelected) {
                            if (me.allowDeselect) {
                                me.doDeselect(record);
                            }
                        } else 
                        
                        {
                            me.doSelect(record, ctrlKey);
                        }
                    }
                }
                
                else if (keyEvent.shiftKey && from) {
                    
                    if (direction === 'up' && fromIdx <= recIdx) {
                        me.deselectRange(lastFocused, recIdx + 1);
                    } else if (direction === 'down' && fromIdx >= recIdx) {
                        me.deselectRange(lastFocused, recIdx - 1);
                    }
                    
                    else if (from !== record) {
                        me.selectRange(from, record, ctrlKey);
                    }
                    me.lastSelected = record;
                } else if (key) {
                    if (!ctrlKey) {
                        me.doSelect(record, false);
                    }
                } else {
                    me.selectWithEvent(record, keyEvent);
                };
                break;
            case 'SIMPLE':
                if (key === keyEvent.A && ctrlKey) {
                    
                    me.selected.beginUpdate();
                    me.selectRange(0, me.store.getCount() - 1);
                    me.selected.endUpdate();
                } else if (isSelected) {
                    me.doDeselect(record);
                } else {
                    me.doSelect(record, true);
                };
                break;
            case 'SINGLE':
                
                if (isSpace) {
                    if (isSelected) {
                        if (me.allowDeselect) {
                            me.doDeselect(record);
                        }
                    } else {
                        me.doSelect(record);
                    }
                }
                
                else if (ctrlKey) {
                    break;
                }
                
                
                else if (me.allowDeselect && isSelected) {
                    me.doDeselect(record);
                } else 
                {
                    me.doSelect(record, false);
                };
                break;
        }
        
        
        
        
        if (!keyEvent.shiftKey) {
            if (me.isSelected(record)) {
                me.selectionStart = record;
            }
        }
    },
    
    selectRange: function(startRow, endRow, keepExisting) {
        var me = this,
            store = me.store,
            selected = me.selected.items,
            result, i, len, toSelect, toDeselect, idx, rec;
        if (me.isLocked()) {
            return;
        }
        result = me.normalizeRowRange(startRow, endRow);
        startRow = result[0];
        endRow = result[1];
        toSelect = [];
        for (i = startRow; i <= endRow; i++) {
            if (!me.isSelected(store.getAt(i))) {
                toSelect.push(store.getAt(i));
            }
        }
        if (!keepExisting) {
            
            toDeselect = [];
            me.suspendChanges();
            for (i = 0 , len = selected.length; i < len; ++i) {
                rec = selected[i];
                idx = store.indexOf(rec);
                if (idx < startRow || idx > endRow) {
                    toDeselect.push(rec);
                }
            }
            for (i = 0 , len = toDeselect.length; i < len; ++i) {
                me.doDeselect(toDeselect[i]);
            }
            me.resumeChanges();
        }
        if (toSelect.length) {
            me.doMultiSelect(toSelect, true);
        } else if (toDeselect) {
            me.maybeFireSelectionChange(toDeselect.length > 0);
        }
    },
    
    deselectRange: function(startRow, endRow) {
        var me = this,
            store = me.store,
            result, i, toDeselect, record;
        if (me.isLocked()) {
            return;
        }
        result = me.normalizeRowRange(startRow, endRow);
        startRow = result[0];
        endRow = result[1];
        toDeselect = [];
        for (i = startRow; i <= endRow; i++) {
            record = store.getAt(i);
            if (me.isSelected(record)) {
                toDeselect.push(record);
            }
        }
        if (toDeselect.length) {
            me.doDeselect(toDeselect);
        }
    },
    normalizeRowRange: function(startRow, endRow) {
        var store = this.store,
            tmp;
        if (!Ext.isNumber(startRow)) {
            startRow = store.indexOf(startRow);
        }
        startRow = Math.max(0, startRow);
        if (!Ext.isNumber(endRow)) {
            endRow = store.indexOf(endRow);
        }
        endRow = Math.min(endRow, store.getCount() - 1);
        
        if (startRow > endRow) {
            tmp = endRow;
            endRow = startRow;
            startRow = tmp;
        }
        return [
            startRow,
            endRow
        ];
    },
    
    select: function(records, keepExisting, suppressEvent) {
        
        if (Ext.isDefined(records) && !(Ext.isArray(records) && !records.length)) {
            this.doSelect(records, keepExisting, suppressEvent);
        }
    },
    
    deselect: function(records, suppressEvent) {
        this.doDeselect(records, suppressEvent);
    },
    doSelect: function(records, keepExisting, suppressEvent) {
        var me = this,
            record;
        if (me.locked) {
            return;
        }
        if (typeof records === "number") {
            record = me.store.getAt(records);
            
            if (!record) {
                return;
            }
            records = [
                record
            ];
        }
        if (me.selectionMode === "SINGLE" && records) {
            record = records.length ? records[0] : records;
            me.doSingleSelect(record, suppressEvent);
        } else {
            me.doMultiSelect(records, keepExisting, suppressEvent);
        }
    },
    doMultiSelect: function(records, keepExisting, suppressEvent) {
        var me = this,
            selected = me.selected,
            change = false,
            result, i, len, record, commit;
        if (me.locked) {
            return;
        }
        records = !Ext.isArray(records) ? [
            records
        ] : records;
        len = records.length;
        if (!keepExisting && selected.getCount() > 0) {
            result = me.deselectDuringSelect(records, suppressEvent);
            if (result[0]) {
                
                
                me.maybeFireSelectionChange(result[1] > 0 && !suppressEvent);
                return;
            } else {
                
                change = result[1] > 0;
            }
        }
        commit = function() {
            if (!selected.getCount()) {
                me.selectionStart = record;
            }
            selected.add(record);
            change = true;
        };
        for (i = 0; i < len; i++) {
            record = records[i];
            if (me.isSelected(record)) {
                
                continue;
            }
            me.onSelectChange(record, true, suppressEvent, commit);
        }
        me.lastSelected = record;
        
        me.maybeFireSelectionChange(change && !suppressEvent);
    },
    deselectDuringSelect: function(toSelect, suppressEvent) {
        var me = this,
            selected = me.selected.getRange(),
            len = selected.length,
            changed = 0,
            failed = false,
            item, i;
        
        me.suspendChanges();
        me.deselectingDuringSelect = true;
        for (i = 0; i < len; ++i) {
            item = selected[i];
            if (!Ext.Array.contains(toSelect, item)) {
                if (me.doDeselect(item, suppressEvent)) {
                    ++changed;
                } else {
                    failed = true;
                }
            }
        }
        me.deselectingDuringSelect = false;
        me.resumeChanges();
        return [
            failed,
            changed
        ];
    },
    
    doDeselect: function(records, suppressEvent) {
        var me = this,
            selected = me.selected,
            i = 0,
            len, record,
            attempted = 0,
            accepted = 0,
            commit;
        if (me.locked || !me.store) {
            return false;
        }
        if (typeof records === "number") {
            
            record = me.store.getAt(records);
            if (!record) {
                return false;
            }
            records = [
                record
            ];
        } else if (!Ext.isArray(records)) {
            records = [
                records
            ];
        }
        commit = function() {
            ++accepted;
            selected.remove(record);
            if (record === me.selectionStart) {
                me.selectionStart = null;
            }
        };
        len = records.length;
        me.suspendChanges();
        for (; i < len; i++) {
            record = records[i];
            if (me.isSelected(record)) {
                if (me.lastSelected === record) {
                    me.lastSelected = selected.last();
                }
                ++attempted;
                me.onSelectChange(record, false, suppressEvent, commit);
            }
        }
        me.resumeChanges();
        
        me.maybeFireSelectionChange(accepted > 0 && !suppressEvent);
        return accepted === attempted;
    },
    doSingleSelect: function(record, suppressEvent) {
        var me = this,
            changed = false,
            selected = me.selected,
            commit;
        if (me.locked) {
            return;
        }
        
        
        if (me.isSelected(record)) {
            return;
        }
        commit = function() {
            
            if (selected.getCount()) {
                me.suspendChanges();
                var result = me.deselectDuringSelect([
                        record
                    ], suppressEvent);
                me.resumeChanges();
                if (result[0]) {
                    
                    return false;
                }
            }
            me.lastSelected = record;
            if (!selected.getCount()) {
                me.selectionStart = record;
            }
            selected.add(record);
            changed = true;
        };
        me.onSelectChange(record, true, suppressEvent, commit);
        if (changed) {
            me.maybeFireSelectionChange(!suppressEvent);
        }
    },
    
    
    maybeFireSelectionChange: function(fireEvent) {
        var me = this;
        if (fireEvent && !me.suspendChange) {
            me.fireEvent('selectionchange', me, me.getSelection());
        }
    },
    
    getLastSelected: function() {
        return this.lastSelected;
    },
    
    getSelection: function() {
        return this.selected.getRange();
    },
    
    getSelectionMode: function() {
        return this.selectionMode;
    },
    
    setSelectionMode: function(selMode) {
        selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
        
        
        this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
    },
    
    isLocked: function() {
        return this.locked;
    },
    
    setLocked: function(locked) {
        this.locked = !!locked;
    },
    
    isRangeSelected: function(startRow, endRow) {
        var me = this,
            store = me.store,
            i, result;
        result = me.normalizeRowRange(startRow, endRow);
        startRow = result[0];
        endRow = result[1];
        
        for (i = startRow; i <= endRow; i++) {
            if (!me.isSelected(store.getAt(i))) {
                return false;
            }
        }
        return true;
    },
    
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.store.getAt(record) : record;
        return this.selected.contains(record);
    },
    
    hasSelection: function() {
        var selected = this.getSelected();
        return !!(selected && selected.getCount());
    },
    refresh: function() {
        var me = this,
            store = me.store,
            rec,
            toBeSelected = [],
            toBeReAdded = [],
            oldSelections = me.getSelection(),
            len = oldSelections.length,
            selection,
            
            
            
            selected = me.getSelected(),
            change,
            i = 0,
            d, storeData;
        
        if (!store || !(selected.isCollection || selected.isRows) || !selected.getCount()) {
            return;
        }
        
        storeData = store.getData();
        
        if (storeData.getSource) {
            d = storeData.getSource();
            if (d) {
                storeData = d;
            }
        }
        me.refreshing = true;
        me.suspendChanges();
        
        
        for (; i < len; i++) {
            selection = oldSelections[i];
            if (storeData.indexOf(selection) !== -1) {
                toBeSelected.push(selection);
            }
            
            else if (!me.pruneRemoved) {
                
                rec = storeData.get(selection.getId());
                if (rec) {
                    toBeSelected.push(rec);
                } else 
                {
                    toBeReAdded.push(selection);
                }
            }
            
            if (me.mode === 'SINGLE' && toBeReAdded.length) {
                break;
            }
        }
        
        
        if (selected.getCount() !== (toBeSelected.length + toBeReAdded.length)) {
            change = true;
        }
        me.clearSelections();
        if (toBeSelected.length) {
            
            me.doSelect(toBeSelected, false, true);
        }
        
        if (toBeReAdded.length) {
            selected.add(toBeReAdded);
            
            if (!me.lastSelected) {
                me.lastSelected = toBeReAdded[toBeReAdded.length - 1];
            }
        }
        me.resumeChanges();
        me.refreshing = false;
        me.maybeFireSelectionChange(change);
    },
    
    clearSelections: function() {
        
        
        
        var selected = this.getSelected();
        
        if (selected) {
            selected.clear();
        }
        this.lastSelected = null;
    },
    
    onStoreAdd: Ext.emptyFn,
    
    
    onStoreClear: function() {
        if (!this.store.isLoading() && this.hasSelection()) {
            this.clearSelections();
            this.maybeFireSelectionChange(true);
        }
    },
    
    
    
    onStoreRemove: function(store, records, index, isMove) {
        var me = this,
            toDeselect = records,
            i, len, rec, moveMap;
        
        if (me.selectionStart && Ext.Array.contains(records, me.selectionStart)) {
            me.selectionStart = null;
        }
        if (isMove || me.locked || !me.pruneRemoved) {
            return;
        }
        
        moveMap = store.isMoving(null, true);
        if (moveMap) {
            toDeselect = null;
            for (i = 0 , len = records.length; i < len; ++i) {
                rec = records[i];
                if (!moveMap[rec.id]) {
                    (toDeselect || (toDeselect = [])).push(rec);
                }
            }
        }
        if (toDeselect) {
            me.deselect(toDeselect);
        }
    },
    
    
    onPageRemove: function(pageMap, pageNumber, records) {
        this.onStoreRemove(this.store, records);
    },
    
    
    onPageAdd: function(pageMap, pageNumber, records) {
        var len = records.length,
            i, record;
        for (i = 0; i < len; i++) {
            record = records[i];
            if (this.selected.get(record.id)) {
                this.selected.replace(record);
            }
        }
    },
    
    getCount: function() {
        return this.selected.getCount();
    },
    
    onUpdate: Ext.emptyFn,
    
    destroy: function() {
        var me = this;
        me.clearListeners();
        me.clearSelections();
        me.bindStore(null);
        me.selected = Ext.destroy(me.selected);
        me.callParent();
    },
    
    onStoreUpdate: Ext.emptyFn,
    onIdChanged: function(store, rec, oldId, newId) {
        this.selected.updateKey(rec, oldId);
    },
    onStoreRefresh: function() {
        this.updateSelectedInstances(this.selected);
    },
    
    updateSelectedInstances: function(selected) {
        var me = this,
            store = me.getStore(),
            lastSelected = me.lastSelected,
            items, length, i, selectedRec, rec;
        if (store.isBufferedStore) {
            return;
        }
        items = selected.getRange();
        length = items.length;
        if (lastSelected) {
            me.lastSelected = store.getById(lastSelected.id);
        }
        
        me.refreshing = true;
        for (i = 0; i < length; ++i) {
            selectedRec = items[i];
            
            rec = store.getById(selectedRec.id);
            
            if (rec) {
                if (rec !== selectedRec) {
                    
                    selected.replace(rec);
                }
            }
            
            else if (me.pruneRemovedOnRefresh()) {
                selected.remove(selectedRec);
            }
        }
        me.refreshing = false;
    },
    
    
    
    
    
    pruneRemovedOnRefresh: function() {
        return this.pruneRemoved;
    },
    
    onStoreLoad: Ext.emptyFn,
    
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            eventName = isSelected ? 'select' : 'deselect';
        if ((suppressEvent || me.fireEvent('before' + eventName, me, record)) !== false && commitFn() !== false) {
            if (!suppressEvent) {
                me.fireEvent(eventName, me, record);
            }
        }
    },
    
    onEditorKey: Ext.emptyFn,
    
    beforeViewRender: function(view) {
        Ext.Array.include(this.views || (this.views = []), view);
    },
    
    onHeaderClick: Ext.emptyFn,
    resolveListenerScope: function(defaultScope) {
        var view = this.view,
            scope;
        if (view) {
            scope = view.resolveListenerScope(defaultScope);
        }
        return scope || this.callParent([
            defaultScope
        ]);
    },
    onVetoUIEvent: Ext.emptyFn,
    
    bindComponent: Ext.emptyFn,
    privates: {
        onBeforeNavigate: Ext.privateFn,
        selectWithEventMulti: function(record, e, isSelected) {
            var me = this,
                shift = e.shiftKey,
                ctrl = e.ctrlKey,
                start = shift ? (me.getSelectionStart()) : null,
                selected = me.getSelection(),
                len = selected.length,
                toDeselect, i, item;
            if (shift && start) {
                me.selectRange(start, record, ctrl);
            } else if (ctrl && isSelected) {
                if (me.allowDeselect) {
                    me.doDeselect(record, false);
                }
            } else if (ctrl) {
                me.doSelect(record, true, false);
            } else if (isSelected && !shift && !ctrl && len > 1) {
                if (me.allowDeselect) {
                    toDeselect = [];
                    for (i = 0; i < len; ++i) {
                        item = selected[i];
                        if (item !== record) {
                            toDeselect.push(item);
                        }
                    }
                    me.doDeselect(toDeselect);
                }
            } else if (!isSelected) {
                me.doSelect(record, false);
            }
        },
        selectWithEventSimple: function(record, e, isSelected) {
            if (isSelected) {
                this.doDeselect(record);
            } else {
                this.doSelect(record, true);
            }
        },
        selectWithEventSingle: function(record, e, isSelected) {
            var me = this,
                allowDeselect = me.allowDeselect;
            if (allowDeselect && !e.ctrlKey) {
                allowDeselect = me.toggleOnClick;
            }
            if (allowDeselect && isSelected) {
                me.doDeselect(record);
            } else {
                me.doSelect(record, false);
            }
        }
    }
});


Ext.define('Ext.selection.DataViewModel', {
    extend: 'Ext.selection.Model',
    alias: 'selection.dataviewmodel',
    requires: [
        'Ext.util.KeyNav'
    ],
    deselectOnContainerClick: true,
    
    
    
    
    
    bindComponent: function(view) {
        var me = this,
            viewListeners;
        if (me.view !== view) {
            if (me.view) {
                me.navigationModel = null;
                Ext.destroy(me.viewListeners, me.navigationListeners);
            }
            me.view = view;
            if (view) {
                viewListeners = me.getViewListeners();
                viewListeners.scope = me;
                viewListeners.destroyable = true;
                me.navigationModel = view.getNavigationModel();
                me.viewListeners = view.on(viewListeners);
                me.navigationListeners = me.navigationModel.on({
                    navigate: me.onNavigate,
                    scope: me,
                    destroyable: true
                });
            }
        }
    },
    getViewListeners: function() {
        var me = this,
            eventListeners = {};
        eventListeners[me.view.triggerCtEvent] = me.onContainerClick;
        return eventListeners;
    },
    onUpdate: function(record) {
        var view = this.view;
        if (view && this.isSelected(record)) {
            view.onItemSelect(record);
        }
    },
    onContainerClick: function() {
        if (this.deselectOnContainerClick) {
            this.deselectAll();
        }
    },
    
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            view = me.view,
            eventName = isSelected ? 'select' : 'deselect',
            recordIndex = me.store.indexOf(record);
        if ((suppressEvent || me.fireEvent('before' + eventName, me, record, recordIndex)) !== false && commitFn() !== false) {
            if (view) {
                if (isSelected) {
                    view.onItemSelect(record);
                } else {
                    view.onItemDeselect(record);
                }
            }
            if (!suppressEvent) {
                me.fireEvent(eventName, me, record, recordIndex);
            }
        }
    },
    destroy: function() {
        this.bindComponent();
        Ext.destroy(this.keyNav);
        this.callParent();
    }
});


Ext.define('Ext.view.NavigationModel', {
    mixins: [
        'Ext.util.Observable',
        'Ext.mixin.Factoryable'
    ],
    alias: 'view.navigation.default',
    
    
    focusCls: Ext.baseCSSPrefix + 'view-item-focused',
    constructor: function() {
        this.mixins.observable.constructor.call(this);
    },
    bindComponent: function(view) {
        if (this.view !== view) {
            this.view = view;
            this.bindView(view);
        }
    },
    bindView: function(view) {
        var me = this,
            dataSource = view.dataSource,
            listeners;
        me.initKeyNav(view);
        if (me.dataSource !== dataSource) {
            me.dataSource = dataSource;
            listeners = me.getStoreListeners();
            listeners.destroyable = true;
            me.dataSourceListeners = view.dataSource.on(listeners);
        }
        listeners = me.getViewListeners();
        listeners.destroyable = true;
        me.viewListeners = me.viewListeners || [];
        me.viewListeners.push(view.on(listeners));
    },
    getStoreListeners: function() {
        var me = this;
        return {
            clear: me.onStoreClear,
            remove: me.onStoreRemove,
            scope: me
        };
    },
    getViewListeners: function() {
        var me = this;
        return {
            containermousedown: me.onContainerMouseDown,
            itemmousedown: me.onItemMouseDown,
            
            itemclick: me.onItemClick,
            itemcontextmenu: me.onItemMouseDown,
            scope: me
        };
    },
    initKeyNav: function(view) {
        var me = this;
        
        
        
        me.keyNav = new Ext.util.KeyNav({
            target: view,
            ignoreInputFields: true,
            eventName: 'itemkeydown',
            defaultEventAction: 'stopEvent',
            processEvent: function(view, record, node, index, event) {
                return event;
            },
            up: me.onKeyUp,
            down: me.onKeyDown,
            right: me.onKeyRight,
            left: me.onKeyLeft,
            pageDown: me.onKeyPageDown,
            pageUp: me.onKeyPageUp,
            home: me.onKeyHome,
            end: me.onKeyEnd,
            tab: me.onKeyTab,
            space: me.onKeySpace,
            enter: me.onKeyEnter,
            A: {
                ctrl: true,
                
                
                handler: me.onSelectAllKeyPress
            },
            scope: me
        });
    },
    addKeyBindings: function(binding) {
        this.keyNav.addBindings(binding);
    },
    enable: function() {
        this.keyNav.enable();
        this.disabled = false;
    },
    disable: function() {
        this.keyNav.disable();
        this.disabled = true;
    },
    onContainerMouseDown: function(view, mousedownEvent) {
        
        if (this.view.containsFocus) {
            mousedownEvent.preventDefault();
        }
    },
    onItemMouseDown: function(view, record, item, index, mousedownEvent) {
        var parentEvent = mousedownEvent.parentEvent;
        
        if (!parentEvent || parentEvent.type !== 'touchstart') {
            this.setPosition(index);
        }
    },
    onItemClick: function(view, record, item, index, clickEvent) {
        
        if (this.record === record) {
            this.fireNavigateEvent(clickEvent);
        } else {
            this.setPosition(index, clickEvent);
        }
    },
    
    beforeViewRefresh: function() {
        this.focusRestorePosition = this.view.dataSource.isBufferedStore ? this.recordIndex : this.record;
    },
    
    onViewRefresh: function() {
        if (this.focusRestorePosition != null) {
            this.setPosition(this.focusRestorePosition);
            this.focusRestorePosition = null;
        }
    },
    
    onStoreClear: function() {
        this.setPosition();
    },
    
    
    onStoreRemove: function() {
        this.setPosition(this.getRecord(), null, null, true);
    },
    setPosition: function(recordIndex, keyEvent, suppressEvent, preventNavigation) {
        var me = this,
            view = me.view,
            selModel = view.getSelectionModel(),
            dataSource = view.dataSource,
            newRecord, newRecordIndex;
        if (recordIndex == null || !view.all.getCount()) {
            me.record = me.recordIndex = null;
        } else {
            if (typeof recordIndex === 'number') {
                newRecordIndex = Math.max(Math.min(recordIndex, dataSource.getCount() - 1), 0);
                newRecord = dataSource.getAt(recordIndex);
            }
            
            else if (recordIndex.isEntity) {
                newRecord = dataSource.getById(recordIndex.id);
                newRecordIndex = dataSource.indexOf(newRecord);
                
                if (newRecordIndex === -1) {
                    newRecord = dataSource.getAt(0);
                    newRecordIndex = 0;
                }
            }
            
            else if (recordIndex.tagName) {
                newRecord = view.getRecord(recordIndex);
                newRecordIndex = dataSource.indexOf(newRecord);
            } else {
                newRecord = newRecordIndex = null;
            }
        }
        
        
        
        
        if (newRecord === me.record) {
            return me.focusPosition(me.recordIndex);
        }
        if (me.item) {
            me.item.removeCls(me.focusCls);
        }
        
        
        me.previousRecordIndex = me.recordIndex;
        me.previousRecord = me.record;
        me.previousItem = me.item;
        
        me.recordIndex = newRecordIndex;
        me.record = newRecord;
        
        preventNavigation = preventNavigation || me.record === me.lastFocused;
        
        if (newRecord) {
            me.focusPosition(me.recordIndex);
        } else {
            me.item = null;
        }
        if (!suppressEvent) {
            selModel.fireEvent('focuschange', selModel, me.previousRecord, me.record);
        }
        
        if (!preventNavigation && keyEvent) {
            me.fireNavigateEvent(keyEvent);
        }
    },
    
    focusPosition: function(recordIndex) {
        var me = this;
        if (recordIndex != null && recordIndex !== -1) {
            if (recordIndex.isEntity) {
                recordIndex = me.view.dataSource.indexOf(recordIndex);
            }
            me.item = me.view.all.item(recordIndex);
            if (me.item) {
                me.lastFocused = me.record;
                me.lastFocusedIndex = me.recordIndex;
                me.focusItem(me.item);
            } else {
                me.record = null;
            }
        } else {
            me.item = null;
        }
    },
    
    focusItem: function(item) {
        item.addCls(this.focusCls);
        item.focus();
    },
    getPosition: function() {
        return this.record ? this.recordIndex : null;
    },
    getRecordIndex: function() {
        return this.recordIndex;
    },
    getItem: function() {
        return this.item;
    },
    getRecord: function() {
        return this.record;
    },
    getLastFocused: function() {
        
        if (this.view.dataSource.indexOf(this.lastFocused) === -1) {
            return null;
        }
        return this.lastFocused;
    },
    onKeyUp: function(keyEvent) {
        var newPosition = this.recordIndex - 1;
        if (newPosition < 0) {
            newPosition = this.view.all.getCount() - 1;
        }
        this.setPosition(newPosition, keyEvent);
    },
    onKeyDown: function(keyEvent) {
        var newPosition = this.recordIndex + 1;
        if (newPosition > this.view.all.getCount() - 1) {
            newPosition = 0;
        }
        this.setPosition(newPosition, keyEvent);
    },
    onKeyRight: function(keyEvent) {
        var newPosition = this.recordIndex + 1;
        if (newPosition > this.view.all.getCount() - 1) {
            newPosition = 0;
        }
        this.setPosition(newPosition, keyEvent);
    },
    onKeyLeft: function(keyEvent) {
        var newPosition = this.recordIndex - 1;
        if (newPosition < 0) {
            newPosition = this.view.all.getCount() - 1;
        }
        this.setPosition(newPosition, keyEvent);
    },
    onKeyPageDown: Ext.emptyFn,
    onKeyPageUp: Ext.emptyFn,
    onKeyHome: function(keyEvent) {
        this.setPosition(0, keyEvent);
    },
    onKeyEnd: function(keyEvent) {
        this.setPosition(this.view.all.getCount() - 1, keyEvent);
    },
    
    
    
    
    
    
    
    
    onKeyTab: function(keyEvent) {
        var view = this.view;
        
        
        
        view.toggleChildrenTabbability(false);
        
        return true;
    },
    onKeySpace: function(keyEvent) {
        this.fireNavigateEvent(keyEvent);
    },
    
    onKeyEnter: function(keyEvent) {
        
        
        keyEvent.stopEvent();
        keyEvent.view.fireEvent('itemclick', keyEvent.view, keyEvent.record, keyEvent.item, keyEvent.recordIndex, keyEvent);
    },
    onSelectAllKeyPress: function(keyEvent) {
        this.fireNavigateEvent(keyEvent);
    },
    fireNavigateEvent: function(keyEvent) {
        var me = this;
        me.fireEvent('navigate', {
            navigationModel: me,
            keyEvent: keyEvent,
            previousRecordIndex: me.previousRecordIndex,
            previousRecord: me.previousRecord,
            previousItem: me.previousItem,
            recordIndex: me.recordIndex,
            record: me.record,
            item: me.item
        });
    },
    destroy: function() {
        var me = this;
        Ext.destroy(me.dataSourceListeners, me.viewListeners, me.keyNav);
        me.keyNav = me.dataSourceListeners = me.viewListeners = me.dataSource = null;
        me.callParent();
    }
});


Ext.define('Ext.view.AbstractView', {
    extend: 'Ext.Component',
    requires: [
        'Ext.LoadMask',
        'Ext.CompositeElementLite',
        'Ext.selection.DataViewModel',
        'Ext.view.NavigationModel'
    ],
    mixins: [
        'Ext.util.StoreHolder'
    ],
    inheritableStatics: {
        getRecord: function(node) {
            return this.getBoundView(node).getRecord(node);
        },
        getBoundView: function(node) {
            return Ext.getCmp(node.getAttribute('data-boundView'));
        }
    },
    defaultBindProperty: 'store',
    
    renderBuffer: document.createElement('div'),
    statics: {
        
        updateDelay: 200,
        
        queueRecordChange: function(view, store, record, operation, modifiedFieldNames) {
            var me = this,
                changeQueue = me.changeQueue || (me.changeQueue = {}),
                recId = record.internalId,
                recChange, updated, len, i, fieldName, value, checkForReversion;
            recChange = changeQueue[recId] || (changeQueue[recId] = {
                operation: operation,
                record: record,
                data: {},
                views: []
            });
            
            updated = recChange.data;
            
            Ext.Array.include(recChange.views, view);
            
            
            if (modifiedFieldNames && (len = modifiedFieldNames.length)) {
                for (i = 0; i < len; i++) {
                    fieldName = modifiedFieldNames[i];
                    value = record.data[fieldName];
                    
                    if (updated.hasOwnProperty(fieldName)) {
                        
                        if (record.isEqual(updated[fieldName], value)) {
                            delete updated[fieldName];
                            checkForReversion = true;
                        }
                    } else 
                    {
                        updated[fieldName] = value;
                    }
                }
                
                
                if (checkForReversion && !Ext.Object.getKeys(updated).length) {
                    delete changeQueue[recId];
                }
            } else 
            {
                Ext.apply(updated, record.data);
            }
            
            if (!me.flushQueueTask) {
                me.flushQueueTask = Ext.util.TaskManager.newTask({
                    
                    run: Ext.global.requestAnimationFrame ? Ext.Function.createAnimationFrame(me.onFlushTick, me) : Ext.Function.bind(me.onFlushTick, me),
                    interval: Ext.view.AbstractView.updateDelay,
                    repeat: 1
                });
            }
            me.flushQueueTask.start();
        },
        
        
        onFlushTick: function() {
            Ext.AnimationQueue.start(this.flushChangeQueue, this);
        },
        
        flushChangeQueue: function() {
            
            var me = this,
                dirtyViews, len, changeQueue, recChange, recId, i, view;
            
            if (Ext.isScrolling) {
                me.flushQueueTask.start();
                return;
            }
            changeQueue = me.changeQueue;
            
            this.changeQueue = {};
            for (recId in changeQueue) {
                recChange = changeQueue[recId];
                dirtyViews = recChange.views;
                len = dirtyViews.length;
                
                for (i = 0; i < len; i++) {
                    view = dirtyViews[i];
                    
                    if (!view.isDestroyed) {
                        view.handleUpdate(view.dataSource, recChange.record, recChange.operation, Ext.Object.getKeys(recChange.data));
                    }
                }
            }
            Ext.AnimationQueue.stop(me.flushChangeQueue, me);
        }
    },
    config: {
        
        selection: null,
        
        store: 'ext-empty-store',
        
        
        navigationModel: {
            type: 'default'
        },
        
        
        selectionModel: {
            type: 'dataviewmodel'
        }
    },
    publishes: [
        'selection'
    ],
    twoWayBindable: [
        'selection'
    ],
    
    throttledUpdate: false,
    
    
    deferInitialRefresh: false,
    
    
    itemCls: Ext.baseCSSPrefix + 'dataview-item',
    
    
    
    
    loadingText: 'Loading...',
    
    
    loadMask: true,
    
    
    loadingUseMsg: true,
    
    
    selectedItemCls: Ext.baseCSSPrefix + 'item-selected',
    
    
    emptyText: "",
    
    
    deferEmptyText: true,
    
    trackOver: false,
    
    blockRefresh: false,
    
    
    preserveScrollOnRefresh: false,
    ariaRole: 'listbox',
    itemAriaRole: 'option',
    
    last: false,
    focusable: true,
    tabIndex: 0,
    triggerEvent: 'itemclick',
    triggerCtEvent: 'containerclick',
    
    
    
    
    refreshNeeded: true,
    addCmpEvents: Ext.emptyFn,
    
    
    
    
    
    
    constructor: function(config) {
        if (config && config.selModel) {
            config.selectionModel = config.selModel;
        }
        this.callParent([
            config
        ]);
    },
    
    initComponent: function() {
        var me = this,
            isDef = Ext.isDefined,
            itemTpl = me.itemTpl,
            memberFn = {};
        if (itemTpl) {
            if (Ext.isArray(itemTpl)) {
                
                itemTpl = itemTpl.join('');
            } else if (Ext.isObject(itemTpl)) {
                
                memberFn = Ext.apply(memberFn, itemTpl.initialConfig);
                itemTpl = itemTpl.html;
            }
            if (!me.itemSelector) {
                me.itemSelector = '.' + me.itemCls;
            }
            itemTpl = Ext.String.format('<tpl for="."><div class="{0}" role="{2}">{1}</div></tpl>', me.itemCls, itemTpl, me.itemAriaRole);
            me.tpl = new Ext.XTemplate(itemTpl,memberFn);
        }
        if (!isDef(me.tpl) || !isDef(me.itemSelector)) {
            Ext.Error.raise({
                sourceClass: 'Ext.view.View',
                tpl: me.tpl,
                itemSelector: me.itemSelector,
                msg: "DataView requires both tpl and itemSelector configurations to be defined."
            });
        }
        me.callParent();
        me.tpl = me.getTpl('tpl');
        
        
        if (isDef(me.overCls) || isDef(me.overClass)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.view.View: Using the deprecated overCls or overClass configuration. Use overItemCls instead.');
            }
            me.overItemCls = me.overCls || me.overClass;
            delete me.overCls;
            delete me.overClass;
        }
        if (isDef(me.selectedCls) || isDef(me.selectedClass)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.view.View: Using the deprecated selectedCls or selectedClass configuration. Use selectedItemCls instead.');
            }
            me.selectedItemCls = me.selectedCls || me.selectedClass;
            delete me.selectedCls;
            delete me.selectedClass;
        }
        if (me.overItemCls) {
            me.trackOver = true;
        }
        me.addCmpEvents();
        
        me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');
        
        if (!me.dataSource) {
            me.dataSource = me.store;
        }
        
        
        me.getNavigationModel().bindComponent(this);
        
        
        
        me.bindStore(me.dataSource, true, 'dataSource');
        if (!me.all) {
            me.all = new Ext.CompositeElementLite();
        }
        
        me.scrollState = {
            top: 0,
            left: 0
        };
        me.savedTabIndexAttribute = 'data-savedtabindex-' + me.id;
    },
    getElConfig: function() {
        var result = this.mixins.renderable.getElConfig.call(this);
        
        if (this.focusable) {
            result.tabIndex = 0;
        }
        return result;
    },
    onRender: function() {
        var mask = this.loadMask;
        this.callParent(arguments);
        if (mask) {
            this.createMask(mask);
        }
    },
    beforeLayout: function() {
        var me = this;
        me.callParent(arguments);
        
        
        if (me.refreshNeeded && !me.pendingRefresh) {
            
            if (me.refreshCounter) {
                me.refresh();
            } else {
                me.doFirstRefresh(me.dataSource);
            }
        }
    },
    getMaskStore: function() {
        return this.store;
    },
    onMaskBeforeShow: function() {
        var me = this,
            loadingHeight = me.loadingHeight;
        if (loadingHeight && loadingHeight > me.getHeight()) {
            me.hasLoadingHeight = true;
            me.oldMinHeight = me.minHeight;
            me.minHeight = loadingHeight;
            me.updateLayout();
        }
    },
    onMaskHide: function() {
        var me = this;
        if (!me.destroying && me.hasLoadingHeight) {
            me.minHeight = me.oldMinHeight;
            me.updateLayout();
            delete me.hasLoadingHeight;
        }
    },
    beforeRender: function() {
        this.callParent(arguments);
        this.getSelectionModel().beforeViewRender(this);
    },
    afterRender: function() {
        this.callParent(arguments);
        
        
        
        if (this.focusable) {
            this.focusEl = this.el;
        }
    },
    getRefItems: function() {
        var mask = this.loadMask,
            result = [];
        if (mask && mask.isComponent) {
            result.push(mask);
        }
        return result;
    },
    getSelection: function() {
        return this.getSelectionModel().getSelection();
    },
    updateSelection: function(selection) {
        var me = this,
            sm;
        if (!me.ignoreNextSelection) {
            me.ignoreNextSelection = true;
            sm = me.getSelectionModel();
            if (selection) {
                sm.select(selection);
            } else {
                sm.deselectAll();
            }
            me.ignoreNextSelection = false;
        }
    },
    updateBindSelection: function(selModel, selection) {
        var me = this,
            selected = null;
        if (!me.ignoreNextSelection) {
            me.ignoreNextSelection = true;
            if (selection.length) {
                selected = selModel.getLastSelected();
                me.hasHadSelection = true;
            }
            if (me.hasHadSelection) {
                me.setSelection(selected);
            }
            me.ignoreNextSelection = false;
        }
    },
    applySelectionModel: function(selModel, oldSelModel) {
        var me = this,
            mode;
        if (oldSelModel) {
            oldSelModel.un('selectionchange', me.updateBindSelection, me);
            Ext.destroy(me.selModelRelayer);
            selModel = Ext.Factory.selection(selModel);
        } else 
        {
            if (selModel && selModel.isSelectionModel) {
                selModel.locked = me.disableSelection;
            } else {
                if (me.simpleSelect) {
                    mode = 'SIMPLE';
                } else if (me.multiSelect) {
                    mode = 'MULTI';
                } else {
                    mode = 'SINGLE';
                }
                if (typeof selModel === 'string') {
                    selModel = {
                        type: selModel
                    };
                }
                selModel = Ext.Factory.selection(Ext.apply({
                    allowDeselect: me.allowDeselect || me.multiSelect,
                    mode: mode,
                    locked: me.disableSelection
                }, selModel));
            }
        }
        me.selModelRelayer = me.relayEvents(selModel, [
            'selectionchange',
            'beforeselect',
            'beforedeselect',
            'select',
            'deselect',
            'focuschange'
        ]);
        selModel.on('selectionchange', me.updateBindSelection, me);
        return selModel;
    },
    updateSelectionModel: function(selectionModel) {
        
        this.selModel = selectionModel;
    },
    applyNavigationModel: function(navigationModel) {
        return Ext.Factory.viewNavigation(navigationModel);
    },
    onFocusEnter: function(e) {
        var me = this,
            navigationModel = me.getNavigationModel(),
            focusPosition;
        if (!me.itemFocused && me.all.getCount()) {
            focusPosition = navigationModel.getLastFocused();
            navigationModel.setPosition(focusPosition || 0, e.event, null, !focusPosition);
            
            me.itemFocused = navigationModel.getPosition() != null;
        }
        if (me.itemFocused) {
            this.el.dom.setAttribute('tabindex', '-1');
        }
    },
    onFocusLeave: function(e) {
        var me = this;
        
        if (me.itemFocused) {
            
            me.getNavigationModel().setPosition(null, e.event, null, true);
            me.itemFocused = false;
            me.el.dom.setAttribute('tabindex', 0);
        }
    },
    onRemoved: function(isDestroying) {
        this.callParent([
            isDestroying
        ]);
        
        this.onFocusLeave({});
    },
    
    refresh: function() {
        var me = this,
            rows = me.all,
            prevRowCount = rows.getCount(),
            refreshCounter = me.refreshCounter,
            targetEl, overflowEl, dom, records, hasFirstRefresh,
            selModel = me.getSelectionModel(),
            navModel = me.getNavigationModel(),
            lastFocusPosition = navModel.getPosition(),
            
            preserveScroll = refreshCounter && rows.getCount() && me.preserveScrollOnRefresh && !me.bufferedRenderer,
            scrollPos;
        if (!me.rendered || me.isDestroyed || me.preventRefresh) {
            return;
        }
        if (!me.hasListeners.beforerefresh || me.fireEvent('beforerefresh', me) !== false) {
            
            me.refreshing = true;
            
            navModel.beforeViewRefresh();
            targetEl = me.getTargetEl();
            records = me.getViewRange();
            dom = targetEl.dom;
            if (preserveScroll) {
                overflowEl = me.getOverflowEl();
                scrollPos = overflowEl.getScroll();
            }
            if (refreshCounter) {
                hasFirstRefresh = true;
                me.clearViewEl();
                me.refreshCounter++;
            } else {
                me.refreshCounter = 1;
            }
            
            
            
            me.tpl.append(targetEl, me.collectData(records, rows.startIndex || 0));
            
            
            if (records.length < 1) {
                
                if (me.emptyText && !me.getStore().isLoading() && (!me.deferEmptyText || hasFirstRefresh)) {
                    me.emptyEl = Ext.core.DomHelper.insertHtml('beforeEnd', targetEl.dom, me.emptyText);
                }
                rows.clear();
            } else {
                me.collectNodes(targetEl.dom);
                me.updateIndexes(0);
            }
            
            navModel.onViewRefresh();
            
            if (me.refreshSelmodelOnRefresh !== false) {
                selModel.refresh();
            }
            me.refreshNeeded = false;
            
            
            me.refreshSize(rows.getCount() !== prevRowCount);
            me.fireEvent('refresh', me, records);
            if (preserveScroll) {
                overflowEl.setScrollLeft(scrollPos.left);
                overflowEl.setScrollTop(scrollPos.top);
            }
            
            
            if (!me.viewReady) {
                
                me.viewReady = true;
                me.fireEvent('viewready', me);
            }
            me.refreshing = false;
            me.refreshScroll();
        }
    },
    
    toggleChildrenTabbability: function(enableTabbing) {
        var attr = this.savedTabIndexAttribute,
            focusEl = this.getTargetEl();
        if (enableTabbing) {
            focusEl.restoreChildrenTabbableState(attr);
        } else {
            focusEl.saveChildrenTabbableState(attr);
        }
    },
    
    
    collectNodes: function(targetEl) {
        var all = this.all;
        all.fill(Ext.fly(targetEl).query(this.getItemSelector()), all.startIndex || 0);
        
        if (this.focusable) {
            all.set({
                tabindex: '-1'
            });
        }
    },
    getViewRange: function() {
        return this.dataSource.getRange();
    },
    
    refreshSize: function(forceLayout) {
        var me = this,
            sizeModel = me.getSizeModel(),
            scroller = me.getScrollable();
        if (sizeModel.height.shrinkWrap || sizeModel.width.shrinkWrap || forceLayout) {
            me.updateLayout();
        }
        
        
        
        
        else if (me.touchScroll && !me.bufferedRenderer) {
            if (scroller) {
                scroller.refresh();
            } else {
                me.on({
                    boxready: me.refreshScroll,
                    scope: me,
                    single: true
                });
            }
        }
    },
    onResize: function() {
        var me = this,
            scroller = me.getScrollable();
        if (scroller && !me._hasScrollListener) {
            scroller.on({
                scroll: me.onViewScroll,
                scope: me,
                onFrame: !!Ext.global.requestAnimationFrame
            });
            me._hasScrollListener = true;
        }
        this.callParent(arguments);
    },
    clearViewEl: function() {
        var me = this,
            targetEl = me.getTargetEl(),
            nodeContainerIsTarget = me.getNodeContainer() === targetEl;
        me.clearEmptyEl();
        
        
        me.all.clear(!nodeContainerIsTarget);
        if (nodeContainerIsTarget) {
            targetEl.dom.innerHTML = '';
        }
    },
    clearEmptyEl: function() {
        var emptyEl = this.emptyEl;
        
        
        if (emptyEl) {
            emptyEl.parentNode.removeChild(emptyEl);
        }
        this.emptyEl = null;
    },
    onViewScroll: function(scroller, x, y) {
        this.fireEvent('scroll', this, x, y);
    },
    
    saveScrollState: function() {
        var me = this,
            state = me.scrollState;
        if (me.rendered) {
            state.left = me.getScrollX();
            state.top = me.getScrollY();
        }
    },
    
    restoreScrollState: function() {
        var me = this,
            state = me.scrollState;
        if (me.rendered) {
            me.setScrollX(state.left);
            me.setScrollY(state.top);
        }
    },
    
    prepareData: function(data, index, record) {
        var associatedData, attr, hasCopied;
        if (record) {
            associatedData = record.getAssociatedData();
            for (attr in associatedData) {
                if (associatedData.hasOwnProperty(attr)) {
                    
                    
                    
                    
                    if (!hasCopied) {
                        data = Ext.Object.chain(data);
                        hasCopied = true;
                    }
                    data[attr] = associatedData[attr];
                }
            }
        }
        return data;
    },
    
    collectData: function(records, startIndex) {
        var data = [],
            i = 0,
            len = records.length,
            record;
        for (; i < len; i++) {
            record = records[i];
            data[i] = this.prepareData(record.data, startIndex + i, record);
        }
        return data;
    },
    
    bufferRender: function(records, index) {
        var me = this,
            div = me.renderBuffer,
            result = document.createDocumentFragment(),
            nodes, len, i;
        me.tpl.overwrite(div, me.collectData(records, index));
        nodes = Ext.fly(div).query(me.getItemSelector());
        for (i = 0 , len = nodes.length; i < len; i++) {
            result.appendChild(nodes[i]);
        }
        result.childrenArray = nodes;
        return result;
    },
    
    nodeContainerSelector: null,
    getNodeContainer: function() {
        var target = this.getTargetEl(),
            selector = this.nodeContainerSelector;
        return selector ? target.down(selector, true) : target;
    },
    
    getNodeContainerSelector: function() {
        return this.nodeContainerSelector;
    },
    
    onUpdate: function(store, record, operation, modifiedFieldNames, details) {
        var me = this,
            isFiltered = details && details.filtered;
        
        
        
        if (!isFiltered && me.getNode(record)) {
            
            
            if (me.throttledUpdate) {
                me.statics().queueRecordChange(me, store, record, operation, modifiedFieldNames);
            } else {
                me.handleUpdate.apply(me, arguments);
            }
        }
    },
    
    handleUpdate: function(store, record) {
        var me = this,
            index, node,
            selModel = me.getSelectionModel();
        if (me.viewReady) {
            index = me.dataSource.indexOf(record);
            
            if (index > -1) {
                
                if (me.getNode(record)) {
                    node = me.bufferRender([
                        record
                    ], index).childrenArray[0];
                    me.all.replaceElement(index, node, true);
                    me.updateIndexes(index, index);
                    
                    selModel.onUpdate(record);
                    me.refreshSizePending = true;
                    if (selModel.isSelected(record)) {
                        me.onItemSelect(record);
                    }
                    if (me.hasListeners.itemupdate) {
                        me.fireEvent('itemupdate', record, index, node);
                    }
                    return node;
                }
            }
        }
    },
    
    
    
    onReplace: function(store, startIndex, oldRecords, newRecords) {
        var me = this,
            endIndex,
            rows = me.all,
            nodes, item, i, j,
            selModel = me.getSelectionModel();
        if (me.rendered) {
            
            nodes = me.bufferRender(newRecords, startIndex, true);
            item = rows.item(startIndex);
            if (item) {
                rows.item(startIndex).insertSibling(nodes, 'before', true);
            } else {
                me.appendNodes(nodes);
            }
            rows.insert(startIndex, nodes.childrenArray);
            startIndex += newRecords.length;
            endIndex = startIndex + oldRecords.length - 1;
            
            rows.removeRange(startIndex, endIndex, true);
            
            if (me.refreshSelmodelOnRefresh !== false) {
                selModel.refresh();
            }
            
            me.updateIndexes(startIndex);
            
            if (me.hasListeners.itemremove) {
                for (i = oldRecords.length , j = endIndex; i >= 0; --i , --j) {
                    me.fireEvent('itemremove', oldRecords[i], j, me);
                }
            }
            if (me.hasListeners.itemadd) {
                me.fireEvent('itemadd', newRecords, startIndex, nodes.childrenArray);
            }
            me.refreshSize();
        }
    },
    
    onAdd: function(store, records, index) {
        var me = this,
            nodes,
            selModel = me.getSelectionModel();
        if (me.rendered) {
            
            
            if (me.all.getCount() === 0) {
                me.refresh();
                nodes = me.all.slice();
            } else {
                nodes = me.doAdd(records, index);
                
                if (me.refreshSelmodelOnRefresh !== false) {
                    selModel.refresh();
                }
                me.updateIndexes(index);
                
                me.refreshSizePending = true;
            }
            if (me.hasListeners.itemadd) {
                me.fireEvent('itemadd', records, index, nodes);
            }
        }
    },
    appendNodes: function(nodes) {
        this.getNodeContainer().appendChild(nodes);
    },
    doAdd: function(records, index) {
        var me = this,
            nodes = me.bufferRender(records, index, true),
            all = me.all,
            count = all.getCount(),
            firstRowIndex = all.startIndex || 0,
            lastRowIndex = all.endIndex || count - 1;
        
        if (count === 0 || index > lastRowIndex) {
            me.appendNodes(nodes);
        }
        
        else if (index <= firstRowIndex) {
            all.item(firstRowIndex).insertSibling(nodes, 'before', true);
        } else 
        {
            all.item(index).insertSibling(nodes, 'before', true);
        }
        all.insert(index, nodes.childrenArray);
        return nodes.childrenArray;
    },
    
    onRemove: function(ds, records, index) {
        var me = this,
            rows = me.all,
            fireItemRemove = me.hasListeners.itemremove,
            currIdx, i, record, nodes, node;
        if (rows.getCount()) {
            if (me.dataSource.getCount() === 0) {
                
                if (fireItemRemove) {
                    me.fireEvent('itemremove', records, index, me.getNodes(index, index + records.length - 1));
                }
                me.preventPrune = true;
                me.refresh();
                me.preventPrune = false;
            } else {
                
                
                if (fireItemRemove) {
                    nodes = [];
                }
                for (i = records.length - 1; i >= 0; --i) {
                    record = records[i];
                    currIdx = index + i;
                    if (nodes) {
                        node = rows.item(currIdx);
                        nodes[i] = node ? node.dom : undefined;
                    }
                    if (rows.item(currIdx)) {
                        me.doRemove(record, currIdx);
                    }
                }
                if (fireItemRemove) {
                    me.fireEvent('itemremove', records, index, nodes, me);
                }
                me.updateIndexes(index);
            }
            
            me.refreshSizePending = true;
        }
    },
    
    doRemove: function(record, index) {
        this.all.removeElement(index, true);
    },
    
    refreshNode: function(record) {
        if (Ext.isNumber(record)) {
            record = this.store.getAt(record);
        }
        this.onUpdate(this.dataSource, record);
    },
    
    updateIndexes: function(startIndex, endIndex) {
        var nodes = this.all.elements,
            node,
            records = this.getViewRange(),
            i,
            myId = this.id;
        startIndex = startIndex || 0;
        endIndex = endIndex || ((endIndex === 0) ? 0 : (nodes.length - 1));
        for (i = startIndex; i <= endIndex; i++) {
            node = nodes[i];
            node.setAttribute('data-recordIndex', i);
            node.setAttribute('data-recordId', records[i].internalId);
            node.setAttribute('data-boundView', myId);
        }
    },
    
    bindStore: function(store, initial, propName) {
        var me = this,
            selModel = me.getSelectionModel();
        selModel.bindStore(store);
        selModel.bindComponent(store ? me : null);
        me.mixins.storeholder.bindStore.apply(me, arguments);
        
        
        
        if (store && me.componentLayoutCounter && !me.preventRefresh) {
            
            me.doFirstRefresh(store, !initial);
        }
    },
    
    doFirstRefresh: function(store, noDefer) {
        var me = this;
        
        if (me.deferInitialRefresh && !noDefer) {
            Ext.defer(me.doFirstRefresh, 1, me, [
                store,
                true
            ]);
        } else {
            
            
            
            
            if (store && !store.isLoading()) {
                me.refresh();
            }
        }
    },
    onUnbindStore: function(store, initial, propertyName) {
        if (propertyName === 'store') {
            this.setMaskBind(null);
            this.getSelectionModel().bindStore(null);
        }
    },
    onBindStore: function(store, initial, propName) {
        var me = this;
        me.setMaskBind(store);
        
        
        if (!initial && propName === 'store') {
            
            
            me.preventRefresh = true;
            
            me.store = store;
            me.bindStore(store, false, 'dataSource');
            me.preventRefresh = false;
        }
    },
    setMaskBind: function(store) {
        var mask = this.loadMask;
        if (this.rendered && mask && store && !mask.bindStore) {
            mask = this.createMask();
        }
        if (mask && mask.bindStore) {
            mask.bindStore(store);
        }
    },
    getStoreListeners: function() {
        var me = this;
        return {
            refresh: me.onDataRefresh,
            replace: me.onReplace,
            add: me.onAdd,
            remove: me.onRemove,
            update: me.onUpdate,
            clear: me.refresh,
            beginupdate: me.onBeginUpdate,
            endupdate: me.onEndUpdate
        };
    },
    onBeginUpdate: function() {
        Ext.suspendLayouts();
    },
    onEndUpdate: function() {
        Ext.resumeLayouts(true);
        if (this.refreshSizePending) {
            this.refreshSize(true);
            this.refreshSizePending = false;
        }
    },
    
    onDataRefresh: function() {
        this.refreshView();
    },
    refreshView: function() {
        var me = this,
            
            
            blocked = me.blockRefresh || !me.rendered || me.up('[collapsed],[isCollapsingOrExpanding],[hidden]');
        
        
        if (blocked) {
            me.refreshNeeded = true;
        } else {
            if (me.bufferedRenderer && me.all.getCount()) {
                me.bufferedRenderer.refreshView();
            } else {
                me.refresh();
            }
        }
    },
    
    findItemByChild: function(node) {
        return Ext.fly(node).findParent(this.getItemSelector(), this.getTargetEl());
    },
    
    findTargetByEvent: function(e) {
        return e.getTarget(this.getItemSelector(), this.getTargetEl());
    },
    
    getSelectedNodes: function() {
        var nodes = [],
            records = this.getSelectionModel().getSelection(),
            ln = records.length,
            i = 0;
        for (; i < ln; i++) {
            nodes.push(this.getNode(records[i]));
        }
        return nodes;
    },
    
    getRecords: function(nodes) {
        var records = [],
            i = 0,
            len = nodes.length,
            data = this.dataSource.data;
        for (; i < len; i++) {
            records[records.length] = data.getByKey(nodes[i].getAttribute('data-recordId'));
        }
        return records;
    },
    
    getRecord: function(node) {
        return this.dataSource.getByInternalId(Ext.getDom(node).getAttribute('data-recordId'));
    },
    
    isSelected: function(node) {
        var r = this.getRecord(node);
        return this.getSelectionModel().isSelected(r);
    },
    
    select: function(records, keepExisting, suppressEvent) {
        this.getSelectionModel().select(records, keepExisting, suppressEvent);
    },
    
    deselect: function(records, suppressEvent) {
        this.getSelectionModel().deselect(records, suppressEvent);
    },
    
    getNode: function(nodeInfo) {
        var me = this,
            out;
        if (me.rendered && (nodeInfo || nodeInfo === 0)) {
            if (Ext.isString(nodeInfo)) {
                
                out = document.getElementById(nodeInfo);
            } else if (nodeInfo.isModel) {
                
                out = me.getNodeByRecord(nodeInfo);
            } else if (Ext.isNumber(nodeInfo)) {
                
                out = me.all.elements[nodeInfo];
            } else {
                if (nodeInfo.target) {
                    
                    nodeInfo = nodeInfo.target;
                }
                out = Ext.fly(nodeInfo).findParent(me.itemSelector, me.getTargetEl());
            }
        }
        
        return out || null;
    },
    
    getNodeByRecord: function(record) {
        var index = this.store.indexOf(record);
        return this.all.elements[index] || null;
    },
    
    getNodes: function(start, end) {
        var all = this.all;
        if (end !== undefined) {
            end++;
        }
        return all.slice(start, end);
    },
    
    indexOf: function(node) {
        node = this.getNode(node);
        if (!node && node !== 0) {
            return -1;
        }
        if (node.getAttribute('data-recordIndex')) {
            return Number(node.getAttribute('data-recordIndex'));
        }
        return this.all.indexOf(node);
    },
    onDestroy: function() {
        var me = this;
        me.all.clear();
        me.emptyEl = null;
        me.callParent();
        me.bindStore(null);
        Ext.destroy(me.navigationModel, me.selectionModel);
    },
    
    onItemSelect: function(record) {
        var node = this.getNode(record);
        if (node) {
            Ext.fly(node).addCls(this.selectedItemCls);
        }
    },
    
    onItemDeselect: function(record) {
        var node = this.getNode(record);
        if (node) {
            Ext.fly(node).removeCls(this.selectedItemCls);
        }
    },
    getItemSelector: function() {
        return this.itemSelector;
    },
    
    addItemCls: function(itemInfo, cls) {
        var item = this.getNode(itemInfo);
        if (item) {
            Ext.fly(item).addCls(cls);
        }
    },
    
    removeItemCls: function(itemInfo, cls) {
        var item = this.getNode(itemInfo);
        if (item) {
            Ext.fly(item).removeCls(cls);
        }
    },
    updateStore: function(store) {
        if (!this.isConfiguring) {
            
            
            delete this.store;
            this.bindStore(store);
        }
    },
    privates: {
        createMask: function(mask) {
            var me = this,
                maskStore = me.getMaskStore(),
                cfg;
            if (maskStore && !maskStore.isEmptyStore && !maskStore.loadsSynchronously()) {
                cfg = {
                    target: me,
                    msg: me.loadingText,
                    useMsg: me.loadingUseMsg,
                    
                    
                    store: maskStore
                };
                
                if (me.loadingCls) {
                    cfg.msgCls = me.loadingCls;
                }
                
                if (Ext.isObject(mask)) {
                    cfg = Ext.apply(cfg, mask);
                }
                
                
                
                
                me.loadMask = new Ext.LoadMask(cfg);
                me.loadMask.on({
                    scope: me,
                    beforeshow: me.onMaskBeforeShow,
                    hide: me.onMaskHide
                });
            }
            return me.loadMask;
        },
        getFocusEl: function() {
            return this.el;
        },
        getOverflowEl: function() {
            
            
            
            return Ext.Component.prototype.getTargetEl.call(this);
        },
        getTargetEl: function() {
            return this.touchScroll ? this.getScrollerEl() : this.callParent();
        }
    }
}, function() {
    
    
    
    
    Ext.deprecate('extjs', '4.0', function() {
        Ext.view.AbstractView.override({
            
            
            
            
            getSelectionCount: function() {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: getSelectionCount will be removed, please interact with the Ext.selection.DataViewModel");
                }
                return this.selModel.getSelection().length;
            },
            
            getSelectedRecords: function() {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: getSelectedRecords will be removed, please interact with the Ext.selection.DataViewModel");
                }
                return this.selModel.getSelection();
            },
            
            
            select: function(records, keepExisting, supressEvents) {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: select will be removed, please access select through a DataView's SelectionModel, ie: view.getSelectionModel().select()");
                }
                var sm = this.getSelectionModel();
                return sm.select.apply(sm, arguments);
            },
            
            clearSelections: function() {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: clearSelections will be removed, please access deselectAll through DataView's SelectionModel, ie: view.getSelectionModel().deselectAll()");
                }
                var sm = this.getSelectionModel();
                return sm.deselectAll();
            }
        });
    });
});


Ext.define('Ext.view.View', {
    extend: 'Ext.view.AbstractView',
    alternateClassName: 'Ext.DataView',
    alias: 'widget.dataview',
    inputTagRe: /^textarea$|^input$/i,
    keyEventRe: /^key/,
    inheritableStatics: {
        EventMap: {
            longpress: 'LongPress',
            mousedown: 'MouseDown',
            mouseup: 'MouseUp',
            click: 'Click',
            dblclick: 'DblClick',
            contextmenu: 'ContextMenu',
            mouseover: 'MouseOver',
            mouseout: 'MouseOut',
            mouseenter: 'MouseEnter',
            mouseleave: 'MouseLeave',
            keydown: 'KeyDown',
            keyup: 'KeyUp',
            keypress: 'KeyPress',
            focus: 'Focus'
        },
        TouchEventMap: {
            touchstart: 'mousedown',
            touchend: 'mouseup',
            tap: 'click',
            doubletap: 'dblclick'
        }
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    afterRender: function() {
        var me = this;
        me.callParent();
        me.mon(me.el, {
            scope: me,
            click: me.handleEvent,
            longpress: me.handleEvent,
            mousedown: me.handleEvent,
            mouseup: me.handleEvent,
            dblclick: me.handleEvent,
            contextmenu: me.handleEvent,
            keydown: me.handleEvent,
            keyup: me.handleEvent,
            keypress: me.handleEvent,
            mouseover: me.handleMouseOver,
            mouseout: me.handleMouseOut
        });
    },
    
    getTargetSelector: function() {
        return this.dataRowSelector || this.itemSelector;
    },
    handleMouseOver: function(e) {
        var me = this,
            
            itemSelector = me.getTargetSelector(),
            item = e.getTarget(itemSelector);
        
        if (!me.isDestroyed) {
            if (item) {
                if (me.mouseOverItem !== item && me.el.contains(item)) {
                    me.mouseOverItem = e.item = item;
                    e.newType = 'mouseenter';
                    me.handleEvent(e);
                }
            } else {
                
                me.handleEvent(e);
            }
        }
    },
    handleMouseOut: function(e) {
        var itemSelector = this.getTargetSelector(),
            item = e.getTarget(itemSelector),
            computedRelatedTarget = e.getRelatedTarget(itemSelector),
            sourceView;
        
        
        if ((item === computedRelatedTarget) && !(item === null && computedRelatedTarget === null)) {
            return;
        }
        
        
        if (!this.isDestroyed) {
            
            if (item && (sourceView = this.self.getBoundView(item))) {
                e.item = item;
                e.newType = 'mouseleave';
                sourceView.handleEvent(e);
                sourceView.mouseOverItem = null;
            } else {
                
                this.handleEvent(e);
            }
        }
    },
    handleEvent: function(e) {
        var me = this,
            isKeyEvent = me.keyEventRe.test(e.type),
            nm = me.getNavigationModel();
        e.view = me;
        if (isKeyEvent) {
            e.item = nm.getItem();
            e.record = nm.getRecord();
        }
        
        
        if (!e.item) {
            e.item = e.getTarget(me.itemSelector);
        }
        if (e.item && !e.record) {
            e.record = me.getRecord(e.item);
        }
        if (me.processUIEvent(e) !== false) {
            me.processSpecialEvent(e);
        }
        
        
        
        
        if (isKeyEvent && ((e.getKey() === e.SPACE && !Ext.fly(e.target).isInputField()) || e.isNavKeyPress(true))) {
            e.preventDefault();
        }
    },
    
    processItemEvent: Ext.emptyFn,
    processContainerEvent: Ext.emptyFn,
    processSpecialEvent: Ext.emptyFn,
    processUIEvent: function(e) {
        
        
        if (!Ext.getBody().isAncestor(e.target)) {
            return;
        }
        var me = this,
            item = e.item,
            self = me.self,
            map = self.EventMap,
            touchMap = self.TouchEventMap,
            index,
            record = e.record,
            type = e.type,
            newType = type;
        
        
        if (e.newType) {
            newType = e.newType;
        }
        if (item) {
            newType = touchMap[newType] || newType;
            index = e.recordIndex = me.indexInStore ? me.indexInStore(record) : me.indexOf(item);
            
            
            
            
            if (!record || me.processItemEvent(record, item, index, e) === false) {
                return false;
            }
            if ((me['onBeforeItem' + map[newType]](record, item, index, e) === false) || (me.fireEvent('beforeitem' + newType, me, record, item, index, e) === false) || (me['onItem' + map[newType]](record, item, index, e) === false)) {
                return false;
            }
            me.fireEvent('item' + newType, me, record, item, index, e);
        } else {
            type = touchMap[type] || type;
            if ((me.processContainerEvent(e) === false) || (me['onBeforeContainer' + map[type]](e) === false) || (me.fireEvent('beforecontainer' + type, me, e) === false) || (me['onContainer' + map[type]](e) === false)) {
                return false;
            }
            me.fireEvent('container' + type, me, e);
        }
        return true;
    },
    
    onItemMouseEnter: function(record, item, index, e) {
        if (this.trackOver) {
            this.highlightItem(item);
        }
    },
    
    onItemMouseLeave: function(record, item, index, e) {
        if (this.trackOver) {
            this.clearHighlight();
        }
    },
    
    onItemMouseDown: Ext.emptyFn,
    onItemLongPress: Ext.emptyFn,
    onItemMouseUp: Ext.emptyFn,
    onItemFocus: Ext.emptyFn,
    onItemClick: Ext.emptyFn,
    onItemDblClick: Ext.emptyFn,
    onItemContextMenu: Ext.emptyFn,
    onItemKeyDown: Ext.emptyFn,
    onItemKeyUp: Ext.emptyFn,
    onItemKeyPress: Ext.emptyFn,
    onBeforeItemLongPress: Ext.emptyFn,
    onBeforeItemMouseDown: Ext.emptyFn,
    onBeforeItemMouseUp: Ext.emptyFn,
    onBeforeItemFocus: Ext.emptyFn,
    onBeforeItemMouseEnter: Ext.emptyFn,
    onBeforeItemMouseLeave: Ext.emptyFn,
    onBeforeItemClick: Ext.emptyFn,
    onBeforeItemDblClick: Ext.emptyFn,
    onBeforeItemContextMenu: Ext.emptyFn,
    onBeforeItemKeyDown: Ext.emptyFn,
    onBeforeItemKeyUp: Ext.emptyFn,
    onBeforeItemKeyPress: Ext.emptyFn,
    
    onContainerMouseDown: Ext.emptyFn,
    onContainerLongPress: Ext.emptyFn,
    onContainerMouseUp: Ext.emptyFn,
    onContainerMouseOver: Ext.emptyFn,
    onContainerMouseOut: Ext.emptyFn,
    onContainerClick: Ext.emptyFn,
    onContainerDblClick: Ext.emptyFn,
    onContainerContextMenu: Ext.emptyFn,
    onContainerKeyDown: Ext.emptyFn,
    onContainerKeyUp: Ext.emptyFn,
    onContainerKeyPress: Ext.emptyFn,
    onBeforeContainerMouseDown: Ext.emptyFn,
    onBeforeContainerLongPress: Ext.emptyFn,
    onBeforeContainerMouseUp: Ext.emptyFn,
    onBeforeContainerMouseOver: Ext.emptyFn,
    onBeforeContainerMouseOut: Ext.emptyFn,
    onBeforeContainerClick: Ext.emptyFn,
    onBeforeContainerDblClick: Ext.emptyFn,
    onBeforeContainerContextMenu: Ext.emptyFn,
    onBeforeContainerKeyDown: Ext.emptyFn,
    onBeforeContainerKeyUp: Ext.emptyFn,
    onBeforeContainerKeyPress: Ext.emptyFn,
    
    setHighlightedItem: function(item) {
        var me = this,
            highlighted = me.highlightedItem,
            overItemCls = me.overItemCls;
        if (highlighted !== item) {
            if (highlighted) {
                Ext.fly(highlighted).removeCls(overItemCls);
                
                
                if (Ext.isIE8) {
                    me.repaintBorder(highlighted);
                    me.repaintBorder(highlighted.nextSibling);
                }
                if (me.hasListeners.unhighlightitem) {
                    me.fireEvent('unhighlightitem', me, highlighted);
                }
            }
            me.highlightedItem = item;
            if (item) {
                Ext.fly(item).addCls(me.overItemCls);
                
                
                if (Ext.isIE8) {
                    me.repaintBorder(item.nextSibling);
                }
                if (me.hasListeners.highlightitem) {
                    me.fireEvent('highlightitem', me, item);
                }
            }
        }
    },
    
    highlightItem: function(item) {
        this.setHighlightedItem(item);
    },
    
    clearHighlight: function() {
        this.setHighlightedItem(undefined);
    },
    handleUpdate: function(store, record) {
        var me = this,
            node, newNode, highlighted;
        if (me.viewReady) {
            node = me.getNode(record);
            newNode = me.callParent(arguments);
            highlighted = me.highlightedItem;
            if (highlighted && highlighted === node) {
                delete me.highlightedItem;
                if (newNode) {
                    me.highlightItem(newNode);
                }
            }
        }
    },
    refresh: function() {
        this.clearHighlight();
        this.callParent(arguments);
    },
    
    focusNode: function(rec) {
        var me = this,
            node = me.getNode(rec),
            el = me.el,
            adjustmentY = 0,
            adjustmentX = 0,
            elRegion = el.getRegion(),
            nodeRegion;
        
        
        elRegion.bottom = elRegion.top + el.dom.clientHeight;
        elRegion.right = elRegion.left + el.dom.clientWidth;
        if (node) {
            nodeRegion = Ext.fly(node).getRegion();
            
            if (nodeRegion.top < elRegion.top) {
                adjustmentY = nodeRegion.top - elRegion.top;
            }
            
            else if (nodeRegion.bottom > elRegion.bottom) {
                adjustmentY = nodeRegion.bottom - elRegion.bottom;
            }
            
            if (nodeRegion.left < elRegion.left) {
                adjustmentX = nodeRegion.left - elRegion.left;
            }
            
            else if (nodeRegion.right > elRegion.right) {
                adjustmentX = nodeRegion.right - elRegion.right;
            }
            if (adjustmentX || adjustmentY) {
                me.scrollBy(adjustmentX, adjustmentY, false);
            }
            
            Ext.fly(node).set({
                tabIndex: -1
            });
            node.focus();
        }
    },
    bindStore: function(store, initial, propertyName) {
        
        var me = this,
            dataSource = me[propertyName],
            selModel = me.getSelectionModel();
        if (dataSource && dataSource.isFeatureStore) {
            selModel.bindStore(dataSource.store);
            selModel.bindComponent(me);
            
            
            
            
            
            if (store.isFeatureStore) {
                
                me.bindStoreListeners(store);
                
                dataSource.bindStore(dataSource.store);
            } else {
                dataSource.bindStore(store);
            }
        } else {
            me.callParent([
                store,
                initial,
                propertyName
            ]);
        }
    },
    privates: {
        
        
        repaintBorder: function(rowIdx) {
            var node = this.getNode(rowIdx);
            if (node) {
                node.className = node.className;
            }
        }
    }
});


Ext.define('Ext.grid.CellContext', {
    
    isCellContext: true,
    constructor: function(view) {
        this.view = view;
    },
    isEqual: function(other) {
        if (other) {
            return this.record === other.record && this.column === other.column;
        }
        return false;
    },
    
    setPosition: function(row, col) {
        var me = this;
        
        if (arguments.length === 1) {
            
            if (row.length) {
                col = row[0];
                col = row[1];
            } else 
            {
                if (row.view) {
                    me.view = row.view;
                }
                col = row.column;
                row = row.row;
            }
        }
        me.setRow(row);
        me.setColumn(col);
        return me;
    },
    setAll: function(view, recordIndex, columnIndex, record, columnHeader) {
        var me = this;
        me.view = view;
        me.rowIdx = recordIndex;
        me.colIdx = columnIndex;
        me.record = record;
        me.column = columnHeader;
        return me;
    },
    setRow: function(row) {
        var me = this,
            dataSource = me.view.dataSource;
        if (row !== undefined) {
            
            if (typeof row === 'number') {
                me.rowIdx = Math.max(Math.min(row, dataSource.getCount() - 1), 0);
                me.record = dataSource.getAt(row);
            }
            
            else if (row.isModel) {
                me.record = row;
                me.rowIdx = dataSource.indexOf(row);
            }
            
            else if (row.tagName) {
                me.record = me.view.getRecord(row);
                me.rowIdx = dataSource.indexOf(me.record);
            }
        }
    },
    setColumn: function(col) {
        var me = this,
            colMgr = me.view.getVisibleColumnManager();
        
        
        
        
        if (col !== undefined) {
            if (typeof col === 'number') {
                me.colIdx = col;
                me.column = colMgr.getHeaderAtIndex(col);
            } else if (col.isHeader) {
                me.column = col;
                
                
                me.colIdx = colMgr.indexOf(col);
            }
        }
    },
    next: function() {
        var me = this,
            mgr = me.view.getVisibleColumnManager();
        me.colIdx++;
        if (me.colIdx === mgr.getColumns().length) {
            me.setPosition(Math.min(me.rowIdx + 1, me.view.dataSource.getCount() - 1), me.colIdx);
        } else {
            me.setColumn(me.colIdx);
        }
    },
    equal: function(other) {
        return (other && other.isCellContext && other.view === this.view && other.record === this.record && other.column === this.column);
    },
    clone: function() {
        var me = this,
            result = new me.self(me.view);
        result.rowIdx = me.rowIdx;
        result.colIdx = me.colIdx;
        result.record = me.record;
        result.column = me.column;
        return result;
    }
});


Ext.define('Ext.view.TableLayout', {
    extend: 'Ext.layout.component.Auto',
    alias: 'layout.tableview',
    type: 'tableview',
    beginLayout: function(ownerContext) {
        var me = this,
            partner = me.owner.lockingPartner,
            context = ownerContext.context;
        if (!me.columnFlusherId) {
            me.columnFlusherId = me.id + '-columns';
            me.rowHeightFlusherId = me.id + '-rows';
        }
        me.callParent([
            ownerContext
        ]);
        
        
        
        
        if (partner && partner.grid.isVisible()) {
            if (!ownerContext.lockingPartnerContext) {
                (ownerContext.lockingPartnerContext = context.getCmp(partner)).lockingPartnerContext = ownerContext;
            }
            ownerContext.rowHeightSynchronizer = me.owner.syncRowHeightBegin();
        }
        
        
        (ownerContext.headerContext = context.getCmp(me.headerCt)).viewContext = ownerContext;
    },
    beginLayoutCycle: function(ownerContext, firstCycle) {
        this.callParent([
            ownerContext,
            firstCycle
        ]);
        if (ownerContext.syncRowHeights) {
            ownerContext.target.syncRowHeightClear(ownerContext.rowHeightSynchronizer);
            ownerContext.syncRowHeights = false;
        }
    },
    calculate: function(ownerContext) {
        var me = this,
            context = ownerContext.context,
            lockingPartnerContext = ownerContext.lockingPartnerContext,
            headerContext = ownerContext.headerContext,
            ownerCtContext = ownerContext.ownerCtContext,
            owner = me.owner,
            columnsChanged = headerContext.getProp('columnsChanged'),
            state = ownerContext.state,
            columnFlusher, otherSynchronizer, synchronizer, rowHeightFlusher,
            bodyDom = owner.body.dom,
            bodyHeight, ctSize;
        
        
        if (!owner.all.getCount() && (!bodyDom || !owner.body.child('table'))) {
            ownerContext.setProp('viewOverflowY', false);
            me.callParent([
                ownerContext
            ]);
            return;
        }
        if (columnsChanged === undefined) {
            
            me.done = false;
            return;
        }
        if (columnsChanged) {
            if (!(columnFlusher = state.columnFlusher)) {
                
                context.queueFlush(state.columnFlusher = columnFlusher = {
                    ownerContext: ownerContext,
                    columnsChanged: columnsChanged,
                    layout: me,
                    id: me.columnFlusherId,
                    flush: me.flushColumnWidths
                });
            }
            if (!columnFlusher.flushed) {
                
                
                me.done = false;
                return;
            }
        }
        
        
        
        if (lockingPartnerContext) {
            if (!(rowHeightFlusher = state.rowHeightFlusher)) {
                
                
                if (!(synchronizer = state.rowHeights)) {
                    state.rowHeights = synchronizer = ownerContext.rowHeightSynchronizer;
                    me.owner.syncRowHeightMeasure(synchronizer);
                    ownerContext.setProp('rowHeights', synchronizer);
                }
                if (!(otherSynchronizer = lockingPartnerContext.getProp('rowHeights'))) {
                    me.done = false;
                    return;
                }
                context.queueFlush(state.rowHeightFlusher = rowHeightFlusher = {
                    ownerContext: ownerContext,
                    synchronizer: synchronizer,
                    otherSynchronizer: otherSynchronizer,
                    layout: me,
                    id: me.rowHeightFlusherId,
                    flush: me.flushRowHeights
                });
            }
            if (!rowHeightFlusher.flushed) {
                me.done = false;
                return;
            }
        }
        me.callParent([
            ownerContext
        ]);
        if (!ownerContext.heightModel.shrinkWrap) {
            
            
            
            ctSize = ownerCtContext.target.layout.getContainerSize(ownerCtContext);
            if (!ctSize.gotHeight) {
                me.done = false;
                return;
            }
            bodyHeight = bodyDom.offsetHeight;
            ownerContext.setProp('viewOverflowY', bodyHeight > ctSize.height);
        }
    },
    measureContentHeight: function(ownerContext) {
        var owner = this.owner,
            bodyDom = owner.body.dom,
            bodyHeight;
        if (!bodyDom) {
            bodyDom = owner.el.down('.' + owner.ownerCt.emptyCls, true);
        }
        bodyHeight = bodyDom ? bodyDom.offsetHeight : 0;
        
        
        if (ownerContext.headerContext.state.boxPlan.tooNarrow) {
            bodyHeight += Ext.getScrollbarSize().height;
        }
        return bodyHeight;
    },
    flushColumnWidths: function() {
        
        var flusher = this,
            me = flusher.layout,
            ownerContext = flusher.ownerContext,
            columnsChanged = flusher.columnsChanged,
            owner = ownerContext.target,
            len = columnsChanged.length,
            column, i, colWidth, lastBox;
        if (ownerContext.state.columnFlusher !== flusher) {
            return;
        }
        
        for (i = 0; i < len; i++) {
            if (!(column = columnsChanged[i])) {
                
                continue;
            }
            colWidth = column.props.width;
            owner.body.select(owner.getColumnSizerSelector(column.target)).setWidth(colWidth);
            
            
            lastBox = column.lastBox;
            if (lastBox) {
                lastBox.width = colWidth;
            }
        }
        flusher.flushed = true;
        if (!me.pending) {
            ownerContext.context.queueLayout(me);
        }
    },
    flushRowHeights: function() {
        
        var flusher = this,
            me = flusher.layout,
            ownerContext = flusher.ownerContext;
        if (ownerContext.state.rowHeightFlusher !== flusher) {
            return;
        }
        ownerContext.target.syncRowHeightFinish(flusher.synchronizer, flusher.otherSynchronizer);
        flusher.flushed = true;
        ownerContext.syncRowHeights = true;
        if (!me.pending) {
            ownerContext.context.queueLayout(me);
        }
    },
    finishedLayout: function(ownerContext) {
        var nodeContainer = Ext.fly(this.owner.getNodeContainer());
        this.callParent([
            ownerContext
        ]);
        if (nodeContainer) {
            nodeContainer.setWidth(ownerContext.headerContext.props.contentWidth);
        }
    }
});


Ext.define('Ext.grid.locking.RowSynchronizer', {
    constructor: function(view, rowEl) {
        var me = this,
            rowTpl;
        me.view = view;
        me.rowEl = rowEl;
        me.els = {};
        me.add('data', view.rowSelector);
        for (rowTpl = view.rowTpl; rowTpl; rowTpl = rowTpl.nextTpl) {
            if (rowTpl.beginRowSync) {
                rowTpl.beginRowSync(me);
            }
        }
    },
    add: function(name, selector) {
        var el = Ext.fly(this.rowEl).down(selector, true);
        if (el) {
            this.els[name] = {
                el: el
            };
        }
    },
    finish: function(other) {
        var me = this,
            els = me.els,
            otherEls = other.els,
            otherEl,
            growth = 0,
            otherGrowth = 0,
            delta, name, otherHeight;
        for (name in els) {
            otherEl = otherEls[name];
            
            
            otherHeight = otherEl ? otherEl.height : 0;
            delta = otherHeight - els[name].height;
            if (delta > 0) {
                growth += delta;
                Ext.fly(els[name].el).setHeight(otherHeight);
            } else {
                otherGrowth -= delta;
            }
        }
        
        otherHeight = other.rowHeight + otherGrowth;
        if (me.rowHeight + growth < otherHeight) {
            Ext.fly(me.rowEl).setHeight(otherHeight);
        }
    },
    measure: function() {
        var me = this,
            els = me.els,
            name;
        me.rowHeight = me.rowEl.offsetHeight;
        for (name in els) {
            els[name].height = els[name].el.offsetHeight;
        }
    },
    reset: function() {
        var me = this,
            dataEl = me.dataEl,
            els = me.els,
            name;
        this.rowEl.style.height = '';
        for (name in els) {
            els[name].el.style.height = '';
        }
    }
});


Ext.define('Ext.view.NodeCache', {
    requires: [
        'Ext.dom.CompositeElementLite'
    ],
    statics: {
        range: document.createRange && document.createRange()
    },
    constructor: function(view) {
        this.view = view;
        this.clear();
        this.el = new Ext.dom.Fly();
    },
    
    clear: function(removeDom) {
        var me = this,
            elements = me.elements,
            i, el,
            range = me.statics().range;
        if (me.count && removeDom) {
            if (range) {
                range.setStartBefore(elements[me.startIndex]);
                range.setEndAfter(elements[me.endIndex]);
                range.deleteContents();
            } else {
                for (i in elements) {
                    el = elements[i];
                    el.parentNode.removeChild(el);
                }
            }
        }
        me.elements = {};
        me.count = me.startIndex = 0;
        me.endIndex = -1;
    },
    
    fill: function(newElements, startIndex, fixedNodes) {
        fixedNodes = fixedNodes || 0;
        var me = this,
            elements = me.elements = {},
            i,
            len = newElements.length - fixedNodes;
        if (!startIndex) {
            startIndex = 0;
        }
        for (i = 0; i < len; i++) {
            elements[startIndex + i] = newElements[i + fixedNodes];
        }
        me.startIndex = startIndex;
        me.endIndex = startIndex + len - 1;
        me.count = len;
        return this;
    },
    insert: function(insertPoint, nodes) {
        var me = this,
            elements = me.elements,
            i,
            nodeCount = nodes.length;
        
        if (me.count) {
            if (insertPoint > me.endIndex + 1 || insertPoint + nodes.length - 1 < me.startIndex) {
                Ext.Error.raise('Discontiguous range would result from inserting ' + nodes.length + ' nodes at ' + insertPoint);
            }
            
            if (insertPoint < me.count) {
                for (i = me.endIndex + nodeCount; i >= insertPoint + nodeCount; i--) {
                    elements[i] = elements[i - nodeCount];
                    elements[i].setAttribute('data-recordIndex', i);
                }
            }
            me.endIndex = me.endIndex + nodeCount;
        } else 
        {
            me.startIndex = insertPoint;
            me.endIndex = insertPoint + nodeCount - 1;
        }
        
        for (i = 0; i < nodeCount; i++ , insertPoint++) {
            elements[insertPoint] = nodes[i];
            elements[insertPoint].setAttribute('data-recordIndex', insertPoint);
        }
        me.count += nodeCount;
    },
    invoke: function(fn, args) {
        var me = this,
            element, i;
        fn = Ext.dom.Element.prototype[fn];
        for (i = me.startIndex; i <= me.endIndex; i++) {
            element = me.item(i);
            if (element) {
                fn.apply(element, args);
            }
        }
        return me;
    },
    item: function(index, asDom) {
        var el = this.elements[index],
            result = null;
        if (el) {
            result = asDom ? this.elements[index] : this.el.attach(this.elements[index]);
        }
        return result;
    },
    first: function(asDom) {
        return this.item(this.startIndex, asDom);
    },
    last: function(asDom) {
        return this.item(this.endIndex, asDom);
    },
    
    
    
    
    moveBlock: function(increment) {
        var me = this,
            elements = me.elements,
            node, end, step, i;
        if (increment < 0) {
            i = me.startIndex - 1;
            end = me.endIndex;
            step = 1;
        } else {
            i = me.endIndex + 1;
            end = me.startIndex;
            step = -1;
        }
        me.startIndex += increment;
        me.endIndex += increment;
        do {
            i += step;
            node = elements[i + increment] = elements[i];
            node.setAttribute('data-recordIndex', i + increment);
            
            if (i < me.startIndex || i > me.endIndex) {
                delete elements[i];
            }
        } while (i !== end);
        delete elements[i];
    },
    getCount: function() {
        return this.count;
    },
    slice: function(start, end) {
        var elements = this.elements,
            result = [],
            i;
        if (!end) {
            end = this.endIndex;
        } else {
            end = Math.min(this.endIndex, end - 1);
        }
        for (i = start || this.startIndex; i <= end; i++) {
            result.push(elements[i]);
        }
        return result;
    },
    
    replaceElement: function(el, replacement, domReplace) {
        var elements = this.elements,
            index = (typeof el === 'number') ? el : this.indexOf(el);
        if (index > -1) {
            replacement = Ext.getDom(replacement);
            if (domReplace) {
                el = elements[index];
                el.parentNode.insertBefore(replacement, el);
                Ext.removeNode(el);
                replacement.setAttribute('data-recordIndex', index);
            }
            this.elements[index] = replacement;
        }
        return this;
    },
    
    indexOf: function(el) {
        var elements = this.elements,
            index;
        el = Ext.getDom(el);
        for (index = this.startIndex; index <= this.endIndex; index++) {
            if (elements[index] === el) {
                return index;
            }
        }
        return -1;
    },
    removeRange: function(start, end, removeDom) {
        var me = this,
            elements = me.elements,
            el, i, removeCount, fromPos;
        if (end == null) {
            end = me.endIndex + 1;
        } else {
            end = Math.min(me.endIndex + 1, end + 1);
        }
        if (start == null) {
            start = me.startIndex;
        }
        removeCount = end - start;
        for (i = start , fromPos = end; i <= me.endIndex; i++ , fromPos++) {
            el = elements[i];
            
            if (removeDom && i < end) {
                el.parentNode.removeChild(el);
            }
            
            if (fromPos <= me.endIndex) {
                el = elements[i] = elements[fromPos];
                el.setAttribute('data-recordIndex', i);
            } else 
            {
                delete elements[i];
            }
        }
        me.count -= removeCount;
        me.endIndex -= removeCount;
    },
    
    removeElement: function(keys, removeDom) {
        var me = this,
            inKeys, key,
            elements = me.elements,
            el, deleteCount,
            keyIndex = 0,
            index, fromIndex;
        
        
        if (Ext.isArray(keys)) {
            inKeys = keys;
            keys = [];
            deleteCount = inKeys.length;
            for (keyIndex = 0; keyIndex < deleteCount; keyIndex++) {
                key = inKeys[keyIndex];
                if (typeof key !== 'number') {
                    key = me.indexOf(key);
                }
                
                
                if (key >= me.startIndex && key <= me.endIndex) {
                    keys[keys.length] = key;
                }
            }
            Ext.Array.sort(keys);
            deleteCount = keys.length;
        } else {
            
            if (keys < me.startIndex || keys > me.endIndex) {
                return;
            }
            deleteCount = 1;
            keys = [
                keys
            ];
        }
        
        
        for (index = fromIndex = keys[0] , keyIndex = 0; index <= me.endIndex; index++ , fromIndex++) {
            
            
            
            if (keyIndex < deleteCount && index === keys[keyIndex]) {
                fromIndex++;
                keyIndex++;
                if (removeDom) {
                    Ext.removeNode(elements[index]);
                }
            }
            
            if (fromIndex <= me.endIndex && fromIndex >= me.startIndex) {
                el = elements[index] = elements[fromIndex];
                el.setAttribute('data-recordIndex', index);
            } else {
                delete elements[index];
            }
        }
        me.endIndex -= deleteCount;
        me.count -= deleteCount;
    },
    
    scroll: function(newRecords, direction, removeCount) {
        var me = this,
            view = me.view,
            store = view.store,
            elements = me.elements,
            recCount = newRecords.length,
            i, el, removeEnd, newNodes,
            nodeContainer = view.getNodeContainer(),
            fireItemRemove = view.hasListeners.itemremove,
            fireItemAdd = view.hasListeners.itemadd,
            range = me.statics().range;
        
        if (direction === -1) {
            if (removeCount) {
                if (range) {
                    range.setStartBefore(elements[(me.endIndex - removeCount) + 1]);
                    range.setEndAfter(elements[me.endIndex]);
                    range.deleteContents();
                    for (i = (me.endIndex - removeCount) + 1; i <= me.endIndex; i++) {
                        el = elements[i];
                        delete elements[i];
                        if (fireItemRemove) {
                            view.fireEvent('itemremove', store.getByInternalId(el.getAttribute('data-recordId')), i, el, view);
                        }
                    }
                } else {
                    for (i = (me.endIndex - removeCount) + 1; i <= me.endIndex; i++) {
                        el = elements[i];
                        delete elements[i];
                        el.parentNode.removeChild(el);
                        if (fireItemRemove) {
                            view.fireEvent('itemremove', store.getByInternalId(el.getAttribute('data-recordId')), i, el, view);
                        }
                    }
                }
                me.endIndex -= removeCount;
            }
            
            
            if (newRecords.length) {
                
                newNodes = view.bufferRender(newRecords, me.startIndex -= recCount);
                for (i = 0; i < recCount; i++) {
                    elements[me.startIndex + i] = newNodes.childrenArray[i];
                }
                nodeContainer.insertBefore(newNodes, nodeContainer.firstChild);
                
                if (fireItemAdd) {
                    view.fireEvent('itemadd', newRecords, me.startIndex, newNodes.childrenArray);
                }
            }
        } else 
        {
            if (removeCount) {
                removeEnd = me.startIndex + removeCount;
                if (range) {
                    range.setStartBefore(elements[me.startIndex]);
                    range.setEndAfter(elements[removeEnd - 1]);
                    range.deleteContents();
                    for (i = me.startIndex; i < removeEnd; i++) {
                        el = elements[i];
                        delete elements[i];
                        if (fireItemRemove) {
                            view.fireEvent('itemremove', store.getByInternalId(el.getAttribute('data-recordId')), i, el, view);
                        }
                    }
                } else {
                    for (i = me.startIndex; i < removeEnd; i++) {
                        el = elements[i];
                        delete elements[i];
                        el.parentNode.removeChild(el);
                        if (fireItemRemove) {
                            view.fireEvent('itemremove', store.getByInternalId(el.getAttribute('data-recordId')), i, el, view);
                        }
                    }
                }
                me.startIndex = removeEnd;
            }
            
            newNodes = view.bufferRender(newRecords, me.endIndex + 1);
            for (i = 0; i < recCount; i++) {
                elements[me.endIndex += 1] = newNodes.childrenArray[i];
            }
            nodeContainer.appendChild(newNodes);
            
            if (fireItemAdd) {
                view.fireEvent('itemadd', newRecords, me.endIndex + 1, newNodes.childrenArray);
            }
        }
        
        me.count = me.endIndex - me.startIndex + 1;
        return newNodes.childrenArray;
    },
    sumHeights: function() {
        var result = 0,
            elements = this.elements,
            i;
        for (i = this.startIndex; i <= this.endIndex; i++) {
            result += elements[i].offsetHeight;
        }
        return result;
    }
}, function() {
    Ext.dom.CompositeElementLite.importElementMethods.call(this);
});


Ext.define('Ext.view.Table', {
    extend: 'Ext.view.View',
    xtype: [
        'tableview',
        'gridview'
    ],
    alternateClassName: 'Ext.grid.View',
    requires: [
        'Ext.grid.CellContext',
        'Ext.view.TableLayout',
        'Ext.grid.locking.RowSynchronizer',
        'Ext.view.NodeCache',
        'Ext.util.DelayedTask',
        'Ext.util.MixedCollection'
    ],
    
    isTableView: true,
    config: {
        selectionModel: {
            type: 'rowmodel'
        }
    },
    inheritableStatics: {
        
        
        events: [
            "blur",
            "focus",
            "move",
            "resize",
            "destroy",
            "beforedestroy",
            "boxready",
            "afterrender",
            "render",
            "beforerender",
            "removed",
            "hide",
            "beforehide",
            "show",
            "beforeshow",
            "enable",
            "disable",
            "added",
            "deactivate",
            "beforedeactivate",
            "activate",
            "beforeactivate",
            "cellkeydown",
            "beforecellkeydown",
            "cellmouseup",
            "beforecellmouseup",
            "cellmousedown",
            "beforecellmousedown",
            "cellcontextmenu",
            "beforecellcontextmenu",
            "celldblclick",
            "beforecelldblclick",
            "cellclick",
            "beforecellclick",
            "refresh",
            "itemremove",
            "itemadd",
            "itemupdate",
            "viewready",
            "beforerefresh",
            "unhighlightitem",
            "highlightitem",
            "focuschange",
            "deselect",
            "select",
            "beforedeselect",
            "beforeselect",
            "selectionchange",
            "containerkeydown",
            "containercontextmenu",
            "containerdblclick",
            "containerclick",
            "containermouseout",
            "containermouseover",
            "containermouseup",
            "containermousedown",
            "beforecontainerkeydown",
            "beforecontainercontextmenu",
            "beforecontainerdblclick",
            "beforecontainerclick",
            "beforecontainermouseout",
            "beforecontainermouseover",
            "beforecontainermouseup",
            "beforecontainermousedown",
            "itemkeydown",
            "itemcontextmenu",
            "itemdblclick",
            "itemclick",
            "itemmouseleave",
            "itemmouseenter",
            "itemmouseup",
            "itemmousedown",
            "rowclick",
            "rowcontextmenu",
            "rowdblclick",
            "rowkeydown",
            "rowmouseup",
            "rowmousedown",
            "rowkeydown",
            "beforeitemkeydown",
            "beforeitemcontextmenu",
            "beforeitemdblclick",
            "beforeitemclick",
            "beforeitemmouseleave",
            "beforeitemmouseenter",
            "beforeitemmouseup",
            "beforeitemmousedown",
            "statesave",
            "beforestatesave",
            "staterestore",
            "beforestaterestore",
            "uievent",
            "groupcollapse",
            "groupexpand"
        ]
    },
    scrollable: true,
    componentLayout: 'tableview',
    baseCls: Ext.baseCSSPrefix + 'grid-view',
    unselectableCls: Ext.baseCSSPrefix + 'unselectable',
    
    firstCls: Ext.baseCSSPrefix + 'grid-cell-first',
    
    lastCls: Ext.baseCSSPrefix + 'grid-cell-last',
    itemCls: Ext.baseCSSPrefix + 'grid-item',
    selectedItemCls: Ext.baseCSSPrefix + 'grid-item-selected',
    selectedCellCls: Ext.baseCSSPrefix + 'grid-cell-selected',
    focusedItemCls: Ext.baseCSSPrefix + 'grid-item-focused',
    overItemCls: Ext.baseCSSPrefix + 'grid-item-over',
    altRowCls: Ext.baseCSSPrefix + 'grid-item-alt',
    dirtyCls: Ext.baseCSSPrefix + 'grid-dirty-cell',
    rowClsRe: new RegExp('(?:^|\\s*)' + Ext.baseCSSPrefix + 'grid-item-alt(?:\\s+|$)','g'),
    cellRe: new RegExp(Ext.baseCSSPrefix + 'grid-cell-([^\\s]+)(?:\\s|$)',''),
    positionBody: true,
    positionCells: false,
    stripeOnUpdate: null,
    
    trackOver: true,
    
    getRowClass: null,
    
    stripeRows: true,
    
    markDirty: true,
    
    ariaRole: 'grid',
    
    
    
    tpl: [
        '{%',
        'view = values.view;',
        'if (!(columns = values.columns)) {',
        'columns = values.columns = view.ownerCt.getVisibleColumnManager().getColumns();',
        '}',
        'values.fullWidth = 0;',
        
        'for (i = 0, len = columns.length; i < len; i++) {',
        'column = columns[i];',
        'values.fullWidth += (column.cellWidth = column.lastBox ? column.lastBox.width : column.width || column.minWidth);',
        '}',
        
        'tableCls=values.tableCls=[];',
        '%}',
        '<div class="' + Ext.baseCSSPrefix + 'grid-item-container" style="width:{fullWidth}px">',
        '{[view.renderTHead(values, out, parent)]}',
        '{%',
        'view.renderRows(values.rows, values.columns, values.viewStartIndex, out);',
        '%}',
        '{[view.renderTFoot(values, out, parent)]}',
        '</div>',
        {
            definitions: 'var view, tableCls, columns, i, len, column;',
            priority: 0
        }
    ],
    outerRowTpl: [
        '<table id="{rowId}" ',
        'data-boundView="{view.id}" ',
        'data-recordId="{record.internalId}" ',
        'data-recordIndex="{recordIndex}" ',
        'class="{[values.itemClasses.join(" ")]}" cellPadding="0" cellSpacing="0" {ariaTableAttr} style="{itemStyle};width:0">',
        
        
        '{%',
        'this.nextTpl.applyOut(values, out, parent)',
        '%}',
        '</table>',
        {
            priority: 9999
        }
    ],
    rowTpl: [
        '{%',
        'var dataRowCls = values.recordIndex === -1 ? "" : " ' + Ext.baseCSSPrefix + 'grid-row";',
        '%}',
        '<tr class="{[values.rowClasses.join(" ")]} {[dataRowCls]}" {rowAttr:attributes} {ariaRowAttr}>',
        '<tpl for="columns">' + '{%',
        'parent.view.renderCell(values, parent.record, parent.recordIndex, parent.rowIndex, xindex - 1, out, parent)',
        '%}',
        '</tpl>',
        '</tr>',
        {
            priority: 0
        }
    ],
    cellTpl: [
        '<td class="{tdCls}" {tdAttr} {[Ext.aria ? "id=\\"" + Ext.id() + "\\"" : ""]} style="width:{column.cellWidth}px;<tpl if="tdStyle">{tdStyle}</tpl>" tabindex="-1" {ariaCellAttr} data-columnid="{[values.column.getItemId()]}">',
        '<div {unselectableAttr} class="' + Ext.baseCSSPrefix + 'grid-cell-inner {innerCls}" ',
        'style="text-align:{align};<tpl if="style">{style}</tpl>" {ariaCellInnerAttr}>{value}</div>',
        '</td>',
        {
            priority: 0
        }
    ],
    
    refreshSelmodelOnRefresh: false,
    tableValues: {},
    
    
    rowValues: {
        itemClasses: [],
        rowClasses: []
    },
    cellValues: {
        classes: [
            Ext.baseCSSPrefix + 'grid-cell ' + Ext.baseCSSPrefix + 'grid-td'
        ]
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        
        if (config.grid.isTree) {
            config.baseCls = Ext.baseCSSPrefix + 'tree-view';
        }
        this.callParent([
            config
        ]);
    },
    
    hasVariableRowHeight: function(fromLockingPartner) {
        var me = this;
        return me.variableRowHeight || me.store.isGrouped() || me.getVisibleColumnManager().hasVariableRowHeight() || 
        
        (!fromLockingPartner && me.lockingPartner && me.lockingPartner.hasVariableRowHeight(true));
    },
    initComponent: function() {
        var me = this;
        if (me.columnLines) {
            me.addCls(me.grid.colLinesCls);
        }
        if (me.rowLines) {
            me.addCls(me.grid.rowLinesCls);
        }
        
        me.body = new Ext.dom.Fly();
        me.body.id = me.id + 'gridBody';
        
        
        if (!me.trackOver) {
            me.overItemCls = null;
        }
        me.headerCt.view = me;
        
        
        me.grid.view = me;
        me.initFeatures(me.grid);
        me.itemSelector = me.getItemSelector();
        me.all = new Ext.view.NodeCache(me);
        me.callParent();
    },
    
    
    applySelectionModel: function(selModel, oldSelModel) {
        var me = this,
            grid = me.ownerGrid,
            defaultType = selModel.type;
        
        if (!oldSelModel) {
            
            if (!(selModel && selModel.isSelectionModel)) {
                selModel = grid.selModel || selModel;
            }
        }
        if (selModel) {
            if (selModel.isSelectionModel) {
                selModel.allowDeselect = grid.allowDeselect || selModel.selectionMode !== 'SINGLE';
                selModel.locked = grid.disableSelection;
            } else {
                if (typeof selModel === 'string') {
                    selModel = {
                        type: selModel
                    };
                } else 
                
                {
                    selModel.type = grid.selType || selModel.selType || selModel.type || defaultType;
                }
                if (!selModel.mode) {
                    if (grid.simpleSelect) {
                        selModel.mode = 'SIMPLE';
                    } else if (grid.multiSelect) {
                        selModel.mode = 'MULTI';
                    }
                }
                selModel = Ext.Factory.selection(Ext.apply({
                    allowDeselect: grid.allowDeselect,
                    locked: grid.disableSelection
                }, selModel));
            }
        }
        return selModel;
    },
    updateSelectionModel: function(selModel, oldSelModel) {
        var me = this;
        if (oldSelModel) {
            oldSelModel.un('selectionchange', me.updateBindSelection, me);
            Ext.destroy(me.selModelRelayer);
        }
        me.selModelRelayer = me.relayEvents(selModel, [
            'selectionchange',
            'beforeselect',
            'beforedeselect',
            'select',
            'deselect',
            'focuschange'
        ]);
        selModel.on('selectionchange', me.updateBindSelection, me);
        me.selModel = selModel;
    },
    getVisibleColumnManager: function() {
        return this.ownerCt.getVisibleColumnManager();
    },
    getColumnManager: function() {
        return this.ownerCt.getColumnManager();
    },
    getTopLevelVisibleColumnManager: function() {
        
        
        return this.ownerGrid.getVisibleColumnManager();
    },
    
    moveColumn: function(fromIdx, toIdx, colsToMove) {
        var me = this,
            multiMove = colsToMove > 1,
            range = multiMove && document.createRange ? document.createRange() : null,
            fragment = multiMove && !range ? document.createDocumentFragment() : null,
            destinationCellIdx = toIdx,
            colCount = me.getGridColumns().length,
            lastIndex = colCount - 1,
            doFirstLastClasses = (me.firstCls || me.lastCls) && (toIdx === 0 || toIdx === colCount || fromIdx === 0 || fromIdx === lastIndex),
            i, j, rows, len, tr, cells, colGroups;
        
        
        if (me.rendered && toIdx !== fromIdx) {
            
            
            rows = me.el.query(me.rowSelector);
            if (toIdx > fromIdx && fragment) {
                destinationCellIdx -= 1;
            }
            for (i = 0 , len = rows.length; i < len; i++) {
                tr = rows[i];
                cells = tr.childNodes;
                
                if (doFirstLastClasses) {
                    if (cells.length === 1) {
                        Ext.fly(cells[0]).addCls(me.firstCls);
                        Ext.fly(cells[0]).addCls(me.lastCls);
                        
                        continue;
                    }
                    if (fromIdx === 0) {
                        Ext.fly(cells[0]).removeCls(me.firstCls);
                        Ext.fly(cells[1]).addCls(me.firstCls);
                    } else if (fromIdx === lastIndex) {
                        Ext.fly(cells[lastIndex]).removeCls(me.lastCls);
                        Ext.fly(cells[lastIndex - 1]).addCls(me.lastCls);
                    }
                    if (toIdx === 0) {
                        Ext.fly(cells[0]).removeCls(me.firstCls);
                        Ext.fly(cells[fromIdx]).addCls(me.firstCls);
                    } else if (toIdx === colCount) {
                        Ext.fly(cells[lastIndex]).removeCls(me.lastCls);
                        Ext.fly(cells[fromIdx]).addCls(me.lastCls);
                    }
                }
                
                
                if (multiMove) {
                    if (range) {
                        range.setStartBefore(cells[fromIdx]);
                        range.setEndAfter(cells[fromIdx + colsToMove - 1]);
                        fragment = range.extractContents();
                    } else {
                        for (j = 0; j < colsToMove; j++) {
                            fragment.appendChild(cells[fromIdx]);
                        }
                    }
                    tr.insertBefore(fragment, cells[destinationCellIdx] || null);
                } else {
                    tr.insertBefore(cells[fromIdx], cells[destinationCellIdx] || null);
                }
            }
            
            colGroups = me.el.query('colgroup');
            for (i = 0 , len = colGroups.length; i < len; i++) {
                
                tr = colGroups[i];
                
                
                if (multiMove) {
                    if (range) {
                        range.setStartBefore(tr.childNodes[fromIdx]);
                        range.setEndAfter(tr.childNodes[fromIdx + colsToMove - 1]);
                        fragment = range.extractContents();
                    } else {
                        for (j = 0; j < colsToMove; j++) {
                            fragment.appendChild(tr.childNodes[fromIdx]);
                        }
                    }
                    tr.insertBefore(fragment, tr.childNodes[destinationCellIdx] || null);
                } else {
                    tr.insertBefore(tr.childNodes[fromIdx], tr.childNodes[destinationCellIdx] || null);
                }
            }
        }
    },
    
    scrollToTop: Ext.emptyFn,
    
    addElListener: function(eventName, fn, scope) {
        this.mon(this, eventName, fn, scope, {
            element: 'el'
        });
    },
    
    getGridColumns: function() {
        return this.ownerCt.getVisibleColumnManager().getColumns();
    },
    
    getHeaderAtIndex: function(index) {
        return this.ownerCt.getVisibleColumnManager().getHeaderAtIndex(index);
    },
    
    getCell: function(record, column) {
        var row = this.getRow(record);
        return Ext.fly(row).down(column.getCellSelector());
    },
    
    getFeature: function(id) {
        var features = this.featuresMC;
        if (features) {
            return features.get(id);
        }
    },
    
    
    findFeature: function(ftype) {
        if (this.features) {
            return Ext.Array.findBy(this.features, function(feature) {
                if (feature.ftype === ftype) {
                    return true;
                }
            });
        }
    },
    
    initFeatures: function(grid) {
        var me = this,
            i, features, feature, len;
        
        me.tpl = Ext.XTemplate.getTpl(this, 'tpl');
        
        me.rowTpl = Ext.XTemplate.getTpl(this, 'rowTpl');
        me.addRowTpl(Ext.XTemplate.getTpl(this, 'outerRowTpl'));
        
        me.cellTpl = Ext.XTemplate.getTpl(this, 'cellTpl');
        me.featuresMC = new Ext.util.MixedCollection();
        features = me.features = me.constructFeatures();
        len = features ? features.length : 0;
        for (i = 0; i < len; i++) {
            feature = features[i];
            
            feature.view = me;
            feature.grid = grid;
            me.featuresMC.add(feature);
            feature.init(grid);
        }
    },
    renderTHead: function(values, out, parent) {
        var headers = values.view.headerFns,
            len, i;
        if (headers) {
            for (i = 0 , len = headers.length; i < len; ++i) {
                headers[i].call(this, values, out, parent);
            }
        }
    },
    
    
    
    
    addHeaderFn: function(fn) {
        var headers = this.headerFns;
        if (!headers) {
            headers = this.headerFns = [];
        }
        headers.push(fn);
    },
    renderTFoot: function(values, out, parent) {
        var footers = values.view.footerFns,
            len, i;
        if (footers) {
            for (i = 0 , len = footers.length; i < len; ++i) {
                footers[i].call(this, values, out, parent);
            }
        }
    },
    addFooterFn: function(fn) {
        var footers = this.footerFns;
        if (!footers) {
            footers = this.footerFns = [];
        }
        footers.push(fn);
    },
    addTpl: function(newTpl) {
        return this.insertTpl('tpl', newTpl);
    },
    addRowTpl: function(newTpl) {
        return this.insertTpl('rowTpl', newTpl);
    },
    addCellTpl: function(newTpl) {
        return this.insertTpl('cellTpl', newTpl);
    },
    insertTpl: function(which, newTpl) {
        var me = this,
            tpl, prevTpl;
        
        if (newTpl.isTemplate) {
            newTpl = Ext.Object.chain(newTpl);
        } else 
        
        
        
        
        
        {
            newTpl = new Ext.XTemplate('{%this.nextTpl.applyOut(values, out, parent);%}',newTpl);
        }
        
        for (tpl = me[which]; newTpl.priority < tpl.priority; tpl = tpl.nextTpl) {
            prevTpl = tpl;
        }
        
        if (prevTpl) {
            prevTpl.nextTpl = newTpl;
        } else 
        {
            me[which] = newTpl;
        }
        newTpl.nextTpl = tpl;
        return newTpl;
    },
    tplApplyOut: function(values, out, parent) {
        if (this.before) {
            if (this.before(values, out, parent) === false) {
                return;
            }
        }
        this.nextTpl.applyOut(values, out, parent);
        if (this.after) {
            this.after(values, out, parent);
        }
    },
    
    constructFeatures: function() {
        var me = this,
            features = me.features,
            feature, result,
            i = 0,
            len;
        if (features) {
            result = [];
            len = features.length;
            for (; i < len; i++) {
                feature = features[i];
                if (!feature.isFeature) {
                    feature = Ext.create('feature.' + feature.ftype, feature);
                }
                result[i] = feature;
            }
        }
        return result;
    },
    beforeRender: function() {
        var me = this;
        me.callParent();
        if (!me.enableTextSelection) {
            me.protoEl.unselectable();
        }
    },
    onBindStore: function(store) {
        var me = this,
            bufferedRenderer = me.bufferedRenderer;
        if (bufferedRenderer && bufferedRenderer.store !== store) {
            bufferedRenderer.bindStore(store);
        }
        
        if (me.all && me.all.getCount()) {
            if (bufferedRenderer) {
                bufferedRenderer.setBodyTop(0);
            }
            me.clearViewEl();
        }
        me.callParent(arguments);
    },
    getStoreListeners: function() {
        var result = this.callParent();
        result.beforepageremove = this.beforePageRemove;
        return result;
    },
    beforePageRemove: function(pageMap, pageNumber) {
        var rows = this.all,
            pageSize = pageMap.getPageSize();
        
        if (rows.startIndex >= (pageNumber - 1) * pageSize && rows.endIndex <= (pageNumber * pageSize - 1)) {
            pageMap.get(pageNumber);
            return false;
        }
    },
    
    onViewScroll: function(scroller, x, y) {
        
        if (!this.ignoreScroll) {
            this.callParent([
                scroller,
                x,
                y
            ]);
        }
    },
    
    
    
    createRowElement: function(record, index, updateColumns) {
        var me = this,
            div = me.renderBuffer,
            tplData = me.collectData([
                record
            ], index);
        tplData.columns = updateColumns;
        me.tpl.overwrite(div, tplData);
        
        return Ext.fly(div).down(me.getNodeContainerSelector(), true).firstChild;
    },
    
    
    
    bufferRender: function(records, index) {
        var me = this,
            div = me.renderBuffer,
            result,
            range = document.createRange ? document.createRange() : null;
        me.tpl.overwrite(div, me.collectData(records, index));
        div = Ext.fly(div).down(me.getNodeContainerSelector(), true);
        if (range) {
            range.selectNodeContents(div);
            result = range.extractContents();
        } else {
            result = document.createDocumentFragment();
            while (div.firstChild) {
                result.appendChild(div.firstChild);
            }
        }
        result.childrenArray = Ext.Array.toArray(result.childNodes);
        return result;
    },
    collectData: function(records, startIndex) {
        var me = this;
        me.rowValues.view = me;
        me.tableValues.view = me;
        me.tableValues.rows = records;
        me.tableValues.columns = null;
        me.tableValues.viewStartIndex = startIndex;
        me.tableValues.touchScroll = me.touchScroll;
        me.tableValues.tableStyle = 'width:' + me.headerCt.getTableWidth() + 'px';
        return me.tableValues;
    },
    
    
    
    collectNodes: function(targetEl) {
        this.all.fill(this.getNodeContainer().childNodes, this.all.startIndex);
    },
    
    
    
    
    
    
    refreshSize: function(forceLayout) {
        var me = this,
            bodySelector = me.getBodySelector();
        
        
        if (bodySelector) {
            
            
            me.body.attach(me.el.down(bodySelector, true));
        }
        if (!me.hasLoadingHeight) {
            
            
            Ext.suspendLayouts();
            me.callParent(arguments);
            
            
            
            if (forceLayout || (me.hasVariableRowHeight() && me.dataSource.getCount())) {
                me.grid.updateLayout();
            }
            Ext.resumeLayouts(true);
        }
    },
    clearViewEl: function(leaveNodeContainer) {
        var me = this,
            all = me.all,
            store = me.getStore(),
            i, item, nodeContainer, targetEl;
        
        
        
        
        
        
        
        
        for (i = all.startIndex; i <= all.endIndex; i++) {
            item = all.item(i, true);
            me.fireEvent('itemremove', store.getByInternalId(item.getAttribute('data-recordId')), i, item, me);
        }
        
        
        me.callParent();
        nodeContainer = Ext.fly(me.getNodeContainer());
        if (nodeContainer && !leaveNodeContainer) {
            targetEl = me.getTargetEl();
            if (targetEl.dom !== nodeContainer.dom) {
                nodeContainer.destroy();
            }
        }
    },
    getMaskTarget: function() {
        
        
        return this.ownerCt.body;
    },
    statics: {
        getBoundView: function(node) {
            return Ext.getCmp(node.getAttribute('data-boundView'));
        }
    },
    getRecord: function(node) {
        var me = this,
            recordIndex;
        
        if (me.store.isDestroyed) {
            return;
        }
        if (node.isModel) {
            return node;
        }
        node = me.getNode(node);
        if (node) {
            
            if (!me.hasActiveFeature()) {
                recordIndex = node.getAttribute('data-recordIndex');
                if (recordIndex) {
                    recordIndex = parseInt(recordIndex, 10);
                    if (recordIndex > -1) {
                        
                        
                        return me.store.data.getAt(recordIndex);
                    }
                }
            }
            return me.dataSource.getByInternalId(node.getAttribute('data-recordId'));
        }
    },
    indexOf: function(node) {
        node = this.getNode(node);
        if (!node && node !== 0) {
            return -1;
        }
        return this.all.indexOf(node);
    },
    indexInStore: function(node) {
        
        
        return node ? this.dataSource.indexOf(this.getRecord(node)) : -1;
    },
    renderRows: function(rows, columns, viewStartIndex, out) {
        var rowValues = this.rowValues,
            rowCount = rows.length,
            i;
        rowValues.view = this;
        rowValues.columns = columns;
        for (i = 0; i < rowCount; i++ , viewStartIndex++) {
            rowValues.itemClasses.length = rowValues.rowClasses.length = 0;
            this.renderRow(rows[i], viewStartIndex, out);
        }
        
        rowValues.view = rowValues.columns = rowValues.record = null;
    },
    
    renderColumnSizer: function(values, out) {
        var columns = values.columns || this.getGridColumns(),
            len = columns.length,
            i, column, width;
        out.push('<colgroup role="presentation">');
        for (i = 0; i < len; i++) {
            column = columns[i];
            width = column.cellWidth ? column.cellWidth : Ext.grid.header.Container.prototype.defaultWidth;
            out.push('<col role="presentation" class="', Ext.baseCSSPrefix, 'grid-cell-', columns[i].getItemId(), '" style="width:' + width + 'px">');
        }
        out.push('</colgroup>');
    },
    
    renderRow: function(record, rowIdx, out) {
        var me = this,
            isMetadataRecord = rowIdx === -1,
            selModel = me.selectionModel,
            rowValues = me.rowValues,
            itemClasses = rowValues.itemClasses,
            rowClasses = rowValues.rowClasses,
            itemCls = me.itemCls,
            cls,
            rowTpl = me.rowTpl;
        
        
        
        
        
        
        
        
        
        rowValues.rowAttr = {};
        
        rowValues.record = record;
        rowValues.recordId = record.internalId;
        
        rowValues.recordIndex = me.store.indexOf(record);
        
        rowValues.rowIndex = rowIdx;
        rowValues.rowId = me.getRowId(record);
        rowValues.itemCls = rowValues.rowCls = '';
        if (!rowValues.columns) {
            rowValues.columns = me.ownerCt.getVisibleColumnManager().getColumns();
        }
        itemClasses.length = rowClasses.length = 0;
        
        
        
        if (!isMetadataRecord) {
            itemClasses[0] = itemCls;
            if (!me.ownerCt.disableSelection && selModel.isRowSelected) {
                
                if (selModel.isRowSelected(record)) {
                    itemClasses.push(me.selectedItemCls);
                }
            }
            if (me.stripeRows && rowIdx % 2 !== 0) {
                itemClasses.push(me.altRowCls);
            }
            if (me.getRowClass) {
                cls = me.getRowClass(record, rowIdx, null, me.dataSource);
                if (cls) {
                    rowClasses.push(cls);
                }
            }
        }
        if (out) {
            rowTpl.applyOut(rowValues, out, me.tableValues);
        } else {
            return rowTpl.apply(rowValues, me.tableValues);
        }
    },
    
    renderCell: function(column, record, recordIndex, rowIndex, columnIndex, out) {
        var me = this,
            fullIndex,
            selModel = me.selectionModel,
            cellValues = me.cellValues,
            classes = cellValues.classes,
            fieldValue = record.data[column.dataIndex],
            cellTpl = me.cellTpl,
            value, clsInsertPoint,
            lastFocused = me.navigationModel.getPosition();
        cellValues.record = record;
        cellValues.column = column;
        cellValues.recordIndex = recordIndex;
        cellValues.rowIndex = rowIndex;
        cellValues.columnIndex = columnIndex;
        cellValues.cellIndex = columnIndex;
        cellValues.align = column.align;
        cellValues.innerCls = column.innerCls;
        cellValues.tdCls = cellValues.tdStyle = cellValues.tdAttr = cellValues.style = "";
        cellValues.unselectableAttr = me.enableTextSelection ? '' : 'unselectable="on"';
        
        classes[1] = column.getCellId();
        
        
        clsInsertPoint = 2;
        if (column.renderer && column.renderer.call) {
            fullIndex = me.ownerCt.columnManager.getHeaderIndex(column);
            value = column.renderer.call(column.usingDefaultRenderer ? column : column.scope || me.ownerCt, fieldValue, cellValues, record, recordIndex, fullIndex, me.dataSource, me);
            if (cellValues.css) {
                
                
                record.cssWarning = true;
                cellValues.tdCls += ' ' + cellValues.css;
                cellValues.css = null;
            }
            
            if (cellValues.tdCls) {
                classes[clsInsertPoint++] = cellValues.tdCls;
            }
        } else {
            value = fieldValue;
        }
        cellValues.value = (value == null || value === '') ? column.emptyCellText : value;
        if (column.tdCls) {
            classes[clsInsertPoint++] = column.tdCls;
        }
        if (me.markDirty && record.dirty && record.isModified(column.dataIndex)) {
            classes[clsInsertPoint++] = me.dirtyCls;
        }
        if (column.isFirstVisible) {
            classes[clsInsertPoint++] = me.firstCls;
        }
        if (column.isLastVisible) {
            classes[clsInsertPoint++] = me.lastCls;
        }
        if (!me.enableTextSelection) {
            classes[clsInsertPoint++] = me.unselectableCls;
        }
        if (selModel && (selModel.isCellModel || selModel.isSpreadsheetModel) && selModel.isCellSelected(me, recordIndex, column)) {
            classes[clsInsertPoint++] = me.selectedCellCls;
        }
        if (lastFocused && lastFocused.record.id === record.id && lastFocused.column === column) {
            classes[clsInsertPoint++] = me.focusedItemCls;
        }
        
        classes.length = clsInsertPoint;
        cellValues.tdCls = classes.join(' ');
        cellTpl.applyOut(cellValues, out);
        
        cellValues.column = null;
    },
    
    getRow: function(nodeInfo) {
        var fly;
        if ((!nodeInfo && nodeInfo !== 0) || !this.rendered) {
            return null;
        }
        
        if (nodeInfo.target) {
            nodeInfo = nodeInfo.target;
        }
        
        if (Ext.isString(nodeInfo)) {
            return Ext.fly(nodeInfo).down(this.rowSelector, true);
        }
        
        if (Ext.isNumber(nodeInfo)) {
            fly = this.all.item(nodeInfo);
            return fly && fly.down(this.rowSelector, true);
        }
        
        if (nodeInfo.isModel) {
            return this.getRowByRecord(nodeInfo);
        }
        fly = Ext.fly(nodeInfo);
        
        if (fly.is(this.itemSelector)) {
            return this.getRowFromItem(fly);
        }
        
        return fly.findParent(this.rowSelector, this.getTargetEl());
    },
    
    getRowId: function(record) {
        return this.id + '-record-' + record.internalId;
    },
    constructRowId: function(internalId) {
        return this.id + '-record-' + internalId;
    },
    getNodeById: function(id) {
        id = this.constructRowId(id);
        return this.retrieveNode(id, false);
    },
    getRowById: function(id) {
        id = this.constructRowId(id);
        return this.retrieveNode(id, true);
    },
    getNodeByRecord: function(record) {
        return this.retrieveNode(this.getRowId(record), false);
    },
    getRowByRecord: function(record) {
        return this.retrieveNode(this.getRowId(record), true);
    },
    getRowFromItem: function(item) {
        var rows = Ext.getDom(item).tBodies[0].childNodes,
            len = rows.length,
            i;
        for (i = 0; i < len; i++) {
            if (Ext.fly(rows[i]).is(this.rowSelector)) {
                return rows[i];
            }
        }
    },
    retrieveNode: function(id, dataRow) {
        var result = this.el.getById(id, true);
        if (dataRow && result) {
            return Ext.fly(result).down(this.rowSelector, true);
        }
        return result;
    },
    
    updateIndexes: Ext.emptyFn,
    
    bodySelector: 'div.' + Ext.baseCSSPrefix + 'grid-item-container',
    
    nodeContainerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-item-container',
    
    itemSelector: 'table.' + Ext.baseCSSPrefix + 'grid-item',
    
    rowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-row',
    
    cellSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell',
    
    
    
    sizerSelector: '.' + Ext.baseCSSPrefix + 'grid-cell',
    innerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-cell-inner',
    
    getBodySelector: function() {
        return this.bodySelector;
    },
    
    getColumnSizerSelector: function(header) {
        var selector = this.sizerSelector + '-' + header.getItemId();
        return 'td' + selector + ',col' + selector;
    },
    
    getItemSelector: function() {
        return this.itemSelector;
    },
    
    getCellSelector: function(header) {
        return header ? header.getCellSelector() : this.cellSelector;
    },
    
    getCellInnerSelector: function(header) {
        return this.getCellSelector(header) + ' ' + this.innerSelector;
    },
    
    addRowCls: function(rowInfo, cls) {
        var row = this.getRow(rowInfo);
        if (row) {
            Ext.fly(row).addCls(cls);
        }
    },
    
    removeRowCls: function(rowInfo, cls) {
        var row = this.getRow(rowInfo);
        if (row) {
            Ext.fly(row).removeCls(cls);
        }
    },
    
    onRowSelect: function(rowIdx) {
        var me = this;
        me.addItemCls(rowIdx, me.selectedItemCls);
        if (Ext.isIE8) {
            me.repaintBorder(rowIdx + 1);
        }
    },
    
    onRowDeselect: function(rowIdx) {
        var me = this;
        me.removeItemCls(rowIdx, me.selectedItemCls);
        if (Ext.isIE8) {
            me.repaintBorder(rowIdx + 1);
        }
    },
    onCellSelect: function(position) {
        var cell = this.getCellByPosition(position);
        if (cell) {
            cell.addCls(this.selectedCellCls);
        }
    },
    onCellDeselect: function(position) {
        var cell = this.getCellByPosition(position, true);
        if (cell) {
            Ext.fly(cell).removeCls(this.selectedCellCls);
        }
    },
    
    
    getCellInclusive: function(position, returnDom) {
        if (position) {
            var row = this.getRow(position.row),
                header = this.ownerCt.getColumnManager().getHeaderAtIndex(position.column);
            if (header && row) {
                return Ext.fly(row).down(this.getCellSelector(header), returnDom);
            }
        }
        return false;
    },
    getCellByPosition: function(position, returnDom) {
        if (position) {
            var view = position.view || this,
                row = view.getRow(position.record || position.row),
                header = position.column.isColumn ? position.column : view.getVisibleColumnManager().getHeaderAtIndex(position.column);
            if (header && row) {
                return Ext.fly(row).down(view.getCellSelector(header), returnDom);
            }
        }
        return false;
    },
    onFocusEnter: function(e) {
        var me = this,
            targetView,
            navigationModel = me.getNavigationModel(),
            lastFocused, focusPosition,
            br = me.bufferedRenderer,
            firstRecord, focusTarget;
        
        e = e.event;
        
        
        
        
        if (!me.cellFocused && me.all.getCount() && me.dataSource.getCount()) {
            focusTarget = e.getTarget(null, null, true);
            
            
            if (focusTarget && me.el.contains(focusTarget) && focusTarget !== me.el && !focusTarget.is(me.getCellSelector())) {
                if (navigationModel.lastFocused) {
                    navigationModel.position = navigationModel.lastFocused;
                }
                me.cellFocused = true;
            } else {
                lastFocused = focusPosition = me.getLastFocused();
                
                if (!focusPosition) {
                    targetView = me.isLockingView ? (me.lockedGrid.isVisible() ? me.lockedView : me.normalView) : me;
                    firstRecord = me.dataSource.getAt(br ? br.getFirstVisibleRowIndex() : 0);
                    
                    
                    if (!firstRecord.isNonData) {
                        focusPosition = new Ext.grid.CellContext(targetView).setPosition({
                            row: firstRecord,
                            column: 0
                        });
                    }
                }
                
                if (!focusPosition) {
                    e.stopEvent();
                    e.getTarget().blur();
                    return;
                }
                navigationModel.setPosition(focusPosition, null, e, null, true);
                
                me.cellFocused = !!navigationModel.getPosition();
            }
        }
        if (me.cellFocused) {
            me.el.dom.setAttribute('tabindex', '-1');
        }
    },
    onFocusLeave: function(e) {
        var me = this;
        
        
        
        
        if (me.cellFocused) {
            
            
            
            if (e.toComponent !== me.lockingPartner) {
                me.getNavigationModel().setPosition(null, null, e.event, null, true);
            }
            me.cellFocused = false;
            me.focusEl = me.el;
            me.focusEl.dom.setAttribute('tabindex', 0);
        }
    },
    
    
    onRowFocus: function(rowIdx, highlight, supressFocus) {
        var me = this;
        if (highlight) {
            me.addItemCls(rowIdx, me.focusedItemCls);
            if (!supressFocus) {
                me.focusRow(rowIdx);
            }
        } else 
        {
            me.removeItemCls(rowIdx, me.focusedItemCls);
        }
        if (Ext.isIE8) {
            me.repaintBorder(rowIdx + 1);
        }
    },
    
    focusRow: function(row, delay) {
        var me = this,
            focusTask = me.getFocusTask();
        if (delay) {
            focusTask.delay(Ext.isNumber(delay) ? delay : 10, me.focusRow, me, [
                row,
                false
            ]);
            return;
        }
        
        focusTask.cancel();
        
        if (me.isVisible(true)) {
            me.getNavigationModel().setPosition(me.getRecord(row));
        }
    },
    
    
    focusNode: function(row, delay) {
        this.focusRow(row, delay);
    },
    scrollRowIntoView: function(row, animate) {
        row = this.getRow(row);
        if (row) {
            this.scrollElIntoView(row, false, animate);
        }
    },
    
    focusCell: function(position, delay) {
        var me = this,
            cell,
            focusTask = me.getFocusTask();
        if (delay) {
            focusTask.delay(Ext.isNumber(delay) ? delay : 10, me.focusCell, me, [
                position,
                false
            ]);
            return;
        }
        
        focusTask.cancel();
        
        
        
        if (me.isVisible(true) && (cell = me.getCellByPosition(position))) {
            me.getNavigationModel().setPosition(position);
        }
    },
    getLastFocused: function() {
        var me = this,
            lastFocused = me.lastFocused;
        if (lastFocused && lastFocused.record && lastFocused.column) {
            
            if (me.dataSource.indexOf(lastFocused.record) !== -1 && me.getVisibleColumnManager().indexOf(lastFocused.column) !== -1 && me.getNode(lastFocused.record)) {
                return lastFocused;
            }
        }
    },
    scrollCellIntoView: function(cell, animate) {
        if (cell.isCellContext) {
            cell = this.getCellByPosition(cell);
        }
        if (cell) {
            this.scrollElIntoView(cell, null, animate);
        }
    },
    scrollElIntoView: function(el, hscroll, animate) {
        var scroller = this.getScrollable();
        if (scroller) {
            scroller.scrollIntoView(el, hscroll, animate);
        }
    },
    syncRowHeightBegin: function() {
        var me = this,
            itemEls = me.all,
            ln = itemEls.count,
            synchronizer = [],
            RowSynchronizer = Ext.grid.locking.RowSynchronizer,
            i, j, rowSync;
        for (i = 0 , j = itemEls.startIndex; i < ln; i++ , j++) {
            synchronizer[i] = rowSync = new RowSynchronizer(me,itemEls.elements[j]);
            rowSync.reset();
        }
        return synchronizer;
    },
    syncRowHeightClear: function(synchronizer) {
        var me = this,
            itemEls = me.all,
            ln = itemEls.count,
            i;
        for (i = 0; i < ln; i++) {
            synchronizer[i].reset();
        }
    },
    syncRowHeightMeasure: function(synchronizer) {
        var ln = synchronizer.length,
            i;
        for (i = 0; i < ln; i++) {
            synchronizer[i].measure();
        }
    },
    syncRowHeightFinish: function(synchronizer, otherSynchronizer) {
        var ln = synchronizer.length,
            i;
        for (i = 0; i < ln; i++) {
            synchronizer[i].finish(otherSynchronizer[i]);
        }
    },
    
    handleUpdate: function(store, record, operation, changedFieldNames) {
        var me = this,
            rowTpl = me.rowTpl,
            markDirty = me.markDirty,
            dirtyCls = me.dirtyCls,
            columnsToUpdate = [],
            hasVariableRowHeight = me.variableRowHeight,
            updateTypeFlags = 0,
            ownerCt = me.ownerCt,
            oldItem, oldItemDom, oldDataRow, newItemDom, newAttrs, attLen, attName, attrIndex, overItemCls, columns, column, len, i, cellUpdateFlag, cell, fieldName, value, defaultRenderer, scope, elData;
        if (me.viewReady) {
            
            oldItemDom = me.getNodeByRecord(record);
            
            if (oldItemDom) {
                overItemCls = me.overItemCls;
                columns = me.ownerCt.getVisibleColumnManager().getColumns();
                
                
                
                for (i = 0 , len = columns.length; i < len; i++) {
                    column = columns[i];
                    
                    
                    
                    cellUpdateFlag = me.shouldUpdateCell(record, column, changedFieldNames);
                    if (cellUpdateFlag) {
                        
                        
                        
                        updateTypeFlags = updateTypeFlags | cellUpdateFlag;
                        columnsToUpdate[columnsToUpdate.length] = column;
                        hasVariableRowHeight = hasVariableRowHeight || column.variableRowHeight;
                    }
                }
                
                
                
                if (!me.getRowFromItem(oldItemDom) || (updateTypeFlags & 1) || (oldItemDom.tBodies[0].childNodes.length > 1)) {
                    oldItem = Ext.fly(oldItemDom, '_internal');
                    elData = oldItemDom._extData;
                    newItemDom = me.createRowElement(record, me.dataSource.indexOf(record), columnsToUpdate);
                    if (oldItem.hasCls(overItemCls)) {
                        Ext.fly(newItemDom).addCls(overItemCls);
                    }
                    
                    
                    
                    
                    if (Ext.isIE9m && oldItemDom.mergeAttributes) {
                        oldItemDom.mergeAttributes(newItemDom, true);
                    } else {
                        newAttrs = newItemDom.attributes;
                        attLen = newAttrs.length;
                        for (attrIndex = 0; attrIndex < attLen; attrIndex++) {
                            attName = newAttrs[attrIndex].name;
                            if (attName !== 'id') {
                                oldItemDom.setAttribute(attName, newAttrs[attrIndex].value);
                            }
                        }
                    }
                    
                    if (elData) {
                        elData.isSynchronized = false;
                    }
                    
                    
                    if (columns.length && (oldDataRow = me.getRow(oldItemDom))) {
                        me.updateColumns(oldDataRow, Ext.fly(newItemDom).down(me.rowSelector, true), columnsToUpdate);
                    }
                    
                    while (rowTpl) {
                        if (rowTpl.syncContent) {
                            
                            
                            if (rowTpl.syncContent(oldItemDom, newItemDom, changedFieldNames ? columnsToUpdate : null) === false) {
                                break;
                            }
                        }
                        rowTpl = rowTpl.nextTpl;
                    }
                } else 
                {
                    
                    if (!me.cellFly) {
                        me.cellFly = new Ext.dom.Fly();
                    }
                    
                    for (i = 0 , len = columnsToUpdate.length; i < len; i++) {
                        column = columnsToUpdate[i];
                        
                        fieldName = column.dataIndex;
                        value = record.get(fieldName);
                        cell = oldItemDom.firstChild.firstChild.childNodes[column.getVisibleIndex()];
                        
                        if (markDirty) {
                            me.cellFly.attach(cell);
                            if (record.isModified(column.dataIndex)) {
                                me.cellFly.addCls(dirtyCls);
                            } else {
                                me.cellFly.removeCls(dirtyCls);
                            }
                        }
                        defaultRenderer = column.usingDefaultRenderer;
                        scope = defaultRenderer ? column : column.scope;
                        
                        if (column.updater) {
                            Ext.callback(column.updater, scope, [
                                cell,
                                value,
                                record,
                                me,
                                me.dataSource
                            ], 0, column, ownerCt);
                        } else {
                            if (column.renderer) {
                                value = Ext.callback(column.renderer, scope, [
                                    value,
                                    null,
                                    record,
                                    0,
                                    0,
                                    me.dataSource,
                                    me
                                ], 0, column, ownerCt);
                            }
                            
                            
                            
                            if (column.producesHTML) {
                                cell.childNodes[0].innerHTML = value;
                            } else {
                                cell.childNodes[0].childNodes[0].data = value;
                            }
                        }
                        
                        if (me.highlightClass) {
                            Ext.fly(cell).addCls(me.highlightClass);
                            
                            
                            if (!me.changedCells) {
                                me.self.prototype.changedCells = [];
                                me.prototype.clearChangedTask = new Ext.util.DelayedTask(me.clearChangedCells,me.prototype);
                                me.clearChangedTask.delay(me.unhighlightDelay);
                            }
                            
                            me.changedCells.push({
                                cell: cell,
                                cls: me.highlightClass,
                                expires: Ext.Date.now() + 1000
                            });
                        }
                    }
                    
                    
                    if (markDirty && !record.dirty) {
                        Ext.fly(oldItemDom, '_internal').select('.' + dirtyCls).removeCls(dirtyCls);
                    }
                }
                
                if (hasVariableRowHeight) {
                    Ext.suspendLayouts();
                }
                
                
                me.fireEvent('itemupdate', record, me.store.indexOf(record), oldItemDom);
                
                if (hasVariableRowHeight) {
                    if (me.bufferedRenderer) {
                        me.bufferedRenderer.refreshSize();
                        
                        
                        me.ownerGrid.updateLayout();
                    } else {
                        me.refreshSize();
                    }
                    
                    Ext.resumeLayouts(true);
                }
            }
        }
    },
    clearChangedCells: function() {
        var me = this,
            now = Ext.Date.now(),
            changedCell;
        for (var i = 0,
            len = me.changedCells.length; i < len; ) {
            changedCell = me.changedCells[i];
            if (changedCell.expires <= now) {
                Ext.fly(changedCell.cell).removeCls(changedCell.highlightClass);
                Ext.Array.erase(me.changedCells, i, 1);
                len--;
            } else {
                break;
            }
        }
        
        if (len) {
            me.clearChangedTask.delay(me.unhighlightDelay);
        }
    },
    updateColumns: function(oldRowDom, newRowDom, columnsToUpdate) {
        var me = this,
            newAttrs, attLen, attName, attrIndex,
            colCount = columnsToUpdate.length,
            colIndex, column, oldCell, newCell,
            cellSelector = me.getCellSelector();
        
        
        if (oldRowDom.mergeAttributes) {
            oldRowDom.mergeAttributes(newRowDom, true);
        } else {
            newAttrs = newRowDom.attributes;
            attLen = newAttrs.length;
            for (attrIndex = 0; attrIndex < attLen; attrIndex++) {
                attName = newAttrs[attrIndex].name;
                if (attName !== 'id') {
                    oldRowDom.setAttribute(attName, newAttrs[attrIndex].value);
                }
            }
        }
        
        for (colIndex = 0; colIndex < colCount; colIndex++) {
            column = columnsToUpdate[colIndex];
            
            
            cellSelector = me.getCellSelector(column);
            oldCell = Ext.fly(oldRowDom).selectNode(cellSelector);
            newCell = Ext.fly(newRowDom).selectNode(cellSelector);
            
            Ext.fly(oldCell).syncContent(newCell);
        }
    },
    
    shouldUpdateCell: function(record, column, changedFieldNames) {
        
        if (!column.preventUpdate) {
            
            
            if (column.hasCustomRenderer) {
                return 1;
            }
            
            
            
            if (changedFieldNames) {
                var len = changedFieldNames.length,
                    i, field;
                for (i = 0; i < len; ++i) {
                    field = changedFieldNames[i];
                    if (field === column.dataIndex || field === record.idProperty) {
                        return 2;
                    }
                }
            } else {
                return 2;
            }
        }
        return 0;
    },
    
    refresh: function() {
        var me = this,
            scroller;
        me.callParent(arguments);
        me.headerCt.setSortState();
        
        
        if (me.touchScroll && me.el && !me.all.getCount() && me.headerCt && me.headerCt.tooNarrow) {
            scroller = me.getScrollable();
            if (scroller) {
                scroller.setSize({
                    x: me.headerCt.getTableWidth(),
                    y: scroller.getSize().y
                });
            }
        }
    },
    processContainerEvent: function(e) {
        
        
        var cmp = Ext.ComponentManager.byElement(e.target.parentNode);
        if (cmp && cmp.up(this.ownerCt)) {
            return false;
        }
    },
    processItemEvent: function(record, item, rowIndex, e) {
        var me = this,
            self = me.self,
            map = self.EventMap,
            type = e.type,
            row, cell, selModel,
            features = me.features,
            len = features.length,
            i, cellIndex, result, feature, column,
            navModel = me.getNavigationModel(),
            focusPosition,
            eventPosition = e.position = me.eventPosition || (me.eventPosition = new Ext.grid.CellContext());
        
        
        
        if (Ext.isIE && type === 'mouseup' && !e.within(me.el)) {
            return false;
        }
        
        if (me.indexInStore(item) !== -1) {
            row = eventPosition.rowElement = Ext.fly(item).down(me.rowSelector, true);
            
            if (Ext.String.startsWith(e.type, 'key') && (focusPosition = navModel.getPosition())) {
                cell = (cell = navModel.getCell()) && cell.dom;
                column = focusPosition.column;
            }
            
            
            if (!cell) {
                cell = e.getTarget(me.getCellSelector(), row);
            }
            type = self.TouchEventMap[type] || type;
            if (cell) {
                if (!cell.parentNode) {
                    
                    
                    return false;
                }
                if (!column) {
                    column = me.getHeaderByCell(cell);
                }
                
                
                cellIndex = me.ownerCt.getColumnManager().getHeaderIndex(column);
            } else {
                cellIndex = -1;
            }
            eventPosition.setAll(me, rowIndex, column ? me.getVisibleColumnManager().getHeaderIndex(column) : -1, record, column);
            eventPosition.cellElement = cell;
            result = me.fireEvent('uievent', type, me, cell, rowIndex, cellIndex, e, record, row);
            
            
            
            if ((result === false || me.callParent(arguments) === false)) {
                selModel = me.getSelectionModel();
                if (selModel.onVetoUIEvent) {
                    selModel.onVetoUIEvent(type, me, cell, rowIndex, cellIndex, e, record, row);
                }
                return false;
            }
            for (i = 0; i < len; ++i) {
                feature = features[i];
                
                
                if (feature.wrapsItem) {
                    if (feature.vetoEvent(record, row, rowIndex, e) === false) {
                        
                        
                        me.processSpecialEvent(e);
                        
                        
                        e.preventDefault();
                        return false;
                    }
                }
            }
            
            
            
            if (cell && type !== 'mouseover' && type !== 'mouseout') {
                result = !
                ((me['onBeforeCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) || (me.fireEvent('beforecell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false) || (me['onCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) || (me.fireEvent('cell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false));
            }
            if (result !== false) {
                result = me.fireEvent('row' + type, me, record, row, rowIndex, e);
            }
            return result;
        } else {
            
            this.processSpecialEvent(e);
            
            
            e.preventDefault();
            return false;
        }
    },
    processSpecialEvent: function(e) {
        var me = this,
            features = me.features,
            ln = features.length,
            type = e.type,
            i, feature, prefix, featureTarget, beforeArgs, args,
            panel = me.ownerCt;
        me.callParent(arguments);
        if (type == 'mouseover' || type == 'mouseout') {
            return;
        }
        type = me.self.TouchEventMap[type] || type;
        for (i = 0; i < ln; i++) {
            feature = features[i];
            if (feature.hasFeatureEvent) {
                featureTarget = e.getTarget(feature.eventSelector, me.getTargetEl());
                if (featureTarget) {
                    prefix = feature.eventPrefix;
                    
                    
                    beforeArgs = feature.getFireEventArgs('before' + prefix + type, me, featureTarget, e);
                    args = feature.getFireEventArgs(prefix + type, me, featureTarget, e);
                    if (
                    (me.fireEvent.apply(me, beforeArgs) === false) || 
                    (panel.fireEvent.apply(panel, beforeArgs) === false) || 
                    (me.fireEvent.apply(me, args) === false) || 
                    (panel.fireEvent.apply(panel, args) === false)) {
                        return false;
                    }
                }
            }
        }
        return true;
    },
    onCellMouseDown: Ext.emptyFn,
    onCellLongPress: Ext.emptyFn,
    onCellMouseUp: Ext.emptyFn,
    onCellClick: Ext.emptyFn,
    onCellDblClick: Ext.emptyFn,
    onCellContextMenu: Ext.emptyFn,
    onCellKeyDown: Ext.emptyFn,
    onCellKeyUp: Ext.emptyFn,
    onCellKeyPress: Ext.emptyFn,
    onBeforeCellMouseDown: Ext.emptyFn,
    onBeforeCellLongPress: Ext.emptyFn,
    onBeforeCellMouseUp: Ext.emptyFn,
    onBeforeCellClick: Ext.emptyFn,
    onBeforeCellDblClick: Ext.emptyFn,
    onBeforeCellContextMenu: Ext.emptyFn,
    onBeforeCellKeyDown: Ext.emptyFn,
    onBeforeCellKeyUp: Ext.emptyFn,
    onBeforeCellKeyPress: Ext.emptyFn,
    
    expandToFit: function(header) {
        this.autoSizeColumn(header);
    },
    
    autoSizeColumn: function(header) {
        if (Ext.isNumber(header)) {
            header = this.getGridColumns[header];
        }
        if (header) {
            if (header.isGroupHeader) {
                header.autoSize();
                return;
            }
            delete header.flex;
            header.setWidth(this.getMaxContentWidth(header));
        }
    },
    
    getMaxContentWidth: function(header) {
        var me = this,
            cells = me.el.query(header.getCellInnerSelector()),
            originalWidth = header.getWidth(),
            i = 0,
            ln = cells.length,
            columnSizer = me.body.select(me.getColumnSizerSelector(header)),
            max = Math.max,
            widthAdjust = 0,
            maxWidth;
        if (ln > 0) {
            if (Ext.supports.ScrollWidthInlinePaddingBug) {
                widthAdjust += me.getCellPaddingAfter(cells[0]);
            }
            if (me.columnLines) {
                widthAdjust += Ext.fly(cells[0].parentNode).getBorderWidth('lr');
            }
        }
        
        columnSizer.setWidth(1);
        
        
        
        
        header.titleEl.setStyle('text-overflow', 'clip');
        
        maxWidth = header.textEl.dom.offsetWidth + header.titleEl.getPadding('lr');
        
        header.titleEl.setStyle('text-overflow', '');
        for (; i < ln; i++) {
            maxWidth = max(maxWidth, cells[i].scrollWidth);
        }
        
        maxWidth += widthAdjust;
        
        maxWidth = max(maxWidth, 40);
        
        columnSizer.setWidth(originalWidth);
        return maxWidth;
    },
    getPositionByEvent: function(e) {
        var me = this,
            cellNode = e.getTarget(me.cellSelector),
            rowNode = e.getTarget(me.itemSelector),
            record = me.getRecord(rowNode),
            header = me.getHeaderByCell(cellNode);
        return me.getPosition(record, header);
    },
    getHeaderByCell: function(cell) {
        if (cell) {
            return this.ownerCt.getVisibleColumnManager().getHeaderById(cell.getAttribute('data-columnId'));
        }
        return false;
    },
    
    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {
        
        
        if (!pos) {
            return false;
        }
        var me = this,
            
            
            row = typeof pos.row === 'number' ? pos.row : pos.rowIdx,
            column = typeof pos.column === 'number' ? pos.column : pos.colIdx,
            rowCount = me.dataSource.getCount(),
            columns = me.ownerCt.getVisibleColumnManager(),
            firstIndex = columns.getHeaderIndex(columns.getFirst()),
            lastIndex = columns.getHeaderIndex(columns.getLast()),
            newRow = row,
            newColumn = column,
            activeHeader = columns.getHeaderAtIndex(column);
        
        if (!activeHeader || activeHeader.hidden || !rowCount) {
            return false;
        }
        e = e || {};
        direction = direction.toLowerCase();
        switch (direction) {
            case 'right':
                
                if (column === lastIndex) {
                    
                    if (preventWrap || row === rowCount - 1) {
                        return false;
                    }
                    if (!e.ctrlKey) {
                        
                        newRow = me.walkRows(row, 1);
                        if (newRow !== row) {
                            newColumn = firstIndex;
                        }
                    }
                } else 
                {
                    if (!e.ctrlKey) {
                        newColumn = columns.getHeaderIndex(columns.getNextSibling(activeHeader));
                    } else {
                        newColumn = lastIndex;
                    }
                };
                break;
            case 'left':
                
                if (column === firstIndex) {
                    
                    if (preventWrap || row === 0) {
                        return false;
                    }
                    if (!e.ctrlKey) {
                        
                        newRow = me.walkRows(row, -1);
                        if (newRow !== row) {
                            newColumn = lastIndex;
                        }
                    }
                } else 
                {
                    if (!e.ctrlKey) {
                        newColumn = columns.getHeaderIndex(columns.getPreviousSibling(activeHeader));
                    } else {
                        newColumn = firstIndex;
                    }
                };
                break;
            case 'up':
                
                if (row === 0) {
                    return false;
                } else 
                {
                    if (!e.ctrlKey) {
                        newRow = me.walkRows(row, -1);
                    } else {
                        
                        newRow = me.walkRows(-1, 1);
                    }
                };
                break;
            case 'down':
                
                if (row === rowCount - 1) {
                    return false;
                } else 
                {
                    if (!e.ctrlKey) {
                        newRow = me.walkRows(row, 1);
                    } else {
                        
                        newRow = me.walkRows(rowCount, -1);
                    }
                };
                break;
        }
        if (verifierFn && verifierFn.call(scope || me, {
            row: newRow,
            column: newColumn
        }) !== true) {
            return false;
        }
        newColumn = columns.getHeaderAtIndex(newColumn);
        return new Ext.grid.CellContext(me).setPosition(newRow, newColumn);
    },
    
    walkRows: function(startRow, distance) {
        
        
        var me = this,
            store = me.dataSource,
            moved = 0,
            lastValid = startRow,
            node,
            limit = (distance < 0) ? 0 : (store.isBufferedStore ? store.getTotalCount() : store.getCount()) - 1,
            increment = limit ? 1 : -1,
            result = startRow;
        do {
            
            if (limit ? result >= limit : result <= limit) {
                return lastValid || limit;
            }
            
            result += increment;
            
            
            if ((node = Ext.fly(me.getRow(result))) && node.isVisible(true)) {
                moved += increment;
                lastValid = result;
            }
        } while (moved !== distance);
        return result;
    },
    
    walkRecs: function(startRec, distance) {
        
        
        var me = this,
            store = me.dataSource,
            moved = 0,
            lastValid = startRec,
            node,
            limit = (distance < 0) ? 0 : (store.isBufferedStore ? store.getTotalCount() : store.getCount()) - 1,
            increment = limit ? 1 : -1,
            testIndex = store.indexOf(startRec),
            rec;
        do {
            
            if (limit ? testIndex >= limit : testIndex <= limit) {
                return lastValid;
            }
            
            testIndex += increment;
            
            
            rec = store.getAt(testIndex);
            if (!rec.isCollapsedPlaceholder && (node = Ext.fly(me.getNodeByRecord(rec))) && node.isVisible(true)) {
                moved += increment;
                lastValid = rec;
            }
        } while (moved !== distance);
        return lastValid;
    },
    
    getFirstVisibleRowIndex: function() {
        var me = this,
            count = (me.dataSource.isBufferedStore ? me.dataSource.getTotalCount() : me.dataSource.getCount()),
            result = me.indexOf(me.all.first()) - 1;
        do {
            result += 1;
            if (result === count) {
                return;
            }
        } while (!Ext.fly(me.getRow(result)).isVisible(true));
        return result;
    },
    
    getLastVisibleRowIndex: function() {
        var me = this,
            result = me.indexOf(me.all.last());
        do {
            result -= 1;
            if (result === -1) {
                return;
            }
        } while (!Ext.fly(me.getRow(result)).isVisible(true));
        return result;
    },
    getHeaderCt: function() {
        return this.headerCt;
    },
    getPosition: function(record, header) {
        return new Ext.grid.CellContext(this).setPosition(record, header);
    },
    onDestroy: function() {
        var me = this,
            features = me.featuresMC,
            len, i;
        if (features) {
            for (i = 0 , len = features.getCount(); i < len; ++i) {
                features.getAt(i).destroy();
            }
        }
        me.cellFly = me.featuresMC = null;
        me.callParent(arguments);
    },
    
    
    
    onReplace: function(store, startIndex, oldRecords, newRecords) {
        var me = this,
            bufferedRenderer = me.bufferedRenderer;
        
        if (me.rendered && bufferedRenderer) {
            bufferedRenderer.onReplace(store, startIndex, oldRecords, newRecords);
        } else {
            me.callParent(arguments);
        }
        me.setPendingStripe(startIndex);
    },
    
    onAdd: function(store, records, index) {
        var me = this,
            bufferedRenderer = me.bufferedRenderer;
        if (me.rendered && bufferedRenderer) {
            bufferedRenderer.onReplace(store, index, [], records);
        } else {
            me.callParent(arguments);
        }
        me.setPendingStripe(index);
    },
    
    onRemove: function(store, records, index) {
        var me = this,
            bufferedRenderer = me.bufferedRenderer;
        
        if (me.rendered && bufferedRenderer) {
            bufferedRenderer.onReplace(store, index, records, []);
        } else {
            me.callParent(arguments);
        }
        me.setPendingStripe(index);
    },
    
    onDataRefresh: function() {
        var me = this,
            owner = me.ownerCt;
        
        if (owner && owner.isCollapsingOrExpanding === 2) {
            owner.on('expand', me.onDataRefresh, me, {
                single: true
            });
            return;
        }
        me.callParent();
    },
    getViewRange: function() {
        var me = this;
        if (me.bufferedRenderer) {
            return me.bufferedRenderer.getViewRange();
        }
        return me.callParent();
    },
    setPendingStripe: function(index) {
        var current = this.stripeOnUpdate;
        if (current === null) {
            current = index;
        } else {
            current = Math.min(current, index);
        }
        this.stripeOnUpdate = current;
    },
    onEndUpdate: function() {
        var me = this,
            stripeOnUpdate = me.stripeOnUpdate,
            startIndex = me.all.startIndex;
        if (me.rendered && (stripeOnUpdate || stripeOnUpdate === 0)) {
            if (stripeOnUpdate < startIndex) {
                stripeOnUpdate = startIndex;
            }
            me.doStripeRows(stripeOnUpdate);
            me.stripeOnUpdate = null;
        }
        me.callParent(arguments);
    },
    
    doStripeRows: function(startRow, endRow) {
        var me = this,
            rows, rowsLn, i, row;
        
        if (me.rendered && me.stripeRows) {
            rows = me.getNodes(startRow, endRow);
            for (i = 0 , rowsLn = rows.length; i < rowsLn; i++) {
                row = rows[i];
                
                row.className = row.className.replace(me.rowClsRe, ' ');
                startRow++;
                
                if (startRow % 2 === 0) {
                    row.className += (' ' + me.altRowCls);
                }
            }
        }
    },
    hasActiveFeature: function() {
        return (this.isGrouping && this.store.isGrouped()) || this.isRowWrapped;
    },
    getCellPaddingAfter: function(cell) {
        return Ext.fly(cell).getPadding('r');
    },
    privates: {
        refreshScroll: function() {
            var me = this,
                bufferedRenderer = me.bufferedRenderer;
            
            
            if (bufferedRenderer) {
                bufferedRenderer.refreshSize();
            } else {
                me.callParent();
            }
        }
    }
});


Ext.define('Ext.grid.Panel', {
    extend: 'Ext.panel.Table',
    requires: [
        'Ext.view.Table'
    ],
    alias: [
        'widget.gridpanel',
        'widget.grid'
    ],
    alternateClassName: [
        'Ext.list.ListView',
        'Ext.ListView',
        'Ext.grid.GridPanel'
    ],
    viewType: 'tableview',
    lockable: false,
    
    rowLines: true
});


Ext.define('Ext.form.CheckboxManager', {
    extend: 'Ext.util.MixedCollection',
    singleton: true,
    getByName: function(name, formId) {
        return this.filterBy(function(item) {
            return item.name == name && item.getFormId() == formId;
        });
    }
});


Ext.define('Ext.form.field.Checkbox', {
    extend: 'Ext.form.field.Base',
    alias: [
        'widget.checkboxfield',
        'widget.checkbox'
    ],
    alternateClassName: 'Ext.form.Checkbox',
    requires: [
        'Ext.XTemplate',
        'Ext.form.CheckboxManager'
    ],
    
    stretchInputElFixed: false,
    childEls: [
        
        'boxLabelEl'
    ],
    
    fieldSubTpl: [
        '<div class="{wrapInnerCls} {noBoxLabelCls}" role="presentation">',
        '<tpl if="labelAlignedBefore">',
        '{beforeBoxLabelTpl}',
        '<label id="{cmpId}-boxLabelEl" data-ref="boxLabelEl" {boxLabelAttrTpl} class="{boxLabelCls} ',
        '{boxLabelCls}-{ui} {boxLabelCls}-{boxLabelAlign} {childElCls}" for="{id}">',
        '{beforeBoxLabelTextTpl}',
        '{boxLabel}',
        '{afterBoxLabelTextTpl}',
        '</label>',
        '{afterBoxLabelTpl}',
        '</tpl>',
        '<input type="button" id="{id}" data-ref="inputEl" role="{role}" {inputAttrTpl}',
        '<tpl if="tabIdx != null"> tabindex="{tabIdx}"</tpl>',
        '<tpl if="disabled"> disabled="disabled"</tpl>',
        '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
        ' class="{fieldCls} {typeCls} {typeCls}-{ui} {inputCls} {inputCls}-{ui} {childElCls} {afterLabelCls}" autocomplete="off" hidefocus="true" />',
        '<tpl if="boxLabel && !labelAlignedBefore">',
        '{beforeBoxLabelTpl}',
        '<label id="{cmpId}-boxLabelEl" data-ref="boxLabelEl" {boxLabelAttrTpl} class="{boxLabelCls} ',
        '{boxLabelCls}-{ui} {boxLabelCls}-{boxLabelAlign} {childElCls}" for="{id}">',
        '{beforeBoxLabelTextTpl}',
        '{boxLabel}',
        '{afterBoxLabelTextTpl}',
        '</label>',
        '{afterBoxLabelTpl}',
        '</tpl>',
        '</div>',
        {
            disableFormats: true,
            compiled: true
        }
    ],
    publishes: {
        checked: 1
    },
    subTplInsertions: [
        
        'beforeBoxLabelTpl',
        
        'afterBoxLabelTpl',
        
        'beforeBoxLabelTextTpl',
        
        'afterBoxLabelTextTpl',
        
        'boxLabelAttrTpl',
        
        'inputAttrTpl'
    ],
    
    isCheckbox: true,
    
    focusCls: 'form-checkbox-focus',
    
    
    fieldBodyCls: Ext.baseCSSPrefix + 'form-cb-wrap',
    
    checked: false,
    
    checkedCls: Ext.baseCSSPrefix + 'form-cb-checked',
    
    
    boxLabelCls: Ext.baseCSSPrefix + 'form-cb-label',
    
    boxLabelAlign: 'after',
    afterLabelCls: Ext.baseCSSPrefix + 'form-cb-after',
    wrapInnerCls: Ext.baseCSSPrefix + 'form-cb-wrap-inner',
    
    
    
    
    noBoxLabelCls: Ext.baseCSSPrefix + 'form-cb-wrap-inner-no-box-label',
    
    inputValue: 'on',
    
    
    
    
    checkChangeEvents: [],
    inputType: 'checkbox',
    isTextInput: false,
    ariaRole: 'checkbox',
    
    onRe: /^on$/i,
    
    inputCls: Ext.baseCSSPrefix + 'form-cb',
    initComponent: function() {
        var me = this,
            value = me.value;
        if (value !== undefined) {
            me.checked = me.isChecked(value, me.inputValue);
        }
        me.callParent(arguments);
        me.getManager().add(me);
    },
    initValue: function() {
        var me = this,
            checked = !!me.checked;
        
        me.originalValue = me.lastValue = checked;
        
        me.setValue(checked);
    },
    getElConfig: function() {
        var me = this;
        
        if (me.isChecked(me.rawValue, me.inputValue)) {
            me.addCls(me.checkedCls);
        }
        return me.callParent();
    },
    getSubTplData: function(fieldData) {
        var me = this,
            boxLabel = me.boxLabel,
            boxLabelAlign = me.boxLabelAlign,
            labelAlignedBefore = boxLabel && boxLabelAlign === 'before';
        return Ext.apply(me.callParent(arguments), {
            disabled: me.readOnly || me.disabled,
            wrapInnerCls: me.wrapInnerCls,
            boxLabel: boxLabel,
            boxLabelCls: me.boxLabelCls,
            boxLabelAlign: boxLabelAlign,
            labelAlignedBefore: labelAlignedBefore,
            afterLabelCls: labelAlignedBefore ? me.afterLabelCls : '',
            noBoxLabelCls: !boxLabel ? me.noBoxLabelCls : '',
            role: me.ariaRole
        });
    },
    initEvents: function() {
        var me = this;
        me.callParent();
        
        
        me.mon(me.inputEl, 'click', me.onBoxClick, me, {
            translate: false
        });
    },
    
    setBoxLabel: function(boxLabel) {
        var me = this;
        me.boxLabel = boxLabel;
        if (me.rendered) {
            me.boxLabelEl.setHtml(boxLabel);
        }
    },
    
    onBoxClick: function() {
        var me = this;
        if (!me.disabled && !me.readOnly) {
            this.setValue(!this.checked);
        }
    },
    
    getRawValue: function() {
        return this.checked;
    },
    
    getValue: function() {
        return this.checked;
    },
    
    getSubmitValue: function() {
        var unchecked = this.uncheckedValue,
            uncheckedVal = Ext.isDefined(unchecked) ? unchecked : null;
        return this.checked ? this.inputValue : uncheckedVal;
    },
    isChecked: function(rawValue, inputValue) {
        return (rawValue === true || rawValue === 'true' || rawValue === '1' || rawValue === 1 || (((Ext.isString(rawValue) || Ext.isNumber(rawValue)) && inputValue) ? rawValue == inputValue : this.onRe.test(rawValue)));
    },
    
    setRawValue: function(value) {
        var me = this,
            inputEl = me.inputEl,
            checked = me.isChecked(value, me.inputValue);
        if (inputEl) {
            me[checked ? 'addCls' : 'removeCls'](me.checkedCls);
        }
        me.checked = me.rawValue = checked;
        if (!me.duringSetValue) {
            me.lastValue = checked;
        }
        return checked;
    },
    
    setValue: function(checked) {
        var me = this,
            boxes, i, len, box;
        
        
        
        
        if (Ext.isArray(checked)) {
            boxes = me.getManager().getByName(me.name, me.getFormId()).items;
            len = boxes.length;
            for (i = 0; i < len; ++i) {
                box = boxes[i];
                box.setValue(Ext.Array.contains(checked, box.inputValue));
            }
        } else {
            
            
            me.duringSetValue = true;
            me.callParent(arguments);
            delete me.duringSetValue;
        }
        return me;
    },
    
    valueToRaw: Ext.identityFn,
    
    onChange: function(newVal, oldVal) {
        var me = this,
            handler = me.handler;
        Ext.callback(me.handler, me.scope, [
            me,
            newVal
        ], 0, me);
        me.callParent(arguments);
        if (me.reference && me.publishState) {
            me.publishState('checked', newVal);
        }
    },
    resetOriginalValue: function(
    fromBoxInGroup) {
        var me = this,
            boxes, box, len, i;
        
        if (!fromBoxInGroup) {
            boxes = me.getManager().getByName(me.name, me.getFormId()).items;
            len = boxes.length;
            for (i = 0; i < len; ++i) {
                box = boxes[i];
                if (box !== me) {
                    boxes[i].resetOriginalValue(true);
                }
            }
        }
        me.callParent();
    },
    
    beforeDestroy: function() {
        this.callParent();
        this.getManager().removeAtKey(this.id);
    },
    
    getManager: function() {
        return Ext.form.CheckboxManager;
    },
    onEnable: function() {
        var me = this,
            inputEl = me.inputEl;
        me.callParent();
        if (inputEl) {
            
            inputEl.dom.disabled = me.readOnly;
        }
    },
    setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl) {
            
            inputEl.dom.disabled = !!readOnly || me.disabled;
        }
        me.callParent(arguments);
    },
    getFormId: function() {
        var me = this,
            form;
        if (!me.formId) {
            form = me.up('form');
            if (form) {
                me.formId = form.id;
            }
        }
        return me.formId;
    }
});


Ext.define('Ext.app.bindinspector.Util', {
    singleton: true,
    getChildStub: function(name, parent) {
        var val, children;
        if (parent) {
            children = parent.children;
            if (children) {
                val = children[name];
            }
        }
        return val || null;
    },
    valueRenderer: function(v) {
        var s;
        if (v === undefined) {
            return 'undefined';
        } else if (v === null) {
            return 'null';
        } else if (Ext.isString(v)) {
            return v;
        } else if (Ext.isDate(v)) {
            return Ext.Date.format(v, 'c');
        } else if (v && v.isModel) {
            s = v.entityName;
            return Ext.String.format('Model({0}, {1})', s.replace('noconflict.', ''), v.getId());
        } else if (v && v.isStore) {
            s = v.entityName || 'Anonymous';
            return 'Store{' + s.replace('noconflict.', '') + '}';
        }
        return v;
    },
    buildBindData: function(bind) {
        var out = [],
            key, o;
        for (key in bind) {
            o = bind[key];
            out.push({
                key: key,
                descriptor: o.descriptor,
                tokens: o.tokens,
                value: o.value,
                binding: o
            });
        }
        return out;
    }
});

Ext.define('Ext.app.bindinspector.ComponentDetail', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.bindinspector-componentdetail',
    requires: [
        'Ext.form.field.Display',
        'Ext.grid.Panel',
        'Ext.layout.container.VBox',
        'Ext.form.field.Checkbox',
        'Ext.app.bindinspector.Util'
    ],
    layout: 'border',
    
    activeCls: Ext.baseCSSPrefix + 'bindinspector-stub-active',
    descriptorCls: Ext.baseCSSPrefix + 'bindinspector-descriptor',
    multipleCls: Ext.baseCSSPrefix + 'bindinspector-mult-val',
    directCls: Ext.baseCSSPrefix + 'bindinspector-direct-val',
    inheritedCls: Ext.baseCSSPrefix + 'bindinspector-inherited-val',
    componentKeyCls: Ext.baseCSSPrefix + 'bindinspector-comp-key',
    componentDescCls: Ext.baseCSSPrefix + 'bindinspector-comp-desc',
    componentValCls: Ext.baseCSSPrefix + 'bindinspector-comp-val',
    lastItemCls: Ext.baseCSSPrefix + 'bindinspector-last-item',
    
    vmPreviewDefault: {
        xtype: 'container',
        region: 'east',
        split: true,
        width: '50%',
        cls: Ext.baseCSSPrefix + 'bindinspector-prev-default',
        padding: 20,
        layout: {
            type: 'hbox',
            align: 'middle',
            pack: 'center'
        },
        items: [
            {
                xtype: 'component',
                flex: 1,
                html: 'Select a component with a ViewModel (or inherited ViewModel) from the ComponentList to view the ViewModel details'
            }
        ]
    },
    initComponent: function() {
        var me = this,
            parentCt = me.up('bindinspector-container'),
            comp = me.component || {},
            env = me.env,
            publishes = comp.publishes,
            bindings = comp.bindings,
            title = 'Bindings &nbsp;&nbsp;&nbsp;⇒ &nbsp;&nbsp;&nbsp;',
            vm = env.getInheritedVM(comp),
            bindData, publishesTbar;
        
        me.bindingsPreviewDefault = {
            xtype: 'panel',
            border: false,
            region: 'center',
            cls: Ext.baseCSSPrefix + 'bindinspector-prev-default',
            bodyPadding: 20,
            layout: {
                type: 'hbox',
                align: 'middle',
                pack: 'center'
            },
            items: [
                {
                    xtype: 'component',
                    flex: 1,
                    html: 'Select a component with bindings from the ComponentList to view the bindings details along with the component\'s inherited ViewModel'
                }
            ],
            dockedItems: [
                {
                    xtype: 'toolbar',
                    dock: 'top',
                    defaultButtonUI: 'default',
                    items: [
                        '->',
                        {
                            text: 'Open in dedicated tab',
                            handler: function(btn) {
                                btn.up('bindinspector-container').onComponentDblclick(null, comp);
                            }
                        }
                    ]
                }
            ]
        };
        
        if (comp.reference) {
            title += '[' + comp.reference + '] &bull; ';
        }
        
        bindData = comp.bindData || Ext.app.bindinspector.Util.buildBindData(bindings);
        
        if (publishes) {
            publishesTbar = [
                {
                    xtype: 'component',
                    html: 'Publishes: &nbsp;&nbsp;<span style="color: #5C5C5C;font-size: 14px;line-height: 14px;">' + Ext.Object.getKeys(publishes).join(' &bull; ') + '</span>'
                }
            ];
        }
        
        me.bindingsGrid = {
            xtype: 'gridpanel',
            title: title += comp.id,
            header: {
                items: [
                    {
                        xtype: 'button',
                        text: 'Open in dedicated tab',
                        ui: 'default-toolbar',
                        handler: function(btn) {
                            btn.up('bindinspector-container').onComponentDblclick(null, comp);
                        }
                    }
                ]
            },
            region: 'center',
            cls: Ext.baseCSSPrefix + 'bindinspector-compdetail-grid',
            flex: 1,
            hideHeaders: true,
            store: {
                model: me.BindingModel,
                data: bindData
            },
            columns: [
                {
                    flex: 1,
                    scope: me,
                    renderer: me.bindingRenderer
                }
            ],
            tbar: publishesTbar,
            bbar: [
                {
                    xtype: 'checkboxfield',
                    itemId: 'highlightToggle',
                    boxLabel: 'Highlight VM target nodes on binding selection',
                    checked: true,
                    listeners: {
                        scope: me,
                        change: me.onHighlightChange
                    }
                }
            ],
            viewConfig: {
                stripeRows: false,
                trackOver: false,
                getRowClass: function(record, index, rowParams, store) {
                    var cls = [];
                    if (index === store.getCount() - 1) {
                        cls.push(me.lastItemCls);
                    }
                    return cls.join(' ');
                }
            },
            listeners: {
                scope: me,
                cellclick: me.onCellClick,
                selectionchange: me.onSelectionChange
            }
        };
        
        me.viewModelTree = {
            xtype: 'bindinspector-viewmodeldetail',
            itemId: 'vm-' + vm.id,
            vm: vm,
            region: 'east',
            split: true,
            width: '50%',
            height: '50%'
        };
        me.items = [];
        me.items.push(bindings ? me.bindingsGrid : me.bindingsPreviewDefault);
        me.items.push(vm ? me.viewModelTree : me.vmPreviewDefault);
        me.callParent(arguments);
    },
    
    bindingRenderer: function(v, meta, rec) {
        var me = this,
            binding = rec.get('binding'),
            key = rec.get('key'),
            descriptor = me.descriptorRenderer(rec.get('descriptor'), meta, rec),
            value = Ext.app.bindinspector.Util.valueRenderer(rec.get('value')),
            src = '',
            bindingType = 'Direct';
        
        if (Ext.isEmpty(value) || value === 'null') {
            value = '<i>No value found</i>';
        }
        key = '<span class="' + me.componentKeyCls + '">' + key + ': </span>';
        descriptor = '<span class="' + me.componentDescCls + '">' + descriptor + '</span>';
        value = '<span class="' + me.componentValCls + '">' + value + '</span>';
        if (binding.isTemplateBinding) {
            bindingType = 'Template';
        } else if (binding.isMultiBinding) {
            bindingType = 'Multi';
        }
        bindingType = Ext.util.Format.format('<div data-qtip="Binding Type" data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" class="' + Ext.baseCSSPrefix + 'bindinspector-bind-type">{0}</div>', bindingType);
        return key + descriptor + '<br>' + value + bindingType;
    },
    
    
    
    onHighlightChange: function(field) {
        var vmDetail = this.down('bindinspector-viewmodeldetail'),
            compDetail = this.down('gridpanel'),
            selModel = compDetail.getSelectionModel();
        
        if (!field.checked) {
            this.onSelectionChange(selModel, null, true);
        } else {
            
            this.onSelectionChange(selModel, selModel.getSelection());
        }
    },
    
    
    onSelectionChange: function(selModel, selected, clear) {
        var vmDetail = this.down('bindinspector-viewmodeldetail'),
            store = vmDetail.getStore(),
            tokens, binding, highlight, store, root, targets, highlighted;
        if ((selected && selected.length === 0) || clear === true) {
            store.suspendEvents();
            vmDetail.getStore().getRootNode().cascadeBy({
                before: function(node) {
                    node.set('highlighted', false);
                }
            });
            store.resumeEvents();
            vmDetail.getView().refresh();
            return;
        }
        selected = selected[0];
        tokens = selected.get('tokens');
        binding = selected.get('binding');
        highlight = this.down('#highlightToggle').checked;
        store = vmDetail.getStore();
        root = store.getRootNode();
        targets = [];
        
        if (tokens && highlight) {
            if (binding.isTemplateBinding) {
                Ext.Array.forEach(tokens, function(token) {
                    targets.push(root.findChild('name', token[0]));
                }, this);
            } else if (binding.isMultiBinding) {} else 
            {
                targets.push(root.findChild('name', tokens[0]));
            }
        }
        store.suspendEvents();
        root.cascadeBy({
            before: function(node) {
                highlighted = targets.length === 0;
                Ext.Array.forEach(targets, function(target) {
                    if (node === target || node.isAncestor(target)) {
                        highlighted = true;
                    }
                });
                
                
                
                node.set('highlighted', highlighted || -1);
            }
        });
        store.resumeEvents();
        vmDetail.getView().refresh();
        
        vmDetail.getSelectionModel().select(targets[0]);
    },
    
    
    onCellClick: function(view, cell, colIdx, record, row, rowIdx, e) {
        var target = e.getTarget('.' + this.activeCls),
            path;
        if (target) {
            path = target.getAttribute('data-path');
            this.showPath(path);
        }
    },
    showPath: function(path) {
        this.selectPath(this.down('bindinspector-viewmodeldetail'), path);
    },
    
    
    selectPath: function(tab, path) {
        var node = tab.getRootNode(),
            parts = path.split('.'),
            len = parts.length,
            i;
        for (i = 0; node && i < len; ++i) {
            node = this.getChildByKey(node, parts[i]);
        }
        if (node) {
            tab.getSelectionModel().select(node);
        }
    },
    getChildByKey: function(node, key) {
        var childNodes = node.childNodes;
        if (childNodes) {
            return Ext.Array.findBy(childNodes, function(child) {
                return child.get('name') === key;
            });
        }
        return null;
    },
    
    descriptorRenderer: function(v, meta, rec) {
        var binding = rec.get('binding'),
            descriptor = rec.get('descriptor'),
            tokens = rec.get('tokens');
        v = v || '';
        if (binding.isTemplateBinding) {
            Ext.Array.forEach(tokens, function(token) {
                var tokenRe = new RegExp('{' + token.join('\\.') + '}','g');
                v = v.replace(tokenRe, this.parseTokens(token));
            }, this);
        } else if (binding.isMultiBinding) {} else 
        {
            return v.replace(descriptor, this.parseTokens(tokens));
        }
        return Ext.String.htmlEncode(v);
    },
    
    parseTokens: function(tokens) {
        var me = this,
            out = [],
            vm = me.env.getInheritedVM(me.component),
            currPath = '',
            currParent = vm.rootStub,
            direct = false,
            inherited = false,
            addlCls = '',
            tip = '',
            baseToken = '',
            ownerVms, len, vmPlural;
        tokens = tokens || [];
        Ext.Array.forEach(tokens, function(token) {
            var stub = Ext.app.bindinspector.Util.getChildStub(token, currParent),
                cls = '',
                value;
            if (stub) {
                value = stub.value;
                if (value !== undefined) {
                    cls = me.activeCls;
                }
            } else {}
            
            out.push('<span data-path="' + currPath + token + '" class="stub ' + cls + '">' + token + '</span>');
            currPath += token + '.';
            currParent = stub;
        }, me);
        
        
        if (tokens[0]) {
            baseToken = tokens[0];
            ownerVMs = vm.dataMap[tokens[0]].ownerVMs;
            len = ownerVMs.length;
            Ext.Array.forEach(ownerVMs, function(v) {
                if (v.id === vm.id) {
                    direct = true;
                }
                if (v.id !== vm.id) {
                    inherited = true;
                }
            });
        }
        
        
        if (direct && inherited) {
            addlCls += ' ' + me.multipleCls;
            vmPlural = len > 1 ? 'VMs' : 'VM';
            tip = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="<b>' + baseToken + '</b>&nbsp; provided by this VM and ' + (len - 1) + ' ancestor ' + vmPlural + '"';
        } else if (direct) {
            addlCls += ' ' + me.directCls;
            tip = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="<b>' + baseToken + '</b>&nbsp; is provided by this VM"';
        } else if (inherited) {
            addlCls += ' ' + me.inheritedCls;
            vmPlural = len > 1 ? 'VMs' : 'VM';
            tip = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="<b>' + baseToken + '</b>&nbsp; is provided by ' + len + ' ancestor ' + vmPlural + '"';
        }
        return '<span ' + tip + 'class="' + me.descriptorCls + addlCls + '">{' + out.join('.') + '}</span>';
    }
}, function() {
    this.prototype.BindingModel = Ext.define(null, {
        extend: 'Ext.data.Model',
        fields: [
            'key',
            'descriptor',
            'tokens',
            'value',
            'binding'
        ]
    });
});


Ext.define('Ext.tree.View', {
    extend: 'Ext.view.Table',
    alias: 'widget.treeview',
    config: {
        selectionModel: {
            type: 'treemodel'
        }
    },
    
    isTreeView: true,
    loadingCls: Ext.baseCSSPrefix + 'grid-tree-loading',
    expandedCls: Ext.baseCSSPrefix + 'grid-tree-node-expanded',
    leafCls: Ext.baseCSSPrefix + 'grid-tree-node-leaf',
    expanderSelector: '.' + Ext.baseCSSPrefix + 'tree-expander',
    checkboxSelector: '.' + Ext.baseCSSPrefix + 'tree-checkbox',
    expanderIconOverCls: Ext.baseCSSPrefix + 'tree-expander-over',
    
    
    
    nodeAnimWrapCls: Ext.baseCSSPrefix + 'tree-animator-wrap',
    ariaRole: 'tree',
    
    loadMask: false,
    
    rootVisible: true,
    
    expandDuration: 250,
    collapseDuration: 250,
    toggleOnDblClick: true,
    stripeRows: false,
    
    uiFields: [
        'expanded',
        'loaded',
        'checked',
        'expandable',
        'leaf',
        'icon',
        'iconCls',
        'loading',
        'qtip',
        'qtitle'
    ],
    
    treeRowTpl: [
        '{%',
        'this.processRowValues(values);',
        'this.nextTpl.applyOut(values, out, parent);',
        '%}',
        {
            priority: 10,
            processRowValues: function(rowValues) {
                var record = rowValues.record,
                    view = rowValues.view;
                
                
                
                rowValues.rowAttr['data-qtip'] = record.get('qtip') || '';
                rowValues.rowAttr['data-qtitle'] = record.get('qtitle') || '';
                if (record.isExpanded()) {
                    rowValues.rowClasses.push(view.expandedCls);
                }
                if (record.isLeaf()) {
                    rowValues.rowClasses.push(view.leafCls);
                }
                if (record.isLoading()) {
                    rowValues.rowClasses.push(view.loadingCls);
                }
            }
        }
    ],
    
    
    
    initComponent: function() {
        var me = this;
        if (me.bufferedRenderer) {
            me.animate = false;
        } else if (me.initialConfig.animate === undefined) {
            me.animate = Ext.enableFx;
        }
        me.store = me.panel.getStore();
        me.onRootChange(me.store.getRoot());
        me.animQueue = {};
        me.animWraps = {};
        me.callParent(arguments);
        me.store.setRootVisible(me.rootVisible);
        me.addRowTpl(Ext.XTemplate.getTpl(me, 'treeRowTpl'));
    },
    onFillComplete: function(treeStore, fillRoot, newNodes) {
        var me = this,
            store = me.store,
            start = store.indexOf(newNodes[0]);
        
        
        fillRoot.triggerUIUpdate();
        
        
        if (!newNodes.length || start === -1) {
            return;
        }
        
        me.onAdd(me.store, newNodes, start);
        me.refreshPartner();
    },
    onBeforeSort: function() {
        this.store.suspendEvents();
    },
    onSort: function(o) {
        
        
        if (o.isStore) {
            this.store.resumeEvents();
            this.refresh();
            this.refreshPartner();
        }
    },
    refreshPartner: function() {
        var partner = this.lockingPartner;
        if (partner) {
            partner.refresh();
        }
    },
    getMaskStore: function() {
        return this.panel.getStore();
    },
    afterRender: function() {
        var me = this;
        me.callParent(arguments);
        me.el.on({
            scope: me,
            delegate: me.expanderSelector,
            mouseover: me.onExpanderMouseOver,
            mouseout: me.onExpanderMouseOut,
            click: {
                delegate: me.checkboxSelector,
                fn: me.onCheckboxChange,
                scope: me
            }
        });
    },
    afterComponentLayout: function() {
        var me = this,
            stretcher = me.stretcher,
            scroller = me.getScrollable();
        me.callParent(arguments);
        if (stretcher) {
            stretcher.setWidth((this.getWidth() - Ext.getScrollbarSize().width));
        }
        if (scroller && !me.bufferedRenderer) {
            scroller.refresh();
        }
    },
    processUIEvent: function(e) {
        
        
        
        if (e.getTarget('.' + this.nodeAnimWrapCls, this.el)) {
            return false;
        }
        return this.callParent(arguments);
    },
    setRootNode: function(node) {
        this.node = node;
    },
    onCheckboxChange: function(e, t) {
        var me = this,
            item = e.getTarget(me.getItemSelector(), me.getTargetEl());
        if (item) {
            me.onCheckChange(me.getRecord(item));
        }
    },
    onCheckChange: function(record) {
        var checked = record.get('checked');
        if (Ext.isBoolean(checked)) {
            checked = !checked;
            record.set('checked', checked);
            this.fireEvent('checkchange', record, checked);
        }
    },
    getChecked: function() {
        var checked = [];
        this.node.cascadeBy(function(rec) {
            if (rec.get('checked')) {
                checked.push(rec);
            }
        });
        return checked;
    },
    isItemChecked: function(rec) {
        return rec.get('checked');
    },
    
    createAnimWrap: function(record, index) {
        var me = this,
            node = me.getNode(record),
            tmpEl;
        tmpEl = Ext.fly(node).insertSibling({
            role: 'presentation',
            tag: 'div',
            cls: me.nodeAnimWrapCls
        }, 'after');
        return {
            record: record,
            node: node,
            el: tmpEl,
            expanding: false,
            collapsing: false,
            animateEl: tmpEl,
            targetEl: tmpEl
        };
    },
    
    getAnimWrap: function(parent, bubble) {
        if (!this.animate) {
            return null;
        }
        var wraps = this.animWraps,
            wrap = wraps[parent.internalId];
        if (bubble !== false) {
            while (!wrap && parent) {
                parent = parent.parentNode;
                if (parent) {
                    wrap = wraps[parent.internalId];
                }
            }
        }
        return wrap;
    },
    doAdd: function(records, index) {
        var me = this,
            record = records[0],
            parent = record.parentNode,
            all = me.all,
            relativeIndex,
            animWrap = me.getAnimWrap(parent),
            targetEl, children, len, nodes;
        if (!animWrap || !animWrap.expanding) {
            return me.callParent(arguments);
        }
        
        
        nodes = me.bufferRender(records, index, true);
        
        parent = animWrap.record;
        
        targetEl = animWrap.targetEl;
        children = targetEl.dom.childNodes;
        len = children.length;
        
        relativeIndex = index - me.indexInStore(parent) - 1;
        
        
        if (!len || relativeIndex >= len) {
            targetEl.appendChild(nodes, true);
        } else 
        
        {
            Ext.fly(children[relativeIndex]).insertSibling(nodes, 'before', true);
        }
        
        all.insert(index, nodes.childrenArray);
        return nodes.childrenArray;
    },
    onRemove: function(ds, records, index) {
        var me = this,
            empty, i;
        if (me.viewReady) {
            empty = me.store.getCount() === 0;
            
            if (me.bufferedRenderer) {
                return me.callParent(arguments);
            }
            
            if (empty) {
                me.refresh();
            } else {
                
                for (i = records.length - 1 , index += i; i >= 0; --i , --index) {
                    me.doRemove(records[i], index);
                }
                me.refreshSizePending = true;
            }
            
            if (me.hasListeners.itemremove) {
                for (i = records.length - 1 , index += i; i >= 0; --i , --index) {
                    me.fireEvent('itemremove', records[i], index, me);
                }
            }
        }
    },
    doRemove: function(record, index) {
        
        
        var me = this,
            all = me.all,
            animWrap = me.getAnimWrap(record),
            item = all.item(index),
            node = item ? item.dom : null;
        if (!node || !animWrap || !animWrap.collapsing) {
            return me.callParent(arguments);
        }
        
        
        animWrap.targetEl.dom.insertBefore(node, animWrap.targetEl.dom.firstChild);
        all.removeElement(index);
    },
    onBeforeExpand: function(parent, records, index) {
        var me = this,
            animWrap;
        if (me.rendered && me.all.getCount() && me.animate) {
            if (me.getNode(parent)) {
                animWrap = me.getAnimWrap(parent, false);
                if (!animWrap) {
                    animWrap = me.animWraps[parent.internalId] = me.createAnimWrap(parent);
                    animWrap.animateEl.setHeight(0);
                } else if (animWrap.collapsing) {
                    
                    
                    animWrap.targetEl.select(me.itemSelector).destroy();
                }
                animWrap.expanding = true;
                animWrap.collapsing = false;
            }
        }
    },
    onExpand: function(parent) {
        var me = this,
            queue = me.animQueue,
            id = parent.getId(),
            node = me.getNode(parent),
            index = node ? me.indexOf(node) : -1,
            animWrap, animateEl, targetEl;
        if (me.singleExpand) {
            me.ensureSingleExpand(parent);
        }
        
        if (index === -1) {
            return;
        }
        animWrap = me.getAnimWrap(parent, false);
        if (!animWrap) {
            parent.isExpandingOrCollapsing = false;
            me.fireEvent('afteritemexpand', parent, index, node);
            return;
        }
        animateEl = animWrap.animateEl;
        targetEl = animWrap.targetEl;
        animateEl.stopAnimation();
        queue[id] = true;
        
        
        
        Ext.on('idle', function() {
            animateEl.dom.style.height = '0px';
        }, null, {
            single: true
        });
        animateEl.animate({
            from: {
                height: 0
            },
            to: {
                height: targetEl.dom.scrollHeight
            },
            duration: me.expandDuration,
            listeners: {
                afteranimate: function() {
                    
                    
                    
                    var items = targetEl.dom.childNodes,
                        activeEl = Ext.Element.getActiveElement();
                    if (items.length) {
                        if (!targetEl.contains(activeEl)) {
                            activeEl = null;
                        }
                        animWrap.el.insertSibling(items, 'before', true);
                        if (activeEl) {
                            activeEl.focus();
                        }
                    }
                    animWrap.el.destroy();
                    me.animWraps[animWrap.record.internalId] = queue[id] = null;
                }
            },
            callback: function() {
                parent.isExpandingOrCollapsing = false;
                if (!me.isDestroyed) {
                    me.refreshSize(true);
                }
                me.fireEvent('afteritemexpand', parent, index, node);
            }
        });
    },
    
    onBeforeCollapse: function(parent, records, index, callback, scope) {
        var me = this,
            animWrap;
        if (me.rendered && me.all.getCount()) {
            if (me.animate) {
                
                
                
                if (parent.isVisible()) {
                    animWrap = me.getAnimWrap(parent);
                    if (!animWrap) {
                        animWrap = me.animWraps[parent.internalId] = me.createAnimWrap(parent, index);
                    } else if (animWrap.expanding) {
                        
                        
                        animWrap.targetEl.select(this.itemSelector).destroy();
                    }
                    animWrap.expanding = false;
                    animWrap.collapsing = true;
                    animWrap.callback = callback;
                    animWrap.scope = scope;
                }
            } else {
                
                me.onCollapseCallback = callback;
                me.onCollapseScope = scope;
            }
        }
    },
    onCollapse: function(parent) {
        var me = this,
            queue = me.animQueue,
            id = parent.getId(),
            node = me.getNode(parent),
            index = node ? me.indexOf(node) : -1,
            animWrap = me.getAnimWrap(parent),
            animateEl;
        
        
        
        if (!me.all.getCount() || !parent.isVisible()) {
            return;
        }
        
        if (!animWrap) {
            parent.isExpandingOrCollapsing = false;
            me.fireEvent('afteritemcollapse', parent, index, node);
            
            Ext.callback(me.onCollapseCallback, me.onCollapseScope);
            me.onCollapseCallback = me.onCollapseScope = null;
            return;
        }
        animateEl = animWrap.animateEl;
        queue[id] = true;
        animateEl.stopAnimation();
        animateEl.animate({
            to: {
                height: 0
            },
            duration: me.collapseDuration,
            listeners: {
                afteranimate: function() {
                    
                    animWrap.el.destroy();
                    me.animWraps[animWrap.record.internalId] = queue[id] = null;
                }
            },
            callback: function() {
                parent.isExpandingOrCollapsing = false;
                if (!me.isDestroyed) {
                    me.refreshSize(true);
                }
                me.fireEvent('afteritemcollapse', parent, index, node);
                
                Ext.callback(animWrap.callback, animWrap.scope);
                animWrap.callback = animWrap.scope = null;
            }
        });
    },
    
    isAnimating: function(node) {
        return !!this.animQueue[node.getId()];
    },
    
    expand: function(record, deep, callback, scope) {
        var me = this,
            doAnimate = !!me.animate,
            result;
        
        if (!doAnimate || !record.isExpandingOrCollapsing) {
            if (!record.isLeaf()) {
                record.isExpandingOrCollapsing = doAnimate;
            }
            
            
            
            Ext.suspendLayouts();
            result = record.expand(deep, callback, scope);
            Ext.resumeLayouts(true);
            return result;
        }
    },
    
    collapse: function(record, deep, callback, scope) {
        var me = this,
            doAnimate = !!me.animate;
        
        if (!doAnimate || !record.isExpandingOrCollapsing) {
            if (!record.isLeaf()) {
                record.isExpandingOrCollapsing = doAnimate;
            }
            return record.collapse(deep, callback, scope);
        }
    },
    
    toggle: function(record, deep, callback, scope) {
        if (record.isExpanded()) {
            this.collapse(record, deep, callback, scope);
        } else {
            this.expand(record, deep, callback, scope);
        }
    },
    onItemDblClick: function(record, item, index) {
        var me = this,
            editingPlugin = me.editingPlugin;
        me.callParent(arguments);
        if (me.toggleOnDblClick && record.isExpandable() && !(editingPlugin && editingPlugin.clicksToEdit === 2)) {
            me.toggle(record);
        }
    },
    onBeforeItemMouseDown: function(record, item, index, e) {
        if (e.getTarget(this.expanderSelector, item)) {
            return false;
        }
        return this.callParent(arguments);
    },
    onItemClick: function(record, item, index, e) {
        if (e.getTarget(this.expanderSelector, item) && record.isExpandable()) {
            this.toggle(record, e.ctrlKey);
            return false;
        }
        return this.callParent(arguments);
    },
    onExpanderMouseOver: function(e, t) {
        e.getTarget(this.cellSelector, 10, true).addCls(this.expanderIconOverCls);
    },
    onExpanderMouseOut: function(e, t) {
        e.getTarget(this.cellSelector, 10, true).removeCls(this.expanderIconOverCls);
    },
    getStoreListeners: function() {
        var me = this,
            result = Ext.apply(me.callParent(), {
                rootchange: me.onRootChange,
                fillcomplete: me.onFillComplete
            });
        if (!this.getStore().remoteSort) {
            Ext.apply(result, {
                beforesort: me.onBeforeSort,
                sort: me.onSort
            });
        }
        return result;
    },
    onBindStore: function(store, initial, propName, oldStore) {
        var oldRoot = oldStore && oldStore.getRootNode(),
            newRoot = store && store.getRootNode();
        this.callParent(arguments);
        
        
        
        if (newRoot !== oldRoot) {
            this.onRootChange(newRoot, oldRoot);
        }
    },
    onRootChange: function(newRoot, oldRoot) {
        var me = this;
        if (oldRoot) {
            me.rootListeners.destroy();
            me.rootListeners = null;
        }
        if (newRoot) {
            me.rootListeners = newRoot.on({
                beforeexpand: me.onBeforeExpand,
                expand: me.onExpand,
                beforecollapse: me.onBeforeCollapse,
                collapse: me.onCollapse,
                destroyable: true,
                scope: me
            });
        }
    },
    ensureSingleExpand: function(node) {
        var parent = node.parentNode;
        if (parent) {
            parent.eachChild(function(child) {
                if (child !== node && child.isExpanded()) {
                    child.collapse();
                }
            });
        }
    },
    shouldUpdateCell: function(record, column, changedFieldNames) {
        if (changedFieldNames) {
            var i = 0,
                len = changedFieldNames.length;
            for (; i < len; ++i) {
                if (Ext.Array.contains(this.uiFields, changedFieldNames[i])) {
                    return true;
                }
            }
        }
        return this.callParent(arguments);
    }
});


Ext.define('Ext.selection.RowModel', {
    extend: 'Ext.selection.DataViewModel',
    alias: 'selection.rowmodel',
    requires: [
        'Ext.grid.CellContext'
    ],
    
    enableKeyNav: true,
    
    
    
    
    isRowModel: true,
    
    deselectOnContainerClick: false,
    onUpdate: function(record) {
        var me = this,
            view = me.view,
            index;
        if (view && me.isSelected(record)) {
            index = view.indexOf(record);
            view.onRowSelect(index);
            if (record === me.lastFocused) {
                view.onRowFocus(index, true);
            }
        }
    },
    
    
    
    getRowsVisible: function() {
        var rowsVisible = false,
            view = this.view,
            firstRow = view.all.first(),
            rowHeight, gridViewHeight;
        if (firstRow) {
            rowHeight = firstRow.getHeight();
            gridViewHeight = view.el.getHeight();
            rowsVisible = Math.floor(gridViewHeight / rowHeight);
        }
        return rowsVisible;
    },
    
    
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            views = me.views || [
                me.view
            ],
            viewsLn = views.length,
            recordIndex = me.store.indexOf(record),
            eventName = isSelected ? 'select' : 'deselect',
            i, view;
        if ((suppressEvent || me.fireEvent('before' + eventName, me, record, recordIndex)) !== false && commitFn() !== false) {
            
            for (i = 0; i < viewsLn; i++) {
                view = views[i];
                recordIndex = view.indexOf(record);
                
                
                if (view.indexOf(record) !== -1) {
                    if (isSelected) {
                        view.onRowSelect(recordIndex, suppressEvent);
                    } else {
                        view.onRowDeselect(recordIndex, suppressEvent);
                    }
                }
            }
            if (!suppressEvent) {
                me.fireEvent(eventName, me, record, recordIndex);
            }
        }
    },
    onEditorTab: function(editingPlugin, e) {
        var me = this,
            view = editingPlugin.context.view,
            record = editingPlugin.getActiveRecord(),
            position = editingPlugin.context,
            direction = e.shiftKey ? 'left' : 'right',
            lastPos;
        
        
        
        
        do {
            lastPos = position;
            position = view.walkCells(position, direction, e, me.preventWrap);
            if (lastPos && lastPos.isEqual(position)) {
                
                
                
                return;
            }
        } while (position && (!position.column.getEditor(record) || !editingPlugin.startEditByPosition(position)));
    },
    
    getCurrentPosition: function() {
        var firstSelection = this.selected.getAt(0);
        if (firstSelection) {
            return new Ext.grid.CellContext(this.view).setPosition(this.store.indexOf(firstSelection), 0);
        }
    },
    selectByPosition: function(position, keepExisting) {
        if (!position.isCellContext) {
            position = new Ext.grid.CellContext(this.view).setPosition(position.row, position.column);
        }
        this.select(position.record, keepExisting);
    },
    
    selectNext: function(keepExisting, suppressEvent) {
        var me = this,
            store = me.store,
            selection = me.getSelection(),
            record = selection[selection.length - 1],
            index = me.view.indexOf(record) + 1,
            success;
        if (index === store.getCount() || index === 0) {
            success = false;
        } else {
            me.doSelect(index, keepExisting, suppressEvent);
            success = true;
        }
        return success;
    },
    
    selectPrevious: function(keepExisting, suppressEvent) {
        var me = this,
            selection = me.getSelection(),
            record = selection[0],
            index = me.view.indexOf(record) - 1,
            success;
        if (index < 0) {
            success = false;
        } else {
            me.doSelect(index, keepExisting, suppressEvent);
            success = true;
        }
        return success;
    },
    isRowSelected: function(record) {
        return this.isSelected(record);
    },
    isCellSelected: function(view, record, columnHeader) {
        return this.isSelected(record);
    }
});


Ext.define('Ext.selection.TreeModel', {
    extend: 'Ext.selection.RowModel',
    alias: 'selection.treemodel',
    
    
    selectOnExpanderClick: false,
    constructor: function(config) {
        var me = this;
        me.callParent([
            config
        ]);
        
        
        
        if (me.pruneRemoved) {
            me.pruneRemoved = false;
            me.pruneRemovedNodes = true;
        }
    },
    getStoreListeners: function() {
        var me = this,
            result = me.callParent();
        result.noderemove = me.onNodeRemove;
        return result;
    },
    onNodeRemove: function(parent, node, isMove) {
        
        if (!isMove) {
            var toDeselect = [];
            this.gatherSelected(node, toDeselect);
            if (toDeselect.length) {
                this.deselect(toDeselect);
            }
        }
    },
    
    
    
    
    pruneRemovedOnRefresh: function() {
        return this.pruneRemovedNodes;
    },
    vetoSelection: function(e) {
        var view = this.view,
            select = this.selectOnExpanderClick,
            veto = !select && e.type === 'click' && e.getTarget(view.expanderSelector || (view.lockingPartner && view.lockingPartner.expanderSelector));
        return veto || this.callParent([
            e
        ]);
    },
    privates: {
        gatherSelected: function(node, toDeselect) {
            var childNodes = node.childNodes,
                i, len, child;
            if (this.selected.containsKey(node.id)) {
                toDeselect.push(node);
            }
            if (childNodes) {
                for (i = 0 , len = childNodes.length; i < len; ++i) {
                    child = childNodes[i];
                    this.gatherSelected(child, toDeselect);
                }
            }
        }
    }
});


Ext.define('Ext.grid.ColumnLayout', {
    extend: 'Ext.layout.container.HBox',
    alias: 'layout.gridcolumn',
    type: 'gridcolumn',
    requires: [
        'Ext.panel.Table'
    ],
    firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first',
    lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last',
    initLayout: function() {
        this.callParent();
        if (this.scrollbarWidth === undefined) {
            this.self.prototype.scrollbarWidth = Ext.getScrollbarSize().width;
        }
    },
    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            view = owner.grid ? owner.grid.getView() : null,
            firstCls = me.firstHeaderCls,
            lastCls = me.lastHeaderCls,
            bothCls = [
                firstCls,
                lastCls
            ],
            items = me.getVisibleItems(),
            len = items.length,
            i, item;
        if (view && view.scrollFlags.x) {
            me.viewScrollX = view.getScrollX();
            owner.suspendEvent('scroll');
            view.suspendEvent('scroll');
        }
        me.callParent([
            ownerContext
        ]);
        
        for (i = 0; i < len; i++) {
            item = items[i];
            if (len === 1) {
                
                item.addCls(bothCls);
            } else if (i === 0) {
                
                item.addCls(firstCls);
                item.removeCls(lastCls);
            } else if (i === len - 1) {
                
                item.removeCls(firstCls);
                item.addCls(lastCls);
            } else {
                item.removeCls(bothCls);
            }
        }
        
        
        
        me.scrollbarWidth = 0;
        if (owner.isRootHeader) {
            me.determineScrollbarWidth(ownerContext);
        }
        if (!me.scrollbarWidth) {
            
            
            
            ownerContext.manageScrollbar = false;
        }
    },
    moveItemBefore: function(item, before) {
        var prevOwner = item.ownerCt;
        
        
        
        
        
        if (item !== before && prevOwner) {
            prevOwner.remove(item, false);
        }
        return this.callParent([
            item,
            before
        ]);
    },
    determineScrollbarWidth: function(ownerContext) {
        var me = this,
            owner = me.owner,
            grid = owner.grid,
            
            
            
            vetoReserveScrollbar = owner.reserveScrollbar === false,
            
            
            
            reserveScrollbar = grid.reserveScrollbar && !vetoReserveScrollbar,
            manageScrollbar = !reserveScrollbar && !vetoReserveScrollbar && grid.view.scrollFlags.y;
        
        
        ownerContext.manageScrollbar = manageScrollbar;
        
        
        
        
        if (!grid.ownerGrid.collapsed && (reserveScrollbar || manageScrollbar)) {
            
            
            
            delete me.scrollbarWidth;
        }
    },
    
    
    calculate: function(ownerContext) {
        var me = this,
            grid = me.owner.grid,
            
            
            viewContext = ownerContext.viewContext,
            state = ownerContext.state,
            context = ownerContext.context,
            lockingPartnerContext, ownerGrid, columnsChanged, columns, len, i, column, scrollbarAdjustment, viewOverflowY;
        me.callParent([
            ownerContext
        ]);
        if (grid && state.parallelDone) {
            lockingPartnerContext = viewContext.lockingPartnerContext;
            ownerGrid = grid.ownerGrid;
            
            
            if (ownerGrid.forceFit && !state.reflexed) {
                if (me.convertWidthsToFlexes(ownerContext)) {
                    me.cacheFlexes(ownerContext);
                    me.done = false;
                    ownerContext.invalidate({
                        state: {
                            reflexed: true,
                            scrollbarAdjustment: me.getScrollbarAdjustment(ownerContext)
                        }
                    });
                    return;
                }
            }
            
            
            if ((columnsChanged = state.columnsChanged) === undefined) {
                columns = ownerContext.target.getVisibleGridColumns();
                columnsChanged = false;
                for (i = 0 , len = columns.length; i < len; i++) {
                    column = context.getCmp(columns[i]);
                    
                    
                    if (!column.lastBox || column.props.width !== column.lastBox.width) {
                        (columnsChanged || (columnsChanged = []))[i] = column;
                    }
                }
                state.columnsChanged = columnsChanged;
                
                ownerContext.setProp('columnsChanged', columnsChanged);
            }
            if (ownerContext.manageScrollbar) {
                
                
                
                scrollbarAdjustment = me.getScrollbarAdjustment(ownerContext);
                if (scrollbarAdjustment) {
                    
                    
                    viewOverflowY = viewContext.getProp('viewOverflowY');
                    if (viewOverflowY === undefined) {
                        
                        me.done = false;
                        return;
                    }
                    if (!viewOverflowY) {
                        
                        
                        
                        if (lockingPartnerContext) {
                            
                            
                            
                            lockingPartnerContext.invalidate();
                            lockingPartnerContext.headerContext.invalidate();
                        }
                        viewContext.invalidate();
                        ownerContext.invalidate({
                            state: {
                                
                                
                                
                                
                                
                                scrollbarAdjustment: 0
                            }
                        });
                    }
                }
            }
        }
    },
    
    
    
    
    finishedLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            view = owner.grid ? owner.grid.getView() : null,
            viewScrollX = me.viewScrollX;
        me.callParent([
            ownerContext
        ]);
        
        
        
        
        
        if (view && view.scrollFlags.x) {
            if (viewScrollX !== undefined && owner.tooNarrow && owner.componentLayoutCounter) {
                owner.setScrollX(viewScrollX);
            }
            view.resumeEvent('scroll');
            owner.resumeEvent('scroll');
        }
    },
    convertWidthsToFlexes: function(ownerContext) {
        var me = this,
            totalWidth = 0,
            calculated = me.sizeModels.calculated,
            childItems, len, i, childContext, item;
        childItems = ownerContext.childItems;
        len = childItems.length;
        for (i = 0; i < len; i++) {
            childContext = childItems[i];
            item = childContext.target;
            totalWidth += childContext.props.width;
            
            if (!(item.fixed || item.resizable === false)) {
                
                
                item.flex = ownerContext.childItems[i].flex = childContext.props.width;
                item.width = null;
                childContext.widthModel = calculated;
            }
        }
        
        return totalWidth !== ownerContext.props.width;
    },
    getScrollbarAdjustment: function(ownerContext) {
        var me = this,
            state = ownerContext.state,
            grid = me.owner.grid,
            scrollbarAdjustment = state.scrollbarAdjustment;
        
        
        
        if (scrollbarAdjustment === undefined) {
            scrollbarAdjustment = 0;
            if (grid.reserveScrollbar || (ownerContext.manageScrollbar && !grid.ownerGrid.layout.ownerContext.heightModel.shrinkWrap)) {
                scrollbarAdjustment = me.scrollbarWidth;
            }
            state.scrollbarAdjustment = scrollbarAdjustment;
        }
        return scrollbarAdjustment;
    },
    
    getContainerSize: function(ownerContext) {
        var me = this,
            got, needed, padding, gotWidth, gotHeight, width, height, result;
        if (me.owner.isRootHeader) {
            result = me.callParent([
                ownerContext
            ]);
            if (result.gotWidth) {
                result.width -= me.getScrollbarAdjustment(ownerContext);
            }
        } else {
            padding = ownerContext.paddingContext.getPaddingInfo();
            got = needed = 0;
            
            
            if (!ownerContext.widthModel.shrinkWrap) {
                ++needed;
                width = ownerContext.getProp('innerWidth');
                gotWidth = (typeof width === 'number');
                if (gotWidth) {
                    ++got;
                    width -= padding.width;
                    if (width < 0) {
                        width = 0;
                    }
                }
            }
            if (!ownerContext.heightModel.shrinkWrap) {
                ++needed;
                height = ownerContext.getProp('innerHeight');
                gotHeight = (typeof height === 'number');
                if (gotHeight) {
                    ++got;
                    height -= padding.height;
                    if (height < 0) {
                        height = 0;
                    }
                }
            }
            return {
                width: width,
                height: height,
                needed: needed,
                got: got,
                gotAll: got == needed,
                gotWidth: gotWidth,
                gotHeight: gotHeight
            };
        }
        return result;
    },
    publishInnerCtSize: function(ownerContext) {
        var me = this,
            owner = me.owner,
            cw = ownerContext.peek('contentWidth'),
            adjustment = 0;
        
        if (cw != null && owner.isRootHeader) {
            adjustment = -ownerContext.state.scrollbarAdjustment;
        }
        return me.callParent([
            ownerContext,
            adjustment
        ]);
    }
});


Ext.define('Ext.dd.DragTracker', {
    uses: [
        'Ext.util.Region'
    ],
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    active: false,
    
    
    trackOver: false,
    
    
    
    tolerance: 5,
    
    autoStart: false,
    
    
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);
        me.dragRegion = new Ext.util.Region(0,0,0,0);
        if (me.el) {
            me.initEl(me.el);
        }
        
        me.mixins.observable.constructor.call(me);
        if (me.disabled) {
            me.disable();
        }
    },
    
    initEl: function(el) {
        var me = this,
            delegate = me.delegate;
        me.el = el = Ext.get(el);
        
        if (delegate && delegate.isElement) {
            me.handle = delegate;
        }
        
        me.delegate = me.handle ? undefined : me.delegate;
        if (!me.handle) {
            me.handle = el;
        }
        
        
        me.handleListeners = {
            scope: me,
            delegate: me.delegate,
            mousedown: me.onMouseDown,
            dragstart: me.onDragStart
        };
        
        
        
        if (!Ext.supports.TouchEvents && (me.trackOver || me.overCls)) {
            Ext.apply(me.handleListeners, {
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut
            });
        }
        me.mon(me.handle, me.handleListeners);
    },
    disable: function() {
        this.disabled = true;
    },
    enable: function() {
        this.disabled = false;
    },
    destroy: function() {
        var me = this;
        
        me.endDrag({});
        me.clearListeners();
        me.el = me.handle = null;
    },
    
    
    onMouseOver: function(e, target) {
        var me = this,
            handleCls, el, i, len, cls;
        if (!me.disabled) {
            
            
            
            
            
            if (e.within(e.target, true, true) || me.delegate) {
                handleCls = me.handleCls;
                me.mouseIsOut = false;
                if (handleCls) {
                    for (i = 0 , len = me.handleEls.length; i < len; i++) {
                        el = me.handleEls[i];
                        cls = el.delegateCls;
                        if (!cls) {
                            cls = el.delegateCls = [
                                handleCls,
                                '-',
                                el.region,
                                '-over'
                            ].join('');
                        }
                        el.addCls([
                            cls,
                            me.overCls
                        ]);
                    }
                }
                me.fireEvent('mouseover', me, e, me.delegate ? e.getTarget(me.delegate, target) : me.handle);
            }
        }
    },
    
    
    onMouseOut: function(e) {
        var me = this,
            el, i, len;
        if (me.mouseIsDown) {
            me.mouseIsOut = true;
        } else {
            if (me.handleCls) {
                for (i = 0 , len = me.handleEls.length; i < len; i++) {
                    el = me.handleEls[i];
                    el.removeCls([
                        el.delegateCls,
                        me.overCls
                    ]);
                }
            }
            me.fireEvent('mouseout', me, e);
        }
    },
    onMouseDown: function(e, target) {
        var me = this;
        
        if (me.disabled || e.dragTracked) {
            return;
        }
        
        me.dragTarget = me.delegate ? target : me.handle.dom;
        me.startXY = me.lastXY = e.getXY();
        me.startRegion = Ext.fly(me.dragTarget).getRegion();
        if (me.fireEvent('mousedown', me, e) === false || me.fireEvent('beforedragstart', me, e) === false || me.onBeforeStart(e) === false) {
            return;
        }
        
        
        me.mouseIsDown = true;
        
        e.dragTracked = true;
        
        me.el.setCapture();
        e.stopPropagation();
        if (me.preventDefault !== false) {
            e.preventDefault();
        }
        Ext.getDoc().on({
            scope: me,
            capture: true,
            mouseup: me.onMouseUp,
            mousemove: me.onMouseMove,
            selectstart: me.stopSelect
        });
        
        
        
        me.dragEnded = false;
        if (!me.tolerance) {
            me.triggerStart();
        } else if (me.autoStart) {
            me.timer = Ext.defer(me.triggerStart, me.autoStart === true ? 1000 : me.autoStart, me, [
                e
            ]);
        }
    },
    onMouseMove: function(e, target) {
        var me = this,
            xy = e.getXY(),
            s = me.startXY;
        e.stopPropagation();
        if (me.preventDefault !== false) {
            e.preventDefault();
        }
        
        
        
        if (me.dragEnded) {
            return;
        }
        me.lastXY = xy;
        if (!me.active) {
            if (Math.max(Math.abs(s[0] - xy[0]), Math.abs(s[1] - xy[1])) > me.tolerance) {
                me.triggerStart(e);
            } else {
                return;
            }
        }
        
        if (me.fireEvent('mousemove', me, e) === false) {
            me.onMouseUp(e);
        } else {
            me.onDrag(e);
            me.fireEvent('drag', me, e);
        }
    },
    onMouseUp: function(e) {
        var me = this;
        
        
        me.mouseIsDown = false;
        
        if (me.mouseIsOut) {
            me.mouseIsOut = false;
            me.onMouseOut(e);
        }
        if (me.preventDefault !== false) {
            e.preventDefault();
        }
        
        if (Ext.isIE && document.releaseCapture) {
            document.releaseCapture();
        }
        me.fireEvent('mouseup', me, e);
        me.endDrag(e);
    },
    
    endDrag: function(e) {
        var me = this,
            wasActive = me.active;
        Ext.getDoc().un({
            mousemove: me.onMouseMove,
            mouseup: me.onMouseUp,
            selectstart: me.stopSelect,
            capture: true,
            scope: me
        });
        me.clearStart();
        me.active = false;
        if (wasActive) {
            me.dragEnded = true;
            me.onEnd(e);
            me.fireEvent('dragend', me, e);
        }
        
        me._constrainRegion = null;
    },
    triggerStart: function(e) {
        var me = this;
        me.clearStart();
        me.active = true;
        me.onStart(e);
        me.fireEvent('dragstart', me, e);
    },
    clearStart: function() {
        var timer = this.timer;
        if (timer) {
            clearTimeout(timer);
            this.timer = null;
        }
    },
    stopSelect: function(e) {
        e.stopEvent();
        return false;
    },
    
    onBeforeStart: function(e) {},
    
    onStart: function(xy) {},
    
    onDrag: function(e) {},
    
    onEnd: function(e) {},
    
    getDragTarget: function() {
        return this.dragTarget;
    },
    
    getDragCt: function() {
        return this.el;
    },
    
    getConstrainRegion: function() {
        var me = this;
        if (me.constrainTo) {
            if (me.constrainTo instanceof Ext.util.Region) {
                return me.constrainTo;
            }
            if (!me._constrainRegion) {
                me._constrainRegion = Ext.fly(me.constrainTo).getViewRegion();
            }
        } else {
            if (!me._constrainRegion) {
                me._constrainRegion = me.getDragCt().getViewRegion();
            }
        }
        return me._constrainRegion;
    },
    getXY: function(constrain) {
        return constrain ? this.constrainModes[constrain](this, this.lastXY) : this.lastXY;
    },
    
    getOffset: function(constrain) {
        var xy = this.getXY(constrain),
            s = this.startXY;
        return [
            xy[0] - s[0],
            xy[1] - s[1]
        ];
    },
    onDragStart: function(e) {
        e.stopPropagation();
    },
    constrainModes: {
        
        point: function(me, xy) {
            var dr = me.dragRegion,
                constrainTo = me.getConstrainRegion();
            
            if (!constrainTo) {
                return xy;
            }
            dr.x = dr.left = dr[0] = dr.right = xy[0];
            dr.y = dr.top = dr[1] = dr.bottom = xy[1];
            dr.constrainTo(constrainTo);
            return [
                dr.left,
                dr.top
            ];
        },
        
        dragTarget: function(me, xy) {
            var s = me.startXY,
                dr = me.startRegion.copy(),
                constrainTo = me.getConstrainRegion(),
                adjust;
            
            if (!constrainTo) {
                return xy;
            }
            
            
            
            dr.translateBy(xy[0] - s[0], xy[1] - s[1]);
            
            if (dr.right > constrainTo.right) {
                xy[0] += adjust = (constrainTo.right - dr.right);
                
                dr.left += adjust;
            }
            if (dr.left < constrainTo.left) {
                xy[0] += (constrainTo.left - dr.left);
            }
            
            
            if (dr.bottom > constrainTo.bottom) {
                xy[1] += adjust = (constrainTo.bottom - dr.bottom);
                
                dr.top += adjust;
            }
            if (dr.top < constrainTo.top) {
                xy[1] += (constrainTo.top - dr.top);
            }
            
            return xy;
        }
    }
});


Ext.define('Ext.grid.plugin.HeaderResizer', {
    extend: 'Ext.plugin.Abstract',
    requires: [
        'Ext.dd.DragTracker',
        'Ext.util.Region'
    ],
    alias: 'plugin.gridheaderresizer',
    disabled: false,
    config: {
        
        dynamic: false
    },
    colHeaderCls: Ext.baseCSSPrefix + 'column-header',
    minColWidth: 40,
    maxColWidth: 1000,
    eResizeCursor: 'col-resize',
    init: function(headerCt) {
        var me = this;
        me.headerCt = headerCt;
        headerCt.on('render', me.afterHeaderRender, me, {
            single: me
        });
        
        if (!me.minColWidth) {
            me.self.prototype.minColWidth = Ext.grid.column.Column.prototype.minWidth;
        }
    },
    
    destroy: function() {
        var tracker = this.tracker;
        if (tracker) {
            delete tracker.onBeforeStart;
            delete tracker.onStart;
            delete tracker.onDrag;
            delete tracker.onEnd;
            tracker.destroy();
            this.tracker = null;
        }
    },
    afterHeaderRender: function() {
        var me = this,
            headerCt = me.headerCt,
            el = headerCt.el;
        headerCt.mon(el, 'mousemove', me.onHeaderCtMouseMove, me);
        me.markerOwner = me.ownerGrid = me.headerCt.up('tablepanel');
        if (me.markerOwner.ownerLockable) {
            me.markerOwner = me.markerOwner.ownerLockable;
        }
        me.tracker = new Ext.dd.DragTracker({
            disabled: me.disabled,
            onBeforeStart: me.onBeforeStart.bind(me),
            onStart: me.onStart.bind(me),
            onDrag: me.onDrag.bind(me),
            onEnd: me.onEnd.bind(me),
            tolerance: 3,
            autoStart: 300,
            el: el
        });
    },
    
    
    
    onHeaderCtMouseMove: function(e) {
        var me = this;
        if (me.headerCt.dragging || me.disabled) {
            if (me.activeHd) {
                me.activeHd.el.dom.style.cursor = '';
                delete me.activeHd;
            }
        } else {
            me.findActiveHeader(e);
        }
    },
    findActiveHeader: function(e) {
        var me = this,
            headerEl = e.getTarget('.' + me.colHeaderCls, 3, true),
            overHeader, resizeHeader, headers;
        if (headerEl) {
            overHeader = Ext.getCmp(headerEl.id);
            
            if (overHeader.isOnRightEdge(e)) {
                
                if (me.headerCt.visibleColumnManager.getColumns().length === 1 && me.headerCt.forceFit) {
                    return;
                }
                resizeHeader = overHeader;
            }
            
            else if (overHeader.isOnLeftEdge(e)) {
                
                headers = me.headerCt.visibleColumnManager.getColumns();
                resizeHeader = headers[Ext.Array.indexOf(headers, overHeader) - 1];
                
                
                if (!resizeHeader && me.ownerGrid.ownerLockable && !me.ownerGrid.isLocked) {
                    headers = me.ownerGrid.ownerLockable.lockedGrid.headerCt.visibleColumnManager.getColumns();
                    resizeHeader = headers[headers.length - 1];
                }
            }
            
            if (resizeHeader) {
                
                
                
                if (resizeHeader.isGroupHeader) {
                    headers = resizeHeader.getGridColumns();
                    resizeHeader = headers[headers.length - 1];
                }
                
                
                if (resizeHeader && !(resizeHeader.fixed || (resizeHeader.resizable === false))) {
                    me.activeHd = resizeHeader;
                    overHeader.el.dom.style.cursor = me.eResizeCursor;
                    if (overHeader.triggerEl) {
                        overHeader.triggerEl.dom.style.cursor = me.eResizeCursor;
                    }
                }
            } else 
            {
                overHeader.el.dom.style.cursor = '';
                if (overHeader.triggerEl) {
                    overHeader.triggerEl.dom.style.cursor = '';
                }
                me.activeHd = null;
            }
        }
        return me.activeHd;
    },
    
    onBeforeStart: function(e) {
        var me = this;
        
        
        
        me.dragHd = Ext.supports.Touch ? me.findActiveHeader(e) : me.activeHd;
        if (!!me.dragHd && !me.headerCt.dragging) {
            
            
            me.xDelta = me.dragHd.getX() + me.dragHd.getWidth() - me.tracker.getXY()[0];
            me.tracker.constrainTo = me.getConstrainRegion();
            return true;
        } else {
            me.headerCt.dragging = false;
            return false;
        }
    },
    
    getConstrainRegion: function() {
        var me = this,
            dragHdEl = me.dragHd.el,
            nextHd,
            ownerGrid = me.ownerGrid,
            widthModel = ownerGrid.getSizeModel().width,
            maxColWidth = widthModel.shrinkWrap ? me.headerCt.getWidth() - me.headerCt.visibleColumnManager.getColumns().length * me.minColWidth : me.maxColWidth,
            result;
        
        
        if (me.headerCt.forceFit) {
            nextHd = me.dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])');
            if (nextHd && me.headerInSameGrid(nextHd)) {
                maxColWidth = dragHdEl.getWidth() + (nextHd.getWidth() - me.minColWidth);
            }
        }
        
        
        else if (ownerGrid.isLocked && widthModel.shrinkWrap) {
            maxColWidth = me.dragHd.up('[scrollerOwner]').getTargetEl().getWidth(true) - ownerGrid.getWidth() - (ownerGrid.ownerLockable.normalGrid.visibleColumnManager.getColumns().length * me.minColWidth + Ext.getScrollbarSize().width);
        }
        result = me.adjustConstrainRegion(dragHdEl.getRegion(), 0, 0, 0, me.minColWidth);
        result.right = dragHdEl.getX() + maxColWidth;
        return result;
    },
    
    
    onStart: function(e) {
        var me = this,
            dragHd = me.dragHd,
            width = dragHd.el.getWidth(),
            headerCt = dragHd.getRootHeaderCt(),
            x, y, markerOwner, lhsMarker, rhsMarker, markerHeight;
        me.headerCt.dragging = true;
        me.origWidth = width;
        
        if (!me.dynamic) {
            markerOwner = me.markerOwner;
            
            
            
            
            if (markerOwner.frame && markerOwner.resizable) {
                me.gridOverflowSetting = markerOwner.el.dom.style.overflow;
                markerOwner.el.dom.style.overflow = 'hidden';
            }
            x = me.getLeftMarkerX(markerOwner);
            lhsMarker = markerOwner.getLhsMarker();
            rhsMarker = markerOwner.getRhsMarker();
            markerHeight = me.ownerGrid.body.getHeight() + headerCt.getHeight();
            y = headerCt.getOffsetsTo(markerOwner)[1] - markerOwner.el.getBorderWidth('t');
            lhsMarker.setLocalY(y);
            rhsMarker.setLocalY(y);
            lhsMarker.setHeight(markerHeight);
            rhsMarker.setHeight(markerHeight);
            me.setMarkerX(lhsMarker, x);
            me.setMarkerX(rhsMarker, x + width);
        }
    },
    
    onDrag: function(e) {
        var me = this;
        if (me.dynamic) {
            me.doResize();
        } else {
            me.setMarkerX(me.getMovingMarker(me.markerOwner), me.calculateDragX(me.markerOwner));
        }
    },
    getMovingMarker: function(markerOwner) {
        return markerOwner.getRhsMarker();
    },
    onEnd: function(e) {
        var me = this,
            markerOwner;
        me.headerCt.dragging = false;
        if (me.dragHd) {
            if (!me.dynamic) {
                markerOwner = me.headerCt.up('tablepanel');
                
                if (markerOwner.ownerLockable) {
                    markerOwner = markerOwner.ownerLockable;
                }
                
                if ('gridOverflowSetting' in me) {
                    markerOwner.el.dom.style.overflow = me.gridOverflowSetting;
                }
                me.setMarkerX(markerOwner.getLhsMarker(), -9999);
                me.setMarkerX(markerOwner.getRhsMarker(), -9999);
            }
            me.doResize();
        }
        
        me.onHeaderCtMouseMove(e);
    },
    doResize: function() {
        var me = this,
            dragHd = me.dragHd,
            nextHd,
            offset = me.tracker.getOffset('point');
        
        if (dragHd && offset[0]) {
            
            if (dragHd.flex) {
                delete dragHd.flex;
            }
            Ext.suspendLayouts();
            
            
            me.adjustColumnWidth(offset[0] - me.xDelta);
            
            
            if (me.headerCt.forceFit) {
                nextHd = dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])');
                if (nextHd && !me.headerInSameGrid(nextHd)) {
                    nextHd = null;
                }
                if (nextHd) {
                    delete nextHd.flex;
                    nextHd.setWidth(nextHd.getWidth() - offset[0]);
                }
            }
            
            Ext.resumeLayouts(true);
        }
    },
    
    headerInSameGrid: function(header) {
        var grid = this.dragHd.up('tablepanel');
        return !!header.up(grid);
    },
    disable: function() {
        var tracker = this.tracker;
        this.disabled = true;
        if (tracker) {
            tracker.disable();
        }
    },
    enable: function() {
        var tracker = this.tracker;
        this.disabled = false;
        if (tracker) {
            tracker.enable();
        }
    },
    calculateDragX: function(markerOwner) {
        return this.tracker.getXY('point')[0] + this.xDelta - markerOwner.getX() - markerOwner.el.getBorderWidth('l');
    },
    getLeftMarkerX: function(markerOwner) {
        return this.dragHd.getX() - markerOwner.getX() - markerOwner.el.getBorderWidth('l') - 1;
    },
    setMarkerX: function(marker, x) {
        marker.setLocalX(x);
    },
    adjustConstrainRegion: function(region, t, r, b, l) {
        return region.adjust(t, r, b, l);
    },
    adjustColumnWidth: function(offsetX) {
        this.dragHd.setWidth(this.origWidth + offsetX);
    }
});


Ext.define('Ext.dd.DragZone', {
    extend: 'Ext.dd.DragSource',
    
    constructor: function(el, config) {
        var me = this,
            scroll = me.containerScroll;
        me.callParent([
            el,
            config
        ]);
        if (scroll) {
            el = me.scrollEl || el;
            el = Ext.get(el);
            if (Ext.isObject(scroll)) {
                el.ddScrollConfig = scroll;
            }
            Ext.dd.ScrollManager.register(el);
        }
    },
    
    
    
    getDragData: function(e) {
        return Ext.dd.Registry.getHandleFromEvent(e);
    },
    
    onInitDrag: function(x, y) {
        this.proxy.update(this.dragData.ddel.cloneNode(true));
        this.onStartDrag(x, y);
        return true;
    },
    
    getRepairXY: function(e) {
        return Ext.fly(this.dragData.ddel).getXY();
    },
    destroy: function() {
        this.callParent();
        if (this.containerScroll) {
            Ext.dd.ScrollManager.unregister(this.scrollEl || this.el);
        }
    }
});


Ext.define('Ext.grid.header.DragZone', {
    extend: 'Ext.dd.DragZone',
    colHeaderSelector: '.' + Ext.baseCSSPrefix + 'column-header',
    colInnerSelector: '.' + Ext.baseCSSPrefix + 'column-header-inner',
    maxProxyWidth: 120,
    constructor: function(headerCt) {
        var me = this;
        me.headerCt = headerCt;
        me.ddGroup = me.getDDGroup();
        me.autoGroup = true;
        me.callParent([
            headerCt.el
        ]);
        me.proxy.el.addCls(Ext.baseCSSPrefix + 'grid-col-dd');
    },
    getDDGroup: function() {
        return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id;
    },
    getDragData: function(e) {
        if (e.getTarget(this.colInnerSelector)) {
            var header = e.getTarget(this.colHeaderSelector),
                headerCmp, ddel;
            if (header) {
                headerCmp = Ext.getCmp(header.id);
                if (!this.headerCt.dragging && headerCmp.draggable && !(headerCmp.isOnLeftEdge(e) || headerCmp.isOnRightEdge(e))) {
                    ddel = document.createElement('div');
                    ddel.role = 'presentation';
                    ddel.innerHTML = headerCmp.text;
                    return {
                        ddel: ddel,
                        header: headerCmp
                    };
                }
            }
        }
        return false;
    },
    onBeforeDrag: function() {
        return !(this.headerCt.dragging || this.disabled);
    },
    onInitDrag: function() {
        this.headerCt.dragging = true;
        this.headerCt.hideMenu();
        this.callParent(arguments);
    },
    onDragDrop: function() {
        this.headerCt.dragging = false;
        this.callParent(arguments);
    },
    afterRepair: function() {
        this.callParent();
        this.headerCt.dragging = false;
    },
    getRepairXY: function() {
        return this.dragData.header.el.getXY();
    },
    disable: function() {
        this.disabled = true;
    },
    enable: function() {
        this.disabled = false;
    }
});



Ext.define('Ext.dd.DDTarget', {
    extend: 'Ext.dd.DragDrop',
    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.initTarget(id, sGroup, config);
        }
    },
    
    getDragEl: Ext.emptyFn,
    
    isValidHandleChild: Ext.emptyFn,
    
    startDrag: Ext.emptyFn,
    
    endDrag: Ext.emptyFn,
    
    onDrag: Ext.emptyFn,
    
    onDragDrop: Ext.emptyFn,
    
    onDragEnter: Ext.emptyFn,
    
    onDragOut: Ext.emptyFn,
    
    onDragOver: Ext.emptyFn,
    
    onInvalidDrop: Ext.emptyFn,
    
    onMouseDown: Ext.emptyFn,
    
    onMouseUp: Ext.emptyFn,
    
    setXConstraint: Ext.emptyFn,
    
    setYConstraint: Ext.emptyFn,
    
    resetConstraints: Ext.emptyFn,
    
    clearConstraints: Ext.emptyFn,
    
    clearTicks: Ext.emptyFn,
    
    setInitPosition: Ext.emptyFn,
    
    setDragElId: Ext.emptyFn,
    
    setHandleElId: Ext.emptyFn,
    
    setOuterHandleElId: Ext.emptyFn,
    
    addInvalidHandleClass: Ext.emptyFn,
    
    addInvalidHandleId: Ext.emptyFn,
    
    addInvalidHandleType: Ext.emptyFn,
    
    removeInvalidHandleClass: Ext.emptyFn,
    
    removeInvalidHandleId: Ext.emptyFn,
    
    removeInvalidHandleType: Ext.emptyFn,
    toString: function() {
        return ("DDTarget " + this.id);
    }
});


Ext.define('Ext.dd.ScrollManager', {
    singleton: true,
    requires: [
        'Ext.dd.DragDropManager'
    ],
    dirTrans: {
        up: -1,
        left: -1,
        down: 1,
        right: 1
    },
    constructor: function() {
        var ddm = Ext.dd.DragDropManager;
        ddm.fireEvents = Ext.Function.createSequence(ddm.fireEvents, this.onFire, this);
        ddm.stopDrag = Ext.Function.createSequence(ddm.stopDrag, this.onStop, this);
        this.doScroll = this.doScroll.bind(this);
        this.ddmInstance = ddm;
        this.els = {};
        this.dragEl = null;
        this.proc = {};
    },
    onStop: function(e) {
        var sm = Ext.dd.ScrollManager;
        sm.dragEl = null;
        sm.clearProc();
    },
    triggerRefresh: function() {
        if (this.ddmInstance.dragCurrent) {
            this.ddmInstance.refreshCache(this.ddmInstance.dragCurrent.groups);
        }
    },
    doScroll: function() {
        var me = this;
        if (me.ddmInstance.dragCurrent) {
            var proc = me.proc,
                procEl = proc.el,
                scrollComponent = proc.component,
                ddScrollConfig = proc.el.ddScrollConfig,
                distance = ddScrollConfig && ddScrollConfig.increment ? ddScrollConfig.increment : me.increment,
                animate = ddScrollConfig && 'animate' in ddScrollConfig ? ddScrollConfig.animate : me.animate,
                afterScroll = function() {
                    me.triggerRefresh();
                };
            if (animate) {
                if (animate === true) {
                    animate = {
                        callback: afterScroll
                    };
                } else {
                    animate.callback = animate.callback ? Ext.Function.createSequence(animate.callback, afterScroll) : afterScroll;
                }
            }
            
            
            if (scrollComponent) {
                
                distance = distance * me.dirTrans[proc.dir];
                
                if (proc.dir === 'up' || proc.dir === 'down') {
                    scrollComponent.scrollBy(0, distance, animate);
                } else {
                    scrollComponent.scrollBy(distance, 0, animate);
                }
            } else {
                procEl.scroll(proc.dir, distance, animate);
            }
            if (!animate) {
                afterScroll();
            }
        }
    },
    clearProc: function() {
        var proc = this.proc;
        if (proc.id) {
            clearInterval(proc.id);
        }
        proc.id = 0;
        proc.el = null;
        proc.dir = "";
    },
    startProc: function(el, dir) {
        var me = this,
            group, freq, scrollComponent,
            proc = me.proc;
        me.clearProc();
        proc.el = el;
        proc.dir = dir;
        group = el.ddScrollConfig ? el.ddScrollConfig.ddGroup : undefined;
        freq = (el.ddScrollConfig && el.ddScrollConfig.frequency) ? el.ddScrollConfig.frequency : me.frequency;
        if (group === undefined || me.ddmInstance.dragCurrent.ddGroup === group) {
            proc.id = Ext.interval(me.doScroll, freq);
        }
    },
    onFire: function(e, isDrop) {
        var me = this,
            pt, proc, els, id, el, elementRegion, configSource;
        if (isDrop || !me.ddmInstance.dragCurrent) {
            return;
        }
        if (!me.dragEl || me.dragEl !== me.ddmInstance.dragCurrent) {
            me.dragEl = me.ddmInstance.dragCurrent;
            
            me.refreshCache();
        }
        pt = e.getPoint();
        proc = me.proc;
        els = me.els;
        for (id in els) {
            el = els[id];
            elementRegion = el._region;
            configSource = el.ddScrollConfig || me;
            if (elementRegion && elementRegion.contains(pt) && el.isScrollable()) {
                if (elementRegion.bottom - pt.y <= configSource.vthresh) {
                    if (proc.el !== el) {
                        me.startProc(el, "down");
                    }
                    return;
                } else if (elementRegion.right - pt.x <= configSource.hthresh) {
                    if (proc.el !== el) {
                        me.startProc(el, "right");
                    }
                    return;
                } else if (pt.y - elementRegion.top <= configSource.vthresh) {
                    if (proc.el !== el) {
                        me.startProc(el, "up");
                    }
                    return;
                } else if (pt.x - elementRegion.left <= configSource.hthresh) {
                    if (proc.el !== el) {
                        me.startProc(el, "left");
                    }
                    return;
                }
            }
        }
        me.clearProc();
    },
    
    register: function(el) {
        if (Ext.isArray(el)) {
            for (var i = 0,
                len = el.length; i < len; i++) {
                this.register(el[i]);
            }
        } else {
            el = Ext.get(el);
            this.els[el.id] = el;
        }
    },
    
    unregister: function(el) {
        if (Ext.isArray(el)) {
            for (var i = 0,
                len = el.length; i < len; i++) {
                this.unregister(el[i]);
            }
        } else {
            el = Ext.get(el);
            delete this.els[el.id];
        }
    },
    
    vthresh: 25 * (window.devicePixelRatio || 1),
    
    hthresh: 25 * (window.devicePixelRatio || 1),
    
    increment: 100,
    
    frequency: 500,
    
    animate: true,
    
    animDuration: 0.4,
    
    ddGroup: undefined,
    
    refreshCache: function() {
        var els = this.els,
            id;
        for (id in els) {
            if (typeof els[id] == 'object') {
                
                els[id]._region = els[id].getRegion();
            }
        }
    }
});


Ext.define('Ext.dd.DropTarget', {
    extend: 'Ext.dd.DDTarget',
    requires: [
        'Ext.dd.ScrollManager'
    ],
    
    constructor: function(el, config) {
        this.el = Ext.get(el);
        Ext.apply(this, config);
        if (this.containerScroll) {
            Ext.dd.ScrollManager.register(this.el);
        }
        this.callParent([
            this.el.dom,
            this.ddGroup || this.group,
            {
                isTarget: true
            }
        ]);
    },
    
    
    
    dropAllowed: Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed: Ext.baseCSSPrefix + 'dd-drop-nodrop',
    
    isTarget: true,
    
    isNotifyTarget: true,
    
    notifyEnter: function(dd, e, data) {
        if (this.overClass) {
            this.el.addCls(this.overClass);
        }
        return this.dropAllowed;
    },
    
    notifyOver: function(dd, e, data) {
        return this.dropAllowed;
    },
    
    notifyOut: function(dd, e, data) {
        if (this.overClass) {
            this.el.removeCls(this.overClass);
        }
    },
    
    notifyDrop: function(dd, e, data) {
        if (this.overClass) {
            this.el.removeCls(this.overClass);
        }
        return false;
    },
    destroy: function() {
        this.callParent();
        if (this.containerScroll) {
            Ext.dd.ScrollManager.unregister(this.el);
        }
    }
});


Ext.define('Ext.dd.Registry', {
    singleton: true,
    constructor: function() {
        this.elements = {};
        this.handles = {};
        this.autoIdSeed = 0;
    },
    getId: function(el, autogen) {
        if (typeof el == "string") {
            return el;
        }
        var id = el.id;
        if (!id && autogen !== false) {
            id = "extdd-" + (++this.autoIdSeed);
            el.id = id;
        }
        return id;
    },
    
    register: function(el, data) {
        data = data || {};
        if (typeof el === "string") {
            el = document.getElementById(el);
        }
        data.ddel = el;
        this.elements[this.getId(el)] = data;
        if (data.isHandle !== false) {
            this.handles[data.ddel.id] = data;
        }
        if (data.handles) {
            var hs = data.handles,
                i, len;
            for (i = 0 , len = hs.length; i < len; i++) {
                this.handles[this.getId(hs[i])] = data;
            }
        }
    },
    
    unregister: function(el) {
        var id = this.getId(el, false),
            data = this.elements[id],
            hs, i, len;
        if (data) {
            delete this.elements[id];
            if (data.handles) {
                hs = data.handles;
                for (i = 0 , len = hs.length; i < len; i++) {
                    delete this.handles[this.getId(hs[i], false)];
                }
            }
        }
    },
    
    getHandle: function(id) {
        if (typeof id != "string") {
            
            id = id.id;
        }
        return this.handles[id];
    },
    
    getHandleFromEvent: function(e) {
        var t = e.getTarget();
        return t ? this.handles[t.id] : null;
    },
    
    getTarget: function(id) {
        if (typeof id != "string") {
            
            id = id.id;
        }
        return this.elements[id];
    },
    
    getTargetFromEvent: function(e) {
        var t = e.getTarget();
        return t ? this.elements[t.id] || this.handles[t.id] : null;
    }
});


Ext.define('Ext.dd.DropZone', {
    extend: 'Ext.dd.DropTarget',
    requires: [
        'Ext.dd.Registry'
    ],
    
    getTargetFromEvent: function(e) {
        return Ext.dd.Registry.getTargetFromEvent(e);
    },
    
    onNodeEnter: function(n, dd, e, data) {},
    
    onNodeOver: function(n, dd, e, data) {
        return this.dropAllowed;
    },
    
    onNodeOut: function(n, dd, e, data) {},
    
    onNodeDrop: function(n, dd, e, data) {
        return false;
    },
    
    onContainerOver: function(dd, e, data) {
        return this.dropNotAllowed;
    },
    
    onContainerDrop: function(dd, e, data) {
        return false;
    },
    
    notifyEnter: function(dd, e, data) {
        return this.dropNotAllowed;
    },
    
    notifyOver: function(dd, e, data) {
        var n = this.getTargetFromEvent(e);
        if (!n) {
            
            if (this.lastOverNode) {
                this.onNodeOut(this.lastOverNode, dd, e, data);
                this.lastOverNode = null;
            }
            return this.onContainerOver(dd, e, data);
        }
        if (this.lastOverNode != n) {
            if (this.lastOverNode) {
                this.onNodeOut(this.lastOverNode, dd, e, data);
            }
            this.onNodeEnter(n, dd, e, data);
            this.lastOverNode = n;
        }
        return this.onNodeOver(n, dd, e, data);
    },
    
    notifyOut: function(dd, e, data) {
        if (this.lastOverNode) {
            this.onNodeOut(this.lastOverNode, dd, e, data);
            this.lastOverNode = null;
        }
    },
    
    notifyDrop: function(dd, e, data) {
        var me = this,
            n = me.getTargetFromEvent(e),
            result = n ? me.onNodeDrop(n, dd, e, data) : me.onContainerDrop(dd, e, data);
        
        
        if (me.lastOverNode) {
            me.onNodeOut(me.lastOverNode, dd, e, data);
            me.lastOverNode = null;
        }
        return result;
    },
    
    triggerCacheRefresh: function() {
        Ext.dd.DDM.refreshCache(this.groups);
    }
});


Ext.define('Ext.grid.header.DropZone', {
    extend: 'Ext.dd.DropZone',
    colHeaderCls: Ext.baseCSSPrefix + 'column-header',
    proxyOffsets: [
        -4,
        -9
    ],
    constructor: function(headerCt) {
        var me = this;
        me.headerCt = headerCt;
        me.ddGroup = me.getDDGroup();
        me.autoGroup = true;
        me.callParent([
            headerCt.el
        ]);
    },
    destroy: function() {
        this.callParent();
        Ext.destroy(this.topIndicator, this.bottomIndicator);
    },
    getDDGroup: function() {
        return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id;
    },
    getTargetFromEvent: function(e) {
        return e.getTarget('.' + this.colHeaderCls);
    },
    getTopIndicator: function() {
        if (!this.topIndicator) {
            this.topIndicator = Ext.getBody().createChild({
                role: 'presentation',
                cls: Ext.baseCSSPrefix + "col-move-top",
                
                "data-sticky": true,
                html: "&#160;"
            });
            this.indicatorXOffset = Math.floor((this.topIndicator.dom.offsetWidth + 1) / 2);
        }
        return this.topIndicator;
    },
    getBottomIndicator: function() {
        if (!this.bottomIndicator) {
            this.bottomIndicator = Ext.getBody().createChild({
                role: 'presentation',
                cls: Ext.baseCSSPrefix + "col-move-bottom",
                
                "data-sticky": true,
                html: "&#160;"
            });
        }
        return this.bottomIndicator;
    },
    getLocation: function(e, t) {
        var x = e.getXY()[0],
            region = Ext.fly(t).getRegion(),
            pos;
        if ((region.right - x) <= (region.right - region.left) / 2) {
            pos = "after";
        } else {
            pos = "before";
        }
        return {
            pos: pos,
            header: Ext.getCmp(t.id),
            node: t
        };
    },
    positionIndicator: function(data, node, e) {
        var me = this,
            dragHeader = data.header,
            dropLocation = me.getLocation(e, node),
            targetHeader = dropLocation.header,
            pos = dropLocation.pos,
            nextHd, prevHd, topIndicator, bottomIndicator, topAnchor, bottomAnchor, topXY, bottomXY, headerCtEl, minX, maxX, allDropZones, ln, i, dropZone;
        
        if (targetHeader === me.lastTargetHeader && pos === me.lastDropPos) {
            return;
        }
        nextHd = dragHeader.nextSibling('gridcolumn:not([hidden])');
        prevHd = dragHeader.previousSibling('gridcolumn:not([hidden])');
        me.lastTargetHeader = targetHeader;
        me.lastDropPos = pos;
        
        if (!targetHeader.draggable && pos === 'before' && targetHeader.getIndex() === 0) {
            return false;
        }
        data.dropLocation = dropLocation;
        if ((dragHeader !== targetHeader) && ((pos === "before" && nextHd !== targetHeader) || (pos === "after" && prevHd !== targetHeader)) && !targetHeader.isDescendantOf(dragHeader)) {
            
            
            
            allDropZones = Ext.dd.DragDropManager.getRelated(me);
            ln = allDropZones.length;
            i = 0;
            for (; i < ln; i++) {
                dropZone = allDropZones[i];
                if (dropZone !== me && dropZone.invalidateDrop) {
                    dropZone.invalidateDrop();
                }
            }
            me.valid = true;
            topIndicator = me.getTopIndicator();
            bottomIndicator = me.getBottomIndicator();
            if (pos === 'before') {
                topAnchor = 'bc-tl';
                bottomAnchor = 'tc-bl';
            } else {
                topAnchor = 'bc-tr';
                bottomAnchor = 'tc-br';
            }
            
            topXY = topIndicator.getAlignToXY(targetHeader.el, topAnchor);
            bottomXY = bottomIndicator.getAlignToXY(targetHeader.el, bottomAnchor);
            
            headerCtEl = me.headerCt.el;
            minX = headerCtEl.getX() - me.indicatorXOffset;
            maxX = headerCtEl.getX() + headerCtEl.getWidth();
            topXY[0] = Ext.Number.constrain(topXY[0], minX, maxX);
            bottomXY[0] = Ext.Number.constrain(bottomXY[0], minX, maxX);
            
            topIndicator.setXY(topXY);
            bottomIndicator.setXY(bottomXY);
            topIndicator.show();
            bottomIndicator.show();
        } else 
        {
            me.invalidateDrop();
        }
    },
    invalidateDrop: function() {
        this.valid = false;
        this.hideIndicators();
    },
    onNodeOver: function(node, dragZone, e, data) {
        var me = this,
            from = data.header,
            doPosition, to, fromPanel, toPanel;
        if (data.header.el.dom === node) {
            doPosition = false;
        } else {
            data.isLock = data.isUnlock = data.crossPanel = false;
            to = me.getLocation(e, node).header;
            
            doPosition = (from.ownerCt === to.ownerCt);
            
            if (!doPosition && (!from.ownerCt.sealed && !to.ownerCt.sealed)) {
                doPosition = true;
                fromPanel = from.up('tablepanel');
                toPanel = to.up('tablepanel');
                if (fromPanel !== toPanel) {
                    data.crossPanel = true;
                    
                    data.isLock = toPanel.isLocked && !fromPanel.isLocked;
                    data.isUnlock = !toPanel.isLocked && fromPanel.isLocked;
                    if ((data.isUnlock && from.lockable === false) || (data.isLock && !from.isLockable())) {
                        doPosition = false;
                    }
                }
            }
        }
        if (doPosition) {
            me.positionIndicator(data, node, e);
        } else {
            me.valid = false;
        }
        return me.valid ? me.dropAllowed : me.dropNotAllowed;
    },
    hideIndicators: function() {
        var me = this;
        me.getTopIndicator().hide();
        me.getBottomIndicator().hide();
        me.lastTargetHeader = me.lastDropPos = null;
    },
    onNodeOut: function() {
        this.hideIndicators();
    },
    
    
    getNestedHeader: function(header, first) {
        var items = header.items,
            pos;
        if (header.isGroupHeader && items.length) {
            pos = !first ? 'first' : 'last';
            header = this.getNestedHeader(items[pos](), first);
        }
        return header;
    },
    onNodeDrop: function(node, dragZone, e, data) {
        
        if (!this.valid) {
            return;
        }
        var me = this,
            dragHeader = data.header,
            dropLocation = data.dropLocation,
            dropPosition = dropLocation.pos,
            targetHeader = dropLocation.header,
            fromCt = dragHeader.ownerCt,
            toCt = targetHeader.ownerCt,
            
            
            visibleColumnManager = me.headerCt.visibleColumnManager,
            visibleFromIdx = visibleColumnManager.getHeaderIndex(dragHeader),
            visibleToIdx, colsToMove, moveMethod, scrollerOwner, savedWidth;
        
        
        
        if (data.isLock || data.isUnlock) {
            scrollerOwner = fromCt.up('[scrollerOwner]');
            visibleToIdx = toCt.items.indexOf(targetHeader);
            if (dropPosition === 'after') {
                visibleToIdx++;
            }
            if (data.isLock) {
                scrollerOwner.lock(dragHeader, visibleToIdx, toCt);
            } else {
                scrollerOwner.unlock(dragHeader, visibleToIdx, toCt);
            }
        } else 
        {
            
            
            
            
            
            visibleToIdx = dropPosition === 'after' ? 
            visibleColumnManager.getHeaderIndex(me.getNestedHeader(targetHeader, 1)) + 1 : 
            visibleColumnManager.getHeaderIndex(me.getNestedHeader(targetHeader, 0));
            me.invalidateDrop();
            
            savedWidth = dragHeader.getWidth();
            
            Ext.suspendLayouts();
            
            
            
            
            fromCt.isDDMoveInGrid = toCt.isDDMoveInGrid = !data.crossPanel;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (dragHeader.isGroupHeader && targetHeader.isGroupHeader) {
                dragHeader.setNestedParent(targetHeader);
            }
            
            
            
            if (dropPosition === 'before') {
                targetHeader.insertNestedHeader(dragHeader);
            } else {
                
                moveMethod = 'move' + dropPosition.charAt(0).toUpperCase() + dropPosition.substr(1);
                toCt[moveMethod](dragHeader, targetHeader);
            }
            
            
            
            
            
            if (visibleToIdx >= 0 && !(targetHeader.isGroupHeader && !targetHeader.items.length) && visibleFromIdx !== visibleToIdx) {
                colsToMove = dragHeader.isGroupHeader ? dragHeader.query(':not([hidden]):not([isGroupHeader])').length : 1;
                
                
                
                if ((visibleFromIdx <= visibleToIdx) && colsToMove > 1) {
                    visibleToIdx -= colsToMove;
                }
                
                
                toCt.getRootHeaderCt().grid.view.moveColumn(visibleFromIdx, visibleToIdx, colsToMove);
            }
            
            
            (fromCt.ownerCt || fromCt).fireEvent('columnmove', fromCt, dragHeader, visibleFromIdx, visibleToIdx);
            fromCt.isDDMoveInGrid = toCt.isDDMoveInGrid = false;
            
            
            
            
            
            
            if (toCt.isGroupHeader && !fromCt.isGroupHeader) {
                
                
                if (fromCt !== toCt) {
                    dragHeader.savedFlex = dragHeader.flex;
                    delete dragHeader.flex;
                    dragHeader.width = savedWidth;
                }
            } else if (!fromCt.isGroupHeader) {
                if (dragHeader.savedFlex) {
                    dragHeader.flex = dragHeader.savedFlex;
                    delete dragHeader.width;
                }
            }
            Ext.resumeLayouts(true);
        }
    }
});


Ext.define('Ext.grid.plugin.HeaderReorderer', {
    extend: 'Ext.plugin.Abstract',
    requires: [
        'Ext.grid.header.DragZone',
        'Ext.grid.header.DropZone'
    ],
    alias: 'plugin.gridheaderreorderer',
    init: function(headerCt) {
        this.headerCt = headerCt;
        headerCt.on({
            render: this.onHeaderCtRender,
            single: true,
            scope: this
        });
    },
    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },
    onHeaderCtRender: function() {
        var me = this;
        me.dragZone = new Ext.grid.header.DragZone(me.headerCt);
        me.dropZone = new Ext.grid.header.DropZone(me.headerCt);
        if (me.disabled) {
            me.dragZone.disable();
        }
    },
    enable: function() {
        this.disabled = false;
        if (this.dragZone) {
            this.dragZone.enable();
        }
    },
    disable: function() {
        this.disabled = true;
        if (this.dragZone) {
            this.dragZone.disable();
        }
    }
});


Ext.define('Ext.grid.header.Container', {
    extend: 'Ext.container.Container',
    requires: [
        'Ext.grid.ColumnLayout',
        'Ext.grid.plugin.HeaderResizer',
        'Ext.grid.plugin.HeaderReorderer',
        'Ext.util.KeyNav'
    ],
    uses: [
        'Ext.grid.column.Column',
        'Ext.grid.ColumnManager',
        'Ext.menu.Menu',
        'Ext.menu.CheckItem',
        'Ext.menu.Separator'
    ],
    mixins: [
        'Ext.util.FocusableContainer'
    ],
    border: true,
    alias: 'widget.headercontainer',
    baseCls: Ext.baseCSSPrefix + 'grid-header-ct',
    dock: 'top',
    
    weight: 100,
    defaultType: 'gridcolumn',
    detachOnRemove: false,
    
    defaultWidth: 100,
    
    
    sortAscText: 'Sort Ascending',
    
    
    sortDescText: 'Sort Descending',
    
    
    sortClearText: 'Clear Sort',
    
    
    columnsText: 'Columns',
    
    headerOpenCls: Ext.baseCSSPrefix + 'column-header-open',
    menuSortAscCls: Ext.baseCSSPrefix + 'hmenu-sort-asc',
    menuSortDescCls: Ext.baseCSSPrefix + 'hmenu-sort-desc',
    menuColsIcon: Ext.baseCSSPrefix + 'cols-icon',
    ddLock: false,
    dragging: false,
    
    sortOnClick: true,
    
    
    enableFocusableContainer: false,
    
    
    sortable: true,
    
    enableColumnHide: true,
    
    
    
    
    
    
    
    
    
    
    initComponent: function() {
        var me = this;
        me.headerCounter = 0;
        me.plugins = me.plugins || [];
        me.defaults = me.defaults || {};
        
        
        
        
        if (!me.isColumn) {
            if (me.enableColumnResize) {
                me.resizer = new Ext.grid.plugin.HeaderResizer();
                me.plugins.push(me.resizer);
            }
            if (me.enableColumnMove) {
                me.reorderer = new Ext.grid.plugin.HeaderReorderer();
                me.plugins.push(me.reorderer);
            }
        }
        
        
        if (me.isColumn && !me.isGroupHeader) {
            if (!me.items || me.items.length === 0) {
                me.isContainer = me.isFocusableContainer = false;
                me.focusable = true;
                me.layout = {
                    type: 'container',
                    calculate: Ext.emptyFn
                };
            }
        } else 
        {
            me.layout = Ext.apply({
                type: 'gridcolumn',
                align: 'stretch'
            }, me.initialConfig.layout);
            
            me.defaults.columnLines = me.columnLines;
            
            if (!me.isGroupHeader) {
                me.isRootHeader = true;
                
                if (!me.hiddenHeaders) {
                    me.enableFocusableContainer = true;
                    me.ariaRole = 'row';
                }
                
                me.columnManager = new Ext.grid.ColumnManager(false,me);
                me.visibleColumnManager = new Ext.grid.ColumnManager(true,me);
                
                
                
                
                
                
                if (me.grid) {
                    me.grid.columnManager = me.columnManager;
                    me.grid.visibleColumnManager = me.visibleColumnManager;
                }
            } else {
                
                me.visibleColumnManager = new Ext.grid.ColumnManager(true,me);
                me.columnManager = new Ext.grid.ColumnManager(false,me);
            }
        }
        me.menuTask = new Ext.util.DelayedTask(me.updateMenuDisabledState,me);
        me.callParent();
    },
    insertNestedHeader: function(moveHeader) {
        var me = this,
            fromCt = moveHeader.ownerCt,
            toCt = me.ownerCt,
            layoutOwner = toCt.layout.owner,
            toIndex;
        if (fromCt) {
            if (me.isGroupHeader && !toCt.isNestedParent) {
                toIndex = layoutOwner.items.indexOf(me);
            }
            fromCt.remove(moveHeader, false);
        }
        if (toIndex === undefined) {
            toIndex = layoutOwner.items.indexOf(me);
        }
        layoutOwner.insert(toIndex, moveHeader);
    },
    isNested: function() {
        return !!this.getRootHeaderCt().down('[isNestedParent]');
    },
    isNestedGroupHeader: function() {
        
        var header = this,
            items = header.getRefOwner().query('>:not([hidden])');
        return (items.length === 1 && items[0] === header);
    },
    maybeShowNestedGroupHeader: function() {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        var items = this.items,
            item;
        if (items && items.length === 1 && (item = items.getAt(0)) && item.hidden) {
            item.show();
        }
    },
    setNestedParent: function(target) {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        target.isNestedParent = false;
        target.ownerCt.isNestedParent = !!(this.ownerCt.items.length === 1 && target.ownerCt.items.length === 1);
    },
    initEvents: function() {
        var me = this,
            onHeaderCtEvent, listeners;
        me.callParent();
        
        if (!me.isColumn && !me.isGroupHeader) {
            onHeaderCtEvent = me.onHeaderCtEvent , listeners = {
                click: onHeaderCtEvent,
                dblclick: onHeaderCtEvent,
                contextmenu: onHeaderCtEvent,
                mouseover: me.onHeaderCtMouseOver,
                mouseout: me.onHeaderCtMouseOut,
                scope: me
            };
            if (Ext.supports.Touch) {
                listeners.longpress = me.onHeaderCtLongPress;
            }
            me.mon(me.el, listeners);
        }
    },
    onHeaderCtEvent: function(e, t) {
        var me = this,
            headerEl = me.getHeaderElByEvent(e),
            header, targetEl, activeHeader;
        if (me.longPressFired) {
            
            
            me.longPressFired = false;
            return;
        }
        if (headerEl && !me.ddLock) {
            header = Ext.getCmp(headerEl.id);
            if (header) {
                targetEl = header[header.clickTargetName];
                
                
                if ((!header.isGroupHeader && !header.isContainer) || e.within(targetEl)) {
                    if (e.type === 'click' || e.type === 'tap') {
                        
                        
                        
                        activeHeader = header.onTitleElClick(e, targetEl, me.sortOnClick);
                        if (activeHeader) {
                            me.onHeaderTriggerClick(activeHeader, e, Ext.supports.Touch ? activeHeader.el : activeHeader.triggerEl);
                        } else {
                            me.onHeaderClick(header, e, t);
                        }
                    } else if (e.type === 'contextmenu') {
                        me.onHeaderContextMenu(header, e, t);
                    } else if (e.type === 'dblclick' && header.resizable) {
                        header.onTitleElDblClick(e, targetEl.dom);
                    }
                }
            }
        }
    },
    onHeaderCtMouseOver: function(e, t) {
        var headerEl, header, targetEl;
        
        
        if (!e.within(this.el, true)) {
            headerEl = e.getTarget('.' + Ext.grid.column.Column.prototype.baseCls);
            header = headerEl && Ext.getCmp(headerEl.id);
            if (header) {
                targetEl = header[header.clickTargetName];
                if (e.within(targetEl)) {
                    header.onTitleMouseOver(e, targetEl.dom);
                }
            }
        }
    },
    onHeaderCtMouseOut: function(e, t) {
        var headerSelector = '.' + Ext.grid.column.Column.prototype.baseCls,
            outHeaderEl = e.getTarget(headerSelector),
            inHeaderEl = e.getRelatedTarget(headerSelector),
            header, targetEl;
        
        if (outHeaderEl !== inHeaderEl) {
            if (outHeaderEl) {
                header = Ext.getCmp(outHeaderEl.id);
                if (header) {
                    targetEl = header[header.clickTargetName];
                    header.onTitleMouseOut(e, targetEl.dom);
                }
            }
            if (inHeaderEl) {
                header = Ext.getCmp(inHeaderEl.id);
                if (header) {
                    targetEl = header[header.clickTargetName];
                    header.onTitleMouseOver(e, targetEl.dom);
                }
            }
        }
    },
    onHeaderCtLongPress: function(e) {
        var me = this,
            headerEl = me.getHeaderElByEvent(e),
            header = Ext.getCmp(headerEl.id);
        if (!header.menuDisabled) {
            me.longPressFired = true;
            me.showMenuBy(e, headerEl, header);
        }
    },
    getHeaderElByEvent: function(e) {
        return e.getTarget('.' + Ext.grid.column.Column.prototype.baseCls);
    },
    isLayoutRoot: function() {
        
        
        
        
        
        if (this.hiddenHeaders) {
            return false;
        }
        return this.callParent();
    },
    
    getRootHeaderCt: function() {
        var me = this;
        return me.isRootHeader ? me : me.up('[isRootHeader]');
    },
    onDestroy: function() {
        var me = this;
        if (me.menu) {
            me.menu.un('hide', me.onMenuHide, me);
        }
        me.menuTask.cancel();
        me.callParent();
        Ext.destroy(me.visibleColumnManager, me.columnManager, me.menu);
        me.columnManager = me.visibleColumnManager = null;
    },
    applyColumnsState: function(columns) {
        if (!columns || !columns.length) {
            return;
        }
        var me = this,
            items = me.items.items,
            count = items.length,
            i = 0,
            length = columns.length,
            c, col, columnState, index,
            moved = false,
            newOrder = [],
            stateHash = {},
            newCols = [];
        
        
        
        
        
        
        
        
        
        
        
        for (c = 0; c < length; c++) {
            columnState = columns[c];
            columnState.index = c;
            stateHash[columnState.id] = columnState;
        }
        for (i = 0; i < count; i++) {
            col = items[i];
            columnState = stateHash[col.getStateId()];
            
            
            if (columnState) {
                index = columnState.index;
                newOrder[index] = col;
                if (i !== index) {
                    moved = true;
                }
                if (col.applyColumnState) {
                    col.applyColumnState(columnState);
                }
            } else 
            
            {
                newCols.push({
                    index: i,
                    column: col
                });
            }
        }
        
        newOrder = Ext.Array.clean(newOrder);
        
        
        length = newCols.length;
        if (length) {
            for (i = 0; i < length; i++) {
                columnState = newCols[i];
                index = columnState.index;
                if (index < newOrder.length) {
                    moved = true;
                    Ext.Array.splice(newOrder, index, 0, columnState.column);
                } else {
                    newOrder.push(columnState.column);
                }
            }
        }
        if (moved) {
            me.removeAll(false);
            me.add(newOrder);
            me.purgeCache();
        }
    },
    getColumnsState: function() {
        var me = this,
            columns = [],
            state;
        me.items.each(function(col) {
            state = col.getColumnState && col.getColumnState();
            if (state) {
                columns.push(state);
            }
        });
        return columns;
    },
    
    
    
    onAdd: function(c) {
        var me = this;
        if (!c.headerId) {
            c.headerId = c.initialConfig.id || Ext.id(null, 'header-');
        }
        if (c.sortable === undefined) {
            c.sortable = me.sortable;
        }
        
        if (!c.getStateId()) {
            
            
            
            
            c.stateId = c.initialConfig.id || ('h' + (++me.headerCounter));
        }
        if (!me._usedIDs) {
            me._usedIDs = {};
        }
        if (me._usedIDs[c.headerId]) {
            Ext.log.warn(this.$className + ' attempted to reuse an existing id: ' + c.headerId);
        }
        me._usedIDs[c.headerId] = true;
        me.callParent(arguments);
        me.onHeadersChanged(c, me.isDDMoveInGrid);
    },
    move: function(fromIdx, toIdx) {
        var me = this,
            items = me.items,
            headerToMove;
        if (fromIdx.isComponent) {
            headerToMove = fromIdx;
            fromIdx = items.indexOf(headerToMove);
        } else {
            headerToMove = items.getAt(fromIdx);
        }
        
        headerToMove.visibleFromIdx = me.getRootHeaderCt().visibleColumnManager.indexOf(headerToMove);
        me.callParent(arguments);
    },
    onMove: function(headerToMove, fromIdx, toIdx) {
        var me = this,
            gridHeaderCt = me.getRootHeaderCt(),
            gridVisibleColumnManager = gridHeaderCt.visibleColumnManager,
            numColsToMove = 1,
            visibleToIdx;
        
        me.onHeadersChanged(headerToMove, true);
        visibleToIdx = gridVisibleColumnManager.indexOf(headerToMove);
        if (visibleToIdx >= headerToMove.visibleFromIdx) {
            visibleToIdx++;
        }
        me.callParent(arguments);
        
        if (headerToMove.isGroupHeader) {
            numColsToMove = headerToMove.visibleColumnManager.getColumns().length;
        }
        gridHeaderCt.onHeaderMoved(headerToMove, numColsToMove, headerToMove.visibleFromIdx, visibleToIdx);
    },
    
    
    
    onRemove: function(c) {
        var me = this,
            ownerCt = me.ownerCt;
        me.callParent(arguments);
        if (!me._usedIDs) {
            me._usedIDs = {};
        }
        delete me._usedIDs[c.headerId];
        if (!me.destroying) {
            
            
            if (!me.isDDMoveInGrid) {
                me.onHeadersChanged(c, false);
            }
            
            
            
            if (me.isGroupHeader && !me.isNestedParent && ownerCt && !me.items.getCount()) {
                
                
                
                if (c.rendered) {
                    me.detachComponent(c);
                }
                
                
                Ext.suspendLayouts();
                ownerCt.remove(me);
                Ext.resumeLayouts(true);
            }
        }
    },
    
    
    
    
    
    onHeadersChanged: function(c, isMove) {
        var gridPanel,
            gridHeaderCt = this.getRootHeaderCt();
        
        this.purgeHeaderCtCache(this);
        if (gridHeaderCt) {
            gridHeaderCt.onColumnsChanged();
            if (!c.isGroupHeader) {
                gridPanel = gridHeaderCt.ownerCt;
                
                
                if (gridPanel && !isMove) {
                    gridPanel.onHeadersChanged(gridHeaderCt, c);
                }
            }
        }
    },
    
    onHeaderMoved: function(header, colsToMove, fromIdx, toIdx) {
        var me = this,
            gridSection = me.ownerCt;
        if (me.rendered) {
            if (gridSection && gridSection.onHeaderMove) {
                gridSection.onHeaderMove(me, header, colsToMove, fromIdx, toIdx);
            }
            me.fireEvent('columnmove', me, header, fromIdx, toIdx);
        }
    },
    
    
    
    
    onColumnsChanged: function() {
        var me = this,
            menu = me.menu,
            columnItemSeparator, columnItem;
        if (me.rendered) {
            me.fireEvent('columnschanged', me);
            
            
            if (menu && (columnItemSeparator = menu.child('#columnItemSeparator'))) {
                columnItem = menu.child('#columnItem');
                
                
                columnItemSeparator.destroy();
                columnItem.destroy();
            }
        }
    },
    
    lookupComponent: function(comp) {
        var result = this.callParent(arguments);
        
        
        if (!result.isGroupHeader && result.width === undefined && !result.flex) {
            result.width = this.defaultWidth;
        }
        return result;
    },
    
    
    setSortState: function() {
        var store = this.up('[store]').store,
            columns = this.visibleColumnManager.getColumns(),
            len = columns.length,
            i, header, sorter;
        for (i = 0; i < len; i++) {
            header = columns[i];
            sorter = store.getSorters().get(header.getSortParam());
            
            header.setSortState(sorter);
        }
    },
    getHeaderMenu: function() {
        var menu = this.getMenu(),
            item;
        if (menu) {
            item = menu.child('#columnItem');
            if (item) {
                return item.menu;
            }
        }
        return null;
    },
    onHeaderVisibilityChange: function(header, visible) {
        var me = this,
            menu = me.getHeaderMenu(),
            item;
        
        me.purgeHeaderCtCache(header.ownerCt);
        if (menu) {
            
            item = me.getMenuItemForHeader(menu, header);
            if (item) {
                item.setChecked(visible, true);
            }
            
            if (menu.isVisible()) {
                me.menuTask.delay(50);
            }
        }
    },
    updateMenuDisabledState: function(menu) {
        var me = this,
            columns = me.query(':not([hidden])'),
            i,
            len = columns.length,
            item, checkItem, method;
        
        if (!menu) {
            menu = me.getMenu();
        }
        for (i = 0; i < len; ++i) {
            item = columns[i];
            checkItem = me.getMenuItemForHeader(menu, item);
            if (checkItem) {
                method = item.isHideable() ? 'enable' : 'disable';
                if (checkItem.menu) {
                    method += 'CheckChange';
                }
                checkItem[method]();
            }
        }
    },
    getMenuItemForHeader: function(menu, header) {
        return header ? menu.down('menucheckitem[headerId=' + header.id + ']') : null;
    },
    onHeaderShow: function(header) {
        var me = this,
            ownerCt = me.ownerCt;
        if (!ownerCt) {
            return;
        }
        if (me.forceFit) {
            delete me.flex;
        }
        me.onHeaderVisibilityChange(header, true);
        ownerCt.onHeaderShow(me, header);
        me.fireEvent('columnshow', me, header);
        me.fireEvent('columnschanged', this);
    },
    onHeaderHide: function(header) {
        var me = this,
            ownerCt = me.ownerCt;
        if (!ownerCt) {
            return;
        }
        me.onHeaderVisibilityChange(header, false);
        ownerCt.onHeaderHide(me, header);
        me.fireEvent('columnhide', me, header);
        me.fireEvent('columnschanged', this);
    },
    
    tempLock: function() {
        this.ddLock = true;
        Ext.Function.defer(function() {
            this.ddLock = false;
        }, 200, this);
    },
    onHeaderResize: function(header, w) {
        var me = this,
            gridSection = me.ownerCt;
        if (gridSection) {
            gridSection.onHeaderResize(me, header, w);
        }
        me.fireEvent('columnresize', me, header, w);
    },
    onHeaderClick: function(header, e, t) {
        var me = this,
            selModel = header.getView().getSelectionModel();
        header.fireEvent('headerclick', me, header, e, t);
        if (me.fireEvent('headerclick', me, header, e, t) !== false) {
            if (selModel.onHeaderClick) {
                selModel.onHeaderClick(me, header, e);
            }
        }
    },
    onHeaderContextMenu: function(header, e, t) {
        header.fireEvent('headercontextmenu', this, header, e, t);
        this.fireEvent('headercontextmenu', this, header, e, t);
    },
    onHeaderTriggerClick: function(header, e, t) {
        var me = this;
        if (header.fireEvent('headertriggerclick', me, header, e, t) !== false && me.fireEvent('headertriggerclick', me, header, e, t) !== false) {
            
            if (header.activeMenu) {
                
                if (e.pointerType) {
                    header.activeMenu.hide();
                } else {
                    header.activeMenu.focus();
                }
            } else {
                me.showMenuBy(e, t, header);
            }
        }
    },
    
    showMenuBy: function(clickEvent, t, header) {
        var menu = this.getMenu(),
            ascItem = menu.down('#ascItem'),
            descItem = menu.down('#descItem'),
            sortableMth;
        
        
        menu.activeHeader = menu.ownerCmp = header;
        header.setMenuActive(menu);
        
        sortableMth = header.sortable ? 'enable' : 'disable';
        if (ascItem) {
            ascItem[sortableMth]();
        }
        if (descItem) {
            descItem[sortableMth]();
        }
        
        menu.autoFocus = !clickEvent || !clickEvent.pointerType;
        menu.showBy(t, 'tl-bl?');
    },
    hideMenu: function() {
        if (this.menu) {
            this.menu.hide();
        }
    },
    
    onMenuHide: function(menu) {
        menu.activeHeader.setMenuActive(false);
    },
    moveHeader: function(fromIdx, toIdx) {
        
        this.tempLock();
        this.move(fromIdx, toIdx);
    },
    purgeHeaderCtCache: function(headerCt) {
        while (headerCt) {
            headerCt.purgeCache();
            if (headerCt.isRootHeader) {
                return;
            }
            headerCt = headerCt.ownerCt;
        }
    },
    purgeCache: function() {
        var me = this,
            visibleColumnManager = me.visibleColumnManager,
            columnManager = me.columnManager;
        
        me.gridVisibleColumns = me.gridDataColumns = me.hideableColumns = null;
        
        if (visibleColumnManager) {
            visibleColumnManager.invalidate();
            columnManager.invalidate();
        }
    },
    
    getMenu: function() {
        var me = this;
        if (!me.menu) {
            me.menu = new Ext.menu.Menu({
                hideOnParentHide: false,
                
                items: me.getMenuItems(),
                listeners: {
                    beforeshow: me.beforeMenuShow,
                    hide: me.onMenuHide,
                    scope: me
                }
            });
            me.fireEvent('menucreate', me, me.menu);
        }
        return me.menu;
    },
    
    beforeMenuShow: function(menu) {
        var me = this,
            columnItem = menu.child('#columnItem'),
            hideableColumns, insertPoint;
        
        
        
        if (!columnItem) {
            hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null;
            
            insertPoint = me.sortable ? 2 : 0;
            if (hideableColumns && hideableColumns.length) {
                menu.insert(insertPoint, [
                    {
                        itemId: 'columnItemSeparator',
                        xtype: 'menuseparator'
                    },
                    {
                        itemId: 'columnItem',
                        text: me.columnsText,
                        iconCls: me.menuColsIcon,
                        menu: {
                            items: hideableColumns
                        },
                        hideOnClick: false
                    }
                ]);
            }
        }
        me.updateMenuDisabledState(me.menu);
    },
    
    
    
    
    
    
    getMenuItems: function() {
        var me = this,
            menuItems = [],
            hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null;
        if (me.sortable) {
            menuItems = [
                {
                    itemId: 'ascItem',
                    text: me.sortAscText,
                    iconCls: me.menuSortAscCls,
                    handler: me.onSortAscClick,
                    scope: me
                },
                {
                    itemId: 'descItem',
                    text: me.sortDescText,
                    iconCls: me.menuSortDescCls,
                    handler: me.onSortDescClick,
                    scope: me
                }
            ];
        }
        if (hideableColumns && hideableColumns.length) {
            if (me.sortable) {
                menuItems.push({
                    itemId: 'columnItemSeparator',
                    xtype: 'menuseparator'
                });
            }
            menuItems.push({
                itemId: 'columnItem',
                text: me.columnsText,
                iconCls: me.menuColsIcon,
                menu: hideableColumns,
                hideOnClick: false
            });
        }
        return menuItems;
    },
    
    onSortAscClick: function() {
        var menu = this.getMenu(),
            activeHeader = menu.activeHeader;
        activeHeader.sort('ASC');
    },
    
    onSortDescClick: function() {
        var menu = this.getMenu(),
            activeHeader = menu.activeHeader;
        activeHeader.sort('DESC');
    },
    
    getColumnMenu: function(headerContainer) {
        var menuItems = [],
            i = 0,
            item,
            items = headerContainer.query('>gridcolumn[hideable]'),
            itemsLn = items.length,
            menuItem;
        for (; i < itemsLn; i++) {
            item = items[i];
            menuItem = new Ext.menu.CheckItem({
                text: item.menuText || item.text,
                checked: !item.hidden,
                hideOnClick: false,
                headerId: item.id,
                menu: item.isGroupHeader ? this.getColumnMenu(item) : undefined,
                checkHandler: this.onColumnCheckChange,
                scope: this
            });
            menuItems.push(menuItem);
        }
        
        return menuItems.length ? menuItems : null;
    },
    onColumnCheckChange: function(checkItem, checked) {
        var header = Ext.getCmp(checkItem.headerId),
            headerId;
        if (header.rendered) {
            header[checked ? 'show' : 'hide']();
            headerId = header.lastCheckedHeaderId;
            
            
            if (checked && headerId) {
                header.getRootHeaderCt().getMenu().down('[headerId=' + headerId + ']').setChecked(true);
                header.lastCheckedHeaderId = null;
            }
        } else {
            header.hidden = !checked;
        }
    },
    
    getColumnCount: function() {
        return this.getGridColumns().length;
    },
    
    getTableWidth: function() {
        var fullWidth = 0,
            headers = this.getVisibleGridColumns(),
            headersLn = headers.length,
            i;
        for (i = 0; i < headersLn; i++) {
            fullWidth += headers[i].getCellWidth() || 0;
        }
        return fullWidth;
    },
    
    getVisibleGridColumns: function() {
        var me = this,
            allColumns, rootHeader, result, len, i, column;
        if (me.gridVisibleColumns) {
            return me.gridVisibleColumns;
        }
        allColumns = me.getGridColumns();
        rootHeader = me.getRootHeaderCt();
        result = [];
        len = allColumns.length;
        
        
        for (i = 0; i < len; i++) {
            column = allColumns[i];
            if (!column.hidden && !column.isColumnHidden(rootHeader)) {
                result[result.length] = column;
            }
        }
        me.gridVisibleColumns = result;
        return result;
    },
    isColumnHidden: function(rootHeader) {
        var owner = this.getRefOwner();
        while (owner && owner !== rootHeader) {
            if (owner.hidden) {
                return true;
            }
            owner = owner.getRefOwner();
        }
        return false;
    },
    
    getGridColumns: function(
    inResult, hiddenAncestor) {
        if (!inResult && this.gridDataColumns) {
            return this.gridDataColumns;
        }
        var me = this,
            result = inResult || [],
            items, i, len, item, lastVisibleColumn;
        hiddenAncestor = hiddenAncestor || me.hidden;
        if (me.items) {
            items = me.items.items;
            
            if (items) {
                for (i = 0 , len = items.length; i < len; i++) {
                    item = items[i];
                    if (item.isGroupHeader) {
                        
                        
                        item.visibleIndex = result.length;
                        item.getGridColumns(result, hiddenAncestor);
                    } else {
                        item.hiddenAncestor = hiddenAncestor;
                        result.push(item);
                    }
                }
            }
        }
        if (!inResult) {
            me.gridDataColumns = result;
        }
        
        if (!inResult && len) {
            
            for (i = 0 , len = result.length; i < len; i++) {
                item = result[i];
                
                
                item.fullColumnIndex = i;
                item.isFirstVisible = item.isLastVisible = false;
                if (!(item.hidden || item.hiddenAncestor)) {
                    if (!lastVisibleColumn) {
                        item.isFirstVisible = true;
                    }
                    lastVisibleColumn = item;
                }
            }
            
            if (lastVisibleColumn) {
                lastVisibleColumn.isLastVisible = true;
            }
        }
        return result;
    },
    
    getHideableColumns: function() {
        var me = this,
            result = me.hideableColumns;
        if (!result) {
            result = me.hideableColumns = me.query('[hideable]');
        }
        return result;
    },
    
    getHeaderIndex: function(header) {
        
        
        if (!this.columnManager) {
            this.columnManager = this.getRootHeaderCt().columnManager;
        }
        return this.columnManager.getHeaderIndex(header);
    },
    
    getHeaderAtIndex: function(index) {
        
        
        if (!this.columnManager) {
            this.columnManager = this.getRootHeaderCt().columnManager;
        }
        return this.columnManager.getHeaderAtIndex(index);
    },
    
    getVisibleHeaderClosestToIndex: function(index) {
        
        
        if (!this.visibleColumnManager) {
            this.visibleColumnManager = this.getRootHeaderCt().visibleColumnManager;
        }
        return this.visibleColumnManager.getVisibleHeaderClosestToIndex(index);
    },
    applyForceFit: function(header) {
        var me = this,
            view = me.view,
            minWidth = Ext.grid.plugin.HeaderResizer.prototype.minColWidth,
            
            useMinWidthForFlex = false,
            defaultWidth = Ext.grid.header.Container.prototype.defaultWidth,
            availFlex = me.el.dom.clientWidth - (view.el.dom.scrollHeight > view.el.dom.clientHeight ? Ext.getScrollbarSize().width : 0),
            totalFlex = 0,
            items = me.getVisibleGridColumns(),
            hidden = header.hidden,
            len, i, item, maxAvailFlexOneColumn, myWidth;
        function getTotalFlex() {
            for (i = 0 , len = items.length; i < len; i++) {
                item = items[i];
                
                if (item === header) {
                    
                    continue;
                }
                item.flex = item.flex || item.width || item.getWidth();
                totalFlex += item.flex;
                item.width = null;
            }
        }
        function applyWidth() {
            
            
            var isCurrentHeader;
            for (i = 0 , len = items.length; i < len; i++) {
                item = items[i];
                isCurrentHeader = (item === header);
                if (useMinWidthForFlex && !isCurrentHeader) {
                    
                    item.flex = minWidth;
                    item.width = null;
                } else if (!isCurrentHeader) {
                    
                    
                    
                    
                    
                    myWidth = item.flex || defaultWidth;
                    item.flex = Math.max(Math.ceil((myWidth / totalFlex) * availFlex), minWidth);
                    item.width = null;
                }
                item.setWidth(item.width || item.flex);
            }
        }
        Ext.suspendLayouts();
        
        maxAvailFlexOneColumn = (availFlex - ((items.length + 1) * minWidth));
        
        
        header.flex = null;
        if (hidden) {
            myWidth = header.width || header.savedWidth;
            header.savedWidth = null;
        } else {
            myWidth = view.getMaxContentWidth(header);
        }
        
        
        if (myWidth > maxAvailFlexOneColumn) {
            header.width = maxAvailFlexOneColumn;
            useMinWidthForFlex = true;
        } else {
            header.width = myWidth;
            
            
            availFlex -= myWidth + defaultWidth;
            getTotalFlex();
        }
        applyWidth();
        Ext.resumeLayouts(true);
    },
    autoSizeColumn: function(header) {
        var view = this.view;
        if (view) {
            view.autoSizeColumn(header);
            if (this.forceFit) {
                this.applyForceFit(header);
            }
        }
    },
    privates: {
        getFocusables: function() {
            return this.isRootHeader ? this.getVisibleGridColumns() : this.items.items;
        },
        createFocusableContainerKeyNav: function(el) {
            var me = this;
            return new Ext.util.KeyNav(el,{
                scope: me,
                down: me.showHeaderMenu,
                left: me.onFocusableContainerLeftKey,
                right: me.onFocusableContainerRightKey,
                space: me.onHeaderActivate,
                enter: me.onHeaderActivate
            });
        },
        showHeaderMenu: function(e) {
            var column = this.getFocusableFromEvent(e);
            if (column && column.triggerEl) {
                this.onHeaderTriggerClick(column, e, column.triggerEl);
            }
        },
        onHeaderActivate: function(e) {
            var column = this.getFocusableFromEvent(e);
            if (column) {
                
                
                if (column.sortable && this.sortOnClick) {
                    column.toggleSortState();
                }
                
                this.onHeaderClick(column, e, column.el);
            }
        },
        onFocusableContainerMousedown: function(e, target) {
            var targetCmp = Ext.ComponentManager.byElement(target);
            if (targetCmp === this) {
                e.preventDefault();
            } else {
                
                
                targetCmp.focus();
            }
        }
    }
});


Ext.define('Ext.grid.ColumnComponentLayout', {
    extend: 'Ext.layout.component.Auto',
    alias: 'layout.columncomponent',
    type: 'columncomponent',
    setWidthInDom: true,
    _paddingReset: {
        paddingTop: '',
        
        paddingBottom: ''
    },
    beginLayout: function(ownerContext) {
        this.callParent(arguments);
        ownerContext.titleContext = ownerContext.getEl('titleEl');
    },
    beginLayoutCycle: function(ownerContext) {
        var me = this,
            owner = me.owner;
        me.callParent(arguments);
        
        if (ownerContext.widthModel.shrinkWrap) {
            owner.el.setWidth('');
        }
        owner.titleEl.setStyle(me._paddingReset);
    },
    
    publishInnerHeight: function(ownerContext, outerHeight) {
        var me = this,
            owner = me.owner,
            innerHeight;
        
        
        
        if (owner.getRootHeaderCt().hiddenHeaders) {
            ownerContext.setProp('innerHeight', 0);
            return;
        }
        
        
        if (!ownerContext.hasRawContent) {
            
            if (owner.headerWrap && !ownerContext.hasDomProp('width')) {
                me.done = false;
                return;
            }
            innerHeight = outerHeight - ownerContext.getBorderInfo().height;
            ownerContext.setProp('innerHeight', innerHeight - owner.titleEl.getHeight(), false);
        }
    },
    
    
    
    measureContentHeight: function(ownerContext) {
        return ownerContext.el.dom.offsetHeight;
    },
    
    publishInnerWidth: function(ownerContext, outerWidth) {
        
        if (!ownerContext.hasRawContent) {
            ownerContext.setProp('innerWidth', outerWidth - ownerContext.getBorderInfo().width, false);
        }
    },
    
    calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) {
        var result = this.callParent(arguments),
            owner = this.owner;
        
        if (!ownerContext.hasRawContent) {
            if (!owner.headerWrap || ownerContext.hasDomProp('width')) {
                return contentHeight + owner.titleEl.getHeight() + ownerContext.getBorderInfo().height;
            }
            
            
            return null;
        }
        return result;
    },
    
    calculateOwnerWidthFromContentWidth: function(ownerContext, contentWidth) {
        var owner = this.owner,
            padWidth = ownerContext.getPaddingInfo().width,
            triggerOffset = this.getTriggerOffset(owner, ownerContext),
            inner;
        
        
        if (owner.isGroupHeader) {
            inner = contentWidth;
        } else {
            inner = Math.max(contentWidth, owner.textEl.getWidth() + ownerContext.titleContext.getPaddingInfo().width);
        }
        return inner + padWidth + triggerOffset;
    },
    getTriggerOffset: function(owner, ownerContext) {
        var width = 0;
        if (ownerContext.widthModel.shrinkWrap && !owner.menuDisabled) {
            
            if (owner.query('>:not([hidden])').length === 0) {
                width = owner.self.triggerElWidth;
            }
        }
        return width;
    }
});


Ext.define('Ext.grid.column.Column', {
    extend: 'Ext.grid.header.Container',
    xtype: 'gridcolumn',
    requires: [
        'Ext.grid.ColumnComponentLayout',
        'Ext.grid.ColumnLayout',
        'Ext.app.bind.Template'
    ],
    
    alternateClassName: 'Ext.grid.Column',
    config: {
        triggerVisible: false
    },
    
    
    
    
    
    
    baseCls: Ext.baseCSSPrefix + 'column-header',
    
    hoverCls: Ext.baseCSSPrefix + 'column-header-over',
    handleWidth: Ext.supports.Touch ? 10 : 4,
    ariaRole: 'columnheader',
    enableFocusableContainer: false,
    sortState: null,
    possibleSortStates: [
        'ASC',
        'DESC'
    ],
    childEls: [
        'titleEl',
        'triggerEl',
        'textEl'
    ],
    
    headerWrap: false,
    renderTpl: [
        '<div id="{id}-titleEl" data-ref="titleEl" {tipMarkup}class="',
        Ext.baseCSSPrefix,
        'column-header-inner<tpl if="!$comp.isContainer"> ',
        Ext.baseCSSPrefix,
        'leaf-column-header</tpl>',
        '<tpl if="empty"> ',
        Ext.baseCSSPrefix,
        'column-header-inner-empty</tpl>">',
        
        
        
        
        '<span class="',
        Ext.baseCSSPrefix,
        'column-header-text-container">',
        '<span class="',
        Ext.baseCSSPrefix,
        'column-header-text-wrapper">',
        '<span id="{id}-textEl" data-ref="textEl" class="',
        Ext.baseCSSPrefix,
        'column-header-text',
        '{childElCls}">',
        '{text}',
        '</span>',
        '</span>',
        '</span>',
        '<tpl if="!menuDisabled">',
        '<div id="{id}-triggerEl" data-ref="triggerEl" role="presentation" class="',
        Ext.baseCSSPrefix,
        'column-header-trigger',
        '{childElCls}" style="{triggerStyle}"></div>',
        '</tpl>',
        '</div>',
        '{%this.renderContainer(out,values)%}'
    ],
    
    
    
    dataIndex: null,
    
    text: '&#160;',
    
    
    menuText: null,
    
    emptyCellText: '&#160;',
    
    sortable: true,
    
    
    
    
    
    
    
    resizable: true,
    
    hideable: true,
    
    menuDisabled: false,
    
    renderer: false,
    
    
    
    
    
    
    align: 'left',
    
    draggable: true,
    
    
    tooltipType: 'qtip',
    
    
    initDraggable: Ext.emptyFn,
    
    tdCls: '',
    
    
    
    producesHTML: true,
    
    
    
    isHeader: true,
    
    isColumn: true,
    tabIndex: -1,
    ascSortCls: Ext.baseCSSPrefix + 'column-header-sort-ASC',
    descSortCls: Ext.baseCSSPrefix + 'column-header-sort-DESC',
    componentLayout: 'columncomponent',
    groupSubHeaderCls: Ext.baseCSSPrefix + 'group-sub-header',
    groupHeaderCls: Ext.baseCSSPrefix + 'group-header',
    clickTargetName: 'titleEl',
    
    detachOnRemove: true,
    
    initResizable: Ext.emptyFn,
    
    rendererNames: {
        column: 'renderer',
        edit: 'editRenderer',
        summary: 'summaryRenderer'
    },
    formatterNames: {
        column: 'formatter',
        edit: 'editFormatter',
        summary: 'summaryFormatter'
    },
    initComponent: function() {
        var me = this;
        if (me.header != null) {
            me.text = me.header;
            me.header = null;
        }
        if (me.cellWrap) {
            me.tdCls = (me.tdCls || '') + ' ' + Ext.baseCSSPrefix + 'wrap-cell';
        }
        
        if (me.columns != null) {
            me.isGroupHeader = true;
            if (me.dataIndex) {
                Ext.Error.raise('Ext.grid.column.Column: Group header may not accept a dataIndex');
            }
            if ((me.width && me.width !== Ext.grid.header.Container.prototype.defaultWidth) || me.flex) {
                Ext.Error.raise('Ext.grid.column.Column: Group header does not support setting explicit widths or flexs. The group header width is calculated by the sum of its children.');
            }
            
            me.items = me.columns;
            me.columns = me.flex = me.width = null;
            me.cls = (me.cls || '') + ' ' + me.groupHeaderCls;
            
            me.sortable = me.resizable = false;
            me.align = 'center';
        } else {
            
            
            
            if (me.flex) {
                me.minWidth = me.minWidth || Ext.grid.plugin.HeaderResizer.prototype.minColWidth;
            }
        }
        me.addCls(Ext.baseCSSPrefix + 'column-header-align-' + me.align);
        
        me.setupRenderer();
        me.setupRenderer('edit');
        me.setupRenderer('summary');
        
        me.callParent(arguments);
    },
    bindFormatter: function(format) {
        var me = this;
        return function(v) {
            return format.format(v, format.scope || me.scope || me.resolveListenerScope());
        };
    },
    bindRenderer: function(renderer) {
        var me = this;
        if (renderer in Ext.util.Format) {
            Ext.log.warn('Use "formatter" config instead of "renderer" to use ' + 'Ext.util.Format to format cell values');
        }
        me.hasCustomRenderer = true;
        return function() {
            return Ext.callback(renderer, me.scope, arguments, 0, me);
        };
    },
    
    setupRenderer: function(type) {
        type = type || 'column';
        var me = this,
            format = me[me.formatterNames[type]],
            renderer = me[me.rendererNames[type]],
            isColumnRenderer = type === 'column',
            scoped;
        if (!format) {
            if (renderer) {
                
                if (typeof renderer === 'string') {
                    renderer = me[me.rendererNames[type]] = me.bindRenderer(renderer);
                }
                
                if (isColumnRenderer) {
                    me.hasCustomRenderer = renderer.length > 1;
                }
            }
            
            else if (isColumnRenderer && me.defaultRenderer) {
                me.renderer = me.defaultRenderer;
                me.usingDefaultRenderer = true;
            }
        } else {
            scoped = format.indexOf('this.') === 0;
            if (scoped) {
                format = format.substring(5);
            }
            
            format = Ext.app.bind.Template.prototype.parseFormat(format);
            me[me.formatterNames[type]] = null;
            
            if (scoped) {
                format.scope = null;
            }
            
            else if (!Ext.util.Format[format.fmt]) {
                Ext.Error.raise('Invalid formatter specified: "' + format.fmt + '"');
            }
            
            me[me.rendererNames[type]] = me.bindFormatter(format);
        }
    },
    getView: function() {
        var rootHeaderCt = this.getRootHeaderCt();
        if (rootHeaderCt) {
            return rootHeaderCt.view;
        }
    },
    onResize: function(width, height, oldWidth, oldHeight) {
        var me = this,
            view, bufferedRenderer;
        me.callParent(arguments);
        if (oldWidth && me.cellWrap) {
            view = me.getView();
            if (view) {
                bufferedRenderer = view.bufferedRenderer;
                
                
                if (bufferedRenderer) {
                    bufferedRenderer.onWrappedColumnWidthChange(oldWidth, width);
                }
            }
        }
    },
    onFocusLeave: function(e) {
        this.callParent([
            e
        ]);
        if (this.activeMenu) {
            this.activeMenu.hide();
        }
    },
    initItems: function() {
        var me = this;
        me.callParent(arguments);
        if (me.isGroupHeader) {
            
            if (me.config.hidden || !me.hasVisibleChildColumns()) {
                me.hide();
            }
        }
    },
    hasVisibleChildColumns: function() {
        var items = this.items.items,
            len = items.length,
            i, item;
        for (i = 0; i < len; ++i) {
            item = items[i];
            if (item.isColumn && !item.hidden) {
                return true;
            }
        }
        return false;
    },
    onAdd: function(child) {
        var me = this;
        if (child.isColumn) {
            child.isSubHeader = true;
            child.addCls(me.groupSubHeaderCls);
        }
        if (me.isGroupHeader && me.hidden && me.hasVisibleChildColumns()) {
            me.show();
        }
        me.callParent([
            child
        ]);
    },
    onRemove: function(child) {
        var me = this;
        if (child.isSubHeader) {
            child.isSubHeader = false;
            child.removeCls(me.groupSubHeaderCls);
        }
        me.callParent([
            child
        ]);
        
        
        
        
        if (!(me.isDestroyed || me.destroying) && !me.hasVisibleChildColumns() && !me.ownerCt.isNested()) {
            me.hide();
        }
    },
    initRenderData: function() {
        var me = this,
            tipMarkup = '',
            tip = me.tooltip,
            text = me.text,
            attr = me.tooltipType === 'qtip' ? 'data-qtip' : 'title';
        if (!Ext.isEmpty(tip)) {
            tipMarkup = attr + '="' + tip + '" ';
        }
        return Ext.applyIf(me.callParent(arguments), {
            text: text,
            empty: text === '&#160;' || text === ' ' || text === '',
            menuDisabled: me.menuDisabled,
            tipMarkup: tipMarkup,
            triggerStyle: this.getTriggerVisible() ? 'display:block' : ''
        });
    },
    applyColumnState: function(state) {
        var me = this;
        
        me.applyColumnsState(state.columns);
        
        
        if (state.hidden != null) {
            me.hidden = state.hidden;
        }
        if (state.locked != null) {
            me.locked = state.locked;
        }
        if (state.sortable != null) {
            me.sortable = state.sortable;
        }
        if (state.width != null) {
            me.flex = null;
            me.width = state.width;
        } else if (state.flex != null) {
            me.width = null;
            me.flex = state.flex;
        }
    },
    getColumnState: function() {
        var me = this,
            items = me.items.items,
            
            iLen = items ? items.length : 0,
            i,
            columns = [],
            state = {
                id: me.getStateId()
            };
        me.savePropsToState([
            'hidden',
            'sortable',
            'locked',
            'flex',
            'width'
        ], state);
        if (me.isGroupHeader) {
            for (i = 0; i < iLen; i++) {
                columns.push(items[i].getColumnState());
            }
            if (columns.length) {
                state.columns = columns;
            }
        }
        if ('width' in state) {
            delete state.flex;
        }
        
        return state;
    },
    getStateId: function() {
        return this.stateId || this.headerId;
    },
    
    setText: function(text) {
        this.text = text;
        if (this.rendered) {
            this.textEl.setHtml(text);
        }
    },
    
    getIndex: function() {
        return this.isGroupColumn ? false : this.getRootHeaderCt().getHeaderIndex(this);
    },
    
    getVisibleIndex: function() {
        
        return this.visibleIndex != null ? this.visibleIndex : this.isGroupColumn ? false : Ext.Array.indexOf(this.getRootHeaderCt().getVisibleGridColumns(), this);
    },
    beforeRender: function() {
        var me = this,
            rootHeaderCt = me.getRootHeaderCt();
        me.callParent();
        
        
        if (!me.isSortable() && !me.groupable && !me.lockable && (rootHeaderCt.grid.enableColumnHide === false || !rootHeaderCt.getHideableColumns().length)) {
            me.menuDisabled = true;
        }
        
        
        
        
        
        if (me.cellWrap) {
            me.variableRowHeight = true;
        }
        me.protoEl.unselectable();
    },
    afterRender: function() {
        var me = this,
            triggerEl = me.triggerEl;
        me.callParent(arguments);
        if (triggerEl && me.self.triggerElWidth === undefined) {
            triggerEl.setStyle('display', 'block');
            me.self.triggerElWidth = triggerEl.getWidth();
            triggerEl.setStyle('display', '');
        }
    },
    
    
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var me = this,
            rootHeaderCt = me.getRootHeaderCt();
        me.callParent(arguments);
        if (rootHeaderCt && (oldWidth != null || me.flex) && width !== oldWidth) {
            rootHeaderCt.onHeaderResize(me, width);
        }
    },
    onDestroy: function() {
        var me = this;
        
        Ext.destroy(me.field);
        me.field = null;
        me.callParent(arguments);
    },
    onTitleMouseOver: function() {
        this.titleEl.addCls(this.hoverCls);
    },
    onTitleMouseOut: function() {
        this.titleEl.removeCls(this.hoverCls);
    },
    onDownKey: function(e) {
        if (this.triggerEl) {
            this.onTitleElClick(e, this.triggerEl.dom || this.el.dom);
        }
    },
    onEnterKey: function(e) {
        this.onTitleElClick(e, this.el.dom);
    },
    
    onTitleElDblClick: function(e) {
        var me = this,
            prev, leafColumns, headerCt;
        
        if (me.isOnLeftEdge(e)) {
            
            
            prev = me.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])');
            
            if (prev && prev.getRootHeaderCt() === me.getRootHeaderCt()) {
                prev.autoSize();
            }
        }
        
        else if (me.isOnRightEdge(e)) {
            
            if (me.isGroupHeader && e.getPoint().isContainedBy(me.layout.innerCt)) {
                leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])');
                me.getRootHeaderCt().autoSizeColumn(leafColumns[leafColumns.length - 1]);
                return;
            } else {
                headerCt = me.getRootHeaderCt();
                
                if (headerCt.visibleColumnManager.getColumns().length === 1 && headerCt.forceFit) {
                    return;
                }
            }
            me.autoSize();
        }
    },
    
    autoSize: function() {
        var me = this,
            leafColumns, numLeaves, i, headerCt;
        
        if (me.isGroupHeader) {
            leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])');
            numLeaves = leafColumns.length;
            headerCt = me.getRootHeaderCt();
            Ext.suspendLayouts();
            for (i = 0; i < numLeaves; i++) {
                headerCt.autoSizeColumn(leafColumns[i]);
            }
            Ext.resumeLayouts(true);
            return;
        }
        me.getRootHeaderCt().autoSizeColumn(me);
    },
    onTitleElClick: function(e, t, sortOnClick) {
        var me = this,
            activeHeader, prevSibling;
        
        if (Ext.supports.Touch) {
            prevSibling = me.previousSibling(':not([hidden])');
            
            if (!me.menuDisabled && me.isOnRightEdge(e, parseInt(me.triggerEl.getStyle('width')))) {
                if (!me.menuDisabled) {
                    activeHeader = me;
                }
            }
            
            else if (prevSibling && !prevSibling.menuDisabled && me.isOnLeftEdge(e)) {
                activeHeader = prevSibling;
            }
        } else {
            
            
            activeHeader = me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl || e.within(me.triggerEl)) ? me : null;
        }
        
        if (sortOnClick !== false && (!activeHeader && !me.isOnLeftEdge(e) && !me.isOnRightEdge(e) || e.getKey())) {
            me.toggleSortState();
        }
        return activeHeader;
    },
    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
        return this.fireEvent.apply(this, arguments);
    },
    isSortable: function() {
        var rootHeader = this.getRootHeaderCt(),
            grid = rootHeader ? rootHeader.grid : null,
            sortable = this.sortable;
        if (grid && grid.sortableColumns === false) {
            sortable = false;
        }
        return sortable;
    },
    toggleSortState: function() {
        if (this.isSortable()) {
            this.sort();
        }
    },
    sort: function(direction) {
        var me = this,
            grid = me.up('tablepanel'),
            store = grid.store;
        
        
        
        
        Ext.suspendLayouts();
        me.sorting = true;
        store.sort(me.getSortParam(), direction, grid.multiColumnSort ? 'multi' : 'replace');
        delete me.sorting;
        Ext.resumeLayouts(true);
    },
    
    getSortParam: function() {
        return this.dataIndex;
    },
    
    
    
    setSortState: function(sorter) {
        var me = this,
            direction = sorter && sorter.getDirection(),
            ascCls = me.ascSortCls,
            descCls = me.descSortCls,
            rootHeaderCt = me.getRootHeaderCt(),
            changed;
        switch (direction) {
            case 'DESC':
                if (!me.hasCls(descCls)) {
                    me.addCls(descCls);
                    me.sortState = 'DESC';
                    changed = true;
                };
                me.removeCls(ascCls);
                break;
            case 'ASC':
                if (!me.hasCls(ascCls)) {
                    me.addCls(ascCls);
                    me.sortState = 'ASC';
                    changed = true;
                };
                me.removeCls(descCls);
                break;
            default:
                me.removeCls([
                    ascCls,
                    descCls
                ]);
                me.sortState = null;
                break;
        }
        
        if (changed) {
            rootHeaderCt.fireEvent('sortchange', rootHeaderCt, me, direction);
        }
    },
    
    isHideable: function() {
        var result = {
                hideCandidate: this,
                result: this.hideable
            };
        if (result.result) {
            this.ownerCt.bubble(this.hasOtherMenuEnabledChildren, null, [
                result
            ]);
        }
        return result.result;
    },
    
    
    hasOtherMenuEnabledChildren: function(result) {
        var visibleChildren, count;
        
        
        if (!this.isXType('headercontainer')) {
            result.result = false;
            return false;
        }
        
        
        
        
        visibleChildren = this.query('>:not([hidden]):not([menuDisabled])');
        count = visibleChildren.length;
        if (Ext.Array.contains(visibleChildren, result.hideCandidate)) {
            count--;
        }
        if (count) {
            return false;
        }
        
        result.hideCandidate = this;
    },
    
    isLockable: function() {
        var result = {
                result: this.lockable !== false
            };
        if (result.result) {
            this.ownerCt.bubble(this.hasMultipleVisibleChildren, null, [
                result
            ]);
        }
        return result.result;
    },
    
    isLocked: function() {
        return this.locked || !!this.up('[isColumn][locked]', '[isRootHeader]');
    },
    
    
    hasMultipleVisibleChildren: function(result) {
        
        if (!this.isXType('headercontainer')) {
            result.result = false;
            return false;
        }
        
        if (this.query('>:not([hidden])').length > 1) {
            return false;
        }
    },
    hide: function() {
        var me = this,
            rootHeaderCt = me.getRootHeaderCt(),
            owner = me.getRefOwner(),
            items;
        
        if (owner.constructing) {
            me.callParent();
            return me;
        }
        if (me.rendered && !me.isVisible()) {
            
            return me;
        }
        
        
        
        if (rootHeaderCt.forceFit) {
            me.visibleSiblingCount = rootHeaderCt.getVisibleGridColumns().length - 1;
            if (me.flex) {
                me.savedWidth = me.getWidth();
                me.flex = null;
            }
        }
        
        if (owner.isGroupHeader) {
            
            if (me.isNestedGroupHeader()) {
                owner.hide();
            }
            if (me.isSubHeader && !me.isGroupHeader && owner.query('>:not([hidden])').length === 1) {
                
                
                owner.lastCheckedHeaderId = me.id;
            }
        }
        Ext.suspendLayouts();
        me.callParent();
        
        rootHeaderCt.onHeaderHide(me);
        Ext.resumeLayouts(true);
        return me;
    },
    show: function() {
        var me = this,
            rootHeaderCt = me.getRootHeaderCt(),
            ownerCt = me.ownerCt;
        if (me.isVisible()) {
            return me;
        }
        if (me.rendered) {
            
            if (rootHeaderCt.forceFit) {
                rootHeaderCt.applyForceFit(me);
            }
        }
        Ext.suspendLayouts();
        
        if (me.isSubHeader && ownerCt.hidden) {
            ownerCt.show(false, true);
        }
        me.callParent(arguments);
        if (me.isGroupHeader) {
            me.maybeShowNestedGroupHeader();
        }
        
        ownerCt = me.getRootHeaderCt();
        if (ownerCt) {
            ownerCt.onHeaderShow(me);
        }
        Ext.resumeLayouts(true);
        return me;
    },
    getCellWidth: function() {
        var me = this,
            result;
        if (me.rendered && me.componentLayout && me.componentLayout.lastComponentSize) {
            
            
            
            
            
            
            result = me.componentLayout.lastComponentSize.width;
        } else if (me.width) {
            result = me.width;
        }
        
        
        else if (!me.isColumn) {
            result = me.getTableWidth();
        }
        return result;
    },
    getCellId: function() {
        return Ext.baseCSSPrefix + 'grid-cell-' + this.getItemId();
    },
    getCellSelector: function() {
        return '.' + this.getCellId();
    },
    getCellInnerSelector: function() {
        return this.getCellSelector() + ' .' + Ext.baseCSSPrefix + 'grid-cell-inner';
    },
    isOnLeftEdge: function(e) {
        return (e.getXY()[0] - this.getX() < this.handleWidth);
    },
    isOnRightEdge: function(e, margin) {
        return (this.getX() + this.getWidth() - e.getXY()[0] <= (margin || this.handleWidth));
    },
    
    
    setMenuActive: function(menu) {
        this.activeMenu = menu;
        this.titleEl[menu ? 'addCls' : 'removeCls'](this.headerOpenCls);
    },
    deprecated: {
        5: {
            methods: {
                bindRenderer: function(renderer) {
                    
                    
                    
                    return function(value) {
                        return Ext.util.Format[renderer](value);
                    };
                }
            }
        }
    }
});


Ext.define('Ext.tree.Column', {
    extend: 'Ext.grid.column.Column',
    alias: 'widget.treecolumn',
    tdCls: Ext.baseCSSPrefix + 'grid-cell-treecolumn',
    autoLock: true,
    lockable: false,
    draggable: false,
    hideable: false,
    iconCls: Ext.baseCSSPrefix + 'tree-icon',
    checkboxCls: Ext.baseCSSPrefix + 'tree-checkbox',
    elbowCls: Ext.baseCSSPrefix + 'tree-elbow',
    expanderCls: Ext.baseCSSPrefix + 'tree-expander',
    textCls: Ext.baseCSSPrefix + 'tree-node-text',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-treecolumn',
    isTreeColumn: true,
    cellTpl: [
        '<tpl for="lines">',
        '<img src="{parent.blankUrl}" class="{parent.childCls} {parent.elbowCls}-img ',
        '{parent.elbowCls}-<tpl if=".">line<tpl else>empty</tpl>" role="presentation"/>',
        '</tpl>',
        '<img src="{blankUrl}" class="{childCls} {elbowCls}-img {elbowCls}',
        '<tpl if="isLast">-end</tpl><tpl if="expandable">-plus {expanderCls}</tpl>" role="presentation"/>',
        '<tpl if="checked !== null">',
        '<input type="button" {ariaCellCheckboxAttr}',
        ' class="{childCls} {checkboxCls}<tpl if="checked"> {checkboxCls}-checked</tpl>"/>',
        '</tpl>',
        '<img src="{blankUrl}" role="presentation" class="{childCls} {baseIconCls} ',
        '{baseIconCls}-<tpl if="leaf">leaf<tpl else>parent</tpl> {iconCls}"',
        '<tpl if="icon">style="background-image:url({icon})"</tpl>/>',
        '<tpl if="href">',
        '<a href="{href}" role="link" target="{hrefTarget}" class="{textCls} {childCls}">{value}</a>',
        '<tpl else>',
        '<span class="{textCls} {childCls}">{value}</span>',
        '</tpl>'
    ],
    initComponent: function() {
        var me = this;
        me.setupRenderer();
        me.origRenderer = me.renderer;
        me.origScope = me.scope || window;
        me.renderer = me.treeRenderer;
        me.scope = me;
        me.callParent();
    },
    treeRenderer: function(value, metaData, record, rowIdx, colIdx, store, view) {
        var me = this,
            cls = record.get('cls'),
            rendererData;
        if (cls) {
            metaData.tdCls += ' ' + cls;
        }
        rendererData = me.initTemplateRendererData(value, metaData, record, rowIdx, colIdx, store, view);
        return me.getTpl('cellTpl').apply(rendererData);
    },
    initTemplateRendererData: function(value, metaData, record, rowIdx, colIdx, store, view) {
        var me = this,
            renderer = me.origRenderer,
            data = record.data,
            parent = record.parentNode,
            rootVisible = view.rootVisible,
            lines = [],
            parentData;
        while (parent && (rootVisible || parent.data.depth > 0)) {
            parentData = parent.data;
            lines[rootVisible ? parentData.depth : parentData.depth - 1] = parentData.isLast ? 0 : 1;
            parent = parent.parentNode;
        }
        return {
            record: record,
            baseIconCls: me.iconCls,
            iconCls: data.iconCls,
            icon: data.icon,
            checkboxCls: me.checkboxCls,
            checked: data.checked,
            elbowCls: me.elbowCls,
            expanderCls: me.expanderCls,
            textCls: me.textCls,
            leaf: data.leaf,
            expandable: record.isExpandable(),
            isLast: record.isLastVisible(),
            blankUrl: Ext.BLANK_IMAGE_URL,
            href: data.href,
            hrefTarget: data.hrefTarget,
            lines: lines,
            metaData: metaData,
            
            
            
            
            childCls: me.getChildCls ? me.getChildCls() + ' ' : '',
            value: renderer ? renderer.apply(me.origScope, arguments) : value
        };
    }
});



Ext.define('Ext.grid.NavigationModel', {
    extend: 'Ext.view.NavigationModel',
    alias: 'view.navigation.grid',
    
    focusCls: Ext.baseCSSPrefix + 'grid-item-focused',
    getViewListeners: function() {
        var me = this;
        return {
            containermousedown: me.onContainerMouseDown,
            cellmousedown: me.onCellMouseDown,
            
            cellclick: me.onCellClick,
            itemmousedown: me.onItemMouseDown,
            
            itemclick: me.onItemClick,
            itemcontextmenu: me.onItemClick,
            scope: me
        };
    },
    initKeyNav: function(view) {
        var me = this;
        me.position = new Ext.grid.CellContext(view);
        
        
        
        me.keyNav = new Ext.util.KeyNav({
            target: view,
            ignoreInputFields: true,
            eventName: 'itemkeydown',
            defaultEventAction: 'stopEvent',
            
            processEvent: function(view, record, row, recordIndex, event) {
                return event;
            },
            up: me.onKeyUp,
            down: me.onKeyDown,
            right: me.onKeyRight,
            left: me.onKeyLeft,
            pageDown: me.onKeyPageDown,
            pageUp: me.onKeyPageUp,
            home: me.onKeyHome,
            end: me.onKeyEnd,
            tab: me.onKeyTab,
            space: me.onKeySpace,
            enter: me.onKeyEnter,
            A: {
                ctrl: true,
                
                
                handler: me.onSelectAllKeyPress
            },
            scope: me
        });
    },
    onCellMouseDown: function(view, cell, cellIndex, record, row, recordIndex, mousedownEvent) {
        var parentEvent = mousedownEvent.parentEvent;
        
        if (!parentEvent || parentEvent.type !== 'touchstart') {
            this.setPosition(mousedownEvent.position, null, mousedownEvent);
        }
    },
    onCellClick: function(view, cell, cellIndex, record, row, recordIndex, clickEvent) {
        
        if (this.position.isEqual(clickEvent.position)) {
            this.fireNavigateEvent(clickEvent);
        } else {
            this.setPosition(clickEvent.position, null, clickEvent);
        }
    },
    onItemMouseDown: function(view, record, item, index, mousedownEvent) {
        var me = this,
            x, columns, len, i, column, b,
            parentEvent = mousedownEvent.parentEvent;
        
        if (!parentEvent || parentEvent.type !== 'touchstart') {
            
            if (!mousedownEvent.position.cellElement) {
                x = mousedownEvent.getX();
                columns = view.getVisibleColumnManager().getColumns();
                len = columns.length;
                for (i = 0; i < len; i++) {
                    column = columns[i];
                    b = columns[i].getBox();
                    if (x >= b.left && x < b.right) {
                        me.setPosition(record, columns[i], mousedownEvent);
                        return;
                    }
                }
            }
        }
    },
    onItemClick: function(view, record, item, index, clickEvent) {
        
        if (!clickEvent.position.cellElement) {
            this.fireNavigateEvent(clickEvent);
        }
    },
    beforeViewRefresh: function() {
        
        
        var position = this.getPosition();
        if (position && position.view === this) {
            this.focusRestorePosition = position;
        } else {
            this.focusRestorePosition = null;
        }
    },
    
    
    onStoreRemove: function() {
        if (this.position) {
            this.setPosition(this.getPosition(), null, null, null, true);
        }
    },
    deferSetPosition: function(delay, recordIndex, columnIndex, keyEvent, suppressEvent, preventNavigation) {
        var setPositionTask = this.view.getFocusTask();
        
        
        setPositionTask.delay(delay, this.setPosition, this, [
            recordIndex,
            columnIndex,
            keyEvent,
            suppressEvent,
            preventNavigation
        ]);
        return setPositionTask;
    },
    setPosition: function(recordIndex, columnIndex, keyEvent, suppressEvent, preventNavigation) {
        var me = this,
            view, selModel, dataSource, newRecordIndex, newColumnIndex, newRecord, newColumn,
            clearing = recordIndex == null && columnIndex == null,
            isClear = me.record == null && me.recordIndex == null && me.item == null;
        
        
        
        
        
        if (recordIndex && recordIndex.isCellContext) {
            view = recordIndex.view;
        } else if (keyEvent && keyEvent.view) {
            view = keyEvent.view;
        } else if (me.lastFocused) {
            view = me.lastFocused.view;
        } else {
            view = me.view;
        }
        selModel = view.getSelectionModel();
        dataSource = view.dataSource;
        
        view.getFocusTask().cancel();
        
        
        if (view.isDestroyed || !view.refreshCounter || clearing && isClear || !view.all.getCount()) {
            return;
        }
        
        
        if (recordIndex && recordIndex.isCellContext) {
            newRecord = recordIndex.record;
            newRecordIndex = recordIndex.rowIdx;
            newColumnIndex = recordIndex.colIdx;
            newColumn = recordIndex.column;
            
            if (dataSource.indexOf(newRecord) === -1) {
                newRecordIndex = dataSource.indexOfId(newRecord.id);
                if (newRecordIndex === -1) {
                    
                    me.recordIndex = -1;
                    newRecord = dataSource.getAt(0);
                    newRecordIndex = 0;
                    newColumnIndex = 0;
                    newColumn = view.getVisibleColumnManager().getColumns()[0];
                } else {
                    newRecord = dataSource.getById(newRecord.id);
                }
            }
        } else {
            
            if (clearing) {
                newRecord = newRecordIndex = null;
            } else {
                
                
                if (columnIndex == null) {
                    columnIndex = me.lastFocused ? me.lastFocused.column : 0;
                }
                if (typeof recordIndex === 'number') {
                    newRecordIndex = Math.max(Math.min(recordIndex, dataSource.getCount() - 1), 0);
                    newRecord = dataSource.getAt(recordIndex);
                }
                
                else if (recordIndex.isEntity) {
                    newRecord = recordIndex;
                    newRecordIndex = dataSource.indexOf(newRecord);
                }
                
                else if (recordIndex.tagName) {
                    newRecord = view.getRecord(recordIndex);
                    newRecordIndex = dataSource.indexOf(newRecord);
                    if (newRecordIndex === -1) {
                        newRecord = null;
                    }
                } else {
                    if (isClear) {
                        return;
                    }
                    clearing = true;
                    newRecord = newRecordIndex = null;
                }
            }
            
            if (newRecord) {
                
                if (newRecordIndex === -1) {
                    
                    me.recordIndex = -1;
                    newRecord = dataSource.getAt(0);
                    newRecordIndex = 0;
                    columnIndex = null;
                }
                
                if (columnIndex == null) {
                    if (!(newColumn = me.column)) {
                        newColumnIndex = 0;
                        newColumn = view.getVisibleColumnManager().getColumns()[0];
                    }
                } else if (typeof columnIndex === 'number') {
                    newColumn = view.getVisibleColumnManager().getColumns()[columnIndex];
                    newColumnIndex = columnIndex;
                } else {
                    newColumn = columnIndex;
                    newColumnIndex = view.getVisibleColumnManager().indexOf(columnIndex);
                }
            } else {
                clearing = true;
                newColumn = newColumnIndex = null;
            }
        }
        
        
        if (newRecordIndex === me.recordIndex && newColumnIndex === me.columnIndex) {
            return me.focusPosition(me.position);
        }
        if (me.cell) {
            me.cell.removeCls(me.focusCls);
        }
        
        
        me.previousRecordIndex = me.recordIndex;
        me.previousRecord = me.record;
        me.previousItem = me.item;
        me.previousCell = me.cell;
        me.previousColumn = me.column;
        me.previousColumnIndex = me.columnIndex;
        me.previousPosition = me.position.clone();
        
        me.position.setAll(view, me.recordIndex = newRecordIndex, me.columnIndex = newColumnIndex, me.record = newRecord, me.column = newColumn);
        if (clearing) {
            me.item = me.cell = null;
        } else {
            me.focusPosition(me.position, preventNavigation);
        }
        
        if (!suppressEvent) {
            selModel.fireEvent('focuschange', selModel, me.previousRecord, me.record);
            view.fireEvent('rowfocus', me.record, me.item, me.recordIndex);
            view.fireEvent('cellfocus', me.record, me.cell, me.position);
        }
        
        if (keyEvent && !preventNavigation && me.cell !== me.previousCell) {
            me.fireNavigateEvent(keyEvent);
        }
    },
    
    focusPosition: function(position) {
        var me = this,
            view, item, row;
        me.item = me.cell = null;
        if (position && position.record && position.column) {
            view = position.view;
            
            
            if (position.rowElement) {
                row = me.item = position.rowElement;
            } else {
                
                item = view.all.item(position.rowIdx, true);
                if (item) {
                    row = me.item = view.getRow(item);
                }
            }
            
            
            if (row) {
                
                
                me.cell = position.cellElement || Ext.fly(row).down(position.column.getCellSelector(), true);
                
                if (me.cell) {
                    me.cell = new Ext.dom.Fly(me.cell);
                    
                    view.lastFocused = me.lastFocused = me.position.clone();
                    me.focusItem(me.cell);
                    view.focusEl = me.cell;
                } else 
                {
                    me.position.setAll();
                    me.record = me.column = me.recordIndex = me.columnIndex = null;
                }
            } else 
            
            {
                row = view.dataSource.indexOf(position.record);
                me.position.setAll();
                me.record = me.column = me.recordIndex = me.columnIndex = null;
                
                
                if (row !== -1 && view.bufferedRenderer) {
                    me.lastKeyEvent = null;
                    view.bufferedRenderer.scrollTo(row, false, me.afterBufferedScrollTo, me);
                }
            }
        }
    },
    
    focusItem: function(item) {
        item.addCls(this.focusCls);
        item.focus();
    },
    getCell: function() {
        return this.cell;
    },
    getPosition: function() {
        var me = this,
            position = me.position,
            curIndex, view, dataSource;
        if (position.record && position.column) {
            view = position.view;
            dataSource = view.dataSource;
            curIndex = dataSource.indexOf(position.record);
            
            if (curIndex === -1) {
                curIndex = dataSource.indexOfId(position.record.id);
                
                if (curIndex === -1) {
                    curIndex = position.rowIdx;
                    
                    
                    if (!dataSource.getAt(curIndex)) {
                        curIndex = -1;
                    }
                }
                position.record = dataSource.getAt(curIndex);
            }
            
            if (curIndex === -1 || view.getVisibleColumnManager().indexOf(position.column) === -1) {
                position.setAll();
                me.record = me.column = me.recordIndex = me.columnIndex = null;
            } else {
                return position;
            }
        }
        return null;
    },
    getLastFocused: function() {
        var me = this,
            view,
            lastFocused = me.lastFocused;
        if (lastFocused && lastFocused.record && lastFocused.column) {
            view = lastFocused.view;
            
            if (view.dataSource.indexOf(lastFocused.record) !== -1 && view.getVisibleColumnManager().indexOf(lastFocused.column) !== -1) {
                return lastFocused;
            }
        }
    },
    onKeyUp: function(keyEvent) {
        var newRecord = keyEvent.view.walkRecs(keyEvent.record, -1);
        if (newRecord) {
            this.setPosition(newRecord, this.columnIndex, keyEvent);
        }
    },
    onKeyDown: function(keyEvent) {
        
        
        var newRecord = keyEvent.record.isExpandingOrCollapsing ? null : keyEvent.view.walkRecs(keyEvent.record, 1);
        if (newRecord) {
            this.setPosition(newRecord, this.columnIndex, keyEvent);
        }
    },
    onKeyRight: function(keyEvent) {
        var newPosition = this.move('right', keyEvent);
        if (newPosition) {
            this.setPosition(newPosition, null, keyEvent);
        }
    },
    onKeyLeft: function(keyEvent) {
        var newPosition = this.move('left', keyEvent);
        if (newPosition) {
            this.setPosition(newPosition, null, keyEvent);
        }
    },
    move: function(dir, keyEvent) {
        var me = this,
            position = me.getPosition();
        if (position && position.record) {
            
            
            return position.view.walkCells(position, dir, null, me.preventWrap);
        }
        
        return null;
    },
    
    onKeyPageDown: function(keyEvent) {
        var me = this,
            view = keyEvent.view,
            rowsVisible = me.getRowsVisible(),
            newIdx, newRecord;
        if (rowsVisible) {
            
            
            
            if (view.bufferedRenderer) {
                newIdx = Math.min(keyEvent.recordIndex + rowsVisible, view.dataSource.getCount() - 1);
                me.lastKeyEvent = keyEvent;
                view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me);
            } else {
                newRecord = view.walkRecs(keyEvent.record, rowsVisible);
                me.setPosition(newRecord, null, keyEvent);
            }
        }
    },
    
    onKeyPageUp: function(keyEvent) {
        var me = this,
            view = keyEvent.view,
            rowsVisible = me.getRowsVisible(),
            newIdx, newRecord;
        if (rowsVisible) {
            
            
            
            if (view.bufferedRenderer) {
                newIdx = Math.max(keyEvent.recordIndex - rowsVisible, 0);
                me.lastKeyEvent = keyEvent;
                view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me);
            } else {
                newRecord = view.walkRecs(keyEvent.record, -rowsVisible);
                me.setPosition(newRecord, null, keyEvent);
            }
        }
    },
    
    onKeyHome: function(keyEvent) {
        var me = this,
            view = keyEvent.view;
        
        if (keyEvent.altKey) {
            if (view.bufferedRenderer) {
                
                
                
                me.lastKeyEvent = keyEvent;
                view.bufferedRenderer.scrollTo(0, false, me.afterBufferedScrollTo, me);
            } else {
                
                me.setPosition(view.walkRecs(keyEvent.record, -view.dataSource.indexOf(keyEvent.record)), null, keyEvent);
            }
        } else 
        {
            me.setPosition(keyEvent.record, 0, keyEvent);
        }
    },
    afterBufferedScrollTo: function(newIdx, newRecord) {
        this.setPosition(newRecord, null, this.lastKeyEvent, null, !this.lastKeyEvent);
    },
    
    onKeyEnd: function(keyEvent) {
        var me = this,
            view = keyEvent.view;
        
        if (keyEvent.altKey) {
            if (view.bufferedRenderer) {
                
                
                
                me.lastKeyEvent = keyEvent;
                view.bufferedRenderer.scrollTo(view.store.getCount() - 1, false, me.afterBufferedScrollTo, me);
            } else {
                
                me.setPosition(view.walkRecs(keyEvent.record, view.dataSource.getCount() - 1 - view.dataSource.indexOf(keyEvent.record)), null, keyEvent);
            }
        } else 
        {
            me.setPosition(keyEvent.record, keyEvent.view.getVisibleColumnManager().getColumns().length - 1, keyEvent);
        }
    },
    
    
    
    getRowsVisible: function() {
        var rowsVisible = false,
            view = this.view,
            firstRow = view.all.first(),
            rowHeight, gridViewHeight;
        if (firstRow) {
            rowHeight = firstRow.getHeight();
            gridViewHeight = view.el.getHeight();
            rowsVisible = Math.floor(gridViewHeight / rowHeight);
        }
        return rowsVisible;
    },
    fireNavigateEvent: function(keyEvent) {
        var me = this;
        me.fireEvent('navigate', {
            view: me.position.view,
            navigationModel: me,
            keyEvent: keyEvent || new Ext.event.Event({}),
            previousPosition: me.previousPosition,
            previousRecordIndex: me.previousRecordIndex,
            previousRecord: me.previousRecord,
            previousItem: me.previousItem,
            previousCell: me.previousCell,
            previousColumnIndex: me.previousColumnIndex,
            previousColumn: me.previousColumn,
            position: me.position,
            recordIndex: me.recordIndex,
            record: me.record,
            item: me.item,
            cell: me.cell,
            columnIndex: me.columnIndex,
            column: me.column
        });
    }
});


Ext.define('Ext.tree.NavigationModel', {
    extend: 'Ext.grid.NavigationModel',
    alias: 'view.navigation.tree',
    initKeyNav: function(view) {
        var me = this,
            columns = me.view.ownerGrid.columns;
        
        me.isTreeGrid = columns && columns.length > 1;
        me.callParent([
            view
        ]);
        me.keyNav.map.addBinding([
            {
                key: '8',
                shift: true,
                handler: me.onAsterisk,
                scope: me
            },
            {
                key: Ext.event.Event.NUM_MULTIPLY,
                handler: me.onAsterisk,
                scope: me
            }
        ]);
        me.view.grid.on({
            columnschanged: me.onColumnsChanged,
            scope: me
        });
    },
    onColumnsChanged: function() {
        
        this.isTreeGrid = this.view.ownerGrid.getVisibleColumnManager().getColumns().length > 1;
    },
    onKeyLeft: function(keyEvent) {
        var me = this,
            view = keyEvent.view,
            record = me.record;
        
        if (me.isTreeGrid && !keyEvent.ctrlKey) {
            return me.callParent([
                keyEvent
            ]);
        }
        
        if (keyEvent.position.column.isTreeColumn && record.isExpanded()) {
            view.collapse(record);
        } else 
        {
            record = record.parentNode;
            if (record && !(record.isRoot() && !view.rootVisible)) {
                me.setPosition(record, null, keyEvent);
            }
        }
    },
    onKeyRight: function(keyEvent) {
        var me = this,
            record = me.record;
        
        if (me.isTreeGrid && !keyEvent.ctrlKey) {
            return me.callParent([
                keyEvent
            ]);
        }
        
        if (!record.isLeaf()) {
            if (keyEvent.position.column.isTreeColumn && !record.isExpanded()) {
                keyEvent.view.expand(record);
            } else if (record.isExpanded()) {
                record = record.childNodes[0];
                if (record) {
                    me.setPosition(record);
                }
            }
        }
    },
    onKeyEnter: function(keyEvent) {
        if (this.record.data.checked != null) {
            this.toggleCheck(keyEvent);
        } else {
            this.callParent([
                keyEvent
            ]);
        }
    },
    onKeySpace: function(keyEvent) {
        if (this.record.data.checked != null) {
            this.toggleCheck(keyEvent);
        } else {
            this.callParent([
                keyEvent
            ]);
        }
    },
    toggleCheck: function(keyEvent) {
        this.view.onCheckChange(this.record);
    },
    
    onAsterisk: function(keyEvent) {
        this.view.ownerCt.expandAll();
    }
});


Ext.define('Ext.tree.Panel', {
    extend: 'Ext.panel.Table',
    alias: 'widget.treepanel',
    alternateClassName: [
        'Ext.tree.TreePanel',
        'Ext.TreePanel'
    ],
    requires: [
        'Ext.tree.View',
        'Ext.selection.TreeModel',
        'Ext.tree.Column',
        'Ext.data.TreeStore',
        'Ext.tree.NavigationModel'
    ],
    viewType: 'treeview',
    treeCls: Ext.baseCSSPrefix + 'tree-panel',
    
    rowLines: false,
    
    lines: true,
    
    useArrows: false,
    
    singleExpand: false,
    ddConfig: {
        enableDrag: true,
        enableDrop: true
    },
    
    
    rootVisible: true,
    
    displayField: 'text',
    
    root: null,
    
    
    normalCfgCopy: [
        'displayField',
        'root',
        'singleExpand',
        'useArrows',
        'lines',
        'rootVisible',
        'scroll'
    ],
    lockedCfgCopy: [
        'displayField',
        'root',
        'singleExpand',
        'useArrows',
        'lines',
        'rootVisible'
    ],
    isTree: true,
    
    
    
    arrowCls: Ext.baseCSSPrefix + 'tree-arrows',
    linesCls: Ext.baseCSSPrefix + 'tree-lines',
    noLinesCls: Ext.baseCSSPrefix + 'tree-no-lines',
    autoWidthCls: Ext.baseCSSPrefix + 'autowidth-table',
    constructor: function(config) {
        config = config || {};
        if (config.animate === undefined) {
            config.animate = Ext.isBoolean(this.animate) ? this.animate : Ext.enableFx;
        }
        this.enableAnimations = config.animate;
        delete config.animate;
        this.callParent([
            config
        ]);
    },
    initComponent: function() {
        var me = this,
            cls = [
                me.treeCls
            ],
            store = me.store,
            view;
        if (me.useArrows) {
            cls.push(me.arrowCls);
            me.lines = false;
        }
        if (me.lines) {
            cls.push(me.linesCls);
        } else if (!me.useArrows) {
            cls.push(me.noLinesCls);
        }
        if (Ext.isString(store)) {
            store = me.store = Ext.StoreMgr.lookup(store);
        } else if (!store || !store.isStore) {
            store = Ext.apply({
                type: 'tree',
                root: me.root,
                fields: me.fields,
                model: me.model,
                proxy: 'memory',
                folderSort: me.folderSort
            }, store);
            store = me.store = Ext.StoreMgr.lookup(store);
        } else if (me.root) {
            store = me.store = Ext.data.StoreManager.lookup(store);
            store.setRoot(me.root);
            if (me.folderSort !== undefined) {
                store.folderSort = me.folderSort;
                store.sort();
            }
        }
        
        store.setRootVisible(me.rootVisible);
        
        if (!store.getRoot()) {
            store.setRoot({});
        }
        me.viewConfig = Ext.apply({
            rootVisible: me.rootVisible,
            animate: me.enableAnimations,
            singleExpand: me.singleExpand,
            node: store.getRoot(),
            hideHeaders: me.hideHeaders,
            navigationModel: 'tree'
        }, me.viewConfig);
        
        if (!me.columns) {
            if (me.initialConfig.hideHeaders === undefined) {
                me.hideHeaders = true;
            }
            me.addCls(me.autoWidthCls);
            me.columns = [
                {
                    xtype: 'treecolumn',
                    text: 'Name',
                    flex: 1,
                    dataIndex: me.displayField
                }
            ];
        }
        if (me.cls) {
            cls.push(me.cls);
        }
        me.cls = cls.join(' ');
        me.callParent();
        view = me.getView();
        
        
        me.relayEvents(view, [
            
            'checkchange',
            
            'afteritemexpand',
            
            'afteritemcollapse'
        ]);
    },
    
    
    bindStore: function(store, initial) {
        var me = this,
            root = store.getRoot(),
            bufferedRenderer = me.bufferedRenderer;
        
        me.callParent(arguments);
        
        
        if (bufferedRenderer) {
            if (bufferedRenderer.store) {
                bufferedRenderer.bindStore(store);
            }
        }
        
        
        store.singleExpand = me.singleExpand;
        
        me.storeListeners = me.mon(store, {
            destroyable: true,
            rootchange: me.onRootChange,
            scope: me
        });
        
        me.storeRelayers = me.relayEvents(store, [
            
            'beforeload',
            
            'load'
        ]);
        
        me.rootRelayers = me.mon(root, {
            destroyable: true,
            
            append: me.createRelayer('itemappend'),
            
            remove: me.createRelayer('itemremove'),
            
            move: me.createRelayer('itemmove', [
                0,
                4
            ]),
            
            insert: me.createRelayer('iteminsert'),
            
            beforeappend: me.createRelayer('beforeitemappend'),
            
            beforeremove: me.createRelayer('beforeitemremove'),
            
            beforemove: me.createRelayer('beforeitemmove'),
            
            beforeinsert: me.createRelayer('beforeiteminsert'),
            
            expand: me.createRelayer('itemexpand', [
                0,
                1
            ]),
            
            collapse: me.createRelayer('itemcollapse', [
                0,
                1
            ]),
            
            beforeexpand: me.createRelayer('beforeitemexpand', [
                0,
                1
            ]),
            
            beforecollapse: me.createRelayer('beforeitemcollapse', [
                0,
                1
            ])
        });
        
        
        
        
        if (!me.rootVisible && !store.autoLoad && !(root.isExpanded() || root.isLoading())) {
            
            
            if (root.isLoaded()) {
                root.data.expanded = true;
                store.onNodeExpand(root, root.childNodes);
            }
            
            
            
            
            
            else if (store.autoLoad !== false) {
                root.data.expanded = false;
                root.expand();
            }
        }
        
        store.ownerTree = me;
        if (!initial) {
            me.view.setRootNode(root);
        }
    },
    
    unbindStore: function() {
        var me = this,
            store = me.store;
        if (store) {
            me.callParent();
            Ext.destroy(me.storeListeners, me.storeRelayers, me.rootRelayers);
            delete store.ownerTree;
            store.singleExpand = null;
        }
    },
    
    setRootNode: function() {
        return this.store.setRoot.apply(this.store, arguments);
    },
    
    getRootNode: function() {
        return this.store.getRoot();
    },
    onRootChange: function(root) {
        this.view.setRootNode(root);
    },
    
    getChecked: function() {
        return this.getView().getChecked();
    },
    isItemChecked: function(rec) {
        return rec.get('checked');
    },
    
    expandNode: function(record, deep, callback, scope) {
        return this.getView().expand(record, deep, callback, scope || this);
    },
    
    collapseNode: function(record, deep, callback, scope) {
        return this.getView().collapse(record, deep, callback, scope || this);
    },
    
    expandAll: function(callback, scope) {
        var me = this,
            root = me.getRootNode();
        if (root) {
            Ext.suspendLayouts();
            root.expand(true, callback, scope || me);
            Ext.resumeLayouts(true);
        }
    },
    
    collapseAll: function(callback, scope) {
        var me = this,
            root = me.getRootNode(),
            view = me.getView();
        if (root) {
            Ext.suspendLayouts();
            scope = scope || me;
            if (view.rootVisible) {
                root.collapse(true, callback, scope);
            } else {
                root.collapseChildren(true, callback, scope);
            }
            Ext.resumeLayouts(true);
        }
    },
    
    expandPath: function(path, options) {
        var args = arguments,
            me = this,
            view = me.view,
            field = (options && options.field) || me.store.model.idProperty,
            select, doFocus,
            separator = (options && options.separator) || '/',
            callback, scope, current, index, keys, rooted, expander;
        
        if (options && typeof options === 'object') {
            field = options.field || me.store.model.idProperty;
            separator = options.separator || '/';
            callback = options.callback;
            scope = options.scope;
            select = options.select;
            doFocus = options.focus;
        } else 
        {
            field = args[1] || me.store.model.idProperty;
            separator = args[2] || '/';
            callback = args[3];
            scope = args[4];
        }
        if (Ext.isEmpty(path)) {
            return Ext.callback(callback, scope || me, [
                false,
                null
            ]);
        }
        keys = path.split(separator);
        
        
        rooted = !keys[0];
        if (rooted) {
            current = me.getRootNode();
            index = 1;
        } else 
        {
            current = me.store.findNode(field, keys[0]);
            index = 0;
        }
        
        if (!current || (rooted && current.get(field) !== keys[1])) {
            return Ext.callback(callback, scope || me, [
                false,
                current
            ]);
        }
        
        
        expander = function(newChildren) {
            var node = this,
                len, i, value;
            
            if (++index === keys.length) {
                if (select) {
                    view.getSelectionModel().select(node);
                }
                if (doFocus) {
                    view.getNavigationModel().setPosition(node, 0);
                }
                return Ext.callback(callback, scope || me, [
                    true,
                    node,
                    view.getNode(node)
                ]);
            }
            
            for (i = 0 , len = newChildren ? newChildren.length : 0; i < len; i++) {
                
                node = newChildren[i];
                value = node.get(field);
                if (value || value === 0) {
                    value = value.toString();
                }
                if (value === keys[index]) {
                    return node.expand(false, expander);
                }
            }
            
            node = this;
            Ext.callback(callback, scope || me, [
                false,
                node,
                view.getNode(node)
            ]);
        };
        current.expand(false, expander);
    },
    
    ensureVisible: function(path, options) {
        
        if (path.isEntity) {
            return this.callParent([
                path,
                options
            ]);
        }
        var me = this,
            field = (options && options.field) || me.store.model.idProperty,
            separator = (options && options.separator) || '/',
            callback, scope, keys, rooted, last, node, parentNode,
            onLastExpanded = function(success, lastExpanded, lastExpandedHtmlNode, targetNode) {
                if (!targetNode && success && lastExpanded) {
                    targetNode = lastExpanded.findChild(field, last);
                }
                
                if (targetNode) {
                    me.doEnsureVisible(targetNode, options);
                } else {
                    Ext.callback(callback, scope || me, [
                        false,
                        lastExpanded
                    ]);
                }
            };
        if (options) {
            callback = options.callback;
            scope = options.scope;
        }
        keys = path.split(separator);
        rooted = !keys[0];
        last = keys.pop();
        
        if (keys.length && !(rooted && keys.length === 1)) {
            me.expandPath(keys.join(separator), field, separator, onLastExpanded);
        } else 
        {
            node = me.store.findNode(field, last);
            if (node) {
                parentNode = node.parentNode;
                if (parentNode && !parentNode.isExpanded()) {
                    parentNode.expand();
                }
                
                onLastExpanded(true, null, null, node);
            } else {
                Ext.callback(callback, scope || me, [
                    false,
                    null
                ]);
            }
        }
    },
    
    selectPath: function(path, field, separator, callback, scope) {
        this.ensureVisible(path, {
            field: field,
            separator: separator,
            select: true,
            callback: callback,
            scope: scope
        });
    }
});


Ext.define('Ext.form.field.VTypes', (function() {
    
    var alpha = /^[a-zA-Z_]+$/,
        alphanum = /^[a-zA-Z0-9_]+$/,
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        email = /^(")?(?:[^\."])(?:(?:[\.])?(?:[\w\-!#$%&'*+/=?^_`{|}~]))*\1@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
        url = /(((^https?)|(^ftp)):\/\/((([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*)|(localhost|LOCALHOST))\/?)/i;
    
    return {
        singleton: true,
        alternateClassName: 'Ext.form.VTypes',
        
        'email': function(value) {
            return email.test(value);
        },
        
        
        'emailText': 'This field should be an e-mail address in the format "user@example.com"',
        
        
        'emailMask': /[\w.\-@'"!#$%&'*+/=?^_`{|}~]/i,
        
        'url': function(value) {
            return url.test(value);
        },
        
        
        'urlText': 'This field should be a URL in the format "http:/' + '/www.example.com"',
        
        
        'alpha': function(value) {
            return alpha.test(value);
        },
        
        
        'alphaText': 'This field should only contain letters and _',
        
        
        'alphaMask': /[a-z_]/i,
        
        'alphanum': function(value) {
            return alphanum.test(value);
        },
        
        
        'alphanumText': 'This field should only contain letters, numbers and _',
        
        
        'alphanumMask': /[a-z0-9_]/i
    };
}()));


Ext.define('Ext.form.trigger.Trigger', {
    alias: 'trigger.trigger',
    requires: [
        'Ext.util.ClickRepeater'
    ],
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    factoryConfig: {
        defaultType: 'trigger'
    },
    
    repeatClick: false,
    
    
    
    
    hidden: false,
    
    hideOnReadOnly: undefined,
    
    
    weight: 0,
    
    
    preventMouseDown: true,
    
    baseCls: Ext.baseCSSPrefix + 'form-trigger',
    
    focusCls: Ext.baseCSSPrefix + 'form-trigger-focus',
    
    overCls: Ext.baseCSSPrefix + 'form-trigger-over',
    
    clickCls: Ext.baseCSSPrefix + 'form-trigger-click',
    
    validIdRe: Ext.validIdRe,
    
    renderTpl: [
        '<div id="{triggerId}" class="{baseCls} {baseCls}-{ui} {cls} {cls}-{ui} {extraCls} ',
        '{childElCls}"<tpl if="triggerStyle"> style="{triggerStyle}"</tpl>>',
        '{[values.$trigger.renderBody(values)]}',
        '</div>'
    ],
    statics: {
        
        weightComparator: function(triggerA, triggerB) {
            return triggerA.weight - triggerB.weight;
        }
    },
    constructor: function(config) {
        var me = this,
            cls;
        Ext.apply(me, config);
        
        if (me.compat4Mode) {
            cls = me.cls;
            me.focusCls = [
                me.focusCls,
                cls + '-focus'
            ];
            me.overCls = [
                me.overCls,
                cls + '-over'
            ];
            me.clickCls = [
                me.clickCls,
                cls + '-click'
            ];
        }
        if (!me.validIdRe.test(me.id)) {
            Ext.Error.raise('Invalid trigger "id": "' + me.id + '"');
        }
    },
    
    afterFieldRender: function() {
        this.initEvents();
    },
    destroy: function() {
        var me = this,
            clickRepeater = me.clickRepeater;
        if (clickRepeater) {
            clickRepeater.destroy();
        }
        if (me.el) {
            me.el.destroy();
        }
        me.el = null;
        me.isDestroyed = true;
    },
    
    getBodyRenderData: Ext.emptyFn,
    
    getEl: function() {
        return this.el || null;
    },
    
    getStateEl: function() {
        return this.el;
    },
    
    hide: function() {
        var me = this,
            el = me.el;
        me.hidden = true;
        if (el) {
            el.hide();
        }
    },
    initEvents: function() {
        var me = this,
            isFieldEnabled = me.isFieldEnabled,
            stateEl = me.getStateEl(),
            el = me.el;
        stateEl.addClsOnOver(me.overCls, isFieldEnabled, me);
        stateEl.addClsOnClick(me.clickCls, isFieldEnabled, me);
        if (me.repeatClick) {
            me.clickRepeater = new Ext.util.ClickRepeater(el,{
                preventDefault: true,
                handler: me.onClick,
                listeners: {
                    mousedown: me.onClickRepeaterMouseDown,
                    scope: me
                },
                scope: me
            });
        } else {
            me.field.mon(el, {
                click: me.onClick,
                mousedown: me.onMouseDown,
                scope: me
            });
        }
    },
    
    isFieldEnabled: function() {
        return !this.field.disabled;
    },
    
    isVisible: function() {
        var me = this,
            field = me.field,
            hidden = false;
        if (me.hidden || !field || !me.rendered || me.isDestroyed) {
            hidden = true;
        }
        return !hidden;
    },
    
    onClick: function() {
        var me = this,
            args = arguments,
            e = me.clickRepeater ? args[1] : args[0],
            handler = me.handler,
            field = me.field;
        if (handler && !field.readOnly && me.isFieldEnabled()) {
            Ext.callback(me.handler, me.scope, [
                field,
                me,
                e
            ], 0, field);
        }
    },
    
    resolveListenerScope: function(scope) {
        return this.field.resolveListenerScope(scope);
    },
    onMouseDown: function(e) {
        
        
        
        
        
        if (e.pointerType !== 'touch' && !this.field.owns(Ext.Element.getActiveElement())) {
            this.field.inputEl.focus();
        }
        if (this.preventMouseDown) {
            
            e.preventDefault();
        }
    },
    onClickRepeaterMouseDown: function(clickRepeater, e) {
        
        
        
        if (!e.parentEvent || e.parentEvent.type === 'mousedown') {
            this.field.inputEl.focus();
        }
        
        e.preventDefault();
    },
    
    onFieldBlur: function() {
        this.getStateEl().removeCls(this.focusCls);
    },
    
    onFieldFocus: function() {
        this.getStateEl().addCls(this.focusCls);
    },
    
    onFieldRender: function() {
        var me = this,
            
            el = me.el = me.field.triggerWrap.selectNode('#' + me.domId, false);
        
        el.setVisibilityMode(Ext.Element.DISPLAY);
        me.rendered = true;
    },
    
    renderBody: function(renderData) {
        var me = this,
            bodyTpl = me.bodyTpl;
        Ext.apply(renderData, me.getBodyRenderData());
        return bodyTpl ? Ext.XTemplate.getTpl(me, 'bodyTpl').apply(renderData) : '';
    },
    
    renderTrigger: function(fieldData) {
        var me = this,
            width = me.width,
            triggerStyle = me.hidden ? 'display:none;' : '';
        if (width) {
            triggerStyle += 'width:' + width;
        }
        return Ext.XTemplate.getTpl(me, 'renderTpl').apply({
            $trigger: me,
            fieldData: fieldData,
            ui: fieldData.ui,
            childElCls: fieldData.childElCls,
            triggerId: me.domId = me.field.id + '-trigger-' + me.id,
            cls: me.cls,
            triggerStyle: triggerStyle,
            extraCls: me.extraCls,
            baseCls: me.baseCls
        });
    },
    setHidden: function(hidden) {
        if (hidden !== this.hidden) {
            this[hidden ? 'hide' : 'show']();
        }
    },
    setVisible: function(visible) {
        this.setHidden(!visible);
    },
    
    show: function() {
        var me = this,
            el = me.el;
        me.hidden = false;
        if (el) {
            el.show();
        }
    }
});


Ext.define('Ext.form.field.Text', {
    extend: 'Ext.form.field.Base',
    alias: 'widget.textfield',
    requires: [
        'Ext.form.field.VTypes',
        'Ext.form.trigger.Trigger',
        'Ext.util.TextMetrics'
    ],
    alternateClassName: [
        'Ext.form.TextField',
        'Ext.form.Text'
    ],
    config: {
        
        hideTrigger: false,
        
        
        triggers: undefined
    },
    
    
    
    
    
    growMin: 30,
    
    growMax: 800,
    
    
    growAppend: 'W',
    
    
    
    
    
    allowBlank: true,
    
    validateBlank: false,
    
    allowOnlyWhitespace: true,
    
    minLength: 0,
    
    maxLength: Number.MAX_VALUE,
    
    
    
    minLengthText: 'The minimum length for this field is {0}',
    
    
    
    maxLengthText: 'The maximum length for this field is {0}',
    
    
    
    
    blankText: 'This field is required',
    
    
    
    
    regexText: '',
    
    
    emptyCls: Ext.baseCSSPrefix + 'form-empty-field',
    
    requiredCls: Ext.baseCSSPrefix + 'form-required-field',
    
    
    valueContainsPlaceholder: false,
    ariaRole: 'textbox',
    
    editable: true,
    
    repeatTriggerClick: false,
    
    
    triggerWrapCls: Ext.baseCSSPrefix + 'form-trigger-wrap',
    triggerWrapFocusCls: Ext.baseCSSPrefix + 'form-trigger-wrap-focus',
    triggerWrapInvalidCls: Ext.baseCSSPrefix + 'form-trigger-wrap-invalid',
    fieldBodyCls: Ext.baseCSSPrefix + 'form-text-field-body',
    
    inputWrapCls: Ext.baseCSSPrefix + 'form-text-wrap',
    inputWrapFocusCls: Ext.baseCSSPrefix + 'form-text-wrap-focus',
    inputWrapInvalidCls: Ext.baseCSSPrefix + 'form-text-wrap-invalid',
    growCls: Ext.baseCSSPrefix + 'form-text-grow',
    
    monitorTab: true,
    
    mimicing: false,
    needArrowKeys: true,
    childEls: [
        
        'triggerWrap',
        
        'inputWrap'
    ],
    preSubTpl: [
        '<div id="{cmpId}-triggerWrap" data-ref="triggerWrap" class="{triggerWrapCls} {triggerWrapCls}-{ui}">',
        '<div id={cmpId}-inputWrap data-ref="inputWrap" class="{inputWrapCls} {inputWrapCls}-{ui}">'
    ],
    postSubTpl: [
        '</div>',
        
        '<tpl for="triggers">{[values.renderTrigger(parent)]}</tpl>',
        '</div>'
    ],
    
    
    
    
    
    initComponent: function() {
        var me = this,
            emptyCls = me.emptyCls;
        if (me.allowOnlyWhitespace === false) {
            me.allowBlank = false;
        }
        if (me.size) {
            Ext.log.warn('Ext.form.field.Text "size" config was deprecated in Ext 5.0. Please specify a "width" or use a layout instead.');
        }
        
        
        if (me.size) {
            me.defaultBodyWidth = me.size * 6.5 + 20;
        }
        if (!me.onTrigger1Click) {
            
            me.onTrigger1Click = me.onTriggerClick;
        }
        me.callParent();
        me.setReadOnly(me.readOnly);
        me.fieldFocusCls = me.baseCls + '-focus';
        me.emptyUICls = emptyCls + ' ' + emptyCls + '-' + me.ui;
        me.addStateEvents('change');
    },
    
    initEvents: function() {
        var me = this,
            el = me.inputEl;
        me.callParent();
        if (me.selectOnFocus || me.emptyText) {
            me.mon(el, 'mousedown', me.onMouseDown, me);
        }
        if (me.maskRe || (me.vtype && me.disableKeyFilter !== true && (me.maskRe = Ext.form.field.VTypes[me.vtype + 'Mask']))) {
            me.mon(el, 'keypress', me.filterKeys, me);
        }
        if (me.enableKeyEvents) {
            me.mon(el, {
                scope: me,
                keyup: me.onKeyUp,
                keydown: me.onKeyDown,
                keypress: me.onKeyPress
            });
        }
    },
    
    isEqual: function(value1, value2) {
        return this.isEqualAsString(value1, value2);
    },
    
    onChange: function(newVal, oldVal) {
        this.callParent(arguments);
        this.autoSize();
    },
    getSubTplData: function(fieldData) {
        var me = this,
            value = me.getRawValue(),
            isEmpty = me.emptyText && value.length < 1,
            maxLength = me.maxLength,
            placeholder;
        
        
        
        if (me.enforceMaxLength) {
            if (maxLength === Number.MAX_VALUE) {
                maxLength = undefined;
            }
        } else {
            maxLength = undefined;
        }
        if (isEmpty) {
            if (Ext.supports.Placeholder) {
                placeholder = me.emptyText;
            } else {
                value = me.emptyText;
                me.valueContainsPlaceholder = true;
            }
        }
        return Ext.apply(me.callParent(arguments), {
            triggerWrapCls: me.triggerWrapCls,
            inputWrapCls: me.inputWrapCls,
            triggers: me.orderedTriggers,
            maxLength: maxLength,
            readOnly: !me.editable || me.readOnly,
            placeholder: placeholder,
            value: value,
            fieldCls: me.fieldCls + ((isEmpty && (placeholder || value)) ? ' ' + me.emptyUICls : '') + (me.allowBlank ? '' : ' ' + me.requiredCls)
        });
    },
    onRender: function() {
        var me = this,
            triggers = me.getTriggers(),
            elements = [],
            id, triggerEl;
        if (Ext.supports.FixedTableWidthBug) {
            
            
            
            me.el._needsTableWidthFix = true;
        }
        me.callParent();
        if (triggers) {
            this.invokeTriggers('onFieldRender');
            
            for (id in triggers) {
                elements.push(triggers[id].el);
            }
            
            triggerEl = me.triggerEl = me.triggerCell = new Ext.CompositeElement(elements,true);
        }
        
        me.inputCell = me.inputWrap;
    },
    afterRender: function() {
        var me = this;
        me.autoSize();
        me.callParent();
        me.invokeTriggers('afterFieldRender');
    },
    onMouseDown: function() {
        var me = this;
        if (!me.hasFocus) {
            me.mon(me.inputEl, 'mouseup', Ext.emptyFn, me, {
                single: true,
                preventDefault: true
            });
        }
    },
    applyTriggers: function(triggers) {
        var me = this,
            hideAllTriggers = me.getHideTrigger(),
            readOnly = me.readOnly,
            orderedTriggers = me.orderedTriggers = [],
            repeatTriggerClick = me.repeatTriggerClick,
            id, triggerCfg, trigger, triggerCls, i;
        if (me.rendered) {
            Ext.Error.raise("Cannot set triggers after field has already been rendered.");
        }
        
        
        if ((me.triggerCls && !triggers) || me.trigger1Cls) {
            Ext.log.warn("Ext.form.field.Text: 'triggerCls' and 'trigger<n>Cls'" + " are deprecated.  Use 'triggers' instead.");
        }
        if (!triggers) {
            
            
            triggers = {};
            if (me.triggerCls && !me.trigger1Cls) {
                me.trigger1Cls = me.triggerCls;
            }
            for (i = 1; triggerCls = me['trigger' + i + 'Cls']; i++) {
                triggers['trigger' + i] = {
                    cls: triggerCls,
                    extraCls: Ext.baseCSSPrefix + 'trigger-index-' + i,
                    handler: 'onTrigger' + i + 'Click',
                    compat4Mode: true,
                    scope: me
                };
            }
        }
        for (id in triggers) {
            if (triggers.hasOwnProperty(id)) {
                triggerCfg = triggers[id];
                triggerCfg.field = me;
                triggerCfg.id = id;
                
                if ((readOnly && triggerCfg.hideOnReadOnly !== false) || (hideAllTriggers && triggerCfg.hidden !== false)) {
                    triggerCfg.hidden = true;
                }
                if (repeatTriggerClick && (triggerCfg.repeatClick !== false)) {
                    triggerCfg.repeatClick = true;
                }
                trigger = triggers[id] = Ext.form.trigger.Trigger.create(triggerCfg);
                orderedTriggers.push(trigger);
            }
        }
        Ext.Array.sort(orderedTriggers, Ext.form.trigger.Trigger.weightComparator);
        return triggers;
    },
    
    invokeTriggers: function(methodName, args) {
        var me = this,
            triggers = me.getTriggers(),
            id, trigger;
        if (triggers) {
            for (id in triggers) {
                if (triggers.hasOwnProperty(id)) {
                    trigger = triggers[id];
                    
                    trigger[methodName].apply(trigger, args || []);
                }
            }
        }
    },
    
    getTrigger: function(id) {
        return this.getTriggers()[id];
    },
    updateHideTrigger: function(hideTrigger) {
        if (this.rendered) {
            this.invokeTriggers(hideTrigger ? 'hide' : 'show');
        }
    },
    
    setEditable: function(editable) {
        var me = this;
        me.editable = editable;
        if (me.rendered) {
            me.setReadOnlyAttr(!editable || me.readOnly);
        }
    },
    
    setReadOnly: function(readOnly) {
        var me = this,
            triggers = me.getTriggers(),
            hideTriggers = me.getHideTrigger(),
            trigger, id;
        readOnly = !!readOnly;
        me.callParent([
            readOnly
        ]);
        if (me.rendered) {
            me.setReadOnlyAttr(readOnly || !me.editable);
            if (triggers) {
                for (id in triggers) {
                    trigger = triggers[id];
                    
                    if (trigger.hideOnReadOnly === true || (trigger.hideOnReadOnly !== false && !hideTriggers)) {
                        trigger[readOnly ? 'hide' : 'show'].call(trigger);
                    }
                }
            }
        }
    },
    
    setReadOnlyAttr: function(readOnly) {
        var me = this,
            readOnlyName = 'readonly',
            inputEl = me.inputEl.dom;
        if (readOnly) {
            inputEl.setAttribute(readOnlyName, readOnlyName);
        } else {
            inputEl.removeAttribute(readOnlyName);
        }
    },
    
    processRawValue: function(value) {
        var me = this,
            stripRe = me.stripCharsRe,
            newValue;
        if (stripRe) {
            newValue = value.replace(stripRe, '');
            if (newValue !== value) {
                me.setRawValue(newValue);
                value = newValue;
            }
        }
        return value;
    },
    
    onDisable: function() {
        this.callParent();
        if (Ext.isIE) {
            this.inputEl.dom.unselectable = 'on';
        }
    },
    
    onEnable: function() {
        this.callParent();
        if (Ext.isIE) {
            this.inputEl.dom.unselectable = '';
        }
    },
    onKeyDown: function(e) {
        this.fireEvent('keydown', this, e);
    },
    onKeyUp: function(e) {
        this.fireEvent('keyup', this, e);
    },
    onKeyPress: function(e) {
        this.fireEvent('keypress', this, e);
    },
    
    reset: function() {
        this.callParent();
        this.applyEmptyText();
    },
    applyEmptyText: function() {
        var me = this,
            emptyText = me.emptyText,
            isEmpty;
        if (me.rendered && emptyText) {
            isEmpty = me.getRawValue().length < 1 && !me.hasFocus;
            if (Ext.supports.Placeholder) {
                me.inputEl.dom.placeholder = emptyText;
            } else if (isEmpty) {
                me.setRawValue(emptyText);
                me.valueContainsPlaceholder = true;
            }
            
            
            if (isEmpty) {
                me.inputEl.addCls(me.emptyUICls);
            } else {
                me.inputEl.removeCls(me.emptyUICls);
            }
            me.autoSize();
        }
    },
    afterFirstLayout: function() {
        this.callParent();
        if (Ext.isIE && this.disabled) {
            var el = this.inputEl;
            if (el) {
                el.dom.unselectable = 'on';
            }
        }
    },
    
    toggleInvalidCls: function(hasError) {
        var method = hasError ? 'addCls' : 'removeCls';
        this.callParent();
        this.triggerWrap[method](this.triggerWrapInvalidCls);
        this.inputWrap[method](this.inputWrapInvalidCls);
    },
    
    beforeFocus: function() {
        var me = this,
            inputEl = me.inputEl,
            emptyText = me.emptyText,
            isEmpty;
        me.callParent(arguments);
        if ((emptyText && !Ext.supports.Placeholder) && (inputEl.dom.value === me.emptyText && me.valueContainsPlaceholder)) {
            me.setRawValue('');
            isEmpty = true;
            inputEl.removeCls(me.emptyUICls);
            me.valueContainsPlaceholder = false;
        } else if (Ext.supports.Placeholder) {
            inputEl.removeCls(me.emptyUICls);
        }
        if (me.selectOnFocus || isEmpty) {
            
            if (Ext.isWebKit) {
                if (!me.inputFocusTask) {
                    me.inputFocusTask = new Ext.util.DelayedTask(me.focusInput,me);
                }
                me.inputFocusTask.delay(1);
            } else {
                me.focusInput();
            }
        }
    },
    focusInput: function() {
        var input = this.inputEl;
        if (input) {
            input = input.dom;
            if (input) {
                input.select();
            }
        }
    },
    onFocus: function(e) {
        var me = this;
        me.callParent(arguments);
        if (me.emptyText) {
            me.autoSize();
        }
        me.addCls(me.fieldFocusCls);
        me.triggerWrap.addCls(me.triggerWrapFocusCls);
        me.inputWrap.addCls(me.inputWrapFocusCls);
        me.invokeTriggers('onFieldFocus', [
            e
        ]);
    },
    
    onBlur: function(e) {
        var me = this;
        me.callParent(arguments);
        me.removeCls(me.fieldFocusCls);
        me.triggerWrap.removeCls(me.triggerWrapFocusCls);
        me.inputWrap.removeCls(me.inputWrapFocusCls);
        me.invokeTriggers('onFieldBlur', [
            e
        ]);
    },
    completeEdit: function() {
        var task = this.inputFocusTask;
        this.callParent(arguments);
        this.applyEmptyText();
        
        
        if (task) {
            task.cancel();
        }
    },
    
    filterKeys: function(e) {
        
        if (e.ctrlKey && !e.altKey) {
            return;
        }
        var key = e.getKey(),
            charCode = String.fromCharCode(e.getCharCode());
        if ((Ext.isGecko || Ext.isOpera) && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))) {
            return;
        }
        if ((!Ext.isGecko && !Ext.isOpera) && e.isSpecialKey() && !charCode) {
            return;
        }
        if (!this.maskRe.test(charCode)) {
            e.stopEvent();
        }
    },
    getState: function() {
        return this.addPropertyToState(this.callParent(), 'value');
    },
    applyState: function(state) {
        this.callParent(arguments);
        if (state.hasOwnProperty('value')) {
            this.setValue(state.value);
        }
    },
    
    getRawValue: function() {
        var me = this,
            v = me.callParent();
        if (v === me.emptyText && me.valueContainsPlaceholder) {
            v = '';
        }
        return v;
    },
    
    setValue: function(value) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
            inputEl.removeCls(me.emptyUICls);
            me.valueContainsPlaceholder = false;
        }
        me.callParent(arguments);
        me.applyEmptyText();
        return me;
    },
    
    getErrors: function(value) {
        value = arguments.length ? (value == null ? '' : value) : this.processRawValue(this.getRawValue());
        var me = this,
            errors = me.callParent([
                value
            ]),
            validator = me.validator,
            vtype = me.vtype,
            vtypes = Ext.form.field.VTypes,
            regex = me.regex,
            format = Ext.String.format,
            msg, trimmed, isBlank;
        if (Ext.isFunction(validator)) {
            msg = validator.call(me, value);
            if (msg !== true) {
                errors.push(msg);
            }
        }
        trimmed = me.allowOnlyWhitespace ? value : Ext.String.trim(value);
        if (trimmed.length < 1 || (value === me.emptyText && me.valueContainsPlaceholder)) {
            if (!me.allowBlank) {
                errors.push(me.blankText);
            }
            
            if (!me.validateBlank) {
                return errors;
            }
            isBlank = true;
        }
        
        
        if (!isBlank && value.length < me.minLength) {
            errors.push(format(me.minLengthText, me.minLength));
        }
        if (value.length > me.maxLength) {
            errors.push(format(me.maxLengthText, me.maxLength));
        }
        if (vtype) {
            if (!vtypes[vtype](value, me)) {
                errors.push(me.vtypeText || vtypes[vtype + 'Text']);
            }
        }
        if (regex && !regex.test(value)) {
            errors.push(me.regexText || me.invalidText);
        }
        return errors;
    },
    
    selectText: function(start, end) {
        var me = this,
            v = me.getRawValue(),
            len = v.length,
            doFocus = true,
            el = me.inputEl.dom,
            range;
        if (len > 0) {
            start = start === undefined ? 0 : Math.min(start, len);
            end = end === undefined ? len : Math.min(end, len);
            if (el.setSelectionRange) {
                el.setSelectionRange(start, end);
            } else if (el.createTextRange) {
                range = el.createTextRange();
                range.moveStart('character', start);
                range.moveEnd('character', end - len);
                range.select();
            }
        }
    },
    
    
    getGrowWidth: function() {
        return this.inputEl.dom.value;
    },
    
    autoSize: function() {
        var me = this,
            triggers, triggerId, trigger, triggerWidth, inputEl, inputWidth, width, value;
        if (me.grow && me.rendered && me.getSizeModel().width.auto) {
            inputEl = me.inputEl;
            triggers = me.getTriggers();
            triggerWidth = 0;
            value = Ext.util.Format.htmlEncode(me.getGrowWidth() || (me.hasFocus ? '' : me.emptyText) || '');
            value += me.growAppend;
            for (triggerId in triggers) {
                triggerWidth += triggers[triggerId].el.getWidth();
            }
            width = inputEl.getTextWidth(value) + triggerWidth + 
            
            me.inputWrap.getBorderWidth('lr') + me.triggerWrap.getBorderWidth('lr');
            width = Math.min(Math.max(width, me.growMin), me.growMax);
            me.bodyEl.setWidth(width);
            me.updateLayout();
            me.fireEvent('autosize', me, width);
        }
    },
    onDestroy: function() {
        var me = this;
        me.invokeTriggers('destroy');
        Ext.destroy(me.triggerRepeater);
        me.callParent();
        if (me.inputFocusTask) {
            me.inputFocusTask.cancel();
            me.inputFocusTask = null;
        }
    },
    onTriggerClick: Ext.emptyFn,
    privates: {
        
        getTdType: function() {
            return 'textfield';
        }
    },
    deprecated: {
        5: {
            methods: {
                
                getTriggerWidth: function() {
                    var triggers = this.getTriggers(),
                        width = 0,
                        id;
                    if (triggers && this.rendered) {
                        for (id in triggers) {
                            if (triggers.hasOwnProperty(id)) {
                                width += triggers[id].el.getWidth();
                            }
                        }
                    }
                    return width;
                }
            }
        }
    }
});

Ext.define('Ext.app.bindinspector.ComponentList', {
    alias: 'widget.bindinspector-componentlist',
    extend: 'Ext.tree.Panel',
    requires: [
        'Ext.form.field.Text'
    ],
    rootVisible: false,
    title: 'Component Tree',
    hideHeaders: true,
    bindingsIconCls: Ext.baseCSSPrefix + 'bindings-icon',
    vmIconCls: Ext.baseCSSPrefix + 'vm-icon',
    missingDataCls: Ext.baseCSSPrefix + 'bindinspector-missing-data',
    filterVisibleCls: Ext.baseCSSPrefix + 'bindinspector-filter-visible',
    lastItemCls: Ext.baseCSSPrefix + 'bindinspector-last-item',
    bindingsIcon: '☍',
    vmIcon: '☶',
    initComponent: function() {
        var me = this,
            nodes = [];
        me.viewConfig = {
            toggleOnDblClick: false,
            getRowClass: function(record, index, rowParams, store) {
                var cls = [];
                
                if (record.get('filtervisible')) {
                    cls.push(me.filterVisibleCls);
                }
                
                if (record.get('sansData')) {
                    cls.push(me.missingDataCls);
                }
                
                if (index === store.getCount() - 1) {
                    cls.push(me.lastItemCls);
                }
                return cls.join(' ');
            }
        };
        
        Ext.Array.forEach(me.components, function(comp) {
            nodes.push(me.buildNode(comp));
        }, me);
        me.store = {
            model: me.Model,
            root: {
                expanded: true,
                children: nodes
            }
        };
        me.columns = [
            {
                
                itemId: 'srcVMIndicator',
                width: 40,
                hidden: true,
                renderer: me.srcVMIndicator,
                scope: me
            },
            {
                xtype: 'treecolumn',
                dataIndex: 'text',
                flex: 1
            }
        ];
        me.dockedItems = [
            {
                
                xtype: 'toolbar',
                itemId: 'queryFieldTb',
                dock: 'top',
                items: [
                    {
                        xtype: 'textfield',
                        reference: 'queryField',
                        itemId: 'queryField',
                        emptyText: 'simple search by reference / ID or use a component query...',
                        flex: 1,
                        triggers: {
                            clear: {
                                cls: Ext.baseCSSPrefix + 'form-clear-trigger',
                                handler: function(field) {
                                    var tree = field.up('treepanel'),
                                        store = tree.store;
                                    field.reset();
                                    tree.clearComponentFilter();
                                    field.focus();
                                }
                            }
                        },
                        listeners: {
                            change: {
                                fn: me.filterComponentTree,
                                buffer: 250,
                                scope: me
                            },
                            afterrender: {
                                fn: function(field) {
                                    var tbEl = field.up('toolbar').getEl();
                                    
                                    field.mon(tbEl, 'mouseenter', function() {
                                        var tip = me.bindingsTip,
                                            showAt, x, y;
                                        tip.stopAnimation();
                                        tip.update('<b>Simple Search</b><br>Enter the string matching the reference or ID of the target component<hr><b>Component Query</b><br>Enter a component query string to find any items matching the query');
                                        tip.setTarget(tbEl);
                                        tip.show();
                                        x = tip.getX();
                                        y = tip.getY();
                                        showAt = tip.getAlignToXY(tbEl, 'l-r');
                                        tip.animate({
                                            from: {
                                                opacity: 0,
                                                x: showAt[0] + 20,
                                                y: showAt[1]
                                            },
                                            to: {
                                                opacity: 1,
                                                x: showAt[0] + 10,
                                                y: showAt[1]
                                            }
                                        });
                                    });
                                },
                                scope: me
                            }
                        }
                    }
                ]
            },
            {
                
                
                xtype: 'toolbar',
                cls: Ext.baseCSSPrefix + 'vm-results-tb',
                itemId: 'vmQueryResultsTb',
                hidden: true,
                dock: 'top',
                defaultButtonUI: 'default',
                items: [
                    '->',
                    {
                        text: 'Clear VM Filter',
                        
                        handler: function() {
                            console.log(this.up('#vmQueryResultsTb'));
                            var tb = this.up('#vmQueryResultsTb'),
                                componentList = tb.up('bindinspector-componentlist'),
                                queryTb = componentList.down('#queryFieldTb'),
                                queryField = queryTb.down('#queryField');
                            tb.hide();
                            queryTb.show();
                            componentList.clearVMSearchIndicators();
                            queryField.setValue(queryField.lastValue);
                            componentList.filterComponentTree(null, queryField.lastValue);
                        }
                    }
                ]
            }
        ];
        me.callParent();
        me.getView().on('itemdblclick', me.onItemDblclick, me);
        me.on('select', me.onItemSelect, me);
        
        me.bindingsTip = Ext.create('Ext.tip.ToolTip', {
            renderTo: document.body,
            anchor: 'left',
            cls: Ext.baseCSSPrefix + 'componentlist-tip',
            bodyPadding: 12
        });
        
        me.getView().on('itemmouseenter', me.showBindingsTip, me);
    },
    
    
    clearVMSearchIndicators: function() {
        var indicatedVM = this.indicatedVM;
        Ext.suspendLayouts();
        if (indicatedVM) {
            Ext.Array.forEach(indicatedVM, function(rec) {
                rec.set('isSrcVM', false);
            });
        }
        this.down('#srcVMIndicator').hide();
        Ext.resumeLayouts(true);
        this.indicatedVM = null;
    },
    
    srcVMIndicator: function(v, meta, rec) {
        var refVM = rec.get('isSrcVM'),
            tip = '',
            vmDetail, firstTierRec, firstTierName, targetRec;
        if (refVM) {
            vmDetail = this.up('bindinspector-container').down('bindinspector-viewmodeldetail');
            firstTierRec = vmDetail.getFirstTierRec(refVM);
            firstTierName = firstTierRec.get('name');
            if (firstTierRec !== refVM) {
                tip += 'Root data node: &nbsp;<span class=\'' + Ext.baseCSSPrefix + 'binding-tip-descriptor\'>' + firstTierName + '</span><hr>';
            }
            targetRec = firstTierRec === refVM ? firstTierRec : refVM;
            tip += targetRec.get('name') + ':';
            tip += '<br>&nbsp;&nbsp;&nbsp;&nbsp;';
            tip += '<span class=\'' + Ext.baseCSSPrefix + 'binding-tip-value\'>' + Ext.app.bindinspector.Util.valueRenderer(targetRec.get('value')) + '</span>';
            meta.tdCls = Ext.baseCSSPrefix + 'bindindicator-vm-src';
            meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="' + tip + '"';
        }
    },
    
    onDestroy: function() {
        this.bindingsTip.destroy();
        this.callParent();
    },
    
    showBindingsTip: function(view, record, item, index, e) {
        var me = this,
            tip = me.bindingsTip,
            sansData = record.get('sansData'),
            bindings, bindingText;
        tip.stopAnimation();
        if (record.get('hasBindings')) {
            bindings = me.ownerCt.env.getCmp(record.get('id')).bindings;
            bindingText = [];
            
            Ext.Object.each(bindings, function(key, val, o) {
                var kv = key + ': ' + '<span class="' + Ext.baseCSSPrefix + 'binding-tip-descriptor">' + val.descriptor + '</span><br>',
                    bindValue = val.value,
                    v;
                if (Ext.isString(bindValue)) {
                    v = bindValue;
                } else if (Ext.isObject(bindValue)) {
                    if (bindValue.isStore === true) {
                        v = 'Store {' + bindValue.entityName + '}';
                    } else if (bindValue.isModel === true) {
                        v = 'Model {' + bindValue.entityName + '}';
                    }
                }
                kv += '<span class="' + Ext.baseCSSPrefix + 'binding-tip-value">' + v + '</span>';
                bindingText.push(kv);
            });
            bindingText = bindingText.join('<hr>');
            if (sansData) {
                bindingText += '<hr>';
                Ext.Array.forEach(sansData, function(missing) {
                    bindingText += '<div class="' + Ext.baseCSSPrefix + 'binding-missing-data">Missing data: ' + missing + '</div>';
                });
            }
            tip.update(bindingText);
            tip.setTarget(item);
            tip.show();
            tip.alignTo(item, 'l-r', [
                20,
                0
            ]);
            tip.animate({
                from: {
                    opacity: 0
                },
                to: {
                    opacity: 1,
                    x: tip.getX() - 10
                }
            });
        }
    },
    
    filterComponentTree: function(field, val) {
        var tree = this,
            field = tree.down('#queryField'),
            newVal = val || field.getValue(),
            store = tree.store,
            queryRe = /[\s>\[\]=()^'"~$@*:+#,]/g,
            valIsArray = Ext.isArray(newVal),
            ids = valIsArray ? newVal : [],
            components = [],
            isQuery, len, i;
        if (Ext.isString(newVal)) {
            isQuery = queryRe.test(Ext.String.trim(newVal));
        }
        if (newVal.length > 0) {
            tree.filteredComponents = [];
            
            if (isQuery) {
                try {
                    components = Ext.ComponentQuery.query(newVal);
                } catch (e) {}
                len = components.length;
                for (i = 0; i < len; i++) {
                    ids.push(components[i].id);
                }
            }
            store.suspendEvents();
            store.filter({
                filterFn: function(node) {
                    var children = node.childNodes,
                        length = children && children.length,
                        visible = false,
                        prop, j;
                    if (isQuery || valIsArray) {
                        visible = Ext.Array.contains(ids, node.get('id'));
                    } else {
                        visible = node.get('text').indexOf(newVal) > -1;
                    }
                    node.set('filtervisible', visible);
                    if (visible) {
                        tree.filteredComponents.push(node);
                    }
                    
                    for (j = 0; j < length; j++) {
                        if (children[j].get('visible')) {
                            visible = true;
                        }
                    }
                    return visible;
                },
                id: 'queryFilter'
            });
            store.resumeEvents();
            tree.getView().refresh();
        } else {
            tree.clearComponentFilter();
        }
    },
    
    clearComponentFilter: function() {
        var tree = this,
            store = tree.store,
            filtered = tree.filteredComponents || [],
            len = filtered.length,
            i = 0;
        for (; i < len; i++) {
            filtered[i].set('filtervisible', false);
        }
        store.clearFilter();
    },
    
    buildNode: function(comp) {
        var me = this,
            ownerCt = me.getRefOwner(),
            childItems = comp.items,
            viewModel = comp.viewModel,
            bindings = comp.bindings,
            hasBindings = !!comp.bindings,
            suffix = [],
            sansData = [],
            missing = {},
            binding, len, i, o, child, ref, key, bindData;
        if (viewModel) {
            suffix.push('<span class="' + me.vmIconCls + '">' + me.vmIcon + '</span>');
            ownerCt.buildVMDataMap(viewModel);
        }
        if (hasBindings) {
            suffix.push('<span class="' + me.bindingsIconCls + '">' + me.bindingsIcon + '</span>');
            for (key in bindings) {
                binding = bindings[key];
                if (binding.descriptor && Ext.isEmpty(binding.value)) {
                    sansData.push(missing[key] = binding.descriptor);
                }
            }
            bindData = comp.bindData = Ext.app.bindinspector.Util.buildBindData(bindings);
        }
        if (sansData.length === 0) {
            sansData = undefined;
        }
        ref = comp.reference ? '<b>[' + comp.reference + ']</b> &bull; ' : '';
        o = {
            id: comp.id,
            text: ref + comp.id + (suffix.length ? (' ' + suffix.join(' ')) : ''),
            hasViewModel: !!viewModel,
            hasBindings: hasBindings,
            hasDeepBindings: hasBindings,
            reference: comp.reference,
            sansData: sansData,
            bindData: bindData,
            children: []
        };
        if (childItems) {
            for (i = 0 , len = childItems.length; i < len; ++i) {
                child = me.buildNode(childItems[i]);
                o.hasDeepBindings = o.hasDeepBindings || child.hasDeepBindings;
                if (child.hasDeepBindings) {
                    o.children.push(child);
                }
            }
        }
        if (o.children.length) {
            o.expanded = true;
            o.leaf = false;
        } else {
            o.leaf = true;
        }
        return o;
    },
    
    onItemDblclick: function(view, rec) {
        this.fireEvent('componentdblclick', this, rec);
    },
    
    onItemSelect: function(selModel, rec) {
        var node = this.getView().getNode(rec);
        this.fireEvent('componentselect', this, rec, node);
    }
}, function() {
    this.prototype.Model = Ext.define(null, {
        extend: 'Ext.data.TreeModel',
        fields: [
            'hasViewModel',
            'hasBindings',
            'reference',
            'hasDeepBindings',
            'reference',
            'sansData',
            'bindData',
            'isSrcVM'
        ]
    });
});


Ext.define('Ext.resizer.BorderSplitter', {
    extend: 'Ext.resizer.Splitter',
    uses: [
        'Ext.resizer.BorderSplitterTracker'
    ],
    alias: 'widget.bordersplitter',
    
    collapseTarget: null,
    getTrackerConfig: function() {
        var trackerConfig = this.callParent();
        trackerConfig.xclass = 'Ext.resizer.BorderSplitterTracker';
        return trackerConfig;
    }
});


Ext.define('Ext.layout.container.Border', {
    extend: 'Ext.layout.container.Container',
    alias: 'layout.border',
    alternateClassName: 'Ext.layout.BorderLayout',
    requires: [
        'Ext.resizer.BorderSplitter',
        'Ext.fx.Anim',
        
        'Ext.layout.container.border.Region'
    ],
    targetCls: Ext.baseCSSPrefix + 'border-layout-ct',
    itemCls: [
        Ext.baseCSSPrefix + 'border-item',
        Ext.baseCSSPrefix + 'box-item'
    ],
    type: 'border',
    isBorderLayout: true,
    
    
    
    padding: undefined,
    percentageRe: /(\d+)%/,
    horzPositionProp: 'left',
    padOnContainerProp: 'left',
    padNotOnContainerProp: 'right',
    
    axisProps: {
        horz: {
            borderBegin: 'west',
            borderEnd: 'east',
            horizontal: true,
            posProp: 'x',
            sizeProp: 'width',
            sizePropCap: 'Width'
        },
        vert: {
            borderBegin: 'north',
            borderEnd: 'south',
            horizontal: false,
            posProp: 'y',
            sizeProp: 'height',
            sizePropCap: 'Height'
        }
    },
    
    centerRegion: null,
    manageMargins: true,
    panelCollapseAnimate: true,
    panelCollapseMode: 'placeholder',
    
    regionWeights: {
        north: 20,
        south: 10,
        center: 0,
        west: -10,
        east: -20
    },
    
    
    
    beginAxis: function(ownerContext, regions, name) {
        var me = this,
            props = me.axisProps[name],
            isVert = !props.horizontal,
            sizeProp = props.sizeProp,
            totalFlex = 0,
            childItems = ownerContext.childItems,
            length = childItems.length,
            center, i, childContext, centerFlex, comp, region, match, size, type, target, placeholder;
        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            comp = childContext.target;
            childContext.layoutPos = {};
            if (comp.region) {
                childContext.region = region = comp.region;
                childContext.isCenter = comp.isCenter;
                childContext.isHorz = comp.isHorz;
                childContext.isVert = comp.isVert;
                childContext.weight = comp.weight || me.regionWeights[region] || 0;
                comp.weight = childContext.weight;
                regions[comp.id] = childContext;
                if (comp.isCenter) {
                    center = childContext;
                    centerFlex = comp.flex;
                    ownerContext.centerRegion = center;
                    
                    continue;
                }
                if (isVert !== childContext.isVert) {
                    
                    continue;
                }
                
                childContext.reverseWeighting = (region == props.borderEnd);
                size = comp[sizeProp];
                type = typeof size;
                if (!comp.collapsed) {
                    if (type == 'string' && (match = me.percentageRe.exec(size))) {
                        childContext.percentage = parseInt(match[1], 10);
                    } else if (comp.flex) {
                        totalFlex += childContext.flex = comp.flex;
                    }
                }
            }
        }
        
        if (center) {
            target = center.target;
            if ((placeholder = target.placeholderFor)) {
                if (!centerFlex && isVert === placeholder.collapsedVertical()) {
                    
                    centerFlex = 0;
                    center.collapseAxis = name;
                }
            } else if (target.collapsed && (isVert === target.collapsedVertical())) {
                
                centerFlex = 0;
                center.collapseAxis = name;
            }
        }
        if (centerFlex == null) {
            
            centerFlex = 1;
        }
        totalFlex += centerFlex;
        return Ext.apply({
            before: isVert ? 'top' : 'left',
            totalFlex: totalFlex
        }, props);
    },
    beginLayout: function(ownerContext) {
        var me = this,
            items = me.getLayoutItems(),
            pad = me.padding,
            type = typeof pad,
            padOnContainer = false,
            childContext, item, length, i, regions, collapseTarget, doShow, hidden, region;
        
        if (ownerContext.heightModel.shrinkWrap) {
            Ext.Error.raise("Border layout does not currently support shrinkWrap height. " + "Please specify a height on component: " + me.owner.id + ", or use a container layout that sets the component's height.");
        }
        if (ownerContext.widthModel.shrinkWrap) {
            Ext.Error.raise("Border layout does not currently support shrinkWrap width. " + "Please specify a width on component: " + me.owner.id + ", or use a container layout that sets the component's width.");
        }
        
        if (pad) {
            if (type == 'string' || type == 'number') {
                pad = Ext.util.Format.parseBox(pad);
            }
        } else {
            pad = ownerContext.getEl('getTargetEl').getPaddingInfo();
            padOnContainer = true;
        }
        ownerContext.outerPad = pad;
        ownerContext.padOnContainer = padOnContainer;
        for (i = 0 , length = items.length; i < length; ++i) {
            item = items[i];
            collapseTarget = me.getSplitterTarget(item);
            if (collapseTarget) {
                
                doShow = undefined;
                hidden = !!item.hidden;
                if (!collapseTarget.split) {
                    if (collapseTarget.isCollapsingOrExpanding) {
                        doShow = !!collapseTarget.collapsed;
                    }
                } else if (hidden !== collapseTarget.hidden) {
                    doShow = !collapseTarget.hidden;
                }
                if (doShow) {
                    item.show();
                } else if (doShow === false) {
                    item.hide();
                }
            }
        }
        
        
        
        me.callParent(arguments);
        items = ownerContext.childItems;
        length = items.length;
        regions = {};
        ownerContext.borderAxisHorz = me.beginAxis(ownerContext, regions, 'horz');
        ownerContext.borderAxisVert = me.beginAxis(ownerContext, regions, 'vert');
        
        
        
        for (i = 0; i < length; ++i) {
            childContext = items[i];
            collapseTarget = me.getSplitterTarget(childContext.target);
            if (collapseTarget) {
                
                region = regions[collapseTarget.id];
                if (!region) {
                    
                    
                    
                    region = ownerContext.getEl(collapseTarget.el, me);
                    region.region = collapseTarget.region;
                }
                childContext.collapseTarget = collapseTarget = region;
                childContext.weight = collapseTarget.weight;
                childContext.reverseWeighting = collapseTarget.reverseWeighting;
                collapseTarget.splitter = childContext;
                childContext.isHorz = collapseTarget.isHorz;
                childContext.isVert = collapseTarget.isVert;
            }
        }
        
        me.sortWeightedItems(items, 'reverseWeighting');
        me.setupSplitterNeighbors(items);
    },
    calculate: function(ownerContext) {
        var me = this,
            containerSize = me.getContainerSize(ownerContext),
            childItems = ownerContext.childItems,
            length = childItems.length,
            horz = ownerContext.borderAxisHorz,
            vert = ownerContext.borderAxisVert,
            pad = ownerContext.outerPad,
            padOnContainer = ownerContext.padOnContainer,
            i, childContext, childMargins, size, horzPercentTotal, vertPercentTotal;
        horz.begin = pad[me.padOnContainerProp];
        vert.begin = pad.top;
        
        
        horzPercentTotal = horz.end = horz.flexSpace = containerSize.width + (padOnContainer ? pad[me.padOnContainerProp] : -pad[me.padNotOnContainerProp]);
        vertPercentTotal = vert.end = vert.flexSpace = containerSize.height + (padOnContainer ? pad.top : -pad.bottom);
        
        
        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();
            
            if (childContext.isHorz || childContext.isCenter) {
                horz.addUnflexed(childMargins.width);
                horzPercentTotal -= childMargins.width;
            }
            if (childContext.isVert || childContext.isCenter) {
                vert.addUnflexed(childMargins.height);
                vertPercentTotal -= childMargins.height;
            }
            
            if (!childContext.flex && !childContext.percentage) {
                if (childContext.isHorz || (childContext.isCenter && childContext.collapseAxis === 'horz')) {
                    size = childContext.getProp('width');
                    horz.addUnflexed(size);
                    
                    if (childContext.collapseTarget) {
                        horzPercentTotal -= size;
                    }
                } else if (childContext.isVert || (childContext.isCenter && childContext.collapseAxis === 'vert')) {
                    size = childContext.getProp('height');
                    vert.addUnflexed(size);
                    
                    if (childContext.collapseTarget) {
                        vertPercentTotal -= size;
                    }
                }
            }
        }
        
        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();
            
            if (childContext.percentage) {
                if (childContext.isHorz) {
                    size = Math.ceil(horzPercentTotal * childContext.percentage / 100);
                    size = childContext.setWidth(size);
                    horz.addUnflexed(size);
                } else if (childContext.isVert) {
                    size = Math.ceil(vertPercentTotal * childContext.percentage / 100);
                    size = childContext.setHeight(size);
                    vert.addUnflexed(size);
                }
            }
        }
        
        
        
        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            if (!childContext.isCenter) {
                me.calculateChildAxis(childContext, horz);
                me.calculateChildAxis(childContext, vert);
            }
        }
        
        
        
        if (me.finishAxis(ownerContext, vert) + me.finishAxis(ownerContext, horz) < 2) {
            me.done = false;
        } else {
            
            
            
            me.finishPositions(childItems);
        }
    },
    
    calculateChildAxis: function(childContext, axis) {
        var collapseTarget = childContext.collapseTarget,
            setSizeMethod = 'set' + axis.sizePropCap,
            sizeProp = axis.sizeProp,
            childMarginSize = childContext.getMarginInfo()[sizeProp],
            region, isBegin, flex, pos, size;
        if (collapseTarget) {
            
            region = collapseTarget.region;
        } else {
            region = childContext.region;
            flex = childContext.flex;
        }
        isBegin = region == axis.borderBegin;
        if (!isBegin && region != axis.borderEnd) {
            
            
            childContext[setSizeMethod](axis.end - axis.begin - childMarginSize);
            pos = axis.begin;
        } else {
            if (flex) {
                size = Math.ceil(axis.flexSpace * (flex / axis.totalFlex));
                size = childContext[setSizeMethod](size);
            } else if (childContext.percentage) {
                
                size = childContext.peek(sizeProp);
            } else {
                size = childContext.getProp(sizeProp);
            }
            size += childMarginSize;
            if (isBegin) {
                pos = axis.begin;
                axis.begin += size;
            } else {
                axis.end = pos = axis.end - size;
            }
        }
        childContext.layoutPos[axis.posProp] = pos;
    },
    eachItem: function(region, fn, scope) {
        var me = this,
            items = me.getLayoutItems(),
            i = 0,
            item;
        if (Ext.isFunction(region)) {
            fn = region;
            scope = fn;
        }
        for (i; i < items.length; i++) {
            item = items[i];
            if (!region || item.region === region) {
                if (fn.call(scope, item) === false) {
                    break;
                }
            }
        }
    },
    
    finishAxis: function(ownerContext, axis) {
        var size = axis.end - axis.begin,
            center = ownerContext.centerRegion;
        if (center) {
            center['set' + axis.sizePropCap](size - center.getMarginInfo()[axis.sizeProp]);
            center.layoutPos[axis.posProp] = axis.begin;
        }
        return Ext.isNumber(size) ? 1 : 0;
    },
    
    finishPositions: function(childItems) {
        var length = childItems.length,
            index, childContext,
            marginProp = this.horzPositionProp;
        for (index = 0; index < length; ++index) {
            childContext = childItems[index];
            childContext.setProp('x', childContext.layoutPos.x + childContext.marginInfo[marginProp]);
            childContext.setProp('y', childContext.layoutPos.y + childContext.marginInfo.top);
        }
    },
    getLayoutItems: function() {
        var owner = this.owner,
            ownerItems = (owner && owner.items && owner.items.items) || [],
            length = ownerItems.length,
            items = [],
            i = 0,
            ownerItem, placeholderFor;
        for (; i < length; i++) {
            ownerItem = ownerItems[i];
            placeholderFor = ownerItem.placeholderFor;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (ownerItem.hidden || ((!ownerItem.floated || ownerItem.isCollapsingOrExpanding === 2) && !(placeholderFor && placeholderFor.isCollapsingOrExpanding === 2))) {
                items.push(ownerItem);
            }
        }
        return items;
    },
    getPlaceholder: function(comp) {
        return comp.getPlaceholder && comp.getPlaceholder();
    },
    getMaxWeight: function(region) {
        return this.getMinMaxWeight(region);
    },
    getMinWeight: function(region) {
        return this.getMinMaxWeight(region, true);
    },
    getMinMaxWeight: function(region, min) {
        var me = this,
            weight = null;
        me.eachItem(region, function(item) {
            if (item.hasOwnProperty('weight')) {
                if (weight === null) {
                    weight = item.weight;
                    return;
                }
                if ((min && item.weight < weight) || item.weight > weight) {
                    weight = item.weight;
                }
            }
        }, this);
        return weight;
    },
    getSplitterTarget: function(splitter) {
        var collapseTarget = splitter.collapseTarget;
        if (collapseTarget && collapseTarget.collapsed) {
            return collapseTarget.placeholder || collapseTarget;
        }
        return collapseTarget;
    },
    isItemBoxParent: function(itemContext) {
        return true;
    },
    isItemShrinkWrap: function(item) {
        return true;
    },
    
    
    
    insertSplitter: function(item, index, hidden, splitterCfg) {
        var region = item.region,
            splitter = Ext.apply({
                xtype: 'bordersplitter',
                collapseTarget: item,
                id: item.id + '-splitter',
                hidden: hidden,
                canResize: item.splitterResize !== false,
                splitterFor: item,
                synthetic: true
            }, 
            splitterCfg),
            at = index + ((region === 'south' || region === 'east') ? 0 : 1);
        if (item.collapseMode === 'mini') {
            splitter.collapsedCls = item.collapsedCls;
        }
        item.splitter = this.owner.add(at, splitter);
    },
    getMoveAfterIndex: function(after) {
        var index = this.callParent(arguments);
        if (after.splitter) {
            index++;
        }
        return index;
    },
    moveItemBefore: function(item, before) {
        var owner = this.owner,
            beforeRegion;
        if (before && before.splitter) {
            beforeRegion = before.region;
            if (beforeRegion === 'south' || beforeRegion === 'east') {
                before = before.splitter;
            }
        }
        return this.callParent([
            item,
            before
        ]);
    },
    
    onAdd: function(item, index) {
        var me = this,
            placeholderFor = item.placeholderFor,
            region = item.region,
            isCenter, split, hidden, cfg;
        me.callParent(arguments);
        if (region) {
            Ext.apply(item, me.regionFlags[region]);
            if (item.initBorderRegion) {
                
                
                item.initBorderRegion();
            }
            isCenter = region === 'center';
            if (isCenter) {
                if (me.centerRegion) {
                    Ext.Error.raise("Cannot have multiple center regions in a BorderLayout.");
                }
                me.centerRegion = item;
            } else {
                split = item.split;
                hidden = !!item.hidden;
                if (typeof split === 'object') {
                    cfg = split;
                    split = true;
                }
                if ((item.isHorz || item.isVert) && (split || item.collapseMode == 'mini')) {
                    me.insertSplitter(item, index, hidden || !split, cfg);
                }
            }
            if (!isCenter && !item.hasOwnProperty('collapseMode')) {
                item.collapseMode = me.panelCollapseMode;
            }
            if (!item.hasOwnProperty('animCollapse')) {
                if (item.collapseMode !== 'placeholder') {
                    
                    
                    item.animCollapse = false;
                } else {
                    item.animCollapse = me.panelCollapseAnimate;
                }
            }
        } else if (placeholderFor) {
            Ext.apply(item, me.regionFlags[placeholderFor.region]);
            item.region = placeholderFor.region;
            item.weight = placeholderFor.weight;
        }
    },
    onDestroy: function() {
        this.centerRegion = null;
        this.callParent();
    },
    onRemove: function(comp, isDestroying) {
        var me = this,
            region = comp.region,
            splitter = comp.splitter,
            owner = me.owner,
            destroying = owner.destroying,
            el;
        if (region) {
            if (comp.isCenter) {
                me.centerRegion = null;
            }
            delete comp.isCenter;
            delete comp.isHorz;
            delete comp.isVert;
            
            if (splitter && !owner.destroying) {
                owner.doRemove(splitter, true);
            }
            
            delete comp.splitter;
        }
        me.callParent(arguments);
        if (!destroying && !isDestroying && comp.rendered) {
            
            el = comp.getEl();
            if (el) {
                el.setStyle('top', '');
                el.setStyle(me.horzPositionProp, '');
            }
        }
    },
    
    
    regionMeta: {
        center: {
            splitterDelta: 0
        },
        north: {
            splitterDelta: 1
        },
        south: {
            splitterDelta: -1
        },
        west: {
            splitterDelta: 1
        },
        east: {
            splitterDelta: -1
        }
    },
    
    regionFlags: {
        center: {
            isCenter: true,
            isHorz: false,
            isVert: false
        },
        north: {
            isCenter: false,
            isHorz: false,
            isVert: true,
            collapseDirection: 'top'
        },
        south: {
            isCenter: false,
            isHorz: false,
            isVert: true,
            collapseDirection: 'bottom'
        },
        west: {
            isCenter: false,
            isHorz: true,
            isVert: false,
            collapseDirection: 'left'
        },
        east: {
            isCenter: false,
            isHorz: true,
            isVert: false,
            collapseDirection: 'right'
        }
    },
    setupSplitterNeighbors: function(items) {
        var edgeRegions = {},
            
            
            
            
            length = items.length,
            touchedRegions = this.touchedRegions,
            i, j, center, count, edge, comp, region, splitter, touched;
        for (i = 0; i < length; ++i) {
            comp = items[i].target;
            region = comp.region;
            if (comp.isCenter) {
                center = comp;
            } else if (region) {
                touched = touchedRegions[region];
                for (j = 0 , count = touched.length; j < count; ++j) {
                    edge = edgeRegions[touched[j]];
                    if (edge) {
                        edge.neighbors.push(comp);
                    }
                }
                if (comp.placeholderFor) {
                    
                    splitter = comp.placeholderFor.splitter;
                } else {
                    splitter = comp.splitter;
                }
                if (splitter) {
                    splitter.neighbors = [];
                }
                edgeRegions[region] = splitter;
            }
        }
        if (center) {
            touched = touchedRegions.center;
            for (j = 0 , count = touched.length; j < count; ++j) {
                edge = edgeRegions[touched[j]];
                if (edge) {
                    edge.neighbors.push(center);
                }
            }
        }
    },
    
    touchedRegions: {
        center: [
            'north',
            'south',
            'east',
            'west'
        ],
        north: [
            'north',
            'east',
            'west'
        ],
        south: [
            'south',
            'east',
            'west'
        ],
        east: [
            'east',
            'north',
            'south'
        ],
        west: [
            'west',
            'north',
            'south'
        ]
    },
    sizePolicies: {
        vert: {
            readsWidth: 0,
            readsHeight: 1,
            setsWidth: 1,
            setsHeight: 0
        },
        horz: {
            readsWidth: 1,
            readsHeight: 0,
            setsWidth: 0,
            setsHeight: 1
        },
        flexAll: {
            readsWidth: 0,
            readsHeight: 0,
            setsWidth: 1,
            setsHeight: 1
        }
    },
    getItemSizePolicy: function(item) {
        var me = this,
            policies = this.sizePolicies,
            collapseTarget, size, policy, placeholderFor;
        if (item.isCenter) {
            placeholderFor = item.placeholderFor;
            if (placeholderFor) {
                if (placeholderFor.collapsedVertical()) {
                    return policies.vert;
                }
                return policies.horz;
            }
            if (item.collapsed) {
                if (item.collapsedVertical()) {
                    return policies.vert;
                }
                return policies.horz;
            }
            return policies.flexAll;
        }
        collapseTarget = item.collapseTarget;
        if (collapseTarget) {
            return collapseTarget.isVert ? policies.vert : policies.horz;
        }
        if (item.region) {
            if (item.isVert) {
                size = item.height;
                policy = policies.vert;
            } else {
                size = item.width;
                policy = policies.horz;
            }
            if (item.flex || (typeof size == 'string' && me.percentageRe.test(size))) {
                return policies.flexAll;
            }
            return policy;
        }
        return me.autoSizePolicy;
    }
}, function() {
    var methods = {
            addUnflexed: function(px) {
                this.flexSpace = Math.max(this.flexSpace - px, 0);
            }
        },
        props = this.prototype.axisProps;
    Ext.apply(props.horz, methods);
    Ext.apply(props.vert, methods);
});


Ext.define('Ext.layout.container.Card', {
    
    extend: 'Ext.layout.container.Fit',
    alternateClassName: 'Ext.layout.CardLayout',
    alias: 'layout.card',
    
    type: 'card',
    hideInactive: true,
    
    deferredRender: false,
    getRenderTree: function() {
        var me = this,
            activeItem = me.getActiveItem();
        if (activeItem) {
            
            if (activeItem.hasListeners.beforeactivate && activeItem.fireEvent('beforeactivate', activeItem) === false) {
                
                
                
                activeItem = me.activeItem = me.owner.activeItem = null;
            }
            
            else if (activeItem.hasListeners.activate) {
                activeItem.on({
                    boxready: function() {
                        activeItem.fireEvent('activate', activeItem);
                    },
                    single: true
                });
            }
            if (me.deferredRender) {
                if (activeItem) {
                    return me.getItemsRenderTree([
                        activeItem
                    ]);
                }
            } else {
                return me.callParent(arguments);
            }
        }
    },
    renderChildren: function() {
        var me = this,
            active = me.getActiveItem();
        if (!me.deferredRender) {
            me.callParent();
        } else if (active) {
            
            me.renderItems([
                active
            ], me.getRenderTarget());
        }
    },
    isValidParent: function(item, target, position) {
        
        
        var itemEl = item.el ? item.el.dom : Ext.getDom(item);
        return (itemEl && itemEl.parentNode === (target.dom || target)) || false;
    },
    
    getActiveItem: function() {
        var me = this,
            
            
            
            
            item = me.activeItem === undefined ? (me.owner && me.owner.activeItem) : me.activeItem,
            result = me.parseActiveItem(item);
        
        if (result && me.owner.items.indexOf(result) !== -1) {
            me.activeItem = result;
        }
        
        
        
        return result == null ? null : (me.activeItem || me.owner.activeItem);
    },
    
    parseActiveItem: function(item) {
        var activeItem;
        if (item && item.isComponent) {
            activeItem = item;
        } else if (typeof item === 'number' || item === undefined) {
            activeItem = this.getLayoutItems()[item || 0];
        } else if (item === null) {
            activeItem = null;
        } else {
            activeItem = this.owner.getComponent(item);
        }
        return activeItem;
    },
    
    
    configureItem: function(item) {
        item.setHiddenState(item !== this.getActiveItem());
        this.callParent(arguments);
    },
    onRemove: function(component) {
        this.callParent([
            component
        ]);
        if (component === this.activeItem) {
            
            
            this.activeItem = undefined;
        }
    },
    
    getAnimation: function(newCard, owner) {
        var newAnim = (newCard || {}).cardSwitchAnimation;
        if (newAnim === false) {
            return false;
        }
        return newAnim || owner.cardSwitchAnimation;
    },
    
    getNext: function() {
        var wrap = arguments[0],
            items = this.getLayoutItems(),
            index = Ext.Array.indexOf(items, this.activeItem);
        return items[index + 1] || (wrap ? items[0] : false);
    },
    
    next: function() {
        var anim = arguments[0],
            wrap = arguments[1];
        return this.setActiveItem(this.getNext(wrap), anim);
    },
    
    getPrev: function() {
        var wrap = arguments[0],
            items = this.getLayoutItems(),
            index = Ext.Array.indexOf(items, this.activeItem);
        return items[index - 1] || (wrap ? items[items.length - 1] : false);
    },
    
    prev: function() {
        var anim = arguments[0],
            wrap = arguments[1];
        return this.setActiveItem(this.getPrev(wrap), anim);
    },
    
    setActiveItem: function(newCard) {
        var me = this,
            owner = me.owner,
            oldCard = me.activeItem,
            rendered = owner.rendered,
            newIndex, focusNewCard;
        newCard = me.parseActiveItem(newCard);
        newIndex = owner.items.indexOf(newCard);
        
        
        if (newIndex === -1) {
            newIndex = owner.items.items.length;
            Ext.suspendLayouts();
            newCard = owner.add(newCard);
            Ext.resumeLayouts();
        }
        
        if (newCard && oldCard !== newCard) {
            
            if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) {
                return false;
            }
            if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) {
                return false;
            }
            if (rendered) {
                Ext.suspendLayouts();
                
                if (!newCard.rendered) {
                    me.renderItem(newCard, me.getRenderTarget(), owner.items.length);
                }
                if (oldCard) {
                    if (me.hideInactive) {
                        focusNewCard = oldCard.el.contains(Ext.Element.getActiveElement());
                        oldCard.hide();
                        if (oldCard.hidden) {
                            oldCard.hiddenByLayout = true;
                            oldCard.fireEvent('deactivate', oldCard, newCard);
                        } else 
                        {
                            return false;
                        }
                    }
                }
                
                if (newCard.hidden) {
                    newCard.show();
                }
                
                
                if (newCard.hidden) {
                    me.activeItem = newCard = null;
                } else {
                    me.activeItem = newCard;
                    
                    
                    
                    
                    if (focusNewCard) {
                        if (!newCard.defaultFocus) {
                            newCard.defaultFocus = ':focusable';
                        }
                        newCard.focus();
                    }
                }
                Ext.resumeLayouts(true);
            } else {
                me.activeItem = newCard;
            }
            newCard.fireEvent('activate', newCard, oldCard);
            return me.activeItem;
        }
        return false;
    }
});


Ext.define('Ext.dom.ButtonElement', {
    extend: 'Ext.dom.Element',
    setSize: function(width, height, animate) {
        var me = this,
            component = me.component;
        me.callParent([
            width,
            height,
            animate
        ]);
        component.btnWrap.setStyle('table-layout', (!width || width === 'auto') ? '' : 'fixed');
        component.btnEl.setStyle('height', (!height || height === 'auto') ? '' : 'auto');
        return me;
    },
    setStyle: function(prop, value) {
        var me = this,
            component = me.component,
            width, height;
        me.callParent([
            prop,
            value
        ]);
        if (prop) {
            if (prop === 'width' || (typeof prop !== 'string' && 'width' in prop)) {
                width = value || prop.width;
                component.btnWrap.setStyle('table-layout', (!width || width === 'auto') ? '' : 'fixed');
            }
            if (prop === 'height' || (typeof prop !== 'string' && 'height' in prop)) {
                height = value || prop.height;
                component.btnEl.setStyle('height', (!height || height === 'auto') ? '' : 'auto');
            }
        }
        return me;
    },
    setHeight: function(height, animate) {
        this.callParent([
            height,
            animate
        ]);
        this.component.btnEl.setStyle('height', (!height || height === 'auto') ? '' : 'auto');
        return this;
    },
    setWidth: function(width, animate) {
        this.callParent([
            width,
            animate
        ]);
        this.component.btnWrap.setStyle('table-layout', (!width || width === 'auto') ? '' : 'fixed');
        return this;
    }
});


Ext.define('Ext.button.Manager', {
    singleton: true,
    alternateClassName: 'Ext.ButtonToggleManager',
    groups: {},
    pressedButton: null,
    buttonSelector: '.' + Ext.baseCSSPrefix + 'btn',
    init: function() {
        var me = this;
        if (!me.initialized) {
            Ext.getDoc().on({
                keydown: me.onDocumentKeyDown,
                mouseup: me.onDocumentMouseUp,
                scope: me
            });
            me.initialized = true;
        }
    },
    
    
    onDocumentKeyDown: function(e) {
        var k = e.getKey(),
            btn;
        
        if (k === e.SPACE || k === e.ENTER) {
            
            btn = e.getTarget(this.buttonSelector);
            
            if (btn) {
                Ext.getCmp(btn.id).onClick(e);
            }
        }
    },
    
    
    
    onButtonMousedown: function(button, e) {
        var pressed = this.pressedButton;
        if (pressed) {
            pressed.onMouseUp(e);
        }
        this.pressedButton = button;
    },
    onDocumentMouseUp: function(e) {
        var pressed = this.pressedButton;
        if (pressed) {
            pressed.onMouseUp(e);
            this.pressedButton = null;
        }
    },
    toggleGroup: function(btn, state) {
        if (state) {
            var g = this.groups[btn.toggleGroup],
                length = g.length,
                i;
            for (i = 0; i < length; i++) {
                if (g[i] !== btn) {
                    g[i].toggle(false);
                }
            }
        }
    },
    register: function(btn) {
        var me = this,
            groups = this.groups,
            group = groups[btn.toggleGroup];
        me.init();
        if (!btn.toggleGroup) {
            return;
        }
        if (!group) {
            group = groups[btn.toggleGroup] = [];
        }
        group.push(btn);
        btn.on('toggle', me.toggleGroup, me);
    },
    unregister: function(btn) {
        if (!btn.toggleGroup) {
            return;
        }
        var me = this,
            group = me.groups[btn.toggleGroup];
        if (group) {
            Ext.Array.remove(group, btn);
            btn.un('toggle', me.toggleGroup, me);
        }
    },
    
    getPressed: function(groupName) {
        var group = this.groups[groupName],
            i = 0,
            len;
        if (group) {
            for (len = group.length; i < len; i++) {
                if (group[i].pressed === true) {
                    return group[i];
                }
            }
        }
        return null;
    }
});


Ext.define('Ext.menu.Manager', {
    singleton: true,
    alternateClassName: 'Ext.menu.MenuMgr',
    uses: [
        'Ext.menu.Menu'
    ],
    groups: {},
    
    hideAll: function() {
        var allMenus = Ext.ComponentQuery.query('menu[floating]:not([hidden])'),
            len = allMenus.length,
            i,
            result = false;
        for (i = 0; i < len; i++) {
            allMenus[i].hide();
            result = true;
        }
        return result;
    },
    
    get: function(menu, config) {
        var result;
        if (typeof menu === 'string') {
            
            result = Ext.getCmp(menu);
            if (result instanceof Ext.menu.Menu) {
                menu = result;
            }
        } else if (Ext.isArray(menu)) {
            
            config = Ext.apply({
                items: menu
            }, config);
            menu = new Ext.menu.Menu(config);
        } else if (!menu.isComponent) {
            
            config = Ext.apply({}, menu, config);
            menu = Ext.ComponentManager.create(config, 'menu');
        }
        return menu;
    },
    
    registerCheckable: function(menuItem) {
        var groups = this.groups,
            groupId = menuItem.group;
        if (groupId) {
            if (!groups[groupId]) {
                groups[groupId] = [];
            }
            groups[groupId].push(menuItem);
        }
    },
    
    unregisterCheckable: function(menuItem) {
        var groups = this.groups,
            groupId = menuItem.group;
        if (groupId) {
            Ext.Array.remove(groups[groupId], menuItem);
        }
    },
    onCheckChange: function(menuItem, state) {
        var groups = this.groups,
            groupId = menuItem.group,
            i = 0,
            group, ln, curr;
        if (groupId && state) {
            group = groups[groupId];
            ln = group.length;
            for (; i < ln; i++) {
                curr = group[i];
                if (curr !== menuItem) {
                    curr.setChecked(false);
                }
            }
        }
    }
});


Ext.define('Ext.button.Button', {
    
    alias: 'widget.button',
    extend: 'Ext.Component',
    requires: [
        'Ext.dom.ButtonElement',
        'Ext.button.Manager',
        'Ext.menu.Manager',
        'Ext.util.ClickRepeater',
        'Ext.util.TextMetrics',
        'Ext.util.KeyMap'
    ],
    mixins: [
        'Ext.mixin.Queryable'
    ],
    alternateClassName: 'Ext.Button',
    config: {
        
        iconAlign: 'left',
        
        text: null,
        
        textAlign: 'center',
        
        arrowVisible: true
    },
    
    
    isButton: true,
    
    _syncFrameHeight: true,
    
    liquidLayout: true,
    
    hidden: false,
    
    disabled: false,
    
    pressed: false,
    
    
    
    
    
    
    
    
    
    
    tabIndex: 0,
    
    
    enableToggle: false,
    
    
    
    menuAlign: 'tl-bl?',
    
    showEmptyMenu: false,
    
    
    
    
    clickEvent: 'click',
    
    preventDefault: true,
    
    handleMouseEvents: true,
    
    tooltipType: 'qtip',
    
    baseCls: Ext.baseCSSPrefix + 'btn',
    
    
    hrefTarget: '_blank',
    
    destroyMenu: true,
    
    
    
    focusable: true,
    ariaRole: 'button',
    defaultBindProperty: 'text',
    childEls: [
        'btnEl',
        'btnWrap',
        'btnInnerEl',
        'btnIconEl'
    ],
    publishes: {
        pressed: 1
    },
    
    menuClickBuffer: 250,
    _btnWrapCls: Ext.baseCSSPrefix + 'btn-wrap',
    _btnCls: Ext.baseCSSPrefix + 'btn-button',
    _baseIconCls: Ext.baseCSSPrefix + 'btn-icon-el',
    _glyphCls: Ext.baseCSSPrefix + 'btn-glyph',
    _innerCls: Ext.baseCSSPrefix + 'btn-inner',
    _textCls: Ext.baseCSSPrefix + 'btn-text',
    _noTextCls: Ext.baseCSSPrefix + 'btn-no-text',
    _hasIconCls: Ext.baseCSSPrefix + 'btn-icon',
    _pressedCls: Ext.baseCSSPrefix + 'btn-pressed',
    overCls: Ext.baseCSSPrefix + 'btn-over',
    _disabledCls: Ext.baseCSSPrefix + 'btn-disabled',
    _menuActiveCls: Ext.baseCSSPrefix + 'btn-menu-active',
    
    _operaArrowCls: Ext.baseCSSPrefix + 'opera12m-btn-arrow',
    
    
    
    renderTpl: '<span id="{id}-btnWrap" data-ref="btnWrap" role="presentation" unselectable="on" style="{btnWrapStyle}" ' + 'class="{btnWrapCls} {btnWrapCls}-{ui} {splitCls}{childElCls}">' + '<span id="{id}-btnEl" data-ref="btnEl" role="presentation" unselectable="on" style="{btnElStyle}" ' + 'class="{btnCls} {btnCls}-{ui} {textCls} {noTextCls} {hasIconCls} ' + '{iconAlignCls} {textAlignCls} {btnElAutoHeightCls}{childElCls}">' + '<tpl if="iconBeforeText">{[values.$comp.renderIcon(values)]}</tpl>' + '<span id="{id}-btnInnerEl" data-ref="btnInnerEl" unselectable="on" ' + 'class="{innerCls} {innerCls}-{ui}{childElCls}">{text}</span>' + '<tpl if="!iconBeforeText">{[values.$comp.renderIcon(values)]}</tpl>' + '</span>' + '</span>' + '{[values.$comp.getAfterMarkup ? values.$comp.getAfterMarkup(values) : ""]}' + 
    '<tpl if="closable">' + '<span id="{id}-closeEl" data-ref="closeEl" class="{baseCls}-close-btn">' + '<tpl if="closeText">' + ' {closeText}' + '</tpl>' + '</span>' + '</tpl>',
    iconTpl: '<span id="{id}-btnIconEl" data-ref="btnIconEl" role="presentation" unselectable="on" class="{baseIconCls} ' + '{baseIconCls}-{ui} {iconCls} {glyphCls}{childElCls}" style="' + '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>' + '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">' + '<tpl if="glyph">&#{glyph};</tpl><tpl if="iconCls || iconUrl">&#160;</tpl>' + '</span>',
    
    scale: 'small',
    
    allowedScales: [
        'small',
        'medium',
        'large'
    ],
    
    
    arrowAlign: 'right',
    
    arrowCls: 'arrow',
    
    
    
    maskOnDisable: false,
    shrinkWrap: 3,
    frame: true,
    autoEl: {
        tag: 'a',
        hidefocus: 'on',
        unselectable: 'on'
    },
    hasFrameTable: function() {
        
        
        return this.href && this.frameTable;
    },
    frameTableListener: function() {
        if (!this.disabled) {
            this.doNavigate();
        }
    },
    doNavigate: function() {
        
        
        
        if (this.hrefTarget === '_blank') {
            window.open(this.getHref(), this.hrefTarget);
        } else {
            location.href = this.getHref();
        }
    },
    
    _triggerRegion: {},
    
    
    
    
    
    
    
    
    
    
    
    initComponent: function() {
        var me = this;
        
        me.addCls(Ext.baseCSSPrefix + 'unselectable');
        if (Ext.isOpera12m && (me.split || me.menu) && me.getArrowVisible()) {
            me.addCls(me._operaArrowCls + '-' + me.arrowAlign);
        }
        me.callParent();
        if (me.menu) {
            
            me.split = true;
            me.setMenu(me.menu, 
            false, true);
        }
        
        if (me.url) {
            me.href = me.url;
        }
        
        me.configuredWithPreventDefault = me.hasOwnProperty('preventDefault');
        if (me.href && !me.configuredWithPreventDefault) {
            me.preventDefault = false;
        }
        if (Ext.isString(me.toggleGroup) && me.toggleGroup !== '') {
            me.enableToggle = true;
        }
        if (me.html && !me.text) {
            me.text = me.html;
            delete me.html;
        }
    },
    getElConfig: function() {
        var me = this,
            config = me.callParent(),
            href = me.getHref(),
            hrefTarget = me.hrefTarget;
        if (config.tag === 'a') {
            if (!me.disabled) {
                config.tabIndex = me.tabIndex;
            }
            if (href) {
                
                
                
                if (!me.disabled) {
                    config.href = href;
                    if (hrefTarget) {
                        config.target = hrefTarget;
                    }
                }
            }
        }
        return config;
    },
    beforeRender: function() {
        this.callParent();
        if (this.pressed) {
            this.addCls(this._pressedCls);
        }
    },
    initRenderData: function() {
        return Ext.apply(this.callParent(), this.getTemplateArgs());
    },
    
    getMenu: function() {
        return this.menu || null;
    },
    
    setMenu: function(menu, destroyMenu, 
    initial) {
        var me = this,
            oldMenu = me.menu;
        if (oldMenu && !initial) {
            if (destroyMenu !== false && me.destroyMenu) {
                oldMenu.destroy();
            }
            oldMenu.ownerCmp = null;
        }
        if (menu) {
            
            menu = Ext.menu.Manager.get(menu, {
                
                
                ownerCmp: me
            });
            
            
            menu.ownerCmp = me;
            me.mon(menu, {
                scope: me,
                show: me.onMenuShow,
                hide: me.onMenuHide
            });
            
            
            if (!oldMenu && me.getArrowVisible()) {
                me.split = true;
                if (me.rendered) {
                    me._addSplitCls();
                    me.updateLayout();
                }
            }
            me.menu = menu;
        } else {
            if (me.rendered) {
                me._removeSplitCls();
                me.updateLayout();
            }
            me.split = false;
            me.menu = null;
        }
    },
    
    onRender: function() {
        var me = this,
            addOnclick, btn, btnListeners;
        me.callParent(arguments);
        
        btn = me.el;
        if (me.tooltip) {
            me.setTooltip(me.tooltip, true);
        }
        
        if (me.handleMouseEvents) {
            btnListeners = {
                scope: me,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousedown: me.onMouseDown
            };
            if (me.split) {
                btnListeners.mousemove = me.onMouseMove;
            }
        } else {
            btnListeners = {
                scope: me
            };
        }
        
        if (Ext.supports.Touch) {
            btnListeners.touchstart = me.onTouchStart;
        }
        
        if (me.menu) {
            me.keyMap = new Ext.util.KeyMap({
                target: me.el,
                key: Ext.event.Event.prototype.DOWN,
                handler: me.onDownKey,
                scope: me
            });
        }
        
        if (me.repeat) {
            me.mon(new Ext.util.ClickRepeater(btn,Ext.isObject(me.repeat) ? me.repeat : {}), 'click', me.onRepeatClick, me);
        } else {
            
            if (btnListeners[me.clickEvent]) {
                addOnclick = true;
            } else {
                btnListeners[me.clickEvent] = me.onClick;
            }
        }
        
        me.mon(btn, btnListeners);
        if (me.hasFrameTable()) {
            me.mon(me.frameTable, 'click', me.frameTableListener, me);
        }
        
        if (addOnclick) {
            me.mon(btn, me.clickEvent, me.onClick, me);
        }
        Ext.button.Manager.register(me);
    },
    onFocusLeave: function(e) {
        this.callParent([
            e
        ]);
        if (this.menu) {
            this.menu.hide();
        }
    },
    
    getTemplateArgs: function() {
        var me = this,
            btnCls = me._btnCls,
            baseIconCls = me._baseIconCls,
            iconAlign = me.getIconAlign(),
            glyph = me.glyph,
            glyphFontFamily = Ext._glyphFontFamily,
            text = me.text,
            hasIcon = me._hasIcon(),
            hasIconCls = me._hasIconCls,
            glyphParts;
        if (typeof glyph === 'string') {
            glyphParts = glyph.split('@');
            glyph = glyphParts[0];
            glyphFontFamily = glyphParts[1];
        }
        return {
            innerCls: me._innerCls,
            splitCls: me.getArrowVisible() ? me.getSplitCls() : '',
            iconUrl: me.icon,
            iconCls: me.iconCls,
            glyph: glyph,
            glyphCls: glyph ? me._glyphCls : '',
            glyphFontFamily: glyphFontFamily,
            text: text || '&#160;',
            closeText: me.closeText,
            textCls: text ? me._textCls : '',
            noTextCls: text ? '' : me._noTextCls,
            hasIconCls: hasIcon ? hasIconCls : '',
            btnWrapCls: me._btnWrapCls,
            btnWrapStyle: me.width ? 'table-layout:fixed;' : '',
            btnElStyle: me.height ? 'height:auto;' : '',
            btnCls: btnCls,
            baseIconCls: baseIconCls,
            iconBeforeText: iconAlign === 'left' || iconAlign === 'top',
            iconAlignCls: hasIcon ? (hasIconCls + '-' + iconAlign) : '',
            textAlignCls: btnCls + '-' + me.getTextAlign()
        };
    },
    renderIcon: function(values) {
        return this.getTpl('iconTpl').apply(values);
    },
    
    setHref: function(href) {
        var me = this,
            hrefTarget = me.hrefTarget,
            dom;
        me.href = href;
        if (!me.configuredWithPreventDefault) {
            me.preventDefault = !href;
        }
        if (me.rendered) {
            dom = me.el.dom;
            
            
            
            if (!href || me.disabled) {
                dom.removeAttribute('href');
                dom.removeAttribute('hrefTarget');
            } else {
                dom.href = me.getHref();
                if (hrefTarget) {
                    dom.target = hrefTarget;
                }
            }
        }
    },
    
    getHref: function() {
        var me = this,
            href = me.href;
        return href ? Ext.urlAppend(href, Ext.Object.toQueryString(Ext.apply({}, me.params, me.baseParams))) : false;
    },
    
    setParams: function(params) {
        var me = this,
            dom;
        me.params = params;
        
        
        
        if (me.rendered) {
            dom = me.el.dom;
            if (me.disabled) {
                dom.removeAttribute('href');
            } else {
                dom.href = me.getHref() || '';
            }
        }
    },
    getSplitCls: function() {
        var me = this;
        return me.split ? (me.baseCls + '-' + me.arrowCls) + ' ' + (me.baseCls + '-' + me.arrowCls + '-' + me.arrowAlign) : '';
    },
    
    setIcon: function(icon) {
        icon = icon || '';
        var me = this,
            btnIconEl = me.btnIconEl,
            oldIcon = me.icon || '';
        me.icon = icon;
        if (icon !== oldIcon) {
            if (btnIconEl) {
                btnIconEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
                me._syncHasIconCls();
                if (me.didIconStateChange(oldIcon, icon)) {
                    me.updateLayout();
                }
            }
            me.fireEvent('iconchange', me, oldIcon, icon);
        }
        return me;
    },
    
    setIconCls: function(cls) {
        cls = cls || '';
        var me = this,
            btnIconEl = me.btnIconEl,
            oldCls = me.iconCls || '';
        me.iconCls = cls;
        if (oldCls !== cls) {
            if (btnIconEl) {
                
                btnIconEl.removeCls(oldCls);
                btnIconEl.addCls(cls);
                me._syncHasIconCls();
                if (me.didIconStateChange(oldCls, cls)) {
                    me.updateLayout();
                }
            }
            me.fireEvent('iconchange', me, oldCls, cls);
        }
        return me;
    },
    
    setGlyph: function(glyph) {
        glyph = glyph || 0;
        var me = this,
            btnIconEl = me.btnIconEl,
            oldGlyph = me.glyph,
            glyphCls = me._glyphCls,
            fontFamily, glyphParts;
        me.glyph = glyph;
        if (btnIconEl) {
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                fontFamily = glyphParts[1] || Ext._glyphFontFamily;
            }
            if (!glyph) {
                btnIconEl.dom.innerHTML = '';
                btnIconEl.removeCls(glyphCls);
            } else if (oldGlyph !== glyph) {
                btnIconEl.dom.innerHTML = '&#' + glyph + ';';
                btnIconEl.addCls(glyphCls);
            }
            if (fontFamily) {
                btnIconEl.setStyle('font-family', fontFamily);
            }
            me._syncHasIconCls();
            if (me.didIconStateChange(oldGlyph, glyph)) {
                me.updateLayout();
            }
        }
        me.fireEvent('glyphchange', me, me.glyph, oldGlyph);
        return me;
    },
    
    setTooltip: function(tooltip, initial) {
        var me = this;
        if (me.rendered) {
            if (!initial || !tooltip) {
                me.clearTip();
            }
            if (tooltip) {
                if (Ext.quickTipsActive && Ext.isObject(tooltip)) {
                    Ext.tip.QuickTipManager.register(Ext.apply({
                        target: me.el.id
                    }, tooltip));
                    me.tooltip = tooltip;
                } else {
                    me.el.dom.setAttribute(me.getTipAttr(), tooltip);
                }
            }
        } else {
            me.tooltip = tooltip;
        }
        return me;
    },
    updateIconAlign: function(align, oldAlign) {
        var me = this,
            btnEl, btnIconEl, hasIconCls;
        if (me.rendered) {
            btnEl = me.btnEl;
            btnIconEl = me.btnIconEl;
            hasIconCls = me._hasIconCls;
            if (oldAlign) {
                btnEl.removeCls(hasIconCls + '-' + oldAlign);
            }
            btnEl.addCls(hasIconCls + '-' + align);
            
            
            if (align === 'top' || align === 'left') {
                btnEl.insertFirst(btnIconEl);
            } else {
                btnEl.appendChild(btnIconEl);
            }
            me.updateLayout();
        }
    },
    updateTextAlign: function(align, oldAlign) {
        var me = this,
            btnEl = me.btnEl,
            btnCls = me._btnCls;
        if (me.rendered) {
            btnEl.removeCls(btnCls + '-' + oldAlign);
            btnEl.addCls(btnCls + '-' + align);
        }
    },
    getTipAttr: function() {
        return this.tooltipType === 'qtip' ? 'data-qtip' : 'title';
    },
    
    getRefItems: function(deep) {
        var menu = this.menu,
            items;
        if (menu) {
            items = menu.getRefItems(deep);
            items.unshift(menu);
        }
        return items || [];
    },
    
    clearTip: function() {
        var me = this,
            el = me.el;
        if (Ext.quickTipsActive && Ext.isObject(me.tooltip)) {
            Ext.tip.QuickTipManager.unregister(el);
        } else {
            el.dom.removeAttribute(me.getTipAttr());
        }
    },
    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.clearTip();
        }
        Ext.destroy(me.repeater);
        me.callParent();
    },
    
    onDestroy: function() {
        var me = this,
            menu = me.menu;
        if (me.rendered) {
            Ext.destroy(me.keyMap);
            delete me.keyMap;
        }
        if (menu && me.destroyMenu) {
            me.menu = Ext.destroy(menu);
        }
        Ext.button.Manager.unregister(me);
        me.callParent();
    },
    
    setHandler: function(handler, scope) {
        this.handler = handler;
        if (arguments.length > 1) {
            this.scope = scope;
        }
        return this;
    },
    applyText: function(text) {
        
        text = text == null ? '' : String(text);
        var me = this,
            btnInnerEl = me.btnInnerEl,
            btnEl = me.btnEl,
            oldText = me.text || '';
        if (text !== oldText) {
            me.text = text;
            if (me.rendered) {
                btnInnerEl.setHtml(text || '&#160;');
                btnEl[text ? 'addCls' : 'removeCls'](me._textCls);
                btnEl[text ? 'removeCls' : 'addCls'](me._noTextCls);
                me.updateLayout();
            }
            me.fireEvent('textchange', me, oldText, text);
        }
    },
    
    didIconStateChange: function(old, current) {
        var currentEmpty = Ext.isEmpty(current);
        return Ext.isEmpty(old) ? !currentEmpty : currentEmpty;
    },
    
    setPressed: function(pressed) {
        return this.toggle(pressed !== false);
    },
    
    toggle: function(state, suppressEvent) {
        var me = this;
        state = state === undefined ? !me.pressed : !!state;
        if (state !== me.pressed) {
            me[state ? 'addCls' : 'removeCls'](me._pressedCls);
            me.pressed = state;
            if (!suppressEvent) {
                me.fireEvent('toggle', me, state);
                Ext.callback(me.toggleHandler, me.scope, [
                    me,
                    state
                ], 0, me);
                if (me.reference && me.publishState) {
                    me.publishState('pressed', state);
                }
            }
        }
        return me;
    },
    maybeShowMenu: function(e) {
        var me = this;
        if (me.menu && !me.ignoreNextClick) {
            me.showMenu(e);
        }
    },
    
    showMenu: function(
    clickEvent) {
        var me = this,
            menu = me.menu,
            isPointerEvent = !clickEvent || clickEvent.pointerType;
        if (menu && me.rendered) {
            if (me.tooltip && Ext.quickTipsActive && me.getTipAttr() !== 'title') {
                Ext.tip.QuickTipManager.getQuickTip().cancelShow(me.el);
            }
            if (menu.isVisible()) {
                
                if (isPointerEvent) {
                    menu.hide();
                } else {
                    menu.focus();
                }
            } else if (!clickEvent || me.showEmptyMenu || menu.items.getCount() > 0) {
                
                menu.autoFocus = !isPointerEvent;
                menu.showBy(me.el, me.menuAlign);
            }
        }
        return me;
    },
    
    hideMenu: function() {
        if (this.hasVisibleMenu()) {
            this.menu.hide();
        }
        return this;
    },
    
    hasVisibleMenu: function() {
        var menu = this.menu;
        return menu && menu.rendered && menu.isVisible();
    },
    
    onRepeatClick: function(repeat, e) {
        this.onClick(e);
    },
    onTouchStart: function(e) {
        this.doPreventDefault(e);
    },
    
    onClick: function(e) {
        var me = this;
        me.doPreventDefault(e);
        
        
        
        
        if (e.type !== 'keydown' && e.button) {
            return;
        }
        if (!me.disabled) {
            me.doToggle();
            me.maybeShowMenu(e);
            me.fireHandler(e);
        }
    },
    doPreventDefault: function(e) {
        if (e && (this.preventDefault || (this.disabled && this.getHref()))) {
            e.preventDefault();
        }
    },
    fireHandler: function(e) {
        var me = this;
        
        if (me.fireEvent('click', me, e) !== false && !me.isDestroyed) {
            Ext.callback(me.handler, me.scope, [
                me,
                e
            ], 0, me);
        }
    },
    doToggle: function() {
        var me = this;
        if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) {
            me.toggle();
        }
    },
    
    onMouseOver: function(e) {
        var me = this;
        if (!me.disabled && !e.within(me.el, true, true)) {
            me.onMouseEnter(e);
        }
    },
    
    onMouseOut: function(e) {
        var me = this;
        if (!e.within(me.el, true, true)) {
            if (me.overMenuTrigger) {
                me.onMenuTriggerOut(e);
            }
            me.onMouseLeave(e);
        }
    },
    
    onMouseMove: function(e) {
        var me = this,
            over = me.overMenuTrigger;
        if (me.split) {
            if (me.isWithinTrigger(e)) {
                if (!over) {
                    me.onMenuTriggerOver(e);
                }
            } else if (over) {
                me.onMenuTriggerOut(e);
            }
        }
    },
    
    isWithinTrigger: function(e) {
        var me = this,
            el = me.el,
            overPosition, triggerRegion;
        overPosition = (me.arrowAlign === 'right') ? e.getX() - me.getX() : e.getY() - el.getY();
        triggerRegion = me.getTriggerRegion();
        return overPosition > triggerRegion.begin && overPosition < triggerRegion.end;
    },
    
    getTriggerRegion: function() {
        var me = this,
            region = me._triggerRegion,
            isRight = me.arrowAlign === 'right',
            getEnd = isRight ? 'getRight' : 'getBottom',
            btnSize = isRight ? me.getWidth() : me.getHeight();
        region.begin = btnSize - (me.el[getEnd]() - me.btnEl[getEnd]());
        region.end = btnSize;
        return region;
    },
    
    onMouseEnter: function(e) {
        
        this.fireEvent('mouseover', this, e);
    },
    
    onMouseLeave: function(e) {
        
        this.fireEvent('mouseout', this, e);
    },
    
    onMenuTriggerOver: function(e) {
        var me = this,
            arrowTip = me.arrowTooltip;
        me.overMenuTrigger = true;
        
        
        if (me.split && arrowTip) {
            me.btnWrap.dom.setAttribute(me.getTipAttr(), arrowTip);
        }
        me.fireEvent('menutriggerover', me, me.menu, e);
    },
    
    onMenuTriggerOut: function(e) {
        var me = this;
        delete me.overMenuTrigger;
        
        if (me.split && me.arrowTooltip) {
            me.btnWrap.dom.setAttribute(me.getTipAttr(), '');
        }
        me.fireEvent('menutriggerout', me, me.menu, e);
    },
    enable: function(silent) {
        var me = this,
            href = me.href,
            hrefTarget = me.hrefTarget,
            dom;
        me.callParent(arguments);
        me.removeCls(me._disabledCls);
        if (me.rendered) {
            dom = me.el.dom;
            dom.setAttribute('tabindex', me.tabIndex);
            
            
            
            if (href) {
                dom.href = href;
            }
            if (hrefTarget) {
                dom.target = hrefTarget;
            }
        }
        return me;
    },
    disable: function(silent) {
        var me = this,
            dom;
        me.callParent(arguments);
        me.addCls(me._disabledCls);
        me.removeCls(me.overCls);
        if (me.rendered) {
            dom = me.el.dom;
            dom.removeAttribute('tabindex');
            
            
            
            if (me.href) {
                dom.removeAttribute('href');
            }
            if (me.hrefTarget) {
                dom.removeAttribute('target');
            }
        }
        return me;
    },
    
    setScale: function(scale) {
        var me = this,
            ui = me.ui.replace('-' + me.scale, '');
        
        if (!Ext.Array.contains(me.allowedScales, scale)) {
            throw ('#setScale: scale must be an allowed scale (' + me.allowedScales.join(', ') + ')');
        }
        me.scale = scale;
        me.setUI(ui);
    },
    setUI: function(ui) {
        var me = this;
        
        if (me.scale && !ui.match(me.scale)) {
            ui = ui + '-' + me.scale;
        }
        me.callParent([
            ui
        ]);
    },
    
    onMouseDown: function(e) {
        var me = this;
        if (Ext.isIE || e.pointerType === 'touch') {
            
            
            
            me.getFocusEl().focus();
        }
        if (!me.disabled && e.button === 0) {
            Ext.button.Manager.onButtonMousedown(me, e);
            me.addCls(me._pressedCls);
        }
    },
    
    onMouseUp: function(e) {
        var me = this;
        
        if (!me.isDestroyed && e.button === 0) {
            if (!me.pressed) {
                me.removeCls(me._pressedCls);
            }
        }
    },
    
    onMenuShow: function() {
        var me = this;
        me.ignoreNextClick = 0;
        me.addCls(me._menuActiveCls);
        me.fireEvent('menushow', me, me.menu);
    },
    
    onMenuHide: function(e) {
        var me = this,
            menuClickBuffer = me.menuClickBuffer;
        me.removeCls(me._menuActiveCls);
        if (menuClickBuffer) {
            me.ignoreNextClick = Ext.defer(me.restoreClick, menuClickBuffer, me);
        }
        me.fireEvent('menuhide', me, me.menu);
    },
    
    restoreClick: function() {
        this.ignoreNextClick = 0;
    },
    
    onDownKey: function(k, e) {
        var me = this;
        if (me.menu && !me.disabled) {
            me.showMenu(e);
            e.stopEvent();
            return false;
        }
    },
    updateArrowVisible: function(visible) {
        var me = this;
        if (me.rendered) {
            if (visible) {
                if (me.menu || me.isSplitButton) {
                    me.split = true;
                    me._addSplitCls();
                }
            } else {
                me._removeSplitCls();
                me.split = false;
            }
        }
        return visible;
    },
    privates: {
        addOverCls: function() {
            if (!this.disabled) {
                this.addCls(this.overCls);
            }
        },
        _addSplitCls: function() {
            var me = this;
            me.btnWrap.addCls(me.getSplitCls());
            if (Ext.isOpera12m) {
                me.addCls(me._operaArrowCls + '-' + me.arrowAlign);
            }
        },
        getFocusEl: function() {
            return this.el;
        },
        
        getTdCls: function() {
            return Ext.baseCSSPrefix + 'button-' + this.ui + '-' + this.scale + '-cell';
        },
        removeOverCls: function() {
            this.removeCls(this.overCls);
        },
        _removeSplitCls: function() {
            var me = this;
            me.btnWrap.removeCls(me.getSplitCls());
            if (Ext.isOpera12m) {
                me.removeCls(me._operaArrowCls + '-' + me.arrowAlign);
            }
        },
        _syncHasIconCls: function() {
            var me = this,
                btnEl = me.btnEl,
                hasIconCls = me._hasIconCls;
            if (btnEl) {
                btnEl[me._hasIcon() ? 'addCls' : 'removeCls']([
                    hasIconCls,
                    hasIconCls + '-' + me.iconAlign
                ]);
            }
        },
        
        _hasIcon: function() {
            return !!(this.icon || this.iconCls || this.glyph);
        },
        wrapPrimaryEl: function(dom) {
            this.el = new Ext.dom.ButtonElement(dom);
            this.callParent([
                dom
            ]);
        }
    }
});


Ext.define('Ext.tab.Tab', {
    extend: 'Ext.button.Button',
    alias: 'widget.tab',
    requires: [
        'Ext.util.KeyNav'
    ],
    
    isTab: true,
    
    tabIndex: -1,
    baseCls: Ext.baseCSSPrefix + 'tab',
    closeElOverCls: Ext.baseCSSPrefix + 'tab-close-btn-over',
    closeElPressedCls: Ext.baseCSSPrefix + 'tab-close-btn-pressed',
    config: {
        
        rotation: 'default',
        
        tabPosition: 'top'
    },
    
    closable: true,
    
    
    closeText: 'Close Tab',
    
    
    active: false,
    
    childEls: [
        'closeEl'
    ],
    scale: false,
    
    
    
    
    ariaRole: 'tab',
    _btnWrapCls: Ext.baseCSSPrefix + 'tab-wrap',
    _btnCls: Ext.baseCSSPrefix + 'tab-button',
    _baseIconCls: Ext.baseCSSPrefix + 'tab-icon-el',
    _glyphCls: Ext.baseCSSPrefix + 'tab-glyph',
    _innerCls: Ext.baseCSSPrefix + 'tab-inner',
    _textCls: Ext.baseCSSPrefix + 'tab-text',
    _noTextCls: Ext.baseCSSPrefix + 'tab-no-text',
    _hasIconCls: Ext.baseCSSPrefix + 'tab-icon',
    _activeCls: Ext.baseCSSPrefix + 'tab-active',
    _closableCls: Ext.baseCSSPrefix + 'tab-closable',
    overCls: Ext.baseCSSPrefix + 'tab-over',
    _pressedCls: Ext.baseCSSPrefix + 'tab-pressed',
    _disabledCls: Ext.baseCSSPrefix + 'tab-disabled',
    _rotateClasses: {
        1: Ext.baseCSSPrefix + 'tab-rotate-right',
        2: Ext.baseCSSPrefix + 'tab-rotate-left'
    },
    
    
    _positions: {
        top: {
            'default': 'top',
            0: 'top',
            1: 'left',
            2: 'right'
        },
        right: {
            'default': 'top',
            0: 'right',
            1: 'top',
            2: 'bottom'
        },
        bottom: {
            'default': 'bottom',
            0: 'bottom',
            1: 'right',
            2: 'left'
        },
        left: {
            'default': 'top',
            0: 'left',
            1: 'bottom',
            2: 'top'
        }
    },
    _defaultRotations: {
        top: 0,
        right: 1,
        bottom: 0,
        left: 2
    },
    initComponent: function() {
        var me = this;
        if (me.card) {
            me.setCard(me.card);
        }
        me.callParent(arguments);
    },
    getActualRotation: function() {
        var rotation = this.getRotation();
        return (rotation !== 'default') ? rotation : this._defaultRotations[this.getTabPosition()];
    },
    updateRotation: function() {
        this.syncRotationAndPosition();
    },
    updateTabPosition: function() {
        this.syncRotationAndPosition();
    },
    syncRotationAndPosition: function() {
        var me = this,
            positions = me._positions,
            rotateClasses = me._rotateClasses,
            position = me.getTabPosition(),
            rotation = me.getActualRotation(),
            oldRotateCls = me._rotateCls,
            rotateCls = me._rotateCls = rotateClasses[rotation],
            oldPositionCls = me._positionCls,
            positionCls = me._positionCls = me._positions[position][rotation];
        if (oldRotateCls !== rotateCls) {
            if (oldRotateCls) {
                me.removeCls(oldRotateCls);
            }
            if (rotateCls) {
                me.addCls(rotateCls);
            }
        }
        if (oldPositionCls !== positionCls) {
            if (oldPositionCls) {
                me.removeClsWithUI(oldPositionCls);
            }
            if (positionCls) {
                me.addClsWithUI(positionCls);
            }
            if (me.rendered) {
                me.updateFrame();
            }
        }
        if (me.rendered) {
            me.setElOrientation();
        }
    },
    onAdded: function(container, pos, instanced) {
        this.callParent([
            container,
            pos,
            instanced
        ]);
        this.syncRotationAndPosition();
    },
    getTemplateArgs: function() {
        var me = this,
            result = me.callParent();
        result.closable = me.closable;
        result.closeText = me.closeText;
        return result;
    },
    beforeRender: function() {
        var me = this,
            tabBar = me.up('tabbar'),
            tabPanel = me.up('tabpanel');
        me.callParent();
        if (me.active) {
            me.addCls(me._activeCls);
        }
        me.syncClosableCls();
        
        if (!me.minWidth) {
            me.minWidth = (tabBar) ? tabBar.minTabWidth : me.minWidth;
            if (!me.minWidth && tabPanel) {
                me.minWidth = tabPanel.minTabWidth;
            }
            if (me.minWidth && me.iconCls) {
                me.minWidth += 25;
            }
        }
        if (!me.maxWidth) {
            me.maxWidth = (tabBar) ? tabBar.maxTabWidth : me.maxWidth;
            if (!me.maxWidth && tabPanel) {
                me.maxWidth = tabPanel.maxTabWidth;
            }
        }
    },
    onRender: function() {
        var me = this;
        me.setElOrientation();
        me.callParent(arguments);
        if (me.closable) {
            me.closeEl.addClsOnOver(me.closeElOverCls);
            me.closeEl.addClsOnClick(me.closeElPressedCls);
        }
        me.initKeyNav();
    },
    initKeyNav: function() {
        var me = this;
        me.keyNav = new Ext.util.KeyNav(me.el,{
            enter: me.onEnterKey,
            del: me.onDeleteKey,
            scope: me
        });
    },
    setElOrientation: function() {
        var me = this,
            rotation = me.getActualRotation(),
            el = me.el;
        if (rotation) {
            el.setVertical(rotation === 1 ? 90 : 270);
        } else {
            el.setHorizontal();
        }
    },
    
    enable: function(silent) {
        var me = this;
        me.callParent(arguments);
        me.removeCls(me._disabledCls);
        return me;
    },
    
    disable: function(silent) {
        var me = this;
        me.callParent(arguments);
        me.addCls(me._disabledCls);
        return me;
    },
    onDestroy: function() {
        var me = this;
        Ext.destroy(me.keyNav);
        delete me.keyNav;
        me.callParent(arguments);
    },
    
    setClosable: function(closable) {
        var me = this;
        
        closable = (!arguments.length || !!closable);
        if (me.closable != closable) {
            me.closable = closable;
            
            if (me.card) {
                me.card.closable = closable;
            }
            me.syncClosableCls();
            if (me.rendered) {
                me.syncClosableElements();
                
                me.updateLayout();
            }
        }
    },
    
    syncClosableElements: function() {
        var me = this,
            closeEl = me.closeEl;
        if (me.closable) {
            if (!closeEl) {
                closeEl = me.closeEl = me.btnWrap.insertSibling({
                    tag: 'a',
                    role: 'presentation',
                    cls: me.baseCls + '-close-btn',
                    href: '#',
                    title: me.closeText
                }, 'after');
            }
            closeEl.addClsOnOver(me.closeElOverCls);
            closeEl.addClsOnClick(me.closeElPressedCls);
        } else if (closeEl) {
            closeEl.destroy();
            delete me.closeEl;
        }
    },
    
    syncClosableCls: function() {
        var me = this,
            closableCls = me._closableCls;
        if (me.closable) {
            me.addCls(closableCls);
        } else {
            me.removeCls(closableCls);
        }
    },
    
    setCard: function(card) {
        var me = this;
        me.card = card;
        if (card.iconAlign) {
            me.setIconAlign(card.iconAlign);
        }
        if (card.textAlign) {
            me.setTextAlign(card.textAlign);
        }
        me.setText(me.title || card.title);
        me.setIconCls(me.iconCls || card.iconCls);
        me.setIcon(me.icon || card.icon);
        me.setGlyph(me.glyph || card.glyph);
    },
    
    onCloseClick: function() {
        var me = this;
        if (me.fireEvent('beforeclose', me) !== false) {
            if (me.tabBar) {
                if (me.tabBar.closeTab(me) === false) {
                    
                    return;
                }
            } else {
                
                me.fireClose();
            }
        }
    },
    
    fireClose: function() {
        this.fireEvent('close', this);
    },
    
    onEnterKey: function(e) {
        var me = this;
        if (me.tabBar) {
            me.tabBar.onClick(e, me.el);
        }
    },
    
    onDeleteKey: function(e) {
        if (this.closable) {
            this.onCloseClick();
        }
    },
    
    beforeClick: function(isCloseClick) {
        if (!isCloseClick) {
            this.focus();
        }
    },
    
    activate: function(supressEvent) {
        var me = this;
        me.active = true;
        me.addCls(me._activeCls);
        if (supressEvent !== true) {
            me.fireEvent('activate', me);
        }
    },
    
    deactivate: function(supressEvent) {
        var me = this;
        me.active = false;
        me.removeCls(me._activeCls);
        if (supressEvent !== true) {
            me.fireEvent('deactivate', me);
        }
    },
    privates: {
        getFramingInfoCls: function() {
            return this.baseCls + '-' + this.ui + '-' + this._positionCls;
        },
        wrapPrimaryEl: function(dom) {
            
            Ext.Button.superclass.wrapPrimaryEl.call(this, dom);
        }
    }
});


Ext.define('Ext.layout.component.Body', {
    
    alias: [
        'layout.body'
    ],
    extend: 'Ext.layout.component.Auto',
    
    type: 'body',
    beginLayout: function(ownerContext) {
        this.callParent(arguments);
        ownerContext.bodyContext = ownerContext.getEl('body');
    },
    beginLayoutCycle: function(ownerContext, firstCycle) {
        var me = this,
            lastWidthModel = me.lastWidthModel,
            lastHeightModel = me.lastHeightModel,
            body = me.owner.body;
        me.callParent(arguments);
        if (lastWidthModel && lastWidthModel.fixed && ownerContext.widthModel.shrinkWrap) {
            body.setWidth(null);
        }
        if (lastHeightModel && lastHeightModel.fixed && ownerContext.heightModel.shrinkWrap) {
            body.setHeight(null);
        }
    },
    
    
    
    
    
    
    calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) {
        var height = this.callParent(arguments);
        if (ownerContext.targetContext != ownerContext) {
            height += ownerContext.getPaddingInfo().height;
        }
        return height;
    },
    calculateOwnerWidthFromContentWidth: function(ownerContext, contentWidth) {
        var width = this.callParent(arguments);
        if (ownerContext.targetContext != ownerContext) {
            width += ownerContext.getPaddingInfo().width;
        }
        return width;
    },
    measureContentWidth: function(ownerContext) {
        return ownerContext.bodyContext.setWidth(ownerContext.bodyContext.el.dom.offsetWidth, false);
    },
    measureContentHeight: function(ownerContext) {
        return ownerContext.bodyContext.setHeight(ownerContext.bodyContext.el.dom.offsetHeight, false);
    },
    publishInnerHeight: function(ownerContext, height) {
        var innerHeight = height - ownerContext.getFrameInfo().height,
            targetContext = ownerContext.targetContext;
        if (targetContext != ownerContext) {
            innerHeight -= ownerContext.getPaddingInfo().height;
        }
        
        return ownerContext.bodyContext.setHeight(innerHeight, !ownerContext.heightModel.natural);
    },
    publishInnerWidth: function(ownerContext, width) {
        var innerWidth = width - ownerContext.getFrameInfo().width,
            targetContext = ownerContext.targetContext;
        if (targetContext != ownerContext) {
            innerWidth -= ownerContext.getPaddingInfo().width;
        }
        ownerContext.bodyContext.setWidth(innerWidth, !ownerContext.widthModel.natural);
    }
});


Ext.define('Ext.tab.Bar', {
    extend: 'Ext.panel.Bar',
    xtype: 'tabbar',
    baseCls: Ext.baseCSSPrefix + 'tab-bar',
    requires: [
        'Ext.tab.Tab',
        'Ext.util.Point',
        'Ext.layout.component.Body'
    ],
    mixins: [
        'Ext.util.FocusableContainer'
    ],
    componentLayout: 'body',
    
    isTabBar: true,
    config: {
        
        tabRotation: 'default',
        
        tabStretchMax: true,
        
        
        
        
        activateOnFocus: true
    },
    
    defaultType: 'tab',
    
    plain: false,
    ariaRole: 'tablist',
    childEls: [
        'body',
        'strip'
    ],
    _stripCls: Ext.baseCSSPrefix + 'tab-bar-strip',
    _baseBodyCls: Ext.baseCSSPrefix + 'tab-bar-body',
    
    renderTpl: '<div id="{id}-body" data-ref="body" role="presentation" class="{baseBodyCls} {baseBodyCls}-{ui} ' + '{bodyCls} {bodyTargetCls}{childElCls}"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>' + '{%this.renderContainer(out,values)%}' + '</div>' + '<div id="{id}-strip" data-ref="strip" role="presentation" class="{stripCls} {stripCls}-{ui}{childElCls}"></div>',
    
    
    _reverseDockNames: {
        left: 'right',
        right: 'left'
    },
    _layoutAlign: {
        top: 'end',
        right: 'begin',
        bottom: 'begin',
        left: 'end'
    },
    
    
    initComponent: function() {
        var me = this,
            initialLayout = me.initialConfig.layout,
            initialAlign = initialLayout && initialLayout.align,
            initialOverflowHandler = initialLayout && initialLayout.overflowHandler,
            layout;
        if (me.plain) {
            me.addCls(me.baseCls + '-plain');
        }
        me.callParent();
        me.setLayout({
            align: initialAlign || (me.getTabStretchMax() ? 'stretchmax' : me._layoutAlign[me.dock]),
            overflowHandler: initialOverflowHandler || 'scroller'
        });
        me.on({
            click: me.onClick,
            element: 'el',
            scope: me
        });
    },
    initRenderData: function() {
        var me = this;
        return Ext.apply(me.callParent(), {
            bodyCls: me.bodyCls,
            baseBodyCls: me._baseBodyCls,
            bodyTargetCls: me.bodyTargetCls,
            stripCls: me._stripCls,
            dock: me.dock
        });
    },
    setDock: function(dock) {
        var me = this,
            items = me.items,
            ownerCt = me.ownerCt,
            item, i, ln;
        items = items && items.items;
        if (items) {
            for (i = 0 , ln = items.length; i < ln; i++) {
                item = items[i];
                if (item.isTab) {
                    item.setTabPosition(dock);
                }
            }
        }
        if (me.rendered) {
            
            me.resetItemMargins();
            if (ownerCt && ownerCt.isHeader) {
                ownerCt.resetItemMargins();
            }
            me.needsScroll = true;
        }
        me.callParent([
            dock
        ]);
    },
    updateTabRotation: function(tabRotation) {
        var me = this,
            items = me.items,
            i, ln, item;
        items = items && items.items;
        if (items) {
            for (i = 0 , ln = items.length; i < ln; i++) {
                item = items[i];
                if (item.isTab) {
                    item.setRotation(tabRotation);
                }
            }
        }
        if (me.rendered) {
            
            me.resetItemMargins();
            me.needsScroll = true;
            me.updateLayout();
        }
    },
    onRender: function() {
        var me = this;
        me.callParent();
        if (Ext.isIE8 && me.vertical) {
            me.el.on({
                mousemove: me.onMouseMove,
                scope: me
            });
        }
    },
    afterLayout: function() {
        this.adjustTabPositions();
        this.callParent(arguments);
    },
    onAdded: function(container, pos, instanced) {
        if (container.isHeader) {
            this.addCls(container.baseCls + '-' + container.ui + '-tab-bar');
        }
        this.callParent([
            container,
            pos,
            instanced
        ]);
    },
    onRemove: function(tab) {
        var me = this;
        if (tab === me.previousTab) {
            me.previousTab = null;
        }
        me.callParent(arguments);
    },
    onRemoved: function(destroying) {
        var ownerCt = this.ownerCt;
        if (ownerCt.isHeader) {
            this.removeCls(ownerCt.baseCls + '-' + ownerCt.ui + '-tab-bar');
        }
        this.callParent([
            destroying
        ]);
    },
    afterComponentLayout: function(width) {
        var me = this,
            needsScroll = me.needsScroll,
            overflowHandler = me.layout.overflowHandler;
        me.callParent(arguments);
        if (overflowHandler && needsScroll && me.tooNarrow && overflowHandler.scrollToItem) {
            overflowHandler.scrollToItem(me.activeTab);
        }
        delete me.needsScroll;
    },
    
    onMouseMove: function(e) {
        var me = this,
            overTab = me._overTab,
            tabInfo, tab;
        if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) {
            return;
        }
        tabInfo = me.getTabInfoFromPoint(e.getXY());
        tab = tabInfo.tab;
        if (tab !== overTab) {
            if (overTab && overTab.rendered) {
                overTab.onMouseLeave(e);
                me._overTab = null;
            }
            if (tab) {
                tab.onMouseEnter(e);
                me._overTab = tab;
                if (!tab.disabled) {
                    me.el.setStyle('cursor', 'pointer');
                }
            } else {
                me.el.setStyle('cursor', 'default');
            }
        }
    },
    onMouseLeave: function(e) {
        var overTab = this._overTab;
        if (overTab && overTab.rendered) {
            overTab.onMouseLeave(e);
        }
    },
    
    
    
    
    
    
    getTabInfoFromPoint: function(xy) {
        var me = this,
            tabs = me.items.items,
            length = tabs.length,
            innerCt = me.layout.innerCt,
            innerCtXY = innerCt.getXY(),
            point = new Ext.util.Point(xy[0],xy[1]),
            i = 0,
            lastBox, tabRegion, closeEl, close, closeXY, closeX, closeY, closeWidth, closeHeight, tabX, tabY, tabWidth, tabHeight, closeRegion, isTabReversed, direction, tab;
        for (; i < length; i++) {
            tab = tabs[i];
            lastBox = tab.lastBox;
            if (!lastBox || !tab.isTab) {
                
                
                
                continue;
            }
            tabX = innerCtXY[0] + lastBox.x;
            tabY = innerCtXY[1] - innerCt.dom.scrollTop + lastBox.y;
            tabWidth = lastBox.width;
            tabHeight = lastBox.height;
            tabRegion = new Ext.util.Region(tabY,tabX + tabWidth,tabY + tabHeight,tabX);
            if (tabRegion.contains(point)) {
                closeEl = tab.closeEl;
                if (closeEl) {
                    
                    
                    
                    if (me._isTabReversed === undefined) {
                        me._isTabReversed = isTabReversed = 
                        
                        (tab.btnWrap.dom.currentStyle.filter.indexOf('rotation=2') !== -1);
                    }
                    direction = isTabReversed ? this._reverseDockNames[me.dock] : me.dock;
                    closeWidth = closeEl.getWidth();
                    closeHeight = closeEl.getHeight();
                    closeXY = me.getCloseXY(closeEl, tabX, tabY, tabWidth, tabHeight, closeWidth, closeHeight, direction);
                    closeX = closeXY[0];
                    closeY = closeXY[1];
                    closeRegion = new Ext.util.Region(closeY,closeX + closeWidth,closeY + closeHeight,closeX);
                    close = closeRegion.contains(point);
                }
                break;
            }
        }
        return {
            tab: tab,
            close: close
        };
    },
    
    getCloseXY: function(closeEl, tabX, tabY, tabWidth, tabHeight, closeWidth, closeHeight, direction) {
        var closeXY = closeEl.getXY(),
            closeX, closeY;
        if (direction === 'right') {
            closeX = tabX + tabWidth - ((closeXY[1] - tabY) + closeHeight);
            closeY = tabY + (closeXY[0] - tabX);
        } else {
            closeX = tabX + (closeXY[1] - tabY);
            closeY = tabY + tabX + tabHeight - closeXY[0] - closeWidth;
        }
        return [
            closeX,
            closeY
        ];
    },
    
    closeTab: function(toClose) {
        var me = this,
            card = toClose.card,
            tabPanel = me.tabPanel,
            toActivate;
        if (card && card.fireEvent('beforeclose', card) === false) {
            return false;
        }
        
        
        
        toActivate = me.findNextActivatable(toClose);
        
        
        Ext.suspendLayouts();
        if (tabPanel && card) {
            
            
            delete toClose.ownerCt;
            
            
            card.fireEvent('close', card);
            tabPanel.remove(card);
            
            if (!tabPanel.getComponent(card)) {
                
                toClose.fireClose();
                me.remove(toClose);
            } else {
                
                toClose.ownerCt = me;
                Ext.resumeLayouts(true);
                return false;
            }
        }
        
        if (toActivate) {
            
            
            if (tabPanel) {
                tabPanel.setActiveTab(toActivate.card);
            } else {
                me.setActiveTab(toActivate);
            }
            toActivate.focus();
        }
        Ext.resumeLayouts(true);
    },
    
    
    findNextActivatable: function(toClose) {
        var me = this;
        if (toClose.active && me.items.getCount() > 1) {
            return (me.previousTab && me.previousTab !== toClose && !me.previousTab.disabled) ? me.previousTab : (toClose.next('tab[disabled=false]') || toClose.prev('tab[disabled=false]'));
        }
    },
    
    setActiveTab: function(tab, initial) {
        var me = this;
        if (!tab.disabled && tab !== me.activeTab) {
            
            if (me.activeTab) {
                if (me.activeTab.isDestroyed) {
                    me.previousTab = null;
                } else {
                    me.previousTab = me.activeTab;
                    me.activeTab.deactivate();
                    me.deactivateFocusable(me.activeTab);
                }
            }
            
            tab.activate();
            me.activateFocusable(tab);
            me.activeTab = tab;
            me.needsScroll = true;
            
            
            if (!initial) {
                me.fireEvent('change', me, tab, tab.card);
                
                me.updateLayout();
            }
        }
    },
    privates: {
        adjustTabPositions: function() {
            var me = this,
                items = me.items.items,
                i = items.length,
                tab, lastBox, el, rotation, prop;
            
            
            
            
            
            if (!Ext.isIE8) {
                
                prop = me._getTabAdjustProp();
                while (i--) {
                    tab = items[i];
                    el = tab.el;
                    lastBox = tab.lastBox;
                    rotation = tab.isTab ? tab.getActualRotation() : 0;
                    if (rotation === 1 && tab.isVisible()) {
                        
                        
                        el.setStyle(prop, (lastBox.x + lastBox.width) + 'px');
                    } else if (rotation === 2 && tab.isVisible()) {
                        
                        
                        el.setStyle(prop, (lastBox.x - lastBox.height) + 'px');
                    }
                }
            }
        },
        applyTargetCls: function(targetCls) {
            this.bodyTargetCls = targetCls;
        },
        
        _getTabAdjustProp: function() {
            return 'left';
        },
        getTargetEl: function() {
            return this.body || this.frameBody || this.el;
        },
        onClick: function(e, target) {
            var me = this,
                tabPanel = me.tabPanel,
                tabEl, tab, isCloseClick, tabInfo;
            if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) {
                return;
            }
            if (Ext.isIE8 && me.vertical) {
                tabInfo = me.getTabInfoFromPoint(e.getXY());
                tab = tabInfo.tab;
                isCloseClick = tabInfo.close;
            } else {
                
                tabEl = e.getTarget('.' + Ext.tab.Tab.prototype.baseCls);
                tab = tabEl && Ext.getCmp(tabEl.id);
                isCloseClick = tab && tab.closeEl && (target === tab.closeEl.dom);
            }
            if (isCloseClick) {
                e.preventDefault();
            }
            if (tab && tab.isDisabled && !tab.isDisabled()) {
                
                
                
                
                tab.beforeClick(isCloseClick);
                if (tab.closable && isCloseClick) {
                    tab.onCloseClick();
                } else {
                    me.doActivateTab(tab);
                }
            }
        },
        doActivateTab: function(tab) {
            var tabPanel = this.tabPanel;
            if (tabPanel) {
                
                if (!tab.disabled) {
                    tabPanel.setActiveTab(tab.card);
                }
            } else {
                this.setActiveTab(tab);
            }
        },
        onFocusableContainerFocus: function(e) {
            var me = this,
                mixin = me.mixins.focusablecontainer,
                child;
            child = mixin.onFocusableContainerFocus.call(me, e);
            if (child && child.isTab) {
                me.doActivateTab(child);
            }
        },
        onFocusableContainerFocusEnter: function(e) {
            var me = this,
                mixin = me.mixins.focusablecontainer,
                child;
            child = mixin.onFocusableContainerFocusEnter.call(me, e);
            if (child && child.isTab) {
                me.doActivateTab(child);
            }
        },
        focusChild: function(child, forward) {
            var me = this,
                mixin = me.mixins.focusablecontainer,
                nextChild;
            nextChild = mixin.focusChild.call(me, child, forward);
            if (me.activateOnFocus && nextChild && nextChild.isTab) {
                me.doActivateTab(nextChild);
            }
        }
    }
});


Ext.define('Ext.tab.Panel', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.tabpanel',
    alternateClassName: [
        'Ext.TabPanel'
    ],
    requires: [
        'Ext.layout.container.Card',
        'Ext.tab.Bar'
    ],
    config: {
        
        
        tabBar: undefined,
        
        tabPosition: 'top',
        
        tabRotation: 'default',
        
        tabStretchMax: true
    },
    
    
    
    
    
    removePanelHeader: true,
    
    plain: false,
    
    itemCls: Ext.baseCSSPrefix + 'tabpanel-child',
    
    minTabWidth: undefined,
    
    maxTabWidth: undefined,
    
    deferredRender: true,
    _defaultTabRotation: {
        top: 0,
        right: 1,
        bottom: 0,
        left: 2
    },
    
    
    
    initComponent: function() {
        var me = this,
            
            activeTab = me.activeTab !== null ? (me.activeTab || 0) : null,
            tabPosition = me.getTabPosition(),
            tabRotation = me.getTabRotation(),
            dockedItems = me.dockedItems,
            header = me.header,
            tabBarHeaderPosition = me.tabBarHeaderPosition,
            tabBar = me.getTabBar(),
            headerItems;
        
        me.layout = new Ext.layout.container.Card(Ext.apply({
            owner: me,
            deferredRender: me.deferredRender,
            itemCls: me.itemCls,
            activeItem: activeTab
        }, me.layout));
        if (tabBarHeaderPosition != null) {
            header = me.header = Ext.apply({}, header);
            headerItems = header.items = (header.items ? header.items.slice() : []);
            header.itemPosition = tabBarHeaderPosition;
            headerItems.push(tabBar);
            header.hasTabBar = true;
        } else {
            dockedItems = [].concat(me.dockedItems || []);
            dockedItems.push(tabBar);
            me.dockedItems = dockedItems;
        }
        me.callParent(arguments);
        
        activeTab = me.activeTab = me.getComponent(activeTab);
        
        if (activeTab) {
            tabBar.setActiveTab(activeTab.tab, true);
        }
    },
    onRender: function() {
        var items = this.items.items,
            len = items.length,
            i;
        this.callParent(arguments);
        
        
        
        
        
        for (i = 0; i < len; ++i) {
            items[i].getBind();
        }
    },
    
    setActiveTab: function(card) {
        var me = this,
            previous;
        
        if (!Ext.isObject(card) || card.isComponent) {
            card = me.getComponent(card);
        }
        previous = me.getActiveTab();
        if (card) {
            Ext.suspendLayouts();
            
            
            if (!card.isComponent) {
                card = me.add(card);
            }
            if (previous === card || me.fireEvent('beforetabchange', me, card, previous) === false) {
                Ext.resumeLayouts();
                return previous;
            }
            
            
            me.activeTab = card;
            
            
            
            me.layout.setActiveItem(card);
            
            card = me.activeTab = me.layout.getActiveItem();
            
            if (card && card !== previous) {
                
                me.tabBar.setActiveTab(card.tab);
                Ext.resumeLayouts(true);
                
                if (previous !== card) {
                    me.fireEvent('tabchange', me, card, previous);
                }
            } else 
            {
                Ext.resumeLayouts(true);
            }
            return card;
        }
        return previous;
    },
    setActiveItem: function(item) {
        return this.setActiveTab(item);
    },
    
    getActiveTab: function() {
        var me = this,
            
            result = me.getComponent(me.activeTab);
        
        if (result && me.items.indexOf(result) !== -1) {
            me.activeTab = result;
        } else {
            me.activeTab = null;
        }
        return me.activeTab;
    },
    applyTabBar: function(tabBar) {
        var me = this,
            
            
            dock = (me.tabBarHeaderPosition != null) ? me.getHeaderPosition() : me.getTabPosition();
        return new Ext.tab.Bar(Ext.apply({
            ui: me.ui,
            dock: dock,
            tabRotation: me.getTabRotation(),
            vertical: (dock == 'left' || dock == 'right'),
            plain: me.plain,
            tabStretchMax: me.getTabStretchMax(),
            tabPanel: me
        }, tabBar));
    },
    updateHeaderPosition: function(headerPosition, oldHeaderPosition) {
        var tabBar = this.getTabBar();
        if (tabBar && (this.tabBarHeaderPosition != null)) {
            tabBar.setDock(headerPosition);
        }
        this.callParent([
            headerPosition,
            oldHeaderPosition
        ]);
    },
    updateTabPosition: function(tabPosition) {
        var tabBar = this.getTabBar();
        if (tabBar && (this.tabBarHeaderPosition == null)) {
            tabBar.setDock(tabPosition);
        }
    },
    updateTabRotation: function(tabRotation) {
        var tabBar = this.getTabBar();
        if (tabBar) {
            tabBar.setTabRotation(tabRotation);
        }
    },
    
    onAdd: function(item, index) {
        var me = this,
            cfg = Ext.apply({}, item.tabConfig),
            tabBar = me.getTabBar(),
            defaultConfig = {
                xtype: 'tab',
                title: item.title,
                icon: item.icon,
                iconCls: item.iconCls,
                glyph: item.glyph,
                ui: tabBar.ui,
                card: item,
                disabled: item.disabled,
                closable: item.closable,
                hidden: item.hidden && !item.hiddenByLayout,
                
                tooltip: item.tooltip,
                tabBar: tabBar,
                tabPosition: tabBar.dock,
                rotation: tabBar.getTabRotation()
            };
        if (item.closeText !== undefined) {
            defaultConfig.closeText = item.closeText;
        }
        cfg = Ext.applyIf(cfg, defaultConfig);
        
        item.tab = me.tabBar.insert(index, cfg);
        item.on({
            scope: me,
            enable: me.onItemEnable,
            disable: me.onItemDisable,
            beforeshow: me.onItemBeforeShow,
            iconchange: me.onItemIconChange,
            iconclschange: me.onItemIconClsChange,
            glyphchange: me.onItemGlyphChange,
            titlechange: me.onItemTitleChange
        });
        if (item.isPanel) {
            if (me.removePanelHeader) {
                if (item.rendered) {
                    if (item.header) {
                        item.header.hide();
                    }
                } else {
                    item.header = false;
                }
            }
            if (item.isPanel && me.border) {
                item.setBorder(false);
            }
        }
        
        if (me.rendered) {
            item.getBind();
        }
        
        
        
        if (me.rendered && me.loader && me.activeTab === undefined && me.layout.activeItem !== null) {
            me.setActiveTab(0);
        }
    },
    
    onItemEnable: function(item) {
        item.tab.enable();
    },
    
    onItemDisable: function(item) {
        item.tab.disable();
    },
    
    onItemBeforeShow: function(item) {
        if (item !== this.activeTab) {
            this.setActiveTab(item);
            return false;
        }
    },
    
    onItemGlyphChange: function(item, newGlyph) {
        item.tab.setGlyph(newGlyph);
    },
    
    onItemIconChange: function(item, newIcon) {
        item.tab.setIcon(newIcon);
    },
    
    onItemIconClsChange: function(item, newIconCls) {
        item.tab.setIconCls(newIconCls);
    },
    
    onItemTitleChange: function(item, newTitle) {
        item.tab.setText(newTitle);
    },
    
    onRemove: function(item, destroying) {
        var me = this;
        item.un({
            scope: me,
            enable: me.onItemEnable,
            disable: me.onItemDisable,
            beforeshow: me.onItemBeforeShow,
            iconchange: me.onItemIconChange,
            iconclschange: me.onItemIconClsChange,
            glyphchange: me.onItemGlyphChange,
            titlechange: me.onItemTitleChange
        });
        if (item.tab && !me.destroying && item.tab.ownerCt === me.tabBar) {
            me.tabBar.remove(item.tab);
        }
    },
    privates: {
        
        doRemove: function(item, autoDestroy) {
            var me = this,
                toActivate;
            
            if (me.removingAll || me.destroying || me.items.getCount() === 1) {
                me.activeTab = null;
            }
            
            
            else if (item.tab && (toActivate = me.tabBar.items.indexOf(me.tabBar.findNextActivatable(item.tab))) !== -1) {
                me.setActiveTab(toActivate);
            }
            this.callParent(arguments);
            if (item.tab) {
                
                delete item.tab.card;
                delete item.tab;
            }
        }
    }
});


Ext.define('Ext.app.bindinspector.Environment', {
    requires: [
        'Ext.util.Collection'
    ],
    
    getCmp: function(id) {
        return this.components.get(id);
    },
    getVM: function(id) {
        return this.viewModels.get(id);
    },
    getInheritedVM: function(comp) {
        var vm = comp.viewModel,
            parent = comp.parent;
        if (vm) {
            return vm;
        }
        if (parent) {
            return this.getInheritedVM(this.getCmp(parent));
        }
        return null;
    },
    
    captureSnapshot: function() {
        var all = Ext.ComponentManager.getAll(),
            len = all.length,
            components = [],
            i, comp;
        this.models = {};
        for (i = 0; i < len; ++i) {
            comp = all[i];
            
            if (comp.afterRender && this.isRootComponent(comp)) {
                components.push(this.buildComponent(comp));
            }
        }
        return {
            isBindData: true,
            version: Ext.getVersion().version,
            models: this.models,
            components: components
        };
    },
    serializeModel: function(Model) {
        var models = this.models,
            name = Model.entityName;
        if (!models[name]) {
            models[name] = Ext.Array.map(Model.getFields(), function(field) {
                return {
                    name: field.getName(),
                    type: field.getType()
                };
            });
        }
    },
    isRootComponent: function(c) {
        var owner = c.getRefOwner();
        if (owner || c.isBindInspector || c === Ext.MessageBox || c.is('quicktip')) {
            return false;
        }
        return true;
    },
    buildComponent: function(comp) {
        var childItems = comp.getRefItems ? comp.getRefItems() : null,
            viewModel = comp.getViewModel(),
            bind = comp.getBind(),
            id = comp.id,
            len, i, o, child;
        if (bind) {
            bind = this.buildBind(bind);
        }
        o = {
            id: id,
            xtype: comp.getXType(),
            publishes: comp.getPublishes(),
            viewModel: viewModel ? this.buildViewModel(viewModel, comp) : null,
            bindings: bind || null,
            reference: comp.reference || null,
            items: []
        };
        if (childItems) {
            for (i = 0 , len = childItems.length; i < len; ++i) {
                if (childItems[i].afterRender) {
                    child = this.buildComponent(childItems[i]);
                    child.parent = id;
                    o.items.push(child);
                }
            }
        }
        return o;
    },
    buildBind: function(bind) {
        var out = {},
            key, o, bindInfo, name, stub;
        for (key in bind) {
            o = bind[key];
            stub = o.stub;
            bindInfo = {
                id: o.id,
                value: this.serializeValue(o.getRawValue()),
                stub: stub ? {
                    id: stub.id,
                    name: stub.name
                } : null
            };
            if (o.isTemplateBinding) {
                bindInfo.isTemplateBinding = true;
                bindInfo.tokens = [];
                Ext.Array.forEach(o.tokens, function(token) {
                    bindInfo.tokens.push(token.split('.'));
                }, this);
                bindInfo.descriptor = o.tpl.text;
            } else if (o.isMultiBinding) {
                bindInfo.isMultiBinding = true;
            } else 
            {
                if (stub) {
                    name = this.buildStubName(stub);
                    bindInfo.tokens = name.split('.');
                    bindInfo.descriptor = '{' + name + '}';
                }
            }
            out[key] = bindInfo;
        }
        return out;
    },
    buildStubName: function(stub) {
        var parent = stub.parent,
            name = '';
        if (parent && !parent.isRootStub) {
            name = this.buildStubName(parent) + '.';
        }
        return name + stub.name;
    },
    buildViewModel: function(vm, comp) {
        var parent = vm.getParent();
        return {
            id: vm.getId(),
            view: comp.id,
            parent: parent ? parent.getId() : null,
            data: this.serializeValue(vm.getData(), true),
            rootStub: this.buildStub(vm.getRoot())
        };
    },
    buildStub: function(stub, isLinkChild) {
        var o = {},
            children = stub.children,
            isLink = stub.isLinkStub,
            outChildren = {},
            key, hasAny, child, sameTarget;
        if (!stub.isRootStub) {
            o.name = stub.name;
            o.parent = stub.parent ? stub.parent.id : null;
            o.isLoading = stub.isLoading();
            o.bindCount = (stub.bindings && stub.bindings.length) || 0;
            o.cumulativeBindCount = o.bindCount;
            o.value = this.serializeValue(stub.getRawValue());
            if (isLink) {
                sameTarget = stub.target === stub.owner;
                o.linkInfo = {
                    sameTarget: sameTarget,
                    descriptor: stub.linkDescriptor,
                    value: this.serializeValue(stub.binding.getValue())
                };
                isLinkChild = true;
            }
        } else {
            o.name = '';
            o.isLoading = false;
            o.bindCount = o.cumulativeBindCount = 0;
        }
        if (children) {
            for (key in children) {
                hasAny = true;
                child = this.buildStub(children[key], isLinkChild);
                outChildren[key] = child;
                o.cumulativeBindCount += child.cumulativeBindCount;
            }
        }
        if (hasAny) {
            o.children = outChildren;
        }
        return o;
    },
    createModel: function(entityName, data) {
        var Model = Ext.app.bindinspector.noconflict[entityName];
        return new Model(data);
    },
    unpackSnapshot: function(data) {
        this.components = new Ext.util.Collection();
        this.viewModels = new Ext.util.Collection();
        Ext.Object.each(data.models, function(key, fields) {
            Ext.define('Ext.app.bindinspector.noconflict.' + key, {
                extend: 'Ext.app.bindinspector.noconflict.BaseModel',
                fields: fields
            });
        });
        Ext.Array.forEach(data.components, function(comp) {
            this.unpackComponent(comp, this.components, this.viewModels);
        }, this);
        this.rootComponents = data.components;
    },
    unpackComponent: function(comp, allComponents, allViewModels) {
        var vm = comp.viewModel,
            items = comp.items,
            bindings = comp.bindings,
            len, i, parentVM, parentData, data, key, binding;
        allComponents.add(comp);
        if (bindings) {
            for (key in bindings) {
                binding = bindings[key];
                binding.value = this.deserializeValue(binding.value);
            }
        }
        if (vm) {
            allViewModels.add(vm);
            parentVM = this.getVM(vm.parent);
            if (parentVM) {
                parentData = Ext.Object.chain(parentVM.data);
            }
            data = this.deserializeValue(vm.data);
            if (parentData) {
                data = Ext.apply(parentData, data);
            }
            vm.data = data;
            this.deserializeStub(vm.rootStub);
        }
        if (items) {
            for (i = 0 , len = items.length; i < len; ++i) {
                this.unpackComponent(items[i], allComponents, allViewModels);
            }
        }
    },
    serializeValue: function(value, checkHasOwn) {
        var info = {},
            type, key, item, childInfo, model;
        if (value && value.constructor === Object) {
            type = 'object';
            info.value = {};
            for (key in value) {
                if (!(checkHasOwn && !value.hasOwnProperty(key))) {
                    childInfo = this.serializeValue(value[key], checkHasOwn);
                    item = {
                        type: childInfo.type,
                        value: childInfo.value
                    };
                    if (childInfo.entityName) {
                        item.entityName = childInfo.entityName;
                    }
                    info.value[key] = item;
                }
            }
        } else if (value && value.isModel) {
            type = 'model';
            info.entityName = value.entityName;
            this.serializeModel(value.self);
            info.value = this.serializeValue(value.data);
        } else if (value && value.isStore) {
            type = 'store';
            model = value.getModel();
            info.entityName = model.entityName;
            if (model.entityName) {
                this.serializeModel(model);
            }
        } else if (Ext.isDate(value)) {
            type = 'date';
            info.value = Ext.Date.format(value, 'c');
        } else if (Ext.isArray(value)) {
            type = 'array';
            info.value = [];
            Ext.Array.forEach(value, function(item) {
                info.value.push(this.serializeValue(item));
            }, this);
        } else {
            type = Ext.typeOf(value);
            info.value = value;
        }
        info.type = type;
        return info;
    },
    deserializeValue: function(info) {
        var type = info.type,
            raw = info.value,
            out, key;
        if (type === 'null') {
            out = null;
        } else if (type === 'undefined') {
            out = undefined;
        } else if (type === 'string' || type === 'boolean' || type === 'number') {
            out = raw;
        } else if (type === 'date') {
            out = Ext.Date.parse(raw, 'c');
        } else if (type === 'object') {
            out = {};
            for (key in raw) {
                out[key] = this.deserializeValue(raw[key]);
            }
        } else if (type === 'model') {
            out = this.createModel(info.entityName, this.deserializeValue(raw));
        } else if (type === 'store') {
            out = {
                isStore: true,
                entityName: info.entityName
            };
        } else if (type === 'array') {
            out = [];
            Ext.Array.forEach(raw, function(item) {
                out.push(this.deserializeValue(item));
            }, this);
        }
        return out;
    },
    deserializeStub: function(stub) {
        var children = stub.children,
            linkInfo = stub.linkInfo,
            key;
        if (stub.value) {
            stub.value = this.deserializeValue(stub.value);
        }
        if (linkInfo) {
            linkInfo.value = this.deserializeValue(linkInfo.value);
        }
        if (children) {
            for (key in children) {
                this.deserializeStub(children[key]);
            }
        }
    }
});

Ext.define('Ext.app.bindinspector.ViewModelDetail', {
    extend: 'Ext.tree.Panel',
    alias: 'widget.bindinspector-viewmodeldetail',
    rootVisible: false,
    cls: Ext.baseCSSPrefix + 'bindinspector-viewmodeldetail',
    inheritedCls: Ext.baseCSSPrefix + 'bindinspector-inherited',
    notInheritedCls: Ext.baseCSSPrefix + 'bindinspector-not-inherited',
    highlightedCls: Ext.baseCSSPrefix + 'bindinspector-highlighted',
    unhighlightedCls: Ext.baseCSSPrefix + 'bindinspector-unhighlighted',
    lastItemCls: Ext.baseCSSPrefix + 'bindinspector-last-item',
    initComponent: function() {
        var me = this,
            vm = me.vm,
            title = 'VM &nbsp;&nbsp;&nbsp;⇒ &nbsp;&nbsp;&nbsp;',
            env = me.up('bindinspector-container').env,
            comp = env.getCmp(vm.view);
        
        if (comp.reference) {
            title += '[' + comp.reference + '] &bull; ';
        }
        me.title = title += comp.id;
        me.viewConfig = {
            getRowClass: function(record, index, rowParams, store) {
                var data = record.get('hasData'),
                    stub = record.get('hasStub'),
                    cls = [],
                    highlighted = record.get('highlighted');
                
                if (record.get('inherited')) {
                    cls.push(me.inheritedCls);
                } else {
                    cls.push(me.notInheritedCls);
                }
                
                
                if (highlighted === true) {
                    cls.push(me.highlightedCls);
                } else if (highlighted === -1) {
                    cls.push(me.unhighlightedCls);
                }
                
                if (index === store.getCount() - 1) {
                    cls.push(me.lastItemCls);
                }
                
                if (data && (!stub || record.get('cumulativeBindCount') === 0)) {
                    cls.push(me.dataOnlyCls);
                }
                return cls.join(' ');
            }
        };
        me.store = {
            model: me.Model,
            root: {
                text: 'Root',
                expanded: true,
                children: me.setupData(vm, vm.data, vm.rootStub)
            }
        };
        me.columns = [
            {
                width: 40,
                tdCls: Ext.baseCSSPrefix + 'bindinspector-indicator-col',
                align: 'center',
                scope: me,
                renderer: me.renderIndicator
            },
            {
                flex: 1,
                xtype: 'treecolumn',
                dataIndex: 'name',
                text: 'Name',
                scope: me,
                renderer: me.renderName
            },
            {
                flex: 1,
                dataIndex: 'value',
                text: 'Value',
                scope: me,
                renderer: Ext.app.bindinspector.Util.valueRenderer
            },
            {
                text: 'Bind #',
                width: 64,
                align: 'center',
                renderer: me.renderBindCount,
                scope: me
            },
            {
                width: 40,
                isSearch: true,
                renderer: me.dataSrcConsumerRenderer,
                scope: me
            }
        ];
        me.callParent();
        me.on('cellclick', me.onSearchCellClick, me);
    },
    dataOnlyNode: 'This item contains data but has nothing requesting the value',
    stubOnlyNode: 'This item has the value requested but no data backing it',
    dataPointLoading: 'Data point is loading (at the time the app snapshot was captured)',
    dataPointLoadingCls: Ext.baseCSSPrefix + 'bindinspector-isloading',
    zeroBindingCls: Ext.baseCSSPrefix + 'bi-zero-bind-count',
    dataOnlyCls: Ext.baseCSSPrefix + 'bindinspector-data-only',
    stubOnlyCls: Ext.baseCSSPrefix + 'bindinspector-stub-only',
    
    onSearchCellClick: function(view, td, cellIndex, rec, tr, rowIndex, e) {
        if (view.getHeaderCt().getHeaderAtIndex(cellIndex).isSearch) {
            this.up('bindinspector-container').fireEvent('vmSearchClick', rec);
        }
    },
    
    getFirstTierRec: function(rec) {
        var isFirstTier = rec.parentNode.isRoot(),
            firstTier;
        if (!isFirstTier) {
            rec.bubble(function(ni) {
                if (ni.parentNode.isRoot()) {
                    firstTier = ni;
                    return false;
                }
            });
        }
        return isFirstTier ? rec : firstTier;
    },
    
    dataSrcConsumerRenderer: function(v, meta, rec) {
        var firstTier = this.getFirstTierRec(rec),
            firstTierName = firstTier.get('name');
        meta.tdCls = Ext.baseCSSPrefix + 'bindinspector-data-search-cell';
        meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="Click to indicate within the Component List all ViewModels with a data property of &nbsp;<b>' + firstTierName + '</b>"';
    },
    
    renderIndicator: function(v, meta, rec) {
        var ownerVMs = rec.get('ownerVMs'),
            len = ownerVMs.length,
            direct = false,
            inherited = false,
            val = '',
            firstTier = this.getFirstTierRec(rec),
            isFirstTier = firstTier === rec,
            firstTierName = firstTier.get('name'),
            vmPlural;
        Ext.Array.forEach(ownerVMs, function(vm) {
            if (vm.id === vm.thisVM) {
                direct = true;
            }
            if (vm.id !== vm.thisVM) {
                inherited = true;
            }
        });
        if (direct && inherited) {
            val = Ext.util.Format.format('<span style="color:#DB7851;">{0}</span>', isFirstTier ? '◓' : '-');
            vmPlural = len > 1 ? 'VMs' : 'VM';
            meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="<b>' + firstTierName + '</b>&nbsp; provided by this VM and ' + (len - 1) + ' ancestor ' + vmPlural + '"';
        } else if (direct) {
            val = isFirstTier ? '●' : '';
            meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="<b>' + firstTierName + '</b>&nbsp; is provided by this VM"';
        } else if (inherited) {
            val = isFirstTier ? '○' : '';
            vmPlural = len > 1 ? 'VMs' : 'VM';
            meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="<b>' + firstTierName + '</b>&nbsp; is provided by ' + len + ' ancestor ' + vmPlural + '"';
        }
        return val;
    },
    
    renderBindCount: function(v, meta, rec) {
        var len = rec.get('children').length,
            bindCount = rec.get('bindCount') || 0,
            total, bindingsText;
        v = bindCount;
        if (v === 0) {
            v = '<span class="' + this.zeroBindingCls + '">' + v + '</span>';
        }
        if (len) {
            total = rec.get('cumulativeBindCount') || '?';
            if (total === 0 || total === '?') {
                v += ' / <span class="' + this.zeroBindingCls + '">' + total + '</span>';
            } else {
                v += ' / ' + total;
            }
        }
        bindingsText = 'Bindings Count = <b>' + bindCount + '</b>';
        if (total && total !== 0 && total !== '?') {
            bindingsText += '<br>Cumulative Bindings Count = <b>' + total + '</b>';
        }
        meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="' + bindingsText + '"';
        return v;
    },
    
    renderName: function(v, meta, rec) {
        var me = this,
            data = rec.get('hasData'),
            stub = rec.get('hasStub'),
            tip = '';
        if (rec.get('isLoading')) {
            meta.tdCls = me.dataPointLoadingCls;
            tip += me.dataPointLoading;
        } else if (data && (!stub || rec.get('cumulativeBindCount') === 0)) {
            tip += me.dataOnlyNode;
        } else if (stub && !data) {
            meta.tdCls = me.stubOnlyCls;
            tip += me.stubOnlyNode;
        }
        if (tip !== '') {
            meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="' + tip + '"';
        }
        return v;
    },
    
    setupData: function(vm, data, stub, inherited, ownerVMs) {
        var merged = {},
            out = [],
            dataMap = vm.dataMap,
            dm = [],
            item, children, stubChild, key, stopDigging, linkInfo;
        if (data && Ext.isObject(data)) {
            if (data.isModel) {
                data = data.data;
                
                stopDigging = true;
            } else if (data.isStore) {
                stopDigging = true;
                data = null;
            }
            if (data) {
                for (key in data) {
                    if (!ownerVMs) {
                        dm = dataMap[key] ? dataMap[key].ownerVMs : [];
                    }
                    item = {
                        name: key,
                        value: data[key],
                        inherited: Ext.isDefined(inherited) ? inherited : !data.hasOwnProperty(key),
                        ownerVMs: Ext.isDefined(ownerVMs) ? ownerVMs : [],
                        hasData: true
                    };
                    Ext.Array.forEach(dm, function(v) {
                        item.ownerVMs.push({
                            id: v.id,
                            view: v.view,
                            thisVM: vm.id
                        });
                    });
                    stubChild = Ext.app.bindinspector.Util.getChildStub(key, stub);
                    if (stubChild) {
                        item.hasStub = true;
                        item.isLoading = stubChild.isLoading;
                        item.iconCls = stubChild.isLoading ? this.dataPointLoadingCls : '';
                        item.bindCount = stubChild.bindCount;
                        item.cumulativeBindCount = stubChild.cumulativeBindCount;
                        item.stub = stubChild;
                    }
                    merged[key] = item;
                }
            }
        }
        if (stub) {
            children = stub.children;
            for (key in children) {
                stubChild = children[key];
                item = merged[key];
                if (!item) {
                    item = {
                        name: key,
                        value: stubChild.value || undefined,
                        inherited: inherited || false,
                        ownerVMs: ownerVMs || [],
                        hasData: false,
                        hasStub: true,
                        isLoading: stubChild.isLoading,
                        iconCls: stubChild.isLoading ? this.dataPointLoadingCls : '',
                        bindCount: stubChild.bindCount,
                        cumulativeBindCount: stubChild.cumulativeBindCount,
                        stub: stubChild
                    };
                    linkInfo = stubChild.linkInfo;
                    if (linkInfo && linkInfo.sameTarget) {
                        item.value = linkInfo.value;
                        
                        
                        item.hasData = item.value !== undefined;
                    }
                    merged[key] = item;
                }
            }
        }
        for (key in merged) {
            item = merged[key];
            
            item.children = this.setupData(vm, item.value, item.stub, item.inherited, item.ownerVMs);
            
            delete item.stub;
            if (item.children && item.children.length) {
                item.expanded = true;
                item.leaf = false;
            } else {
                item.leaf = true;
            }
            out.push(merged[key]);
        }
        return out;
    }
}, function() {
    this.prototype.Model = Ext.define(null, {
        extend: 'Ext.data.TreeModel',
        fields: [
            'name',
            'value',
            'inherited',
            'hasData',
            'hasStub',
            'isLoading',
            'bindCount',
            'cumulativeBindCount',
            'highlighted'
        ]
    });
});

Ext.define('Ext.app.bindinspector.noconflict.BaseModel', {
    extend: 'Ext.data.Model',
    schema: {
        id: 'Ext_app_bindinspector',
        namespace: 'Ext.app.bindinspector'
    }
});

Ext.define('Ext.app.bindinspector.Container', {
    extend: 'Ext.container.Container',
    alias: 'widget.bindinspector-container',
    requires: [
        'Ext.layout.container.Border',
        'Ext.tab.Panel',
        'Ext.app.bindinspector.ComponentDetail',
        'Ext.app.bindinspector.ComponentList',
        'Ext.app.bindinspector.Environment',
        'Ext.app.bindinspector.Util',
        'Ext.app.bindinspector.ViewModelDetail',
        'Ext.app.bindinspector.noconflict.BaseModel'
    ],
    isBindInspector: true,
    referenceHolder: true,
    layout: 'border',
    cls: Ext.baseCSSPrefix + 'bindinspector-container',
    
    
    
    
    
    
    
    
    componentPreviewDefault: {
        xtype: 'container',
        cls: Ext.baseCSSPrefix + 'bindinspector-prev-default',
        padding: 20,
        layout: {
            type: 'hbox',
            align: 'middle',
            pack: 'center'
        },
        items: [
            {
                xtype: 'component',
                flex: 1,
                html: 'Select a component with bindings from the ComponentList to view the bindings details along with the component\'s inherited ViewModel'
            }
        ]
    },
    initComponent: function() {
        var me = this;
        Ext.data.schema.Schema.get('Ext_app_bindinspector').clear();
        me.items = [
            {
                xtype: 'bindinspector-componentlist',
                reference: 'componentList',
                region: 'west',
                width: 400,
                split: true,
                collapsible: true,
                components: me.env.rootComponents,
                listeners: {
                    scope: me,
                    componentdblclick: me.onComponentDblclick,
                    componentselect: me.onComponentSelect
                }
            },
            {
                xtype: 'tabpanel',
                region: 'center',
                reference: 'tabs',
                items: [
                    {
                        title: 'Preview',
                        reference: 'preview',
                        layout: 'fit',
                        items: [
                            me.componentPreviewDefault
                        ]
                    }
                ],
                listeners: {
                    add: function() {
                        this.getTabBar().setVisible(this.items.getCount() > 1);
                    },
                    remove: function() {
                        this.getTabBar().setVisible(this.items.getCount() > 1);
                    }
                }
            }
        ];
        me.callParent();
        me.on('vmSearchClick', me.onVMSearchClick, me);
    },
    
    
    
    onVMSearchClick: function(rec) {
        var componentList = this.lookupReference('componentList'),
            store = componentList.getStore(),
            ownerVMs = rec.get('ownerVMs'),
            sourceVMs = [],
            bindCount = rec.get('bindCount'),
            names = [],
            consumerNodes = [],
            query = [],
            source, descriptor;
        
        store.suspendEvents();
        componentList.clearVMSearchIndicators();
        Ext.Array.forEach(ownerVMs, function(vm) {
            source = store.getById(vm.view);
            source.set('isSrcVM', rec);
            sourceVMs.push(source);
        });
        store.resumeEvents();
        componentList.getView().refresh();
        
        
        componentList.indicatedVM = sourceVMs;
        
        if (bindCount > 0) {
            
            rec.bubble(function(node) {
                var nodeName = node.get('name');
                if (nodeName) {
                    names.push(nodeName);
                }
            });
            
            descriptor = names.reverse().join('.');
            
            componentList.getStore().getRootNode().cascadeBy(function(node) {
                if (node.get('hasBindings')) {
                    Ext.Array.forEach(node.get('bindData'), function(binding) {
                        var tokenStr = [];
                        if (binding.tokens) {
                            Ext.Array.forEach(binding.tokens, function(token) {
                                if (Ext.isArray(token)) {
                                    Ext.Array.forEach(token, function(t) {
                                        tokenStr.push(t);
                                    });
                                } else {
                                    tokenStr.push(token);
                                }
                            });
                        }
                        if (tokenStr.join('.') === descriptor) {
                            consumerNodes.push(node);
                        }
                    });
                }
            });
        }
        
        Ext.Array.forEach(consumerNodes.concat(sourceVMs), function(node) {
            query.push(node.getId());
        });
        
        componentList.expand();
        componentList.filterComponentTree(null, query);
        
        Ext.Array.forEach(sourceVMs, function(src) {
            if (!Ext.Array.contains(consumerNodes)) {
                src.set('filtervisible', false);
            }
        });
        componentList.down('#srcVMIndicator').show();
        
        componentList.down('#queryFieldTb').hide();
        
        componentList.down('#vmQueryResultsTb').show();
    },
    
    
    onComponentSelect: function(tree, rec, node) {
        var me = this,
            id = rec.getId(),
            preview = me.lookupReference('preview');
        if (preview.referringID !== id) {
            Ext.suspendLayouts();
            preview.removeAll();
            preview.add({
                xtype: 'bindinspector-componentdetail',
                env: me.env,
                component: me.env.getCmp(id)
            });
            Ext.resumeLayouts(true);
            preview.referringID = id;
        }
        preview.show();
    },
    
    onComponentDblclick: function(tree, rec, node) {
        var id = rec.id,
            tabId = 'bindtab-' + id,
            tabs = this.lookupReference('tabs'),
            tab = tabs.items.get(tabId),
            component, reference;
        if (!tab) {
            component = this.env.getCmp(id);
            reference = component.reference;
            tab = tabs.add({
                xtype: 'bindinspector-componentdetail',
                env: this.env,
                itemId: tabId,
                title: reference ? '[' + reference + '] ' + id : id,
                closable: true,
                component: component
            });
        }
        tabs.setActiveTab(tab);
    },
    
    
    buildVMDataMap: function(vm) {
        var env = this.env,
            currVM = vm,
            dataMap = vm.dataMap,
            viewModels = [],
            data, isDirect;
        
        if (!dataMap) {
            dataMap = vm.dataMap = {};
            
            while (currVM) {
                viewModels.push(currVM);
                currVM = env.getVM(currVM.parent);
            }
            
            
            Ext.Array.forEach(viewModels, function(item) {
                var key;
                data = item.data;
                if (data && Ext.isObject(data)) {
                    for (key in data) {
                        isDirect = data.hasOwnProperty(key);
                        if (!dataMap[key]) {
                            dataMap[key] = {
                                isDirect: isDirect
                            };
                        }
                        dataMap[key].ownerVMs = dataMap[key].ownerVMs || [];
                        if (isDirect) {
                            dataMap[key].ownerVMs.push(item);
                        }
                    }
                }
            });
        }
        return vm;
    }
});


Ext.define('Ext.util.ComponentDragger', {
    extend: 'Ext.dd.DragTracker',
    
    
    
    autoStart: 500,
    
    constructor: function(comp, config) {
        this.comp = comp;
        this.initialConstrainTo = config.constrainTo;
        this.callParent([
            config
        ]);
    },
    onStart: function(e) {
        var me = this,
            comp = me.comp;
        
        me.startPosition = comp.getXY();
        
        
        if (comp.ghost && !comp.liveDrag) {
            me.proxy = comp.ghost();
            me.dragTarget = me.proxy.header.el;
        }
        
        if (me.constrain || me.constrainDelegate) {
            me.constrainTo = me.calculateConstrainRegion();
        }
        if (comp.beginDrag) {
            comp.beginDrag();
        }
    },
    calculateConstrainRegion: function() {
        var me = this,
            comp = me.comp,
            constrainTo = me.initialConstrainTo,
            constraintInsets = comp.constraintInsets,
            constrainEl, delegateRegion, elRegion,
            dragEl = me.proxy ? me.proxy.el : comp.el,
            shadow = dragEl.shadow,
            shadowSize = (shadow && !me.constrainDelegate && comp.constrainShadow && !shadow.disabled) ? shadow.getShadowSize() : 0;
        
        if (!(constrainTo instanceof Ext.util.Region)) {
            constrainEl = Ext.fly(constrainTo);
            constrainTo = constrainEl.getViewRegion();
            
            constrainTo.right = constrainTo.left + constrainEl.dom.clientWidth;
        } else {
            
            constrainTo = constrainTo.copy();
        }
        
        if (constraintInsets) {
            constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets);
            constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.length);
        }
        
        if (shadowSize) {
            constrainTo.adjust(shadowSize[0], -shadowSize[1], -shadowSize[2], shadowSize[3]);
        }
        
        
        
        if (!me.constrainDelegate) {
            delegateRegion = Ext.fly(me.dragTarget).getRegion();
            elRegion = dragEl.getRegion();
            constrainTo.adjust(delegateRegion.top - elRegion.top, delegateRegion.right - elRegion.right, delegateRegion.bottom - elRegion.bottom, delegateRegion.left - elRegion.left);
        }
        return constrainTo;
    },
    
    onDrag: function(e) {
        var me = this,
            comp = (me.proxy && !me.comp.liveDrag) ? me.proxy : me.comp,
            offset = me.getOffset(me.constrain || me.constrainDelegate ? 'dragTarget' : null);
        comp.setPagePosition(me.startPosition[0] + offset[0], me.startPosition[1] + offset[1]);
    },
    onEnd: function(e) {
        var comp = this.comp;
        if (comp.isDestroyed || comp.destroying) {
            return;
        }
        if (this.proxy && !comp.liveDrag) {
            comp.unghost();
        }
        if (comp.endDrag) {
            comp.endDrag();
        }
    }
});


Ext.define('Ext.window.Window', {
    extend: 'Ext.panel.Panel',
    alternateClassName: 'Ext.Window',
    requires: [
        'Ext.util.ComponentDragger',
        'Ext.util.Region'
    ],
    alias: 'widget.window',
    
    
    
    
    
    
    
    
    
    
    
    baseCls: Ext.baseCSSPrefix + 'window',
    
    resizable: true,
    
    draggable: true,
    
    constrain: false,
    
    constrainHeader: false,
    
    
    plain: false,
    
    minimizable: false,
    
    maximizable: false,
    
    minHeight: 50,
    
    minWidth: 50,
    
    expandOnShow: true,
    
    collapsible: false,
    
    closable: true,
    
    
    hidden: true,
    
    autoRender: true,
    
    hideMode: 'offsets',
    
    floating: true,
    itemCls: Ext.baseCSSPrefix + 'window-item',
    overlapHeader: true,
    ignoreHeaderBorderManagement: true,
    
    alwaysFramed: true,
    
    isRootCfg: {
        isRoot: true
    },
    
    isWindow: true,
    ariaRole: 'dialog',
    
    
    
    
    
    
    
    initComponent: function() {
        var me = this;
        
        
        me.frame = false;
        me.callParent();
        if (me.plain) {
            me.addClsWithUI('plain');
        }
        me.addStateEvents([
            'maximize',
            'restore',
            'resize',
            'dragend'
        ]);
    },
    getElConfig: function() {
        var me = this,
            elConfig;
        elConfig = me.callParent();
        elConfig.tabIndex = -1;
        return elConfig;
    },
    
    
    getState: function() {
        var me = this,
            state = me.callParent() || {},
            maximized = !!me.maximized,
            ghostBox = me.ghostBox,
            pos;
        state.maximized = maximized;
        if (maximized) {
            pos = me.restorePos;
        } else if (ghostBox) {
            
            
            pos = [
                ghostBox.x,
                ghostBox.y
            ];
        } else {
            pos = me.getPosition();
        }
        Ext.apply(state, {
            size: maximized ? me.restoreSize : me.getSize(),
            pos: pos
        });
        return state;
    },
    applyState: function(state) {
        var me = this;
        if (state) {
            me.maximized = state.maximized;
            if (me.maximized) {
                me.hasSavedRestore = true;
                me.restoreSize = state.size;
                me.restorePos = state.pos;
            } else {
                Ext.apply(me, {
                    width: state.size.width,
                    height: state.size.height,
                    x: state.pos[0],
                    y: state.pos[1]
                });
            }
        }
    },
    
    onRender: function(ct, position) {
        var me = this;
        me.callParent(arguments);
        
        if (me.header) {
            me.header.on({
                scope: me,
                click: me.onHeaderClick
            });
        }
        
        if (me.maximizable) {
            me.header.on({
                scope: me,
                dblclick: me.toggleMaximize
            });
        }
    },
    
    afterRender: function() {
        var me = this,
            header = me.header,
            keyMap;
        
        if (me.maximized) {
            me.maximized = false;
            me.maximize();
            if (header) {
                header.removeCls(header.indicateDragCls);
            }
        }
        me.callParent();
        if (me.closable) {
            keyMap = me.getKeyMap();
            keyMap.on(27, me.onEsc, me);
        } else {
            keyMap = me.keyMap;
        }
        if (keyMap && me.hidden) {
            keyMap.disable();
        }
    },
    
    onEsc: function(k, e) {
        var mgr = Ext['FocusManager'];
        
        if (!Ext.enableFocusManager || mgr.focusedCmp === this) {
            e.stopEvent();
            this.close();
        }
    },
    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            Ext.un('resize', me.onWindowResize, me);
            delete me.animateTarget;
            me.hide();
            Ext.destroy(me.keyMap);
        }
        me.callParent();
    },
    
    addTools: function() {
        var me = this,
            tools = [],
            noArgs = [];
        
        me.callParent();
        if (me.minimizable) {
            tools.push({
                type: 'minimize',
                handler: 'minimize',
                scope: me
            });
        }
        if (me.maximizable) {
            tools.push({
                type: me.maximized ? 'restore' : 'maximize',
                handler: 'toggleMaximize',
                scope: me
            });
        }
        if (tools.length) {
            me.addTool(tools);
        }
    },
    onShow: function() {
        var me = this;
        me.callParent(arguments);
        if (me.expandOnShow) {
            me.expand(false);
        }
        me.syncMonitorWindowResize();
        if (me.keyMap) {
            me.keyMap.enable();
        }
    },
    
    doClose: function() {
        var me = this;
        
        if (me.hidden) {
            me.fireEvent('close', me);
            if (me.closeAction == 'destroy') {
                me.destroy();
            }
        } else {
            
            me.hide(me.animateTarget, me.doClose, me);
        }
    },
    
    afterHide: function() {
        var me = this;
        
        me.syncMonitorWindowResize();
        
        if (me.keyMap) {
            me.keyMap.disable();
        }
        
        me.callParent(arguments);
    },
    
    onWindowResize: function() {
        var me = this,
            sizeModel;
        
        if (!me.isDestroyed) {
            if (me.maximized) {
                me.fitContainer();
            } else {
                sizeModel = me.getSizeModel();
                if (sizeModel.width.natural || sizeModel.height.natural) {
                    me.updateLayout();
                }
                me.doConstrain();
            }
        }
    },
    
    minimize: function() {
        this.fireEvent('minimize', this);
        return this;
    },
    resumeHeaderLayout: function(changed) {
        this.header.resumeLayouts(changed ? this.isRootCfg : null);
    },
    afterCollapse: function() {
        var me = this,
            header = me.header,
            tools = me.tools;
        if (header && me.maximizable) {
            header.suspendLayouts();
            tools.maximize.hide();
            this.resumeHeaderLayout(true);
        }
        if (me.resizer) {
            me.resizer.disable();
        }
        me.callParent(arguments);
    },
    afterExpand: function() {
        var me = this,
            header = me.header,
            tools = me.tools,
            changed;
        if (header) {
            header.suspendLayouts();
            if (me.maximizable) {
                tools.maximize.show();
                changed = true;
            }
            this.resumeHeaderLayout(changed);
        }
        if (me.resizer) {
            me.resizer.enable();
        }
        me.callParent(arguments);
    },
    
    maximize: function(animate) {
        var me = this,
            header = me.header,
            tools = me.tools,
            width = me.width,
            height = me.height,
            restore, changed;
        if (!me.maximized) {
            me.expand(false);
            if (!me.hasSavedRestore) {
                restore = me.restoreSize = {
                    width: Ext.isNumber(width) ? width : null,
                    height: Ext.isNumber(height) ? height : null
                };
                me.restorePos = me.getPosition(true);
            }
            
            if (header) {
                header.suspendLayouts();
                if (tools.maximize) {
                    tools.maximize.setType('restore');
                }
                if (me.collapseTool) {
                    me.collapseTool.hide();
                    changed = true;
                }
                me.resumeHeaderLayout(changed);
            }
            me.el.disableShadow();
            if (me.dd) {
                me.dd.disable();
                if (header) {
                    header.removeCls(header.indicateDragCls);
                }
            }
            if (me.resizer) {
                me.resizer.disable();
            }
            me.el.addCls(Ext.baseCSSPrefix + 'window-maximized');
            me.container.addCls(Ext.baseCSSPrefix + 'window-maximized-ct');
            me.syncMonitorWindowResize();
            me.fitContainer(animate = (animate || !!me.animateTarget) ? {
                callback: function() {
                    me.maximized = true;
                    me.fireEvent('maximize', me);
                }
            } : null);
            if (!animate) {
                me.maximized = true;
                me.fireEvent('maximize', me);
            }
        }
        return me;
    },
    
    restore: function(animate) {
        var me = this,
            tools = me.tools,
            header = me.header,
            newBox = me.restoreSize,
            changed;
        if (me.maximized) {
            me.hasSavedRestore = null;
            me.removeCls(Ext.baseCSSPrefix + 'window-maximized');
            
            if (header) {
                header.suspendLayouts();
                if (tools.maximize) {
                    tools.maximize.setType('maximize');
                }
                if (me.collapseTool) {
                    me.collapseTool.show();
                    changed = true;
                }
                me.resumeHeaderLayout(changed);
            }
            
            newBox.x = me.restorePos[0];
            newBox.y = me.restorePos[1];
            me.setBox(newBox, animate = (animate || !!me.animateTarget) ? {
                callback: function() {
                    me.el.enableShadow(null, true);
                    me.maximized = false;
                    me.fireEvent('restore', me);
                }
            } : null);
            
            me.restorePos = me.restoreSize = null;
            
            if (me.dd) {
                me.dd.enable();
                if (header) {
                    header.addCls(header.indicateDragCls);
                }
            }
            if (me.resizer) {
                me.resizer.enable();
            }
            me.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct');
            me.syncMonitorWindowResize();
            if (!animate) {
                me.el.enableShadow(null, true);
                me.maximized = false;
                me.fireEvent('restore', me);
            }
        }
        return me;
    },
    
    syncMonitorWindowResize: function() {
        var me = this,
            currentlyMonitoring = me._monitoringResize,
            
            yes = me.monitorResize || me.constrain || me.constrainHeader || me.maximized,
            
            veto = me.hidden || me.destroying || me.isDestroyed;
        if (yes && !veto) {
            
            if (!currentlyMonitoring) {
                
                
                Ext.on('resize', me.onWindowResize, me, {
                    buffer: 1
                });
                me._monitoringResize = true;
            }
        } else if (currentlyMonitoring) {
            
            Ext.un('resize', me.onWindowResize, me);
            me._monitoringResize = false;
        }
    },
    
    toggleMaximize: function() {
        return this[this.maximized ? 'restore' : 'maximize']();
    },
    createGhost: function() {
        var ghost = this.callParent(arguments);
        ghost.xtype = 'window';
        return ghost;
    },
    
    getDefaultFocus: function() {
        var me = this,
            result,
            defaultComp = me.defaultButton || me.defaultFocus,
            selector;
        if (defaultComp !== undefined) {
            
            if (Ext.isNumber(defaultComp)) {
                result = me.query('button')[defaultComp];
            }
            
            else if (Ext.isString(defaultComp)) {
                selector = defaultComp;
                
                
                
                if (Ext.validIdRe.test(selector)) {
                    result = me.down(Ext.makeIdSelector(selector));
                }
                
                if (!result) {
                    result = me.down(selector);
                }
            }
            
            else if (defaultComp.focus) {
                result = defaultComp;
            }
        }
        return result;
    },
    privates: {
        
        getFocusEl: function() {
            var me = this;
            
            
            
            return Ext.enableFocusManager ? me.el : (me.getDefaultFocus() || me.el);
        },
        
        
        initDraggable: function() {
            
            this.initSimpleDraggable();
        },
        onHeaderClick: function(header, e) {
            var delegate;
            if (header.el.contains(e.getTarget())) {
                delegate = this.getDefaultFocus();
                if (delegate) {
                    delegate.focus();
                }
            }
        },
        initResizable: function() {
            this.callParent(arguments);
            if (this.maximized) {
                this.resizer.disable();
            }
        }
    }
});


Ext.define('Ext.tip.Tip', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.tip',
    alternateClassName: 'Ext.Tip',
    
    
    
    minWidth: 40,
    
    maxWidth: 500,
    
    shadow: "sides",
    
    defaultAlign: "tl-bl?",
    
    constrainPosition: true,
    
    autoRender: true,
    hidden: true,
    baseCls: Ext.baseCSSPrefix + 'tip',
    focusOnToFront: false,
    
    closeAction: 'hide',
    
    alwaysFramed: true,
    frameHeader: false,
    initComponent: function() {
        var me = this;
        me.floating = Ext.apply({}, {
            shadow: me.shadow,
            constrain: me.constrainPosition
        }, me.self.prototype.floating);
        me.callParent(arguments);
        
        me.constrain = me.constrain || me.constrainPosition;
    },
    
    showAt: function(xy) {
        var me = this;
        this.callParent(arguments);
        
        if (me.isVisible()) {
            me.setPagePosition(xy[0], xy[1]);
            if (me.constrainPosition || me.constrain) {
                me.doConstrain();
            }
            me.toFront(true);
        }
    },
    privates: {
        
        initDraggable: function() {
            var me = this;
            me.draggable = {
                el: me.getDragEl(),
                delegate: me.header.el,
                constrain: me,
                constrainTo: me.el.dom.parentNode
            };
            
            Ext.Component.prototype.initDraggable.call(me);
        }
    },
    
    ghost: undefined,
    unghost: undefined
});


Ext.define('Ext.tip.ToolTip', {
    extend: 'Ext.tip.Tip',
    alias: 'widget.tooltip',
    alternateClassName: 'Ext.ToolTip',
    
    
    
    autoHide: true,
    
    showDelay: 500,
    
    hideDelay: 200,
    
    dismissDelay: 5000,
    
    
    trackMouse: false,
    
    
    anchorToTarget: true,
    
    anchorOffset: 0,
    
    
    targetCounter: 0,
    quickShowInterval: 250,
    
    hideAction: 'hide',
    
    fadeOutDuration: 1000,
    ariaRole: 'tooltip',
    
    initComponent: function() {
        var me = this;
        me.callParent(arguments);
        me.lastActive = new Date();
        me.setTarget(me.target);
        me.origAnchor = me.anchor;
    },
    
    onRender: function(ct, position) {
        var me = this;
        me.callParent(arguments);
        me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
        if (me.sticky) {
            
            me.el.dom.setAttribute('data-sticky', true);
        }
        me.anchorEl = me.el.createChild({
            role: 'presentation',
            cls: Ext.baseCSSPrefix + 'tip-anchor ' + me.anchorCls
        });
    },
    
    setTarget: function(target) {
        var me = this,
            t = Ext.get(target),
            tg;
        if (me.target) {
            tg = Ext.get(me.target);
            if (Ext.supports.Touch) {
                me.mun(tg, 'tap', me.onTargetOver, me);
            } else {
                me.mun(tg, {
                    mouseover: me.onTargetOver,
                    mouseout: me.onTargetOut,
                    mousemove: me.onMouseMove,
                    scope: me
                });
            }
        }
        me.target = t;
        if (t) {
            if (Ext.supports.Touch) {
                me.mon(t, {
                    tap: me.onTargetOver,
                    scope: me
                });
            } else {
                me.mon(t, {
                    mouseover: me.onTargetOver,
                    mouseout: me.onTargetOut,
                    mousemove: me.onMouseMove,
                    scope: me
                });
            }
        }
        if (me.anchor) {
            me.anchorTarget = me.target;
        }
    },
    
    onMouseMove: function(e) {
        var me = this,
            t, xy;
        
        if (!me.target || me.target.contains(e.target)) {
            t = me.delegate ? e.getTarget(me.delegate) : me.triggerElement = true;
            if (t) {
                me.targetXY = e.getXY();
                if (t === me.triggerElement) {
                    if (!me.hidden && me.trackMouse) {
                        xy = me.getTargetXY();
                        if (me.constrainPosition) {
                            xy = me.el.adjustForConstraints(xy, me.el.parent());
                        }
                        me.setPagePosition(xy);
                    }
                } else {
                    me.hide();
                    me.lastActive = new Date(0);
                    me.onTargetOver(e);
                }
            } else if ((!me.closable && me.isVisible()) && me.autoHide !== false) {
                me.delayHide();
            }
        }
    },
    
    getTargetXY: function() {
        var me = this,
            mouseOffset, offsets, xy, dw, dh, de, bd, scrollX, scrollY, axy, sz, constrainPosition;
        if (me.delegate) {
            me.anchorTarget = me.triggerElement;
        }
        if (me.anchor) {
            me.targetCounter++;
            offsets = me.getOffsets();
            xy = (me.anchorToTarget && !me.trackMouse) ? me.getAlignToXY(me.anchorTarget, me.getAnchorAlign()) : me.targetXY;
            dw = Ext.Element.getViewportWidth() - 5;
            dh = Ext.Element.getViewportHeight() - 5;
            de = document.documentElement;
            bd = document.body;
            scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5;
            scrollY = (de.scrollTop || bd.scrollTop || 0) + 5;
            axy = [
                xy[0] + offsets[0],
                xy[1] + offsets[1]
            ];
            sz = me.getSize();
            constrainPosition = me.constrainPosition;
            me.anchorEl.removeCls(me.anchorCls);
            if (me.targetCounter < 2 && constrainPosition) {
                if (axy[0] < scrollX) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'l-r';
                        if (me.mouseOffset) {
                            me.mouseOffset[0] *= -1;
                        }
                    }
                    me.anchor = 'left';
                    return me.getTargetXY();
                }
                if (axy[0] + sz.width > dw) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'r-l';
                        if (me.mouseOffset) {
                            me.mouseOffset[0] *= -1;
                        }
                    }
                    me.anchor = 'right';
                    return me.getTargetXY();
                }
                if (axy[1] < scrollY) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 't-b';
                        if (me.mouseOffset) {
                            me.mouseOffset[1] *= -1;
                        }
                    }
                    me.anchor = 'top';
                    return me.getTargetXY();
                }
                if (axy[1] + sz.height > dh) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'b-t';
                        if (me.mouseOffset) {
                            me.mouseOffset[1] *= -1;
                        }
                    }
                    me.anchor = 'bottom';
                    return me.getTargetXY();
                }
            }
            me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
            me.anchorEl.addCls(me.anchorCls);
            me.targetCounter = 0;
            return axy;
        } else {
            mouseOffset = me.getMouseOffset();
            return (me.targetXY) ? [
                me.targetXY[0] + mouseOffset[0],
                me.targetXY[1] + mouseOffset[1]
            ] : mouseOffset;
        }
    },
    
    calculateConstrainedPosition: function(constrainTo) {
        var me = this,
            visible, result, floatParentBox;
        
        if (!constrainTo && me.isContainedFloater()) {
            visible = me.isVisible();
            if (!visible) {
                me.el.show();
            }
            result = me.getTargetXY();
            if (!visible) {
                me.el.hide();
            }
            floatParentBox = me.floatParent.getTargetEl().getViewRegion();
            result[0] -= floatParentBox.left;
            result[1] -= floatParentBox.top;
        } else {
            result = me.callOverridden(arguments);
        }
        return result;
    },
    getMouseOffset: function() {
        var me = this,
            offset = me.anchor ? [
                0,
                0
            ] : [
                15,
                18
            ];
        if (me.mouseOffset) {
            offset[0] += me.mouseOffset[0];
            offset[1] += me.mouseOffset[1];
        }
        return offset;
    },
    fadeOut: function() {
        var me = this;
        me.el.fadeOut({
            duration: me.fadeOutDuration,
            callback: function() {
                me.hide();
                me.el.setOpacity('');
            }
        });
    },
    
    getAnchorPosition: function() {
        var me = this,
            m;
        if (me.anchor) {
            me.tipAnchor = me.anchor.charAt(0);
        } else {
            m = me.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);
            if (!m) {
                Ext.Error.raise('The AnchorTip.defaultAlign value "' + me.defaultAlign + '" is invalid.');
            }
            me.tipAnchor = m[1].charAt(0);
        }
        switch (me.tipAnchor) {
            case 't':
                return 'top';
            case 'b':
                return 'bottom';
            case 'r':
                return 'right';
        }
        return 'left';
    },
    
    getAnchorAlign: function() {
        switch (this.anchor) {
            case 'top':
                return 'tl-bl';
            case 'left':
                return 'tl-tr';
            case 'right':
                return 'tr-tl';
            default:
                return 'bl-tl';
        }
    },
    
    getOffsets: function() {
        var me = this,
            mouseOffset, offsets,
            ap = me.getAnchorPosition().charAt(0);
        if (me.anchorToTarget && !me.trackMouse) {
            switch (ap) {
                case 't':
                    offsets = [
                        0,
                        9
                    ];
                    break;
                case 'b':
                    offsets = [
                        0,
                        -13
                    ];
                    break;
                case 'r':
                    offsets = [
                        -13,
                        0
                    ];
                    break;
                default:
                    offsets = [
                        9,
                        0
                    ];
                    break;
            }
        } else {
            switch (ap) {
                case 't':
                    offsets = [
                        -15 - me.anchorOffset,
                        30
                    ];
                    break;
                case 'b':
                    offsets = [
                        -19 - me.anchorOffset,
                        -13 - me.el.dom.offsetHeight
                    ];
                    break;
                case 'r':
                    offsets = [
                        -15 - me.el.dom.offsetWidth,
                        -13 - me.anchorOffset
                    ];
                    break;
                default:
                    offsets = [
                        25,
                        -13 - me.anchorOffset
                    ];
                    break;
            }
        }
        mouseOffset = me.getMouseOffset();
        offsets[0] += mouseOffset[0];
        offsets[1] += mouseOffset[1];
        return offsets;
    },
    
    onTargetOver: function(e) {
        var me = this,
            delegate = me.delegate,
            t;
        if (me.disabled || e.within(me.target.dom, true)) {
            return;
        }
        t = delegate ? e.getTarget(delegate) : true;
        if (t) {
            me.triggerElement = t;
            me.triggerEvent = e;
            me.clearTimer('hide');
            me.targetXY = e.getXY();
            me.delayShow();
        }
    },
    
    delayShow: function(trackMouse) {
        
        
        
        
        
        
        
        
        var me = this,
            xy = me.el && (trackMouse === false || !me.trackMouse) && me.getTargetXY();
        if (me.hidden && !me.showTimer) {
            if (Ext.Date.getElapsed(me.lastActive) < me.quickShowInterval) {
                me.show();
            } else {
                me.showTimer = Ext.defer(me.showFromDelay, me.showDelay, me, [
                    xy
                ]);
            }
        } else if (!me.hidden && me.autoHide !== false) {
            me.show(xy);
        }
    },
    showFromDelay: function(xy) {
        this.fromDelayShow = true;
        this.show(xy);
        delete this.fromDelayShow;
    },
    onShowVeto: function() {
        this.callParent();
        delete this.triggerElement;
        this.clearTimer('show');
    },
    
    onTargetOut: function(e) {
        var me = this,
            triggerEl = me.triggerElement,
            
            
            target = triggerEl === true ? me.target : triggerEl;
        
        
        if (me.disabled || !triggerEl || e.within(target, true)) {
            return;
        }
        if (me.showTimer) {
            me.clearTimer('show');
            me.triggerElement = null;
        }
        if (me.autoHide !== false) {
            me.delayHide();
        }
    },
    
    delayHide: function() {
        var me = this;
        if (!me.hidden && !me.hideTimer) {
            me.hideTimer = Ext.defer(me[me.hideAction], me.hideDelay, me);
        }
    },
    
    hide: function() {
        var me = this;
        me.clearTimer('dismiss');
        me.lastActive = new Date();
        if (me.anchorEl) {
            me.anchorEl.hide();
        }
        me.callParent(arguments);
        delete me.triggerElement;
    },
    
    show: function(xy) {
        var me = this;
        
        
        this.callParent();
        if (this.hidden === false) {
            if (me.anchor) {
                me.anchor = me.origAnchor;
            }
            if (!me.calledFromShowAt) {
                
                me.showAt(xy || me.getTargetXY());
            }
        }
    },
    
    showAt: function(xy) {
        var me = this;
        me.lastActive = new Date();
        me.clearTimers();
        me.calledFromShowAt = true;
        
        if (!me.isVisible()) {
            this.callParent(arguments);
        }
        
        if (me.isVisible()) {
            me.setPagePosition(xy[0], xy[1]);
            if (me.constrainPosition || me.constrain) {
                me.doConstrain();
            }
            me.toFront(true);
            me.el.syncUnderlays();
            if (me.dismissDelay && me.autoHide !== false) {
                me.dismissTimer = Ext.defer(me.hide, me.dismissDelay, me);
            }
        }
        delete me.calledFromShowAt;
    },
    
    syncAnchor: function() {
        var me = this,
            anchorPos, targetPos, offset;
        switch (me.tipAnchor.charAt(0)) {
            case 't':
                anchorPos = 'b';
                targetPos = 'tl';
                offset = [
                    20 + me.anchorOffset,
                    1
                ];
                break;
            case 'r':
                anchorPos = 'l';
                targetPos = 'tr';
                offset = [
                    -1,
                    12 + me.anchorOffset
                ];
                break;
            case 'b':
                anchorPos = 't';
                targetPos = 'bl';
                offset = [
                    20 + me.anchorOffset,
                    -1
                ];
                break;
            default:
                anchorPos = 'r';
                targetPos = 'tl';
                offset = [
                    1,
                    12 + me.anchorOffset
                ];
                break;
        }
        me.anchorEl.alignTo(me.el, anchorPos + '-' + targetPos, offset);
        me.anchorEl.setStyle('z-index', parseInt(me.el.getZIndex(), 10) || 0 + 1).setVisibilityMode(Ext.Element.DISPLAY);
    },
    
    afterSetPosition: function(x, y) {
        var me = this;
        me.callParent(arguments);
        if (me.anchor) {
            me.syncAnchor();
            if (!me.anchorEl.isVisible()) {
                me.anchorEl.show();
            }
        } else {
            me.anchorEl.hide();
        }
    },
    _timerNames: {},
    
    clearTimer: function(name) {
        var me = this,
            names = me._timerNames,
            propName = names[name] || (names[name] = name + 'Timer'),
            timer = me[propName];
        if (timer) {
            clearTimeout(timer);
            me[propName] = null;
        }
    },
    
    clearTimers: function() {
        var me = this;
        me.clearTimer('show');
        me.clearTimer('dismiss');
        me.clearTimer('hide');
    },
    
    onShow: function() {
        var me = this;
        me.callParent();
        me.mon(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
    },
    
    onHide: function() {
        var me = this;
        me.callParent();
        me.mun(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
    },
    
    onDocMouseDown: function(e) {
        var me = this;
        if (!me.closable && !e.within(me.el.dom)) {
            me.disable();
            Ext.defer(me.doEnable, 100, me);
        }
    },
    
    doEnable: function() {
        if (!this.isDestroyed) {
            this.enable();
        }
    },
    
    onDisable: function() {
        this.callParent();
        this.clearTimers();
        this.hide();
    },
    beforeDestroy: function() {
        var me = this;
        me.clearTimers();
        Ext.destroy(me.anchorEl);
        delete me.anchorEl;
        delete me.target;
        delete me.anchorTarget;
        delete me.triggerElement;
        me.callParent();
    },
    
    onDestroy: function() {
        Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
        this.callParent();
    }
});


Ext.define('Ext.tip.QuickTip', {
    extend: 'Ext.tip.ToolTip',
    alias: 'widget.quicktip',
    alternateClassName: 'Ext.QuickTip',
    
    
    interceptTitles: false,
    
    title: '&#160;',
    
    tagConfig: {
        namespace: 'data-',
        attribute: 'qtip',
        width: 'qwidth',
        target: 'target',
        title: 'qtitle',
        hide: 'hide',
        cls: 'qclass',
        align: 'qalign',
        anchor: 'anchor',
        showDelay: 'qshowDelay',
        hideAction: 'hideAction',
        anchorTarget: 'anchorTarget'
    },
    shrinkWrapDock: true,
    
    initComponent: function() {
        var me = this;
        me.target = me.target || Ext.getDoc();
        me.targets = me.targets || {};
        me.callParent();
    },
    setTagConfig: function(cfg) {
        this.tagConfig = Ext.apply({}, cfg);
        
        delete this.tagConfig.attr;
    },
    
    register: function(config) {
        var configs = Ext.isArray(config) ? config : arguments,
            i = 0,
            len = configs.length,
            target, j, targetLen;
        for (; i < len; i++) {
            config = configs[i];
            target = config.target;
            if (target) {
                if (Ext.isArray(target)) {
                    for (j = 0 , targetLen = target.length; j < targetLen; j++) {
                        this.targets[Ext.id(target[j])] = config;
                    }
                } else {
                    this.targets[Ext.id(target)] = config;
                }
            }
        }
    },
    
    unregister: function(el) {
        delete this.targets[Ext.id(el)];
    },
    
    cancelShow: function(el) {
        var me = this,
            activeTarget = me.activeTarget;
        el = Ext.get(el).dom;
        if (me.isVisible()) {
            if (activeTarget && activeTarget.el === el) {
                me.hide();
            }
        } else if (activeTarget && activeTarget.el === el) {
            me.clearTimer('show');
        }
    },
    
    getTipCfg: function(target, event) {
        var titleText = target.title,
            cfg = this.tagConfig,
            attr = cfg.attr || (cfg.attr = cfg.namespace + cfg.attribute),
            text;
        if (this.interceptTitles && titleText && Ext.isString(titleText)) {
            target.setAttribute(attr, titleText);
            target.removeAttribute('title');
            return {
                text: titleText
            };
        } else {
            target = Ext.fly(target).findParent(function(dom) {
                
                return (text = dom.getAttribute(attr));
            });
            if (target) {
                return {
                    target: target,
                    text: text
                };
            }
        }
    },
    onTargetOver: function(event) {
        
        
        this.doTargetOver(event.getTarget(this.delegate), null, event);
    },
    
    doTargetOver: function(target, xy, event) {
        var me = this,
            hasShowDelay, hideAction, delay, elTarget, cfg, ns, tipConfig, autoHide, targets, targetEl, value, key;
        if (me.disabled) {
            return;
        }
        if (typeof target === 'string') {
            target = Ext.getDom(target);
        }
        me.targetXY = xy || (event ? event.getXY() : Ext.fly(target).getXY());
        
        if (!target || target.nodeType !== 1 || target == document.documentElement || target == document.body) {
            return;
        }
        if (me.activeTarget && ((target === me.activeTarget.el) || Ext.fly(me.activeTarget.el).contains(target))) {
            
            
            
            
            if (me.targetTextEmpty()) {
                me.onShowVeto();
                delete me.activeTarget;
            } else {
                me.clearTimer('hide');
                me.show();
            }
            return;
        }
        if (target) {
            targets = me.targets;
            for (key in targets) {
                if (targets.hasOwnProperty(key)) {
                    value = targets[key];
                    targetEl = Ext.fly(value.target);
                    if (targetEl && (targetEl.dom === target || targetEl.contains(target))) {
                        elTarget = targetEl.dom;
                        break;
                    }
                }
            }
            if (elTarget) {
                me.activeTarget = me.targets[elTarget.id];
                me.activeTarget.el = target;
                me.anchor = me.activeTarget.anchor;
                if (me.anchor) {
                    me.anchorTarget = target;
                }
                hasShowDelay = parseInt(me.activeTarget.showDelay, 10);
                if (hasShowDelay) {
                    delay = me.showDelay;
                    me.showDelay = hasShowDelay;
                }
                me.delayShow();
                if (hasShowDelay) {
                    me.showDelay = delay;
                }
                if (!(hideAction = me.activeTarget.hideAction)) {
                    delete me.hideAction;
                } else {
                    me.hideAction = hideAction;
                }
                return;
            }
        }
        
        elTarget = Ext.fly(target, '_quicktip-target');
        cfg = me.tagConfig;
        ns = cfg.namespace;
        tipConfig = me.getTipCfg(target, event);
        if (tipConfig) {
            
            
            if (tipConfig.target) {
                target = tipConfig.target;
                elTarget = Ext.fly(target, '_quicktip-target');
            }
            autoHide = elTarget.getAttribute(ns + cfg.hide);
            me.activeTarget = {
                el: target,
                text: tipConfig.text,
                width: +elTarget.getAttribute(ns + cfg.width) || null,
                autoHide: autoHide != "user" && autoHide !== 'false',
                title: elTarget.getAttribute(ns + cfg.title),
                cls: elTarget.getAttribute(ns + cfg.cls),
                align: elTarget.getAttribute(ns + cfg.align),
                showDelay: parseInt(elTarget.getAttribute(ns + cfg.showDelay) || 0, 10),
                hideAction: elTarget.getAttribute(ns + cfg.hideAction),
                anchorTarget: elTarget.getAttribute(ns + cfg.anchorTarget)
            };
            
            if (!me.initialConfig.hasOwnProperty('anchor')) {
                me.anchor = elTarget.getAttribute(ns + cfg.anchor);
            }
            
            if (me.anchor && !me.initialConfig.hasOwnProperty('anchorTarget')) {
                me.anchorTarget = me.activeTarget.anchorTarget || target;
            }
            hasShowDelay = parseInt(me.activeTarget.showDelay, 10);
            if (hasShowDelay) {
                delay = me.showDelay;
                me.showDelay = hasShowDelay;
            }
            me.delayShow();
            if (hasShowDelay) {
                me.showDelay = delay;
            }
        }
    },
    
    onTargetOut: function(e) {
        var me = this,
            active = me.activeTarget,
            hasHideDelay, delay;
        
        
        if (active && e.within(me.activeTarget.el) && !me.getTipCfg(e.getTarget(), e)) {
            return;
        }
        me.clearTimer('show');
        delete me.activeTarget;
        if (me.autoHide !== false) {
            hasHideDelay = active && parseInt(active.hideDelay, 10);
            if (hasHideDelay) {
                delay = me.hideDelay;
                me.hideDelay = hasHideDelay;
            }
            me.delayHide();
            if (hasHideDelay) {
                me.hideDelay = delay;
            }
        }
    },
    targetTextEmpty: function() {
        var me = this,
            target = me.activeTarget,
            cfg = me.tagConfig,
            el, text;
        if (target) {
            el = target.el;
            if (el) {
                text = el.getAttribute(cfg.namespace + cfg.attribute);
                
                
                
                if (!text && !me.targets[Ext.id(target.target)]) {
                    return true;
                }
            }
        }
        return false;
    },
    show: function() {
        var me = this,
            fromDelay = me.fromDelayShow;
        
        
        if (fromDelay && me.targetTextEmpty()) {
            me.onShowVeto();
            delete me.activeTarget;
            return;
        }
        me.callParent(arguments);
    },
    
    showAt: function(xy) {
        var me = this,
            target = me.activeTarget,
            header = me.header,
            dismiss, cls;
        if (target) {
            if (!me.rendered) {
                me.render(Ext.getBody());
                me.activeTarget = target;
            }
            me.suspendLayouts();
            if (target.title) {
                me.setTitle(target.title);
                header.show();
            } else if (header) {
                header.hide();
            }
            me.update(target.text);
            me.autoHide = target.autoHide;
            dismiss = target.dismissDelay;
            me.dismissDelay = Ext.isNumber(dismiss) ? dismiss : me.dismissDelay;
            if (target.mouseOffset) {
                xy[0] += target.mouseOffset[0];
                xy[1] += target.mouseOffset[1];
            }
            cls = me.lastCls;
            if (cls) {
                me.removeCls(cls);
                delete me.lastCls;
            }
            cls = target.cls;
            if (cls) {
                me.addCls(cls);
                me.lastCls = cls;
            }
            me.setWidth(target.width);
            if (me.anchor) {
                me.constrainPosition = false;
            } else if (target.align) {
                
                xy = me.getAlignToXY(target.el, target.align);
                me.constrainPosition = false;
            } else {
                me.constrainPosition = true;
            }
            me.resumeLayouts(true);
        }
        me.callParent([
            xy
        ]);
    },
    
    hide: function() {
        delete this.activeTarget;
        this.callParent();
    }
});


Ext.define('Ext.tip.QuickTipManager', {
    requires: [
        'Ext.tip.QuickTip'
    ],
    singleton: true,
    alternateClassName: 'Ext.QuickTips',
    disabled: false,
    
    init: function(autoRender, config) {
        var me = this;
        if (!me.tip) {
            if (!Ext.isReady) {
                Ext.onReady(function() {
                    Ext.tip.QuickTipManager.init(autoRender, config);
                });
                return false;
            }
            var tipConfig = Ext.apply({
                    
                    sticky: true,
                    disabled: me.disabled,
                    id: 'ext-quicktips-tip'
                }, config),
                className = tipConfig.className,
                xtype = tipConfig.xtype;
            if (className) {
                delete tipConfig.className;
            } else if (xtype) {
                className = 'widget.' + xtype;
                delete tipConfig.xtype;
            }
            if (autoRender !== false) {
                tipConfig.renderTo = document.body;
                if (tipConfig.renderTo.tagName.toUpperCase() != 'BODY') {
                    
                    Ext.Error.raise({
                        sourceClass: 'Ext.tip.QuickTipManager',
                        sourceMethod: 'init',
                        msg: 'Cannot init QuickTipManager: no document body'
                    });
                }
            }
            me.tip = Ext.create(className || 'Ext.tip.QuickTip', tipConfig);
            
            
            Ext.quickTipsActive = true;
        }
    },
    
    destroy: function() {
        Ext.destroy(this.tip);
        this.tip = undefined;
    },
    
    ddDisable: function() {
        var me = this,
            tip = me.tip;
        
        if (tip && !me.disabled) {
            tip.disable();
        }
    },
    
    ddEnable: function() {
        var me = this,
            tip = me.tip;
        
        if (tip && !me.disabled) {
            tip.enable();
        }
    },
    
    enable: function() {
        var me = this,
            tip = me.tip;
        if (tip) {
            tip.enable();
        }
        me.disabled = false;
    },
    
    disable: function() {
        var me = this,
            tip = me.tip;
        if (tip) {
            tip.disable();
        }
        me.disabled = true;
    },
    
    isEnabled: function() {
        var tip = this.tip;
        return tip !== undefined && !tip.disabled;
    },
    
    getQuickTip: function() {
        return this.tip;
    },
    
    register: function() {
        var tip = this.tip;
        tip.register.apply(tip, arguments);
    },
    
    unregister: function() {
        var tip = this.tip;
        tip.unregister.apply(tip, arguments);
    },
    
    tips: function() {
        var tip = this.tip;
        tip.register.apply(tip, arguments);
    }
});

Ext.define('Ext.app.bindinspector.Inspector', {
    extend: 'Ext.window.Window',
    requires: [
        'Ext.app.bindinspector.Container',
        'Ext.layout.container.Fit',
        'Ext.tip.QuickTipManager'
    ],
    title: 'Bind Inspector',
    layout: 'fit',
    autoShow: true,
    maximized: true,
    maximizable: true,
    width: 960,
    height: 575,
    initComponent: function() {
        var snapshot = (new Ext.app.bindinspector.Environment()).captureSnapshot(),
            env = new Ext.app.bindinspector.Environment();
        env.unpackSnapshot(snapshot);
        this.items = {
            xtype: 'bindinspector-container',
            env: env
        };
        this.callParent();
    },
    afterRender: function() {
        this.callParent(arguments);
        Ext.tip.QuickTipManager.init();
    }
});


Ext.define('Ext.button.Split', {
    
    alias: 'widget.splitbutton',
    extend: 'Ext.button.Button',
    alternateClassName: 'Ext.SplitButton',
    
    isSplitButton: true,
    
    
    
    arrowCls: 'split',
    split: true,
    
    
    setArrowHandler: function(handler, scope) {
        this.arrowHandler = handler;
        this.scope = scope;
    },
    
    onClick: function(e) {
        var me = this;
        me.doPreventDefault(e);
        if (!me.disabled) {
            if (me.isWithinTrigger(e)) {
                
                
                e.preventDefault();
                me.maybeShowMenu(e);
                me.fireEvent("arrowclick", me, e);
                if (me.arrowHandler) {
                    me.arrowHandler.call(me.scope || me, me, e);
                }
            } else {
                me.doToggle();
                me.fireHandler(e);
            }
        }
    }
});


Ext.define('Ext.button.Cycle', {
    
    alias: 'widget.cycle',
    extend: 'Ext.button.Split',
    alternateClassName: 'Ext.CycleButton',
    
    
    
    
    
    
    
    
    
    
    getButtonText: function(item) {
        var me = this,
            text = '';
        if (item && me.showText === true) {
            if (me.prependText) {
                text += me.prependText;
            }
            text += item.text;
            return text;
        }
        return me.text;
    },
    
    setActiveItem: function(item, suppressEvent) {
        var me = this,
            changeHandler = me.changeHandler,
            forceIcon = me.forceIcon,
            forceGlyph = me.forceGlyph;
        me.settingActive = true;
        if (!Ext.isObject(item)) {
            item = me.menu.getComponent(item);
        }
        if (item) {
            me.setText(me.getButtonText(item));
            me.setIconCls(forceIcon ? forceIcon : item.iconCls);
            me.setGlyph(forceGlyph ? forceGlyph : item.glyph);
            me.activeItem = item;
            if (!item.checked) {
                item.setChecked(true, false);
            }
            if (!suppressEvent) {
                Ext.callback(me.changeHandler, me.scope, [
                    me,
                    item
                ], 0, me);
                me.fireEvent('change', me, item);
            }
        }
        me.settingActive = false;
    },
    
    getActiveItem: function() {
        return this.activeItem;
    },
    
    initComponent: function() {
        var me = this,
            checked = 0,
            items, i, iLen, item;
        
        
        items = (me.menu.items || []).concat(me.items || []);
        me.menu = Ext.applyIf({
            cls: Ext.baseCSSPrefix + 'cycle-menu',
            items: []
        }, me.menu);
        iLen = items.length;
        
        for (i = 0; i < iLen; i++) {
            item = items[i];
            item = Ext.applyIf({
                group: me.id,
                itemIndex: i,
                checkHandler: me.checkHandler,
                scope: me,
                checked: item.checked || false
            }, item);
            me.menu.items.push(item);
            if (item.checked) {
                checked = i;
            }
        }
        me.itemCount = me.menu.items.length;
        me.callParent(arguments);
        me.on('click', me.toggleSelected, me);
        me.setActiveItem(checked, true);
    },
    
    checkHandler: function(item, pressed) {
        if (pressed && !this.settingActive) {
            this.setActiveItem(item);
        }
    },
    
    toggleSelected: function() {
        var me = this,
            m = me.menu,
            checkItem;
        checkItem = me.activeItem.next(':not([disabled])') || m.items.getAt(0);
        checkItem.setChecked(true);
    }
});


Ext.define('Ext.button.Segmented', {
    extend: 'Ext.container.Container',
    xtype: 'segmentedbutton',
    requires: [
        'Ext.button.Button'
    ],
    config: {
        
        allowDepress: false,
        
        allowMultiple: false,
        
        allowToggle: true,
        
        vertical: false,
        
        defaultUI: 'default'
    },
    beforeRenderConfig: {
        
        value: undefined
    },
    
    defaultBindProperty: 'value',
    publishes: [
        'value'
    ],
    twoWayBindable: [
        'value'
    ],
    layout: 'segmentedbutton',
    defaultType: 'button',
    maskOnDisable: false,
    isSegmentedButton: true,
    baseCls: Ext.baseCSSPrefix + 'segmented-button',
    itemCls: Ext.baseCSSPrefix + 'segmented-button-item',
    
    _firstCls: Ext.baseCSSPrefix + 'segmented-button-first',
    
    _lastCls: Ext.baseCSSPrefix + 'segmented-button-last',
    
    _middleCls: Ext.baseCSSPrefix + 'segmented-button-middle',
    
    applyValue: function(value, oldValue) {
        var me = this,
            allowMultiple = me.getAllowMultiple(),
            buttonValue, button, values, oldValues, items, i, ln;
        values = (value instanceof Array) ? value : (value == null) ? [] : [
            value
        ];
        oldValues = (oldValue instanceof Array) ? oldValue : (oldValue == null) ? [] : [
            oldValue
        ];
        
        
        me._isApplyingValue = true;
        if (!me.rendered) {
            
            items = me.items.items;
            for (i = 0 , ln = items.length; i < ln; i++) {
                button = items[i];
                if (button.pressed) {
                    buttonValue = button.value;
                    if (buttonValue == null) {
                        buttonValue = me.items.indexOf(button);
                    }
                    if (!Ext.Array.contains(values, buttonValue)) {
                        values.push(buttonValue);
                    }
                }
            }
        }
        ln = values.length;
        if (ln > 1 && !allowMultiple) {
            Ext.Error.raise('Cannot set multiple values when allowMultiple is false');
        }
        
        for (i = 0; i < ln; i++) {
            value = values[i];
            button = me._lookupButtonByValue(value);
            if (button) {
                buttonValue = button.value;
                if ((buttonValue != null) && buttonValue !== value) {
                    
                    
                    values[i] = buttonValue;
                }
                if (!button.pressed) {
                    button.setPressed(true);
                }
            } else {
                
                Ext.Error.raise("Invalid value '" + value + "' for segmented button: '" + me.id + "'");
            }
        }
        value = allowMultiple ? values : ln ? values[0] : null;
        
        for (i = 0 , ln = oldValues.length; i < ln; i++) {
            oldValue = oldValues[i];
            if (!Ext.Array.contains(values, oldValue)) {
                me._lookupButtonByValue(oldValue).setPressed(false);
            }
        }
        me._isApplyingValue = false;
        return value;
    },
    beforeRender: function() {
        var me = this;
        me.addCls(me.baseCls + me._getClsSuffix());
        me._syncItemClasses(true);
        me.callParent();
    },
    onAdd: function(item) {
        var me = this,
            syncItemClasses = '_syncItemClasses';
        var items = me.items.items,
            ln = items.length,
            i = 0,
            value, defaultUI;
        if (item.ui === 'default' && !item.hasOwnProperty('ui')) {
            defaultUI = me.getDefaultUI();
            if (defaultUI !== 'default') {
                item.ui = defaultUI;
            }
        }
        for (; i < ln; i++) {
            if (items[i] !== item) {
                value = items[i].value;
                if (value != null && value === item.value) {
                    Ext.Error.raise("Segmented button '" + me.id + "' cannot contain multiple items with value: '" + value + "'");
                }
            }
        }
        me.mon(item, {
            hide: syncItemClasses,
            show: syncItemClasses,
            toggle: '_onItemToggle',
            scope: me
        });
        if (me.getAllowToggle()) {
            item.enableToggle = true;
            if (!me.getAllowMultiple()) {
                item.toggleGroup = me.getId();
                item.allowDepress = me.getAllowDepress();
            }
        }
        item.addCls(me.itemCls + me._getClsSuffix());
        me._syncItemClasses();
        me.callParent([
            item
        ]);
    },
    onRemove: function(item) {
        var me = this;
        item.removeCls(me.itemCls + me._getClsSuffix());
        me._syncItemClasses();
        me.callParent([
            item
        ]);
    },
    beforeLayout: function() {
        if (Ext.isChrome) {
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            this.el.dom.offsetWidth;
        }
        this.callParent();
    },
    updateDefaultUI: function(defaultUI) {
        var items = this.items,
            item, i, ln;
        if (this.rendered) {
            Ext.Error.raise("Changing the ui config of a segmented button after render is not supported.");
        } else if (items) {
            if (items.items) {
                
                items = items.items;
            }
            for (i = 0 , ln = items.length; i < ln; i++) {
                item = items[i];
                if (item.ui === 'default' && defaultUI !== 'default' && !item.hasOwnProperty('ui')) {
                    items[i].ui = defaultUI;
                }
            }
        }
    },
    updateAllowDepress: function(newAllowDepress, oldAllowDepress) {
        if (this.rendered && (newAllowDepress !== oldAllowDepress)) {
            Ext.Error.raise("Changing the allowDepress config of a segmented button after render is not supported.");
        }
    },
    updateAllowMultiple: function(newAllowMultiple, oldAllowMultiple) {
        if (this.rendered && (newAllowMultiple !== oldAllowMultiple)) {
            Ext.Error.raise("Changing the allowMultiple config of a segmented button after render is not supported.");
        }
    },
    updateAllowToggle: function(newAllowToggle, oldAllowToggle) {
        if (this.rendered && (newAllowToggle !== oldAllowToggle)) {
            Ext.Error.raise("Changing the allowToggle config of a segmented button after render is not supported.");
        }
    },
    updateVertical: function(newVertical, oldVertical) {
        if (this.rendered && (newVertical !== oldVertical)) {
            Ext.Error.raise("Changing the orientation of a segmented button after render is not supported.");
        }
    },
    privates: {
        _getClsSuffix: function() {
            return this.getVertical() ? '-vertical' : '-horizontal';
        },
        
        _getFirstCls: function() {
            return this._firstCls;
        },
        
        _getLastCls: function() {
            return this._lastCls;
        },
        
        _lookupButtonByValue: function(value) {
            var items = this.items.items,
                ln = items.length,
                i = 0,
                button = null,
                buttonValue, btn;
            for (; i < ln; i++) {
                btn = items[i];
                buttonValue = btn.value;
                if ((buttonValue != null) && buttonValue === value) {
                    button = btn;
                    break;
                }
            }
            if (!button && typeof value === 'number') {
                
                button = items[value];
            }
            return button;
        },
        
        _onItemToggle: function(button, pressed) {
            if (this._isApplyingValue) {
                return;
            }
            var me = this,
                Array = Ext.Array,
                allowMultiple = me.allowMultiple,
                buttonValue = (button.value != null) ? button.value : me.items.indexOf(button),
                value = me.getValue(),
                valueIndex;
            if (allowMultiple) {
                valueIndex = Array.indexOf(value, buttonValue);
            }
            if (pressed) {
                if (allowMultiple) {
                    if (valueIndex === -1) {
                        value.push(buttonValue);
                    }
                } else {
                    value = buttonValue;
                }
            } else {
                if (allowMultiple) {
                    if (valueIndex > -1) {
                        value.splice(valueIndex, 1);
                    }
                } else if (value === buttonValue) {
                    value = null;
                }
            }
            me.setValue(value);
            me.fireEvent('toggle', me, button, pressed);
        },
        
        _syncItemClasses: function(force) {
            var me = this,
                firstCls, middleCls, lastCls, items, ln, visibleItems, item, i;
            if (!force && !me.rendered) {
                return;
            }
            firstCls = me._getFirstCls();
            middleCls = me._middleCls;
            lastCls = me._getLastCls();
            items = me.items.items;
            ln = items.length;
            visibleItems = [];
            for (i = 0; i < ln; i++) {
                item = items[i];
                if (!item.hidden) {
                    visibleItems.push(item);
                }
            }
            ln = visibleItems.length;
            
            for (i = 0; i < ln; i++) {
                visibleItems[i].removeCls([
                    firstCls,
                    middleCls,
                    lastCls
                ]);
            }
            
            if (ln > 1) {
                
                visibleItems[0].addCls(firstCls);
                
                for (i = 1; i < ln - 1; i++) {
                    visibleItems[i].addCls(middleCls);
                }
                
                visibleItems[ln - 1].addCls(lastCls);
            }
        }
    }
});


Ext.define('Ext.layout.container.Table', {
    
    alias: [
        'layout.table'
    ],
    extend: 'Ext.layout.container.Container',
    alternateClassName: 'Ext.layout.TableLayout',
    
    
    type: 'table',
    createsInnerCt: true,
    targetCls: Ext.baseCSSPrefix + 'table-layout-ct',
    tableCls: Ext.baseCSSPrefix + 'table-layout',
    cellCls: Ext.baseCSSPrefix + 'table-layout-cell',
    
    tableAttrs: null,
    
    
    getItemSizePolicy: function(item) {
        return this.autoSizePolicy;
    },
    initInheritedState: function(inheritedState, inheritedStateInner) {
        inheritedStateInner.inShrinkWrapTable = true;
    },
    getLayoutItems: function() {
        var me = this,
            result = [],
            items = me.callParent(),
            item,
            len = items.length,
            i;
        for (i = 0; i < len; i++) {
            item = items[i];
            if (!item.hidden) {
                result.push(item);
            }
        }
        return result;
    },
    getHiddenItems: function() {
        var result = [],
            items = this.owner.items.items,
            len = items.length,
            i = 0,
            item;
        for (; i < len; ++i) {
            item = items[i];
            if (item.rendered && item.hidden) {
                result.push(item);
            }
        }
        return result;
    },
    
    renderChildren: function() {
        var me = this,
            items = me.getLayoutItems(),
            tbody = me.owner.getTargetEl().child('table', true).tBodies[0],
            rows = tbody.rows,
            i = 0,
            len = items.length,
            hiddenItems = me.getHiddenItems(),
            cells, curCell, rowIdx, cellIdx, item, trEl, tdEl, itemCt, el;
        
        cells = me.calculateCells(items);
        
        
        
        for (; i < len; i++) {
            curCell = cells[i];
            rowIdx = curCell.rowIdx;
            cellIdx = curCell.cellIdx;
            item = items[i];
            
            trEl = rows[rowIdx];
            if (!trEl) {
                trEl = tbody.insertRow(rowIdx);
                if (me.trAttrs) {
                    trEl.set(me.trAttrs);
                }
            }
            
            itemCt = tdEl = Ext.get(trEl.cells[cellIdx] || trEl.insertCell(cellIdx));
            if (me.needsDivWrap()) {
                
                itemCt = tdEl.first() || tdEl.createChild({
                    tag: 'div',
                    role: 'presentation'
                });
                itemCt.setWidth(null);
            }
            
            if (!item.rendered) {
                me.renderItem(item, itemCt, 0);
            } else if (!me.isValidParent(item, itemCt, rowIdx, cellIdx, tbody)) {
                me.moveItem(item, itemCt, 0);
            }
            
            if (me.tdAttrs) {
                tdEl.set(me.tdAttrs);
            }
            if (item.tdAttrs) {
                tdEl.set(item.tdAttrs);
            }
            tdEl.set({
                colSpan: item.colspan || 1,
                rowSpan: item.rowspan || 1,
                id: item.cellId || '',
                cls: me.cellCls + ' ' + (item.cellCls || '')
            });
            
            if (!cells[i + 1] || cells[i + 1].rowIdx !== rowIdx) {
                cellIdx++;
                while (trEl.cells[cellIdx]) {
                    trEl.deleteCell(cellIdx);
                }
            }
        }
        
        rowIdx++;
        while (tbody.rows[rowIdx]) {
            tbody.deleteRow(rowIdx);
        }
        
        
        for (i = 0 , len = hiddenItems.length; i < len; ++i) {
            me.ensureInDocument(hiddenItems[i].getEl());
        }
    },
    ensureInDocument: function(el) {
        var dom = el.dom.parentNode;
        while (dom) {
            if (dom.tagName.toUpperCase() == 'BODY') {
                return;
            }
            dom = dom.parentNode;
        }
        Ext.getDetachedBody().appendChild(el);
    },
    calculate: function(ownerContext) {
        if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {
            this.done = false;
        } else {
            var targetContext = ownerContext.targetContext,
                widthShrinkWrap = ownerContext.widthModel.shrinkWrap,
                heightShrinkWrap = ownerContext.heightModel.shrinkWrap,
                shrinkWrap = heightShrinkWrap || widthShrinkWrap,
                table = shrinkWrap && targetContext.el.child('table', true),
                targetPadding = shrinkWrap && targetContext.getPaddingInfo();
            if (widthShrinkWrap) {
                ownerContext.setContentWidth(table.offsetWidth + targetPadding.width, true);
            }
            if (heightShrinkWrap) {
                ownerContext.setContentHeight(table.offsetHeight + targetPadding.height, true);
            }
        }
    },
    finalizeLayout: function() {
        if (this.needsDivWrap()) {
            
            var items = this.getLayoutItems(),
                i,
                iLen = items.length,
                item;
            for (i = 0; i < iLen; i++) {
                item = items[i];
                Ext.fly(item.el.dom.parentNode).setWidth(item.getWidth());
            }
        }
    },
    
    calculateCells: function(items) {
        var cells = [],
            rowIdx = 0,
            colIdx = 0,
            cellIdx = 0,
            totalCols = this.columns || Infinity,
            rowspans = [],
            
            i = 0,
            j,
            len = items.length,
            item;
        for (; i < len; i++) {
            item = items[i];
            
            while (colIdx >= totalCols || rowspans[colIdx] > 0) {
                if (colIdx >= totalCols) {
                    
                    colIdx = 0;
                    cellIdx = 0;
                    rowIdx++;
                    
                    for (j = 0; j < totalCols; j++) {
                        if (rowspans[j] > 0) {
                            rowspans[j]--;
                        }
                    }
                } else {
                    colIdx++;
                }
            }
            
            cells.push({
                rowIdx: rowIdx,
                cellIdx: cellIdx
            });
            
            for (j = item.colspan || 1; j; --j) {
                rowspans[colIdx] = item.rowspan || 1;
                ++colIdx;
            }
            ++cellIdx;
        }
        return cells;
    },
    getRenderTree: function() {
        var me = this,
            items = me.getLayoutItems(),
            cells,
            rows = [],
            result = Ext.apply({
                tag: 'table',
                role: 'presentation',
                cls: me.tableCls,
                cellspacing: 0,
                cellpadding: 0,
                cn: {
                    tag: 'tbody',
                    role: 'presentation',
                    cn: rows
                }
            }, me.tableAttrs),
            tdAttrs = me.tdAttrs,
            needsDivWrap = me.needsDivWrap(),
            i,
            len = items.length,
            item, curCell, tr, rowIdx, cellIdx, cell;
        
        cells = me.calculateCells(items);
        for (i = 0; i < len; i++) {
            item = items[i];
            curCell = cells[i];
            rowIdx = curCell.rowIdx;
            cellIdx = curCell.cellIdx;
            
            tr = rows[rowIdx];
            if (!tr) {
                tr = rows[rowIdx] = {
                    tag: 'tr',
                    role: 'presentation',
                    cn: []
                };
                if (me.trAttrs) {
                    Ext.apply(tr, me.trAttrs);
                }
            }
            
            cell = tr.cn[cellIdx] = {
                tag: 'td',
                role: 'presentation'
            };
            if (tdAttrs) {
                Ext.apply(cell, tdAttrs);
            }
            Ext.apply(cell, {
                colSpan: item.colspan || 1,
                rowSpan: item.rowspan || 1,
                id: item.cellId || '',
                cls: me.cellCls + ' ' + (item.cellCls || '')
            });
            if (needsDivWrap) {
                
                cell = cell.cn = {
                    tag: 'div',
                    role: 'presentation'
                };
            }
            me.configureItem(item);
            
            cell.cn = item.getRenderTree();
        }
        return result;
    },
    isValidParent: function(item, target, rowIdx, cellIdx) {
        var tbody, correctCell, table;
        
        if (arguments.length === 3) {
            table = item.el.up('table');
            return table && table.dom.parentNode === target.dom;
        }
        tbody = this.owner.getTargetEl().child('table', true).tBodies[0];
        correctCell = tbody.rows[rowIdx].cells[cellIdx];
        return item.el.dom.parentNode === correctCell;
    },
    
    needsDivWrap: function() {
        return Ext.isOpera10_5;
    }
});


Ext.define('Ext.container.ButtonGroup', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.buttongroup',
    alternateClassName: 'Ext.ButtonGroup',
    requires: [
        'Ext.layout.container.Table'
    ],
    
    
    baseCls: Ext.baseCSSPrefix + 'btn-group',
    
    layout: {
        type: 'table'
    },
    defaultType: 'button',
    
    frame: true,
    
    frameHeader: false,
    
    titleAlign: 'center',
    noTitleCls: 'notitle',
    ariaRole: 'group',
    initComponent: function() {
        
        var me = this,
            cols = me.columns;
        if (cols) {
            me.layout = Ext.apply({}, {
                columns: cols
            }, me.layout);
        }
        if (!me.title) {
            me.addClsWithUI(me.noTitleCls);
        }
        me.callParent(arguments);
    },
    
    onBeforeAdd: function(component) {
        if (component.isButton) {
            if (this.defaultButtonUI && component.ui === 'default' && !component.hasOwnProperty('ui')) {
                component.ui = this.defaultButtonUI;
            } else {
                component.ui = component.ui + '-toolbar';
            }
        }
        this.callParent(arguments);
    },
    privates: {
        applyDefaults: function(c) {
            if (!Ext.isString(c)) {
                c = this.callParent(arguments);
            }
            return c;
        }
    }
});


Ext.define('Ext.container.Monitor', {
    target: null,
    selector: '',
    scope: null,
    addHandler: null,
    removeHandler: null,
    invalidateHandler: null,
    disabled: 0,
    constructor: function(config) {
        Ext.apply(this, config);
    },
    bind: function(target) {
        var me = this;
        me.target = target;
        target.on('beforedestroy', me.disable, me);
        me.onContainerAdd(target);
    },
    unbind: function() {
        var me = this,
            target = me.target;
        if (target) {
            target.un('beforedestroy', me.disable, me);
        }
        me.items = null;
    },
    disable: function() {
        ++this.disabled;
    },
    enable: function() {
        if (this.disabled > 0) {
            --this.disabled;
        }
    },
    handleAdd: function(ct, comp) {
        if (!this.disabled) {
            if (comp.is(this.selector)) {
                this.onItemAdd(comp.ownerCt, comp);
            }
            if (comp.isQueryable) {
                this.onContainerAdd(comp);
            }
        }
    },
    onItemAdd: function(ct, comp) {
        var me = this,
            items = me.items,
            handler = me.addHandler;
        if (!me.disabled) {
            if (handler) {
                handler.call(me.scope || comp, comp);
            }
            if (items) {
                items.add(comp);
            }
        }
    },
    onItemRemove: function(ct, comp) {
        var me = this,
            items = me.items,
            handler = me.removeHandler;
        if (!me.disabled) {
            if (handler) {
                handler.call(me.scope || comp, comp);
            }
            if (items) {
                items.remove(comp);
            }
        }
    },
    onContainerAdd: function(ct, preventChildren) {
        var me = this,
            items, len,
            handleAdd = me.handleAdd,
            handleRemove = me.handleRemove,
            i, comp;
        if (ct.isContainer) {
            ct.on('add', handleAdd, me);
            ct.on('dockedadd', handleAdd, me);
            ct.on('remove', handleRemove, me);
            ct.on('dockedremove', handleRemove, me);
        }
        
        
        if (preventChildren !== true) {
            items = ct.query(me.selector);
            for (i = 0 , len = items.length; i < len; ++i) {
                comp = items[i];
                me.onItemAdd(comp.ownerCt, comp);
            }
        }
        items = ct.query('>container');
        for (i = 0 , len = items.length; i < len; ++i) {
            me.onContainerAdd(items[i], true);
        }
    },
    handleRemove: function(ct, comp) {
        var me = this;
        
        
        if (!me.disabled) {
            if (comp.is(me.selector)) {
                me.onItemRemove(ct, comp);
            }
            if (comp.isQueryable) {
                me.onContainerRemove(ct, comp);
            }
        }
    },
    onContainerRemove: function(ct, comp) {
        var me = this,
            items, i, len, item;
        
        
        if (!comp.isDestroyed && !comp.destroying && comp.isContainer) {
            me.removeCtListeners(comp);
            items = comp.query(me.selector);
            for (i = 0 , len = items.length; i < len; ++i) {
                item = items[i];
                me.onItemRemove(item.ownerCt, item);
            }
            items = comp.query('container');
            for (i = 0 , len = items.length; i < len; ++i) {
                me.removeCtListeners(items[i]);
            }
        } else {
            
            me.invalidateItems(true);
        }
    },
    removeCtListeners: function(comp) {
        var me = this;
        comp.un('add', me.handleAdd, me);
        comp.un('dockedadd', me.handleAdd, me);
        comp.un('remove', me.handleRemove, me);
        comp.un('dockedremove', me.handleRemove, me);
    },
    getItems: function() {
        var me = this,
            items = me.items;
        if (!items) {
            items = me.items = new Ext.util.MixedCollection();
            items.addAll(me.target.query(me.selector));
        }
        return items;
    },
    invalidateItems: function(triggerHandler) {
        var me = this,
            handler = me.invalidateHandler;
        if (triggerHandler && handler) {
            handler.call(me.scope || me, me);
        }
        me.items = null;
    }
});


Ext.define('Ext.plugin.Responsive', {
    extend: 'Ext.mixin.Responsive',
    alias: 'plugin.responsive',
    pluginId: 'responsive',
    isPlugin: true,
    constructor: function(config) {
        var me = this,
            cmp = config.cmp,
            c = Ext.apply({
                responsiveConfig: cmp.responsiveConfig,
                responsiveFormulas: cmp.responsiveFormulas
            }, config);
        delete c.cmp;
        me.cmp = cmp;
        if (!cmp) {
            Ext.Error.raise('Responsive plugin must be constructed by Component');
        }
        me.initConfig(c);
        
        if (me.transformed) {
            cmp.setConfig(me.transformed);
            me.transformed = null;
        }
    },
    init: Ext.emptyFn,
    privates: {
        transformInstanceConfig: function(config) {
            
            
            var transformed = this.callParent([
                    config
                ]);
            
            if (transformed.ptype) {
                transformed = Ext.apply({}, transformed);
                delete transformed.ptype;
            }
            this.transformed = transformed;
            var ret = Ext.apply({}, config);
            delete ret.ptype;
            delete ret.responsiveConfig;
            
            delete ret.responsiveFormulas;
            return ret;
        },
        updateResponsiveState: function() {
            var config = this.getResponsiveState();
            
            this.cmp.setConfig(config);
        }
    }
});


Ext.define('Ext.plugin.Viewport', {
    extend: 'Ext.plugin.Responsive',
    alias: 'plugin.viewport',
    
    setCmp: function(cmp) {
        this.cmp = cmp;
        if (cmp && !cmp.isViewport) {
            this.decorate(cmp);
            if (cmp.renderConfigs) {
                cmp.flushRenderConfigs();
            }
            cmp.setupViewport();
        }
    },
    statics: {
        decorate: function(target) {
            Ext.applyIf(target.prototype || target, {
                ariaRole: 'application',
                viewportCls: Ext.baseCSSPrefix + 'viewport'
            });
            Ext.override(target, {
                isViewport: true,
                preserveElOnDestroy: true,
                initComponent: function() {
                    this.callParent();
                    this.setupViewport();
                },
                handleViewportResize: function() {
                    var me = this,
                        Element = Ext.dom.Element,
                        width = Element.getViewportWidth(),
                        height = Element.getViewportHeight();
                    if (width != me.width || height != me.height) {
                        me.setSize(width, height);
                    }
                },
                setupViewport: function() {
                    var me = this,
                        el = me.el = Ext.getBody();
                    
                    Ext.getScrollbarSize();
                    
                    me.width = me.height = undefined;
                    Ext.fly(document.documentElement).addCls(me.viewportCls);
                    el.setHeight = el.setWidth = Ext.emptyFn;
                    el.dom.scroll = 'no';
                    me.allowDomMove = false;
                    me.renderTo = me.el;
                    if (Ext.supports.Touch) {
                        me.initMeta();
                    }
                },
                afterLayout: function(layout) {
                    if (Ext.supports.Touch) {
                        document.body.scrollTop = 0;
                    }
                    this.callParent([
                        layout
                    ]);
                },
                onRender: function() {
                    var me = this;
                    me.callParent(arguments);
                    
                    
                    me.width = Ext.Element.getViewportWidth();
                    me.height = Ext.Element.getViewportHeight();
                    
                    if (Ext.supports.TouchEvents) {
                        me.mon(Ext.getDoc(), {
                            touchmove: function(e) {
                                e.preventDefault();
                            },
                            translate: false,
                            delegated: false
                        });
                    }
                },
                initInheritedState: function(inheritedState, inheritedStateInner) {
                    var me = this,
                        root = Ext.rootInheritedState;
                    if (inheritedState !== root) {
                        
                        
                        me.initInheritedState(me.inheritedState = root, me.inheritedStateInner = Ext.Object.chain(root));
                    } else {
                        me.callParent([
                            inheritedState,
                            inheritedStateInner
                        ]);
                    }
                },
                beforeDestroy: function() {
                    var me = this,
                        root = Ext.rootInheritedState,
                        key;
                    
                    
                    
                    for (key in root) {
                        if (key !== 'rtl') {
                            delete root[key];
                        }
                    }
                    me.removeUIFromElement();
                    me.el.removeCls(me.baseCls);
                    Ext.fly(document.body.parentNode).removeCls(me.viewportCls);
                    me.callParent();
                },
                addMeta: function(name, content) {
                    var meta = document.createElement('meta');
                    meta.setAttribute('name', name);
                    meta.setAttribute('content', content);
                    Ext.getHead().appendChild(meta);
                },
                initMeta: function() {
                    var me = this,
                        maxScale = me.maxUserScale || 1;
                    me.addMeta('viewport', 'width=device-width, initial-scale=1, maximum-scale=' + maxScale + ', user-scalable=' + (maxScale !== 1 ? 'yes' : 'no'));
                    me.addMeta('apple-mobile-web-app-capable', 'yes');
                },
                privates: {
                    
                    applyTargetCls: function(targetCls) {
                        this.el.addCls(targetCls);
                    },
                    
                    disableTabbing: function() {
                        var el = this.el;
                        if (el) {
                            el.saveChildrenTabbableState();
                        }
                    },
                    enableTabbing: function() {
                        var el = this.el;
                        if (el) {
                            el.restoreChildrenTabbableState();
                        }
                    },
                    getOverflowEl: function() {
                        return Ext.get(document.documentElement);
                    }
                }
            });
        }
    },
    privates: {
        updateResponsiveState: function() {
            
            
            
            
            
            this.cmp.handleViewportResize();
            this.callParent();
        }
    }
}, function(Viewport) {
    Viewport.prototype.decorate = Viewport.decorate;
});


Ext.define('Ext.container.Viewport', {
    extend: 'Ext.container.Container',
    requires: [
        'Ext.plugin.Viewport'
    ],
    mixins: [
        'Ext.mixin.Responsive'
    ],
    alias: 'widget.viewport',
    alternateClassName: 'Ext.Viewport',
    
    
    
    
    
    
    
    
    
    privates: {
        updateResponsiveState: function() {
            
            
            
            
            
            this.handleViewportResize();
            this.mixins.responsive.updateResponsiveState.call(this);
        }
    }
}, function() {
    Ext.plugin.Viewport.decorate(this);
});


Ext.define('Ext.layout.container.Anchor', {
    
    alias: 'layout.anchor',
    extend: 'Ext.layout.container.Auto',
    alternateClassName: 'Ext.layout.AnchorLayout',
    
    type: 'anchor',
    
    
    defaultAnchor: '100%',
    parseAnchorRE: /^(r|right|b|bottom)$/i,
    manageOverflow: true,
    
    
    
    
    setsItemSize: true,
    beginLayoutCycle: function(ownerContext) {
        var me = this,
            dimensions = 0,
            anchorSpec, childContext, childItems, i, length, target;
        me.callParent(arguments);
        childItems = ownerContext.childItems;
        
        length = childItems.length;
        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            anchorSpec = childContext.target.anchorSpec;
            if (anchorSpec) {
                if (childContext.widthModel.calculated && anchorSpec.right) {
                    dimensions |= 1;
                }
                if (childContext.heightModel.calculated && anchorSpec.bottom) {
                    dimensions |= 2;
                }
                if (dimensions == 3) {
                    
                    break;
                }
            }
        }
        ownerContext.anchorDimensions = dimensions;
        me.sanityCheck(ownerContext);
    },
    calculateItems: function(ownerContext, containerSize) {
        var me = this,
            childItems = ownerContext.childItems,
            length = childItems.length,
            gotHeight = containerSize.gotHeight,
            gotWidth = containerSize.gotWidth,
            ownerHeight = containerSize.height,
            ownerWidth = containerSize.width,
            knownDimensions = (gotWidth ? 1 : 0) | (gotHeight ? 2 : 0),
            anchorDimensions = ownerContext.anchorDimensions,
            anchorSpec, childContext, childMargins, height, i, width;
        if (!anchorDimensions) {
            return true;
        }
        for (i = 0; i < length; i++) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();
            anchorSpec = childContext.target.anchorSpec;
            
            
            
            
            if (gotWidth && childContext.widthModel.calculated) {
                width = anchorSpec.right(ownerWidth) - childMargins.width;
                width = me.adjustWidthAnchor(width, childContext);
                childContext.setWidth(width);
            }
            
            if (gotHeight && childContext.heightModel.calculated) {
                height = anchorSpec.bottom(ownerHeight) - childMargins.height;
                height = me.adjustHeightAnchor(height, childContext);
                childContext.setHeight(height);
            }
        }
        
        return (knownDimensions & anchorDimensions) === anchorDimensions;
    },
    sanityCheck: function(ownerContext) {
        var shrinkWrapWidth = ownerContext.widthModel.shrinkWrap,
            shrinkWrapHeight = ownerContext.heightModel.shrinkWrap,
            children = ownerContext.childItems,
            anchorSpec, comp, childContext, i, length;
        for (i = 0 , length = children.length; i < length; ++i) {
            childContext = children[i];
            comp = childContext.target;
            anchorSpec = comp.anchorSpec;
            if (anchorSpec) {
                if (childContext.widthModel.calculated && anchorSpec.right) {
                    if (shrinkWrapWidth) {
                        Ext.log({
                            level: 'warn',
                            msg: 'Right anchor on ' + comp.id + ' in shrinkWrap width container'
                        });
                    }
                }
                if (childContext.heightModel.calculated && anchorSpec.bottom) {
                    if (shrinkWrapHeight) {
                        Ext.log({
                            level: 'warn',
                            msg: 'Bottom anchor on ' + comp.id + ' in shrinkWrap height container'
                        });
                    }
                }
            }
        }
    },
    
    anchorFactory: {
        offset: function(delta) {
            return function(v) {
                return v + delta;
            };
        },
        ratio: function(ratio) {
            return function(v) {
                return Math.floor(v * ratio);
            };
        },
        standard: function(diff) {
            return function(v) {
                return v - diff;
            };
        }
    },
    parseAnchor: function(a, start, cstart) {
        if (a && a != 'none') {
            var factory = this.anchorFactory,
                delta;
            if (this.parseAnchorRE.test(a)) {
                return factory.standard(cstart - start);
            }
            if (a.indexOf('%') != -1) {
                return factory.ratio(parseFloat(a.replace('%', '')) * 0.01);
            }
            delta = parseInt(a, 10);
            if (!isNaN(delta)) {
                return factory.offset(delta);
            }
        }
        return null;
    },
    
    adjustWidthAnchor: function(value, childContext) {
        return value;
    },
    
    adjustHeightAnchor: function(value, childContext) {
        return value;
    },
    configureItem: function(item) {
        var me = this,
            owner = me.owner,
            anchor = item.anchor,
            anchorsArray, anchorWidth, anchorHeight;
        me.callParent(arguments);
        if (!item.anchor && item.items && !Ext.isNumber(item.width)) {
            item.anchor = anchor = me.defaultAnchor;
        }
        
        if (owner.anchorSize) {
            if (typeof owner.anchorSize == 'number') {
                anchorWidth = owner.anchorSize;
            } else {
                anchorWidth = owner.anchorSize.width;
                anchorHeight = owner.anchorSize.height;
            }
        } else {
            anchorWidth = owner.initialConfig.width;
            anchorHeight = owner.initialConfig.height;
        }
        if (anchor) {
            
            anchorsArray = anchor.split(' ');
            item.anchorSpec = {
                right: me.parseAnchor(anchorsArray[0], item.initialConfig.width, anchorWidth),
                bottom: me.parseAnchor(anchorsArray[1], item.initialConfig.height, anchorHeight)
            };
        }
    },
    sizePolicy: {
        $: {
            readsWidth: 1,
            readsHeight: 1,
            setsWidth: 0,
            setsHeight: 0
        },
        b: {
            readsWidth: 1,
            readsHeight: 0,
            setsWidth: 0,
            setsHeight: 1
        },
        r: {
            $: {
                readsWidth: 0,
                readsHeight: 1,
                setsWidth: 1,
                setsHeight: 0
            },
            b: {
                readsWidth: 0,
                readsHeight: 0,
                setsWidth: 1,
                setsHeight: 1
            }
        }
    },
    getItemSizePolicy: function(item) {
        var anchorSpec = item.anchorSpec,
            key = '$',
            policy = this.sizePolicy,
            sizeModel;
        if (anchorSpec) {
            sizeModel = this.owner.getSizeModel();
            if (anchorSpec.right && !sizeModel.width.shrinkWrap) {
                policy = policy.r;
            }
            if (anchorSpec.bottom && !sizeModel.height.shrinkWrap) {
                key = 'b';
            }
        }
        return policy[key];
    }
});


Ext.define('Ext.dashboard.Panel', {
    extend: 'Ext.panel.Panel',
    xtype: 'dashboard-panel',
    cls: Ext.baseCSSPrefix + 'dashboard-panel',
    anchor: '100%',
    layout: 'fit',
    frame: true,
    closable: true,
    collapsible: true,
    animCollapse: true,
    titleCollapse: true,
    stateful: true,
    draggable: {
        moveOnDrag: false
    },
    animateClose: true,
    loadMask: true,
    loadMessage: 'Loading...',
    minHeight: 90,
    resizable: true,
    resizeHandles: 's',
    
    
    doClose: function() {
        var me = this;
        if (me.animateClose) {
            if (!me.closing) {
                me.closing = true;
                me.el.animate({
                    opacity: 0,
                    callback: me.finishClose,
                    scope: me
                });
            }
        } else {
            me.finishClose();
        }
    },
    finishClose: function() {
        var me = this,
            closeAction = me.closeAction;
        me.closing = false;
        me.fireEvent('close', me);
        
        
        Ext.suspendLayouts();
        me[closeAction]();
        Ext.resumeLayouts(true);
        if (closeAction === 'hide') {
            
            me.el.setOpacity(1);
        }
    },
    afterRender: function() {
        this.callParent();
        if (this.loading) {
            this.onViewBeforeLoad();
        }
    },
    getLoadMask: function() {
        var me = this,
            loadMask = me.rendered && me.loadMask,
            config;
        if (loadMask && !loadMask.isComponent) {
            config = {
                target: me
            };
            if (loadMask === true) {
                loadMask = config;
            } else {
                Ext.apply(config, loadMask);
            }
            me.loadMask = loadMask = Ext.ComponentManager.create(config, 'loadmask');
        }
        return loadMask || null;
    },
    onAdd: function(view) {
        this.callParent(arguments);
        view.on({
            beforeload: 'onViewBeforeLoad',
            load: 'onViewLoaded',
            scope: this
        });
    },
    onViewBeforeLoad: function() {
        this.loading = true;
        var loadMask = this.getLoadMask();
        if (loadMask) {
            loadMask.show();
        }
    },
    onViewLoaded: function() {
        this.loading = false;
        var loadMask = this.getLoadMask();
        if (loadMask) {
            loadMask.hide();
        }
        var view = this.items.getAt(0);
        if (view.getTitle) {
            var title = view.getTitle();
            if (title) {
                this.setTitle(title);
            }
        }
    },
    
    setBox: function(box) {
        
        
        this.setSize(box.width, box.height);
    },
    
    getState: function() {
        var me = this,
            state = me.callParent() || {};
        if (!state.collapsed) {
            me.addPropertyToState(state, 'height', me.rendered ? me.getHeight() : me.height || me.minHeight || 100);
        }
        return state;
    }
});


Ext.define('Ext.dashboard.Column', {
    extend: 'Ext.container.Container',
    xtype: 'dashboard-column',
    requires: [
        'Ext.layout.container.Anchor',
        'Ext.dashboard.Panel'
    ],
    layout: 'anchor',
    isDashboardColumn: true,
    defaultType: 'dashboard-panel',
    cls: Ext.baseCSSPrefix + 'dashboard-column',
    synthetic: true,
    
    onRemove: function(dashPanel, isDestroying) {
        var me = this,
            ownerCt = me.ownerCt,
            remainingSiblings, numRemaining,
            totalColumnWidth = 0,
            i;
        
        if (ownerCt && me.items.getCount() === 0) {
            
            remainingSiblings = Ext.Array.filter(ownerCt.query('>' + me.xtype + '[rowIndex=' + me.rowIndex + ']'), function(c) {
                return c !== me;
            });
            numRemaining = remainingSiblings.length;
            
            if (!me.destroying && !me.isDestroyed) {
                ownerCt.remove(me);
                
                if (numRemaining === 1) {
                    remainingSiblings[0].columnWidth = 1;
                } else 
                
                {
                    for (i = 0; i < numRemaining; i++) {
                        totalColumnWidth += remainingSiblings[i].columnWidth || 0;
                    }
                    for (i = 0; i < numRemaining; i++) {
                        remainingSiblings[i].columnWidth = remainingSiblings[i].columnWidth / totalColumnWidth;
                    }
                }
                
                
                if (isDestroying) {
                    ownerCt.updateLayout();
                }
            }
        }
    }
});


Ext.define('Ext.layout.container.Column', {
    extend: 'Ext.layout.container.Auto',
    alias: [
        'layout.column'
    ],
    alternateClassName: 'Ext.layout.ColumnLayout',
    type: 'column',
    itemCls: Ext.baseCSSPrefix + 'column',
    targetCls: Ext.baseCSSPrefix + 'column-layout-ct',
    
    columnWidthSizePolicy: {
        readsWidth: 0,
        readsHeight: 1,
        setsWidth: 1,
        setsHeight: 0
    },
    createsInnerCt: true,
    manageOverflow: true,
    
    
    setsItemSize: true,
    needsItemSize: true,
    isItemShrinkWrap: function(ownerContext) {
        return true;
    },
    getItemSizePolicy: function(item, ownerSizeModel) {
        if (item.columnWidth) {
            if (!ownerSizeModel) {
                ownerSizeModel = this.owner.getSizeModel();
            }
            if (!ownerSizeModel.width.shrinkWrap) {
                return this.columnWidthSizePolicy;
            }
        }
        return this.autoSizePolicy;
    },
    calculateItems: function(ownerContext, containerSize) {
        var me = this,
            targetContext = ownerContext.targetContext,
            items = ownerContext.childItems,
            len = items.length,
            contentWidth = 0,
            gotWidth = containerSize.gotWidth,
            blocked, availableWidth, i, itemContext, itemMarginWidth, itemWidth;
        
        if (gotWidth === false) {
            
            
            targetContext.domBlock(me, 'width');
            blocked = true;
        } else if (gotWidth) {
            availableWidth = containerSize.width;
        } else {
            
            
            return true;
        }
        
        
        for (i = 0; i < len; ++i) {
            itemContext = items[i];
            
            
            
            itemMarginWidth = itemContext.getMarginInfo().width;
            if (!itemContext.widthModel.calculated) {
                itemWidth = itemContext.getProp('width');
                if (typeof itemWidth !== 'number') {
                    itemContext.block(me, 'width');
                    blocked = true;
                }
                contentWidth += itemWidth + itemMarginWidth;
            }
        }
        if (!blocked) {
            availableWidth = (availableWidth < contentWidth) ? 0 : availableWidth - contentWidth;
            for (i = 0; i < len; ++i) {
                itemContext = items[i];
                if (itemContext.widthModel.calculated) {
                    itemMarginWidth = itemContext.marginInfo.width;
                    
                    itemWidth = itemContext.target.columnWidth;
                    itemWidth = Math.floor(itemWidth * availableWidth) - itemMarginWidth;
                    itemWidth = itemContext.setWidth(itemWidth);
                    
                    contentWidth += itemWidth + itemMarginWidth;
                }
            }
            ownerContext.setContentWidth(contentWidth + ownerContext.paddingContext.getPaddingInfo().width);
        }
        
        return !blocked;
    }
});


Ext.define('Ext.resizer.SplitterTracker', {
    extend: 'Ext.dd.DragTracker',
    requires: [
        'Ext.util.Region'
    ],
    enabled: true,
    overlayCls: Ext.baseCSSPrefix + 'resizable-overlay',
    createDragOverlay: function() {
        var overlay,
            El = Ext.dom.Element;
        overlay = this.overlay = Ext.getBody().createChild({
            role: 'presentation',
            cls: this.overlayCls,
            html: '&#160;'
        });
        overlay.unselectable();
        overlay.setSize(El.getDocumentWidth(), El.getDocumentHeight());
        overlay.show();
    },
    getPrevCmp: function() {
        var splitter = this.getSplitter();
        return splitter.previousSibling(':not([hidden])');
    },
    getNextCmp: function() {
        var splitter = this.getSplitter();
        return splitter.nextSibling(':not([hidden])');
    },
    
    
    onBeforeStart: function(e) {
        var me = this,
            prevCmp = me.getPrevCmp(),
            nextCmp = me.getNextCmp(),
            collapseEl = me.getSplitter().collapseEl,
            target = e.getTarget(),
            box;
        if (!prevCmp || !nextCmp) {
            return false;
        }
        if (collapseEl && target === collapseEl.dom) {
            return false;
        }
        
        if (nextCmp.collapsed || prevCmp.collapsed) {
            return false;
        }
        
        me.prevBox = prevCmp.getEl().getBox();
        me.nextBox = nextCmp.getEl().getBox();
        me.constrainTo = box = me.calculateConstrainRegion();
        if (!box) {
            return false;
        }
        return box;
    },
    
    onStart: function(e) {
        var splitter = this.getSplitter();
        this.createDragOverlay();
        splitter.addCls(splitter.baseCls + '-active');
    },
    
    calculateConstrainRegion: function() {
        var me = this,
            splitter = me.getSplitter(),
            splitWidth = splitter.getWidth(),
            defaultMin = splitter.defaultSplitMin,
            orient = splitter.orientation,
            prevBox = me.prevBox,
            prevCmp = me.getPrevCmp(),
            nextBox = me.nextBox,
            nextCmp = me.getNextCmp(),
            
            
            
            prevConstrainRegion, nextConstrainRegion, constrainOptions;
        
        if (orient === 'vertical') {
            constrainOptions = {
                prevCmp: prevCmp,
                nextCmp: nextCmp,
                prevBox: prevBox,
                nextBox: nextBox,
                defaultMin: defaultMin,
                splitWidth: splitWidth
            };
            
            
            prevConstrainRegion = new Ext.util.Region(prevBox.y,me.getVertPrevConstrainRight(constrainOptions),prevBox.bottom,me.getVertPrevConstrainLeft(constrainOptions));
            
            nextConstrainRegion = new Ext.util.Region(nextBox.y,me.getVertNextConstrainRight(constrainOptions),nextBox.bottom,me.getVertNextConstrainLeft(constrainOptions));
        } else {
            
            prevConstrainRegion = new Ext.util.Region(prevBox.y + (prevCmp.minHeight || defaultMin),prevBox.right,
            
            (prevCmp.maxHeight ? prevBox.y + prevCmp.maxHeight : nextBox.bottom - (nextCmp.minHeight || defaultMin)) + splitWidth,prevBox.x);
            
            nextConstrainRegion = new Ext.util.Region(
            
            (nextCmp.maxHeight ? nextBox.bottom - nextCmp.maxHeight : prevBox.y + (prevCmp.minHeight || defaultMin)) - splitWidth,nextBox.right,nextBox.bottom - (nextCmp.minHeight || defaultMin),nextBox.x);
        }
        
        return prevConstrainRegion.intersect(nextConstrainRegion);
    },
    
    performResize: function(e, offset) {
        var me = this,
            splitter = me.getSplitter(),
            orient = splitter.orientation,
            prevCmp = me.getPrevCmp(),
            nextCmp = me.getNextCmp(),
            owner = splitter.ownerCt,
            flexedSiblings = owner.query('>[flex]'),
            len = flexedSiblings.length,
            vertical = orient === 'vertical',
            i = 0,
            dimension = vertical ? 'width' : 'height',
            totalFlex = 0,
            item, size;
        
        for (; i < len; i++) {
            item = flexedSiblings[i];
            size = vertical ? item.getWidth() : item.getHeight();
            totalFlex += size;
            item.flex = size;
        }
        offset = vertical ? offset[0] : offset[1];
        if (prevCmp) {
            size = me.prevBox[dimension] + offset;
            if (prevCmp.flex) {
                prevCmp.flex = size;
            } else {
                prevCmp[dimension] = size;
            }
        }
        if (nextCmp) {
            size = me.nextBox[dimension] - offset;
            if (nextCmp.flex) {
                nextCmp.flex = size;
            } else {
                nextCmp[dimension] = size;
            }
        }
        owner.updateLayout();
    },
    
    
    
    endDrag: function() {
        var me = this;
        if (me.overlay) {
            me.overlay.destroy();
            delete me.overlay;
        }
        me.callParent(arguments);
    },
    
    
    onEnd: function(e) {
        var me = this,
            splitter = me.getSplitter();
        splitter.removeCls(splitter.baseCls + '-active');
        me.performResize(e, me.getResizeOffset());
    },
    
    
    onDrag: function(e) {
        var me = this,
            offset = me.getOffset('dragTarget'),
            splitter = me.getSplitter(),
            splitEl = splitter.getEl(),
            orient = splitter.orientation;
        if (orient === "vertical") {
            splitEl.setX(me.startRegion.left + offset[0]);
        } else {
            splitEl.setY(me.startRegion.top + offset[1]);
        }
    },
    getSplitter: function() {
        return this.splitter;
    },
    getVertPrevConstrainRight: function(o) {
        
        
        return (o.prevCmp.maxWidth ? o.prevBox.x + o.prevCmp.maxWidth : o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin)) + o.splitWidth;
    },
    getVertPrevConstrainLeft: function(o) {
        return o.prevBox.x + (o.prevCmp.minWidth || o.defaultMin);
    },
    getVertNextConstrainRight: function(o) {
        return o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin);
    },
    getVertNextConstrainLeft: function(o) {
        
        
        return (o.nextCmp.maxWidth ? o.nextBox.right - o.nextCmp.maxWidth : o.prevBox.x + (o.prevBox.minWidth || o.defaultMin)) - o.splitWidth;
    },
    getResizeOffset: function() {
        return this.getOffset('dragTarget');
    }
});


Ext.define('Ext.layout.container.ColumnSplitterTracker', {
    extend: 'Ext.resizer.SplitterTracker',
    
    onStart: function(e) {
        Ext.apply(this.getSplitter().el.dom.style, {
            top: 0,
            left: 0
        });
        this.callParent(arguments);
    },
    endDrag: function() {
        var me = this;
        me.callParent(arguments);
        
        me.getSplitter().el.dom.style.left = 0;
    },
    performResize: function(e, offset) {
        var me = this,
            prevCmp = me.getPrevCmp(),
            nextCmp = me.getNextCmp(),
            splitter = me.getSplitter(),
            owner = splitter.ownerCt,
            delta = offset[0],
            prevWidth, nextWidth, ratio;
        if (prevCmp && nextCmp) {
            prevCmp.width = prevWidth = me.prevBox.width + delta;
            nextCmp.width = nextWidth = me.nextBox.width - delta;
            ratio = (prevCmp.columnWidth + nextCmp.columnWidth) / (prevWidth + nextWidth);
            prevCmp.columnWidth = prevWidth * ratio;
            nextCmp.columnWidth = nextWidth * ratio;
        }
        owner.updateLayout();
    }
});


Ext.define('Ext.layout.container.ColumnSplitter', {
    extend: 'Ext.resizer.Splitter',
    xtype: 'columnsplitter',
    requires: [
        'Ext.layout.container.ColumnSplitterTracker'
    ],
    isSplitter: true,
    synthetic: true,
    cls: Ext.baseCSSPrefix + 'splitter-vertical',
    orientation: 'vertical',
    collapseDirection: 'left',
    trackerClass: 'Ext.layout.container.ColumnSplitterTracker',
    width: 7,
    height: 1,
    getTrackerConfig: function() {
        var tracker = this.callParent();
        tracker.xclass = this.trackerClass;
        return tracker;
    }
});


Ext.define('Ext.layout.container.Dashboard', {
    extend: 'Ext.layout.container.Column',
    alias: 'layout.dashboard',
    requires: [
        'Ext.layout.container.ColumnSplitter'
    ],
    type: 'dashboard',
    firstColumnCls: Ext.baseCSSPrefix + 'dashboard-column-first',
    lastColumnCls: Ext.baseCSSPrefix + 'dashboard-column-last',
    
    getSplitterConfig: function() {
        return {
            xtype: 'columnsplitter'
        };
    },
    
    getColumns: function(items) {
        var array = Ext.Array;
        return array.filter(array.from(items), function(item) {
            return item.target && item.target.isSplitter !== true;
        });
    },
    beginLayout: function(ownerContext) {
        var me = this;
        me.callParent([
            ownerContext
        ]);
        
        
        var childItems = ownerContext.childItems,
            rows = (ownerContext.rows = []),
            length = childItems.length,
            totalWidth = 2,
            columnTargets = 0,
            lastRow = 0,
            maxColumns = me.owner.getMaxColumns(),
            child, i, prev, row, splitter, target, width;
        for (i = 0; i < length; ++i) {
            target = (child = childItems[i]).target;
            splitter = target && target.isSplitter;
            columnTargets += (splitter ? 0 : 1);
            width = splitter ? 0 : target.columnWidth || 1;
            if (totalWidth + width > 1 || (maxColumns && (columnTargets > maxColumns))) {
                if (prev) {
                    
                    
                    
                    prev.orphan = 1;
                    prev.el.setHeight(0);
                }
                totalWidth = 0;
                columnTargets = 1;
                if (rows.length) {
                    
                    
                    
                    lastRow = rows.length - 1;
                    me.syncFirstLast(me.getColumns(rows[lastRow].items));
                }
                rows.push(row = {
                    index: rows.length,
                    items: [],
                    maxHeight: 0
                });
            }
            totalWidth += width;
            row.items.push(child);
            child.row = row;
            target.rowIndex = row.index;
            if (splitter) {
                child.el.setHeight(1);
            }
            prev = child;
        }
        if (rows.length) {
            me.syncFirstLast(me.getColumns(rows[rows.length - 1].items));
        }
    },
    beforeLayoutCycle: function(ownerContext) {
        var me = this,
            items = me.owner.items;
        
        
        if (me.splitterGen !== items.generation) {
            me.syncSplitters();
            
            me.splitterGen = items.generation;
        }
        me.callParent(arguments);
    },
    finishedLayout: function(ownerContext) {
        var items = ownerContext.childItems,
            len = items.length,
            box, child, i, target, row;
        this.callParent([
            ownerContext
        ]);
        for (i = 0; i < len; i += 2) {
            target = (child = items[i]).target;
            box = target.lastBox;
            row = child.row;
            row.maxHeight = Math.max(row.maxHeight, box.height);
            
            
            target.width = box.width;
        }
        for (i = 1; i < len; i += 2) {
            target = (child = items[i]).target;
            if (!child.orphan) {
                target.el.setHeight(child.row.maxHeight);
            }
        }
    },
    
    syncSplitters: function() {
        var me = this,
            owner = me.owner,
            items = owner.items.items,
            index = items.length,
            ok = true,
            shouldBeSplitter = false,
            item, splitter;
        
        while (index-- > 0) {
            item = items[index];
            if (shouldBeSplitter) {
                if (item.isSplitter) {
                    shouldBeSplitter = false;
                } else {
                    
                    
                    
                    if (ok) {
                        ok = false;
                        owner.suspendLayouts();
                    }
                    splitter = owner.add(index + 1, me.getSplitterConfig());
                }
            } else {
                if (item.isSplitter) {
                    
                    
                    
                    if (ok) {
                        ok = false;
                        owner.suspendLayouts();
                    }
                    owner.remove(item);
                } else {
                    shouldBeSplitter = true;
                }
            }
        }
        
        
        while (items.length && (item = items[0]).isSplitter) {
            if (ok) {
                ok = false;
                owner.suspendLayouts();
            }
            owner.remove(item);
        }
        if (!ok) {
            owner.resumeLayouts();
        }
    },
    syncFirstLast: function(items) {
        var me = this,
            firstCls = me.firstColumnCls,
            lastCls = me.lastColumnCls,
            len,
            firstAndLast = [
                firstCls,
                lastCls
            ],
            i, item, last;
        items = Ext.Array.from(items);
        len = items.length;
        for (i = 0; i < len; ++i) {
            item = items[i].target;
            last = (i === len - 1);
            if (!i) {
                
                if (last) {
                    item.addCls(firstAndLast);
                } else {
                    item.addCls(firstCls);
                    item.removeCls(lastCls);
                }
            } else if (last) {
                item.addCls(lastCls);
                item.removeCls(firstCls);
            } else {
                item.removeCls(firstAndLast);
            }
        }
    }
});


Ext.define('Ext.dashboard.DropZone', {
    extend: 'Ext.dd.DropTarget',
    ddScrollConfig: {
        vthresh: 50,
        hthresh: -1,
        animate: true,
        increment: 200
    },
    containerScroll: true,
    
    
    overClass: Ext.baseCSSPrefix + 'dashboard-dd-over',
    constructor: function(dashboard, cfg) {
        this.dashboard = dashboard;
        dashboard.body.ddScrollConfig = this.ddScrollConfig;
        this.callParent([
            dashboard.body,
            cfg
        ]);
    },
    getOverEvent: function(dd, e, data) {
        var dashboard = this.dashboard,
            items = dashboard.items.items,
            bodyBox = dashboard.body.getBox(),
            count = items.length,
            xy = e.getXY(),
            x = xy[0] - bodyBox.x,
            y = xy[1] - bodyBox.y,
            over = {
                columnIndex: 0,
                column: null,
                dashboard: dashboard,
                above: null,
                extensible: false,
                beforeAfter: 0,
                data: data,
                panel: data.panel,
                rawEvent: e,
                source: dd,
                status: this.dropAllowed
            },
            t, ht, i, k, item, w, childCount, childItems, childItem;
        for (i = 0; i < count; i += 2) {
            item = items[i];
            w = item.lastBox.width;
            if (items[i + 1]) {
                w += items[i + 1].lastBox.width;
            }
            
            if (e.within(item.el)) {
                over.columnIndex = i;
                over.column = item;
                over.extensible = this.isRowExtensible(item.rowIndex);
                t = Math.min(80, w * 0.2);
                over.beforeAfter = t = (over.extensible && ((x < t) ? -1 : ((x > w - t) ? 1 : 0)));
                if (!t || !over.extensible) {
                    childItems = item.items.items;
                    
                    
                    
                    for (k = 0 , childCount = childItems.length; k < childCount; ++k) {
                        childItem = childItems[k];
                        ht = childItem.el.getHeight();
                        
                        if (y < ht / 2) {
                            
                            
                            
                            over.above = childItem;
                            break;
                        }
                        
                        y -= ht;
                    }
                }
                break;
            }
            x -= w;
        }
        return over;
    },
    notifyOver: function(dd, e, data) {
        var me = this,
            dashboard = me.dashboard,
            over = me.getOverEvent(dd, e, data),
            colEl = over.column && over.column.el,
            proxy = dd.proxy,
            proxyProxy,
            aboveItem = over.above,
            colWidth,
            width = 0,
            padding,
            hasListeners = dashboard.hasListeners;
        data.lastOver = over;
        if ((!hasListeners.validatedrop || dashboard.fireEvent('validatedrop', over) !== false) && (!hasListeners.beforedragover || dashboard.fireEvent('beforedragover', over) !== false)) {
            proxyProxy = dd.panelProxy.getProxy();
            
            proxy.getProxy().setWidth('auto');
            if (colEl) {
                width = colWidth = colEl.getWidth();
                
                if (over.beforeAfter) {
                    dd.panelProxy.moveProxy(colEl.dom, colEl.dom.firstChild);
                    width = colWidth / 2;
                    proxyProxy.setWidth(width);
                } else {
                    if (aboveItem) {
                        dd.panelProxy.moveProxy(aboveItem.el.dom.parentNode, aboveItem.el.dom);
                    } else {
                        dd.panelProxy.moveProxy(colEl.dom, null);
                    }
                    proxyProxy.setWidth('auto');
                }
                if (width) {}
                
                proxyProxy.setStyle({
                    'float': 'none',
                    'clear': 'none',
                    'margin-left': (over.beforeAfter > 0) ? (colWidth - width - colEl.getPadding('lr')) + 'px' : '',
                    'margin-top': '7px'
                });
            } else {
                padding = dashboard.body.getPadding('lr');
                proxyProxy.setStyle({
                    'float': 'left',
                    'clear': 'left',
                    'margin': '0 7px 0 7px'
                });
                proxyProxy.setWidth(dashboard.body.getWidth() - padding);
                
                dd.panelProxy.moveProxy(dashboard.body.dom.firstChild.firstChild, null);
            }
            this.scrollPos = dashboard.body.getScroll();
            if (hasListeners.dragover) {
                dashboard.fireEvent('dragover', over);
            }
        }
        return over.status;
    },
    isRowExtensible: function(rowIndex) {
        var me = this,
            dashboard = me.dashboard,
            maxColumns = dashboard.getMaxColumns() || 1;
        return Ext.Array.from(dashboard.query('>dashboard-column[rowIndex=' + rowIndex + ']')).length < maxColumns;
    },
    notifyDrop: function(dd, e, data) {
        this.callParent(arguments);
        var dashboard = this.dashboard,
            over = data.lastOver,
            panel = over.panel,
            fromCt = panel.ownerCt,
            toCt = over.column,
            side = toCt ? over.beforeAfter : 1,
            currentIndex = fromCt.items.indexOf(panel),
            newIndex = toCt ? (over.above ? toCt.items.indexOf(over.above) : toCt.items.getCount()) : 0,
            colIndex, newCol,
            hasListeners = dashboard.hasListeners;
        
        
        
        
        
        
        if (fromCt === toCt) {
            if (fromCt.items.getCount() === 1) {
                
                return;
            }
            if (!side) {
                if (currentIndex < newIndex) {
                    --newIndex;
                }
                
                if (currentIndex === newIndex) {
                    
                    return;
                }
            }
        }
        if ((hasListeners.validatedrop && dashboard.fireEvent('validatedrop', over) === false) || (hasListeners.beforedrop && dashboard.fireEvent('beforedrop', over) === false)) {
            return;
        }
        Ext.suspendLayouts();
        panel.isMoving = true;
        if (side) {
            colIndex = dashboard.items.indexOf(toCt);
            
            if (colIndex < 0) {
                colIndex = dashboard.items.getCount();
            } else if (side > 0) {
                ++colIndex;
            }
            newCol = dashboard.createColumn();
            if (toCt) {
                newCol.columnWidth = toCt.columnWidth = toCt.columnWidth / 2;
                delete toCt.width;
            } else {
                newCol.columnWidth = 1;
            }
            
            toCt = dashboard.insert(colIndex, newCol);
            newIndex = 0;
        }
        
        panel.el.dom.style.display = '';
        toCt.insert(newIndex, panel);
        panel.isMoving = false;
        toCt.updateLayout();
        Ext.resumeLayouts(true);
        if (hasListeners.drop) {
            dashboard.fireEvent('drop', over);
        }
    }
});


Ext.define('Ext.dashboard.Part', {
    mixins: [
        'Ext.mixin.Factoryable',
        'Ext.mixin.Identifiable'
    ],
    requires: [
        'Ext.util.ObjectTemplate'
    ],
    alias: 'part.part',
    factoryConfig: {
        type: 'part'
    },
    isPart: true,
    
    _lastId: 0,
    config: {
        id: null,
        
        dashboard: null,
        
        viewTemplate: {
            collapsed: '{collapsed}',
            columnIndex: '{columnIndex}',
            id: '{id}',
            title: '{title}',
            height: '{height}'
        }
    },
    viewTemplateOptions: {
        excludeProperties: {
            bind: 1
        }
    },
    valueRe: /^[{][a-z]*[}]$/i,
    constructor: function(config) {
        this.initConfig(config);
    },
    applyViewTemplate: function(template) {
        if (!Ext.isObject(template)) {
            Ext.Error.raise('The viewTemplate for ' + this.$className + ' is not an Object');
        }
        return Ext.util.ObjectTemplate.create(template, this.viewTemplateOptions);
    },
    
    displayForm: function(instance, currentConfig, callback, scope) {
        callback.call(scope || this, {});
    },
    
    createView: function(config) {
        var me = this,
            template = me.getViewTemplate(),
            ret = template.apply(config);
        ret.dashboard = me.getDashboard();
        ret.part = me;
        ret._partConfig = config;
        return ret;
    }
});


Ext.define('Ext.dashboard.Dashboard', {
    extend: 'Ext.panel.Panel',
    xtype: 'dashboard',
    requires: [
        'Ext.layout.container.Dashboard',
        'Ext.dashboard.DropZone',
        'Ext.dashboard.Column',
        'Ext.dashboard.Part'
    ],
    isDashboard: true,
    cls: Ext.baseCSSPrefix + 'dashboard',
    bodyCls: Ext.baseCSSPrefix + 'dashboard-body',
    defaultType: 'dashboard-column',
    scrollable: true,
    layout: null,
    stateful: false,
    idSeed: 1,
    config: {
        
        parts: null
    },
    renderConfig: {
        
        maxColumns: 4
    },
    
    
    
    
    
    initComponent: function() {
        var me = this;
        if (!me.layout) {
            me.layout = {
                type: 'dashboard'
            };
        }
        me.callParent();
    },
    applyParts: function(parts, collection) {
        if (!collection) {
            collection = new Ext.util.Collection({
                decoder: Ext.Factory.part
            });
        }
        var id, part;
        for (id in parts) {
            part = parts[id];
            if (Ext.isString(part)) {
                part = {
                    type: part
                };
            }
            part.id = id;
            part.dashboard = this;
            collection.add(part);
        }
        return collection;
    },
    
    getPart: function(type) {
        var parts = this.getParts();
        return parts.getByKey(type);
    },
    addNew: function(type, columnIndex, beforeAfter) {
        var me = this,
            part = me.getPart(type);
        part.displayForm(null, null, function(config) {
            config.type = type;
            me.addView(config, columnIndex, beforeAfter);
        });
    },
    addView: function(instance, columnIndex, beforeAfter) {
        var me = this,
            items = me.items,
            count = items.getCount(),
            index = columnIndex || 0,
            view = instance.id ? instance : me.createView(instance),
            columnWidths = me.columnWidths,
            column;
        if (!count) {
            
            column = me.add(0, me.createColumn({
                columnWidth: 1
            }));
            count = 1;
            columnWidths = me.columnWidths = [];
        }
        if (index >= count) {
            index = count - 1;
            beforeAfter = 1;
        }
        
        if (!beforeAfter) {
            column = items.getAt(index);
            if (column) {
                return column.add(view);
            }
        }
        if (beforeAfter > 0) {
            
            ++index;
        }
        column = me.createColumn();
        if (columnWidths) {
            column.columnWidth = columnWidths[index] || (columnWidths[index] = 1);
        }
        if (!column.items) {
            column.items = [];
        }
        column.items.push(view);
        column = me.add(index, column);
        return column.items.first();
    },
    createColumn: function(config) {
        var cycle = this.cycleLayout;
        return Ext.apply({
            items: [],
            bubbleEvents: [
                'add',
                'remove',
                'childmove',
                'resize'
            ],
            listeners: {
                remove: this.onRemoveItem,
                expand: cycle,
                collapse: cycle,
                scope: this
            }
        }, config);
    },
    createView: function(config) {
        var me = this,
            type = config.type,
            part = me.getPart(type),
            view = part.createView(config);
        if (!view.id) {
            view.id = me.id + '_' + type + (me.idSeed++);
        }
        view.bubbleEvents = Ext.Array.from(view.bubbleEvents).concat([
            'expand',
            'collapse'
        ]);
        view.stateful = me.stateful;
        return view;
    },
    initEvents: function() {
        this.callParent();
        this.dd = new Ext.dashboard.DropZone(this,this.dropConfig);
    },
    
    cycleLayout: function() {
        this.updateLayout();
    },
    beforeDestroy: function() {
        if (this.dd) {
            Ext.destroy(this.dd);
        }
        this.callParent();
    },
    
    
    applyState: function(state) {
        delete state.items;
        var me = this;
        me.callParent([
            state
        ]);
        var columnWidths = state.columnWidths,
            items = me.items.items,
            length = items.length,
            i, n;
        
        if (columnWidths) {
            n = columnWidths.length;
            me.columnWidths = [];
            for (i = 0; i < length; ++i) {
                me.columnWidths.push(items[i].columnWidth = (i < n) ? columnWidths[i] : (1 / length));
            }
        }
    },
    getState: function() {
        var me = this,
            columnWidths = [],
            items = me.items.items,
            state = me.callParent() || {},
            length = items.length,
            i, item;
        for (i = 0; i < length; ++i) {
            if (!(item = items[i]).isSplitter) {
                columnWidths.push(item.columnWidth);
            }
        }
        state.columnWidths = columnWidths;
        state.idSeed = me.idSeed;
        state.items = me.serializeItems();
        me.columnWidths = columnWidths;
        return state;
    },
    initItems: function() {
        var me = this,
            defaultContent = me.defaultContent,
            state;
        if (me.stateful) {
            state = Ext.state.Manager.get(me.getStateId());
            defaultContent = (state && state.items) || defaultContent;
        }
        if (!me.items && defaultContent) {
            me.items = me.deserializeItems(defaultContent);
        }
        me.callParent();
    },
    deserializeItems: function(serialized) {
        var me = this,
            length = serialized.length,
            columns = [],
            columnWidths = me.columnWidths,
            maxColumns = me.getMaxColumns(),
            column, columnIndex, columnWidth, i, item, partConfig;
        for (i = 0; i < length; ++i) {
            partConfig = serialized[i];
            columnIndex = Math.min(partConfig.columnIndex || 0, maxColumns - 1);
            delete partConfig.columnIndex;
            if (!(column = columns[columnIndex])) {
                columns[columnIndex] = column = me.createColumn();
                columnWidth = columnWidths && columnWidths[columnIndex];
                if (columnWidth) {
                    column.columnWidth = columnWidth;
                }
            }
            item = me.createView(partConfig);
            column.items.push(item);
        }
        for (i = 0 , length = columns.length; i < length; ++i) {
            column = columns[i];
            if (!column.columnWidth) {
                column.columnWidth = 1 / length;
            }
        }
        return columns;
    },
    serializeItem: function(item) {
        return Ext.apply({
            type: item.part.id,
            id: item.id,
            columnIndex: item.columnIndex
        }, item._partConfig);
    },
    serializeItems: function() {
        var me = this,
            items = me.items.items,
            length = items.length,
            ret = [],
            columnIndex = 0,
            child, childItems, i, item, j, k;
        for (i = 0; i < length; ++i) {
            item = items[i];
            if (!item.isSplitter) {
                childItems = item.items.items;
                for (j = 0 , k = childItems.length; j < k; ++j) {
                    child = childItems[j];
                    child.columnIndex = columnIndex;
                    ret.push(me.serializeItem(child));
                }
                ++columnIndex;
            }
        }
        return ret;
    },
    onRemoveItem: function(column, item) {
        
        
        if (item.stateful && !item.isMoving) {
            Ext.state.Manager.clear(item.getStateId());
        }
    }
});


Ext.define('Ext.dom.Layer', {
    extend: 'Ext.Element',
    alternateClassName: 'Ext.Layer',
    
    
    
    
    
    
    
    
    
    
    isLayer: true,
    
    constructor: function(config, existingEl) {
        config = config || {};
        var me = this,
            dh = Ext.DomHelper,
            cp = config.parentEl,
            pel = cp ? Ext.getDom(cp) : document.body,
            hm = config.hideMode,
            cls = Ext.baseCSSPrefix + (config.fixed ? 'fixed-layer' : 'layer'),
            dom, id, element, shadowConfig;
        if (existingEl) {
            dom = Ext.getDom(existingEl);
            if (!dom.parentNode) {
                pel.appendChild(dom);
            }
        }
        if (!dom) {
            dom = dh.append(pel, config.dh || {
                tag: 'div',
                cls: cls
            });
        }
        
        if (config.id) {
            dom.id = config.id;
        }
        id = dom.id;
        if (id) {
            element = Ext.cache[id];
            if (element) {
                
                
                
                delete Ext.cache[id];
                element.dom = null;
            }
        }
        this.callParent([
            dom
        ]);
        if (existingEl) {
            me.addCls(cls);
        }
        if (config.preventSync) {
            me.preventSync = true;
        }
        if (config.cls) {
            me.addCls(config.cls);
        }
        me.constrain = config.constrain !== false;
        
        
        
        if (hm) {
            me.setVisibilityMode(Ext.Element[hm.toUpperCase()]);
        } else if (config.useDisplay) {
            me.setVisibilityMode(Ext.Element.DISPLAY);
        } else {
            me.setVisibilityMode(Ext.Element.VISIBILITY);
        }
        if (config.shadow) {
            me.shadowOffset = config.shadowOffset || 4;
            shadowConfig = {
                offset: me.shadowOffset,
                fixed: config.fixed
            };
            if (config.shadow !== true) {
                shadowConfig.mode = config.shadow;
            }
            me.enableShadow(shadowConfig);
        } else {
            me.shadowOffset = 0;
        }
        if (config.shim) {
            me.enableShim({
                fixed: config.fixed
            });
        }
        
        
        
        if (config.hidden === true) {
            me.hide();
        } else if (config.hidden === false) {
            me.show();
        }
    }
});



Ext.define('Ext.event.publisher.MouseEnterLeave', {
    extend: 'Ext.event.publisher.Dom',
    type: 'mouseEnterLeave'
}, function(MouseEnterLeave) {
    var eventMap = {
            mouseover: 'mouseenter',
            mouseout: 'mouseleave'
        };
    if (!Ext.supports.MouseEnterLeave) {
        MouseEnterLeave.override({
            handledDomEvents: [
                'mouseover',
                'mouseout'
            ],
            handledEvents: [
                'mouseenter',
                'mouseleave'
            ],
            doDelegatedEvent: function(e) {
                var target, relatedTarget, id, el, type, event;
                
                e = this.callParent([
                    e
                ]);
                target = e.getTarget();
                relatedTarget = e.getRelatedTarget();
                if (relatedTarget && Ext.fly(target).contains(relatedTarget)) {
                    return;
                }
                id = target.id;
                if (id) {
                    el = Ext.cache[id];
                    if (el) {
                        type = eventMap[e.type];
                        e = e.chain({
                            type: type
                        });
                        if (el.hasListeners[type]) {
                            event = el.events[type];
                            if (event) {
                                
                                
                                
                                event = event.directs;
                                if (event) {
                                    e.setCurrentTarget(el.dom);
                                    event.fire(e, e.target);
                                }
                            }
                        }
                    }
                }
            }
        });
    }
    MouseEnterLeave.instance = new MouseEnterLeave();
});


Ext.define('Ext.flash.Component', {
    extend: 'Ext.Component',
    alternateClassName: 'Ext.FlashComponent',
    alias: 'widget.flash',
    
    flashVersion: '9.0.115',
    
    backgroundColor: '#ffffff',
    
    wmode: 'opaque',
    
    
    
    
    
    swfWidth: '100%',
    
    swfHeight: '100%',
    
    expressInstall: false,
    
    
    renderTpl: [
        '<div id="{swfId}" role="presentation"></div>'
    ],
    
    
    initComponent: function() {
        if (!('swfobject' in window)) {
            Ext.Error.raise('The SWFObject library is not loaded. Ext.flash.Component requires SWFObject version 2.2 or later: http://code.google.com/p/swfobject/');
        }
        if (!this.url) {
            Ext.Error.raise('The "url" config is required for Ext.flash.Component');
        }
        this.callParent();
    },
    beforeRender: function() {
        this.callParent();
        Ext.applyIf(this.renderData, {
            swfId: this.getSwfId()
        });
    },
    afterRender: function() {
        var me = this,
            flashParams = Ext.apply({}, me.flashParams),
            flashVars = Ext.apply({}, me.flashVars);
        me.callParent();
        flashParams = Ext.apply({
            allowScriptAccess: 'always',
            bgcolor: me.backgroundColor,
            wmode: me.wmode
        }, flashParams);
        flashVars = Ext.apply({
            allowedDomain: document.location.hostname
        }, flashVars);
        new swfobject.embedSWF(me.url,me.getSwfId(),me.swfWidth,me.swfHeight,me.flashVersion,me.expressInstall ? me.statics.EXPRESS_INSTALL_URL : undefined,flashVars,flashParams,me.flashAttributes,me.swfCallback.bind(me));
    },
    
    swfCallback: function(e) {
        var me = this;
        if (e.success) {
            me.swf = Ext.get(e.ref);
            me.onSuccess();
            me.fireEvent('success', me);
        } else {
            me.onFailure();
            me.fireEvent('failure', me);
        }
    },
    
    getSwfId: function() {
        return this.swfId || (this.swfId = "extswf" + this.getAutoId());
    },
    onSuccess: function() {
        
        
        this.swf.setStyle('visibility', 'inherit');
    },
    onFailure: Ext.emptyFn,
    beforeDestroy: function() {
        var me = this,
            swf = me.swf;
        if (swf) {
            swfobject.removeSWF(me.getSwfId());
            Ext.destroy(swf);
            delete me.swf;
        }
        me.callParent();
    },
    statics: {
        
        EXPRESS_INSTALL_URL: 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf'
    }
});


Ext.define('Ext.form.action.Action', {
    alternateClassName: 'Ext.form.Action',
    
    
    
    
    
    
    
    
    
    
    
    
    
    submitEmptyText: true,
    
    
    
    
    
    constructor: function(config) {
        if (config) {
            Ext.apply(this, config);
        }
        
        var params = config.params;
        if (Ext.isString(params)) {
            this.params = Ext.Object.fromQueryString(params);
        }
    },
    
    run: Ext.emptyFn,
    
    
    
    onFailure: function(response) {
        var form = this.form,
            formActive = form && !form.destroying && !form.isDestroyed;
        this.response = response;
        this.failureType = Ext.form.action.Action.CONNECT_FAILURE;
        if (formActive) {
            form.afterAction(this, false);
        }
    },
    
    processResponse: function(response) {
        this.response = response;
        if (!response.responseText && !response.responseXML) {
            return true;
        }
        return (this.result = this.handleResponse(response));
    },
    
    getUrl: function() {
        return this.url || this.form.url;
    },
    
    getMethod: function() {
        return (this.method || this.form.method || 'POST').toUpperCase();
    },
    
    getParams: function() {
        return Ext.apply({}, this.params, this.form.baseParams);
    },
    
    createCallback: function() {
        var me = this,
            undef,
            form = me.form;
        return {
            success: me.onSuccess,
            failure: me.onFailure,
            scope: me,
            timeout: (this.timeout * 1000) || (form.timeout * 1000),
            upload: form.fileUpload ? me.onSuccess : undef
        };
    },
    statics: {
        
        CLIENT_INVALID: 'client',
        
        SERVER_INVALID: 'server',
        
        CONNECT_FAILURE: 'connect',
        
        LOAD_FAILURE: 'load'
    }
});


Ext.define('Ext.form.action.Load', {
    extend: 'Ext.form.action.Action',
    requires: [
        'Ext.data.Connection'
    ],
    alternateClassName: 'Ext.form.Action.Load',
    alias: 'formaction.load',
    type: 'load',
    
    run: function() {
        Ext.Ajax.request(Ext.apply(this.createCallback(), {
            method: this.getMethod(),
            url: this.getUrl(),
            headers: this.headers,
            params: this.getParams()
        }));
    },
    
    onSuccess: function(response) {
        var result = this.processResponse(response),
            form = this.form,
            formActive = form && !form.destroying && !form.isDestroyed;
        if (result === true || !result.success || !result.data) {
            this.failureType = Ext.form.action.Action.LOAD_FAILURE;
            if (formActive) {
                form.afterAction(this, false);
            }
            return;
        }
        if (formActive) {
            form.clearInvalid();
            form.setValues(result.data);
            form.afterAction(this, true);
        }
    },
    
    handleResponse: function(response) {
        var reader = this.form.reader,
            rs, data;
        if (reader) {
            rs = reader.read(response);
            data = rs.records && rs.records[0] ? rs.records[0].data : null;
            return {
                success: rs.success,
                data: data
            };
        }
        return Ext.decode(response.responseText);
    }
});


Ext.define('Ext.form.action.Submit', {
    extend: 'Ext.form.action.Action',
    alternateClassName: 'Ext.form.Action.Submit',
    alias: 'formaction.submit',
    type: 'submit',
    
    
    run: function() {
        var me = this,
            form = me.form;
        if (me.clientValidation === false || form.isValid()) {
            me.doSubmit();
        } else {
            
            me.failureType = Ext.form.action.Action.CLIENT_INVALID;
            form.afterAction(me, false);
        }
    },
    
    doSubmit: function() {
        var me = this,
            ajaxOptions = Ext.apply(me.createCallback(), {
                url: me.getUrl(),
                method: me.getMethod(),
                headers: me.headers
            }),
            form = me.form,
            jsonSubmit = me.jsonSubmit || form.jsonSubmit,
            paramsProp = jsonSubmit ? 'jsonData' : 'params',
            formInfo;
        
        
        if (form.hasUpload()) {
            formInfo = me.buildForm();
            ajaxOptions.form = formInfo.formEl;
            ajaxOptions.isUpload = true;
        } else {
            ajaxOptions[paramsProp] = me.getParams(jsonSubmit);
        }
        Ext.Ajax.request(ajaxOptions);
        if (formInfo) {
            me.cleanup(formInfo);
        }
    },
    cleanup: function(formInfo) {
        var formEl = formInfo.formEl,
            uploadEls = formInfo.uploadEls,
            uploadFields = formInfo.uploadFields,
            len = uploadFields.length,
            i, field;
        for (i = 0; i < len; ++i) {
            field = uploadFields[i];
            if (!field.clearOnSubmit) {
                field.restoreInput(uploadEls[i]);
            }
        }
        if (formEl) {
            Ext.removeNode(formEl);
        }
    },
    
    getParams: function(useModelValues) {
        var falseVal = false,
            configParams = this.callParent(),
            fieldParams = this.form.getValues(falseVal, falseVal, this.submitEmptyText !== falseVal, useModelValues, 
            true);
        return Ext.apply({}, fieldParams, configParams);
    },
    
    buildForm: function() {
        var me = this,
            fieldsSpec = [],
            formSpec, formEl,
            basicForm = me.form,
            params = me.getParams(),
            uploadFields = [],
            uploadEls = [],
            fields = basicForm.getFields().items,
            i,
            len = fields.length,
            field, key, value, v, vLen, el;
        for (i = 0; i < len; ++i) {
            field = fields[i];
            if (field.isFileUpload()) {
                uploadFields.push(field);
            }
        }
        for (key in params) {
            if (params.hasOwnProperty(key)) {
                value = params[key];
                if (Ext.isArray(value)) {
                    vLen = value.length;
                    for (v = 0; v < vLen; v++) {
                        fieldsSpec.push(me.getFieldConfig(key, value[v]));
                    }
                } else {
                    fieldsSpec.push(me.getFieldConfig(key, value));
                }
            }
        }
        formSpec = {
            tag: 'form',
            role: 'presentation',
            action: me.getUrl(),
            method: me.getMethod(),
            target: me.target ? (Ext.isString(me.target) ? me.target : Ext.fly(me.target).dom.name) : '_self',
            style: 'display:none',
            cn: fieldsSpec
        };
        if (!formSpec.target) {
            Ext.Error.raise('Invalid form target.');
        }
        
        if (uploadFields.length) {
            formSpec.encoding = formSpec.enctype = 'multipart/form-data';
        }
        
        formEl = Ext.DomHelper.append(Ext.getBody(), formSpec);
        
        
        
        len = uploadFields.length;
        for (i = 0; i < len; ++i) {
            el = uploadFields[i].extractFileInput();
            formEl.appendChild(el);
            uploadEls.push(el);
        }
        return {
            formEl: formEl,
            uploadFields: uploadFields,
            uploadEls: uploadEls
        };
    },
    getFieldConfig: function(name, value) {
        return {
            tag: 'input',
            type: 'hidden',
            name: name,
            value: Ext.String.htmlEncode(value)
        };
    },
    
    onSuccess: function(response) {
        var form = this.form,
            formActive = form && !form.destroying && !form.isDestroyed,
            success = true,
            result = this.processResponse(response);
        if (result !== true && !result.success) {
            if (result.errors && formActive) {
                form.markInvalid(result.errors);
            }
            this.failureType = Ext.form.action.Action.SERVER_INVALID;
            success = false;
        }
        if (formActive) {
            form.afterAction(this, success);
        }
    },
    
    handleResponse: function(response) {
        var form = this.form,
            errorReader = form.errorReader,
            rs, errors, i, len, records, result;
        if (errorReader) {
            rs = errorReader.read(response);
            records = rs.records;
            errors = [];
            if (records) {
                for (i = 0 , len = records.length; i < len; i++) {
                    errors[i] = records[i].data;
                }
            }
            if (errors.length < 1) {
                errors = null;
            }
            result = {
                success: rs.success,
                errors: errors
            };
        } else {
            try {
                result = Ext.decode(response.responseText);
            } catch (e) {
                result = {
                    success: false,
                    errors: []
                };
            }
        }
        return result;
    }
});


Ext.define('Ext.form.field.TextArea', {
    extend: 'Ext.form.field.Text',
    alias: [
        'widget.textareafield',
        'widget.textarea'
    ],
    alternateClassName: 'Ext.form.TextArea',
    requires: [
        'Ext.XTemplate',
        'Ext.util.DelayedTask'
    ],
    
    
    
    
    
    
    
    
    
    
    fieldSubTpl: [
        '<textarea id="{id}" role="{role}" {inputAttrTpl}',
        '<tpl if="name"> name="{name}"</tpl>',
        '<tpl if="placeholder"> placeholder="{placeholder}"</tpl>',
        '<tpl if="maxLength !== undefined"> maxlength="{maxLength}"</tpl>',
        '<tpl if="readOnly"> readonly="readonly"</tpl>',
        '<tpl if="disabled"> disabled="disabled"</tpl>',
        '<tpl if="tabIdx != null"> tabindex="{tabIdx}"</tpl>',
        ' class="{fieldCls} {typeCls} {typeCls}-{ui} {inputCls}" ',
        '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
        ' autocomplete="off">\n',
        '<tpl if="value">{[Ext.util.Format.htmlEncode(values.value)]}</tpl>',
        '</textarea>',
        {
            disableFormats: true
        }
    ],
    
    growMin: 60,
    
    growMax: 1000,
    
    growAppend: '\n-',
    
    enterIsSpecial: false,
    
    preventScrollbars: false,
    returnRe: /\r/g,
    inputCls: Ext.baseCSSPrefix + 'form-textarea',
    extraFieldBodyCls: Ext.baseCSSPrefix + 'form-textarea-body',
    constructor: function(config) {
        this.callParent([
            config
        ]);
        if (this.cols) {
            Ext.log.warn('Ext.form.field.TextArea "cols" config was removed in Ext 5.0. Please specify a "width" or use a layout instead.');
        }
        if (this.rows) {
            Ext.log.warn('Ext.form.field.TextArea "rows" config was removed in Ext 5.0. Please specify a "height" or use a layout instead.');
        }
    },
    
    getSubTplData: function(fieldData) {
        var me = this,
            fieldStyle = me.getFieldStyle(),
            ret = me.callParent(arguments);
        if (me.grow) {
            if (me.preventScrollbars) {
                ret.fieldStyle = (fieldStyle || '') + ';overflow:hidden;height:' + me.growMin + 'px';
            }
        }
        return ret;
    },
    afterRender: function() {
        var me = this;
        me.callParent(arguments);
        me.needsMaxCheck = me.enforceMaxLength && me.maxLength !== Number.MAX_VALUE && !Ext.supports.TextAreaMaxLength;
        if (me.needsMaxCheck) {
            me.inputEl.on('paste', me.onPaste, me);
        }
    },
    
    
    
    
    
    
    transformRawValue: function(value) {
        return this.stripReturns(value);
    },
    getValue: function() {
        return this.stripReturns(this.callParent());
    },
    valueToRaw: function(value) {
        value = this.stripReturns(value);
        return this.callParent([
            value
        ]);
    },
    stripReturns: function(value) {
        if (value && typeof value === 'string') {
            value = value.replace(this.returnRe, '');
        }
        return value;
    },
    onPaste: function() {
        var me = this;
        if (!me.pasteTask) {
            me.pasteTask = new Ext.util.DelayedTask(me.pasteCheck,me);
        }
        
        me.pasteTask.delay(1);
    },
    pasteCheck: function() {
        var me = this,
            value = me.getValue(),
            max = me.maxLength;
        if (value.length > max) {
            value = value.substr(0, max);
            me.setValue(value);
        }
    },
    
    fireKey: function(e) {
        var me = this,
            key = e.getKey(),
            value;
        if (e.isSpecialKey() && (me.enterIsSpecial || (key !== e.ENTER || e.hasModifier()))) {
            me.fireEvent('specialkey', me, e);
        }
        if (me.needsMaxCheck && key !== e.BACKSPACE && key !== e.DELETE && !e.isNavKeyPress() && !me.isCutCopyPasteSelectAll(e, key)) {
            value = me.getValue();
            if (value.length >= me.maxLength) {
                e.stopEvent();
            }
        }
    },
    isCutCopyPasteSelectAll: function(e, key) {
        if (e.ctrlKey) {
            return key === e.A || key === e.C || key === e.V || key === e.X;
        }
        return false;
    },
    
    autoSize: function() {
        var me = this,
            inputEl, inputHeight, height, curWidth, value;
        if (me.grow && me.rendered && me.getSizeModel().height.auto) {
            inputEl = me.inputEl;
            
            curWidth = inputEl.getWidth(true);
            value = Ext.util.Format.htmlEncode(inputEl.dom.value) || '&#160;';
            value += me.growAppend;
            
            value = value.replace(/\n/g, '<br/>');
            height = Ext.util.TextMetrics.measure(inputEl, value, curWidth).height + inputEl.getPadding('tb') + 
            
            me.inputWrap.getBorderWidth('tb') + me.triggerWrap.getBorderWidth('tb');
            height = Math.min(Math.max(height, me.growMin), me.growMax);
            me.bodyEl.setHeight(height);
            me.updateLayout();
            me.fireEvent('autosize', me, height);
        }
    },
    beforeDestroy: function() {
        var task = this.pasteTask;
        if (task) {
            task.cancel();
            this.pasteTask = null;
        }
        this.callParent();
    }
});



Ext.define('Ext.window.MessageBox', {
    extend: 'Ext.window.Window',
    requires: [
        'Ext.toolbar.Toolbar',
        'Ext.form.field.Text',
        'Ext.form.field.TextArea',
        'Ext.button.Button',
        'Ext.layout.container.Anchor',
        'Ext.layout.container.HBox',
        'Ext.ProgressBar'
    ],
    alias: 'widget.messagebox',
    
    OK: 1,
    
    YES: 2,
    
    NO: 4,
    
    CANCEL: 8,
    
    OKCANCEL: 9,
    
    YESNO: 6,
    
    YESNOCANCEL: 14,
    
    INFO: Ext.baseCSSPrefix + 'message-box-info',
    
    WARNING: Ext.baseCSSPrefix + 'message-box-warning',
    
    QUESTION: Ext.baseCSSPrefix + 'message-box-question',
    
    ERROR: Ext.baseCSSPrefix + 'message-box-error',
    
    hideMode: 'offsets',
    closeAction: 'hide',
    resizable: false,
    scrollable: true,
    title: '&#160;',
    defaultMinWidth: 250,
    defaultMaxWidth: 600,
    defaultMinHeight: 110,
    defaultMaxHeight: 500,
    
    
    minWidth: null,
    maxWidth: null,
    minHeight: null,
    maxHeight: null,
    constrain: true,
    cls: [
        Ext.baseCSSPrefix + 'message-box',
        Ext.baseCSSPrefix + 'hidden-offsets'
    ],
    layout: {
        type: 'vbox',
        align: 'stretch'
    },
    
    shrinkWrapDock: true,
    
    defaultTextHeight: 75,
    
    minProgressWidth: 250,
    
    minPromptWidth: 250,
    
    
    buttonText: {
        ok: 'OK',
        yes: 'Yes',
        no: 'No',
        cancel: 'Cancel'
    },
    
    buttonIds: [
        'ok',
        'yes',
        'no',
        'cancel'
    ],
    
    titleText: {
        confirm: 'Confirm',
        prompt: 'Prompt',
        wait: 'Loading...',
        alert: 'Attention'
    },
    
    baseIconCls: Ext.baseCSSPrefix + 'message-box-icon',
    ariaRole: 'alertdialog',
    makeButton: function(btnIdx) {
        var btnId = this.buttonIds[btnIdx];
        return new Ext.button.Button({
            handler: this.btnCallback,
            itemId: btnId,
            scope: this,
            text: this.buttonText[btnId],
            minWidth: 75
        });
    },
    btnCallback: function(btn, event) {
        var me = this,
            value, field;
        
        
        
        
        if (event && event.type === 'keydown' && !event.isSpecialKey()) {
            event.getTarget(null, null, true).on({
                keyup: function(e) {
                    me.btnCallback(btn, e);
                },
                single: true
            });
            return;
        }
        if (me.cfg.prompt || me.cfg.multiline) {
            if (me.cfg.multiline) {
                field = me.textArea;
            } else {
                field = me.textField;
            }
            value = field.getValue();
            field.reset();
        }
        
        me.hide();
        me.userCallback(btn.itemId, value, me.cfg);
    },
    hide: function() {
        var me = this,
            cls = me.cfg ? me.cfg.cls : '';
        me.progressBar.reset();
        if (cls) {
            me.removeCls(cls);
        }
        me.callParent(arguments);
    },
    constructor: function(cfg) {
        var me = this;
        me.callParent(arguments);
        
        
        me.minWidth = me.defaultMinWidth = (me.minWidth || me.defaultMinWidth);
        me.maxWidth = me.defaultMaxWidth = (me.maxWidth || me.defaultMaxWidth);
        me.minHeight = me.defaultMinHeight = (me.minHeight || me.defaultMinHeight);
        me.maxHeight = me.defaultMaxHeight = (me.maxHeight || me.defaultMaxHeight);
    },
    initComponent: function(cfg) {
        var me = this,
            baseId = me.id,
            i, button;
        
        me.title = me.title || '&#160;';
        me.iconCls = me.iconCls || '';
        me.topContainer = new Ext.container.Container({
            layout: 'hbox',
            padding: 10,
            style: {
                overflow: 'hidden'
            },
            items: [
                me.iconComponent = new Ext.Component({
                    cls: me.baseIconCls
                }),
                me.promptContainer = new Ext.container.Container({
                    flex: 1,
                    layout: {
                        type: 'vbox',
                        align: 'stretch'
                    },
                    items: [
                        me.msg = new Ext.Component({
                            id: baseId + '-msg',
                            cls: me.baseCls + '-text'
                        }),
                        me.textField = new Ext.form.field.Text({
                            id: baseId + '-textfield',
                            enableKeyEvents: true,
                            listeners: {
                                keydown: me.onPromptKey,
                                scope: me
                            }
                        }),
                        me.textArea = new Ext.form.field.TextArea({
                            id: baseId + '-textarea',
                            height: 75
                        })
                    ]
                })
            ]
        });
        me.progressBar = new Ext.ProgressBar({
            id: baseId + '-progressbar',
            margin: '0 10 10 10'
        });
        me.items = [
            me.topContainer,
            me.progressBar
        ];
        
        me.msgButtons = [];
        for (i = 0; i < 4; i++) {
            button = me.makeButton(i);
            me.msgButtons[button.itemId] = button;
            me.msgButtons.push(button);
        }
        me.bottomTb = new Ext.toolbar.Toolbar({
            id: baseId + '-toolbar',
            ui: 'footer',
            dock: 'bottom',
            layout: {
                pack: 'center'
            },
            items: [
                me.msgButtons[0],
                me.msgButtons[1],
                me.msgButtons[2],
                me.msgButtons[3]
            ]
        });
        me.dockedItems = [
            me.bottomTb
        ];
        me.on('close', me.onClose, me);
        me.callParent();
    },
    onClose: function() {
        var btn = this.header.child('[type=close]');
        
        btn.itemId = 'cancel';
        this.btnCallback(btn);
        delete btn.itemId;
    },
    onPromptKey: function(textField, e) {
        var me = this;
        if (e.keyCode === e.RETURN || e.keyCode === 10) {
            if (me.msgButtons.ok.isVisible()) {
                me.msgButtons.ok.handler.call(me, me.msgButtons.ok);
            } else if (me.msgButtons.yes.isVisible()) {
                me.msgButtons.yes.handler.call(me, me.msgButtons.yes);
            }
        }
    },
    reconfigure: function(cfg) {
        var me = this,
            buttons = 0,
            hideToolbar = true,
            oldButtonText = me.buttonText,
            resizer = me.resizer,
            header = me.header,
            headerCfg = header && !header.isHeader,
            message = cfg && (cfg.message || cfg.msg),
            resizeTracker, width, height, i, textArea, textField, msg, progressBar, msgButtons;
        
        me.updateButtonText();
        me.cfg = cfg = cfg || {};
        if (cfg.width) {
            width = cfg.width;
        }
        if (cfg.height) {
            height = cfg.height;
        }
        me.minWidth = cfg.minWidth || me.defaultMinWidth;
        me.maxWidth = cfg.maxWidth || me.defaultMaxWidth;
        me.minHeight = cfg.minHeight || me.defaultMinHeight;
        me.maxHeight = cfg.maxHeight || me.defaultMaxHeight;
        if (resizer) {
            resizeTracker = resizer.resizeTracker;
            resizer.minWidth = resizeTracker.minWidth = me.minWidth;
            resizer.maxWidth = resizeTracker.maxWidth = me.maxWidth;
            resizer.minHeight = resizeTracker.minHeight = me.minHeight;
            resizer.maxHeight = resizeTracker.maxHeight = me.maxHeight;
        }
        
        delete me.defaultFocus;
        if (cfg.defaultFocus) {
            me.defaultFocus = cfg.defaultFocus;
        }
        
        me.animateTarget = cfg.animateTarget || undefined;
        
        me.modal = cfg.modal !== false;
        
        
        
        
        
        me.setTitle(cfg.title || (headerCfg && header.title) || me.title);
        me.setIconCls(cfg.iconCls || (headerCfg && header.iconCls) || me.iconCls);
        
        if (Ext.isObject(cfg.buttons)) {
            me.buttonText = cfg.buttons;
            buttons = 0;
        } else {
            me.buttonText = cfg.buttonText || me.buttonText;
            buttons = Ext.isNumber(cfg.buttons) ? cfg.buttons : 0;
        }
        
        
        buttons = buttons | me.updateButtonText();
        
        me.buttonText = oldButtonText;
        
        
        Ext.suspendLayouts();
        me.width = me.height = null;
        if (width || height) {
            if (width) {
                me.setWidth(width);
            }
            if (height) {
                me.setHeight(height);
            }
        }
        me.hidden = false;
        if (!me.rendered) {
            me.render(Ext.getBody());
        }
        
        me.closable = cfg.closable !== false && !cfg.wait;
        
        header = me.header;
        if (header) {
            header.child('[type=close]').setVisible(me.closable);
            
            if (!cfg.title && !me.closable && !cfg.iconCls) {
                header.hide();
            } else {
                header.show();
            }
        }
        
        me.liveDrag = !cfg.proxyDrag;
        
        me.userCallback = Ext.Function.bindCallback(cfg.callback || cfg.fn || Ext.emptyFn, cfg.scope || Ext.global);
        
        me.setIcon(cfg.icon);
        
        msg = me.msg;
        if (message) {
            msg.setHtml(message);
            msg.show();
        } else {
            msg.hide();
        }
        
        textArea = me.textArea;
        textField = me.textField;
        if (cfg.prompt || cfg.multiline) {
            me.multiline = cfg.multiline;
            if (cfg.multiline) {
                textArea.setValue(cfg.value);
                textArea.setHeight(cfg.defaultTextHeight || me.defaultTextHeight);
                textArea.show();
                textField.hide();
                me.defaultFocus = textArea;
            } else {
                textField.setValue(cfg.value);
                textArea.hide();
                textField.show();
                me.defaultFocus = textField;
            }
        } else {
            textArea.hide();
            textField.hide();
        }
        
        progressBar = me.progressBar;
        if (cfg.progress || cfg.wait) {
            progressBar.show();
            me.updateProgress(0, cfg.progressText);
            if (cfg.wait === true) {
                progressBar.wait(cfg.waitConfig);
            }
        } else {
            progressBar.hide();
        }
        
        msgButtons = me.msgButtons;
        for (i = 0; i < 4; i++) {
            if (buttons & Math.pow(2, i)) {
                
                if (!me.defaultFocus) {
                    me.defaultFocus = msgButtons[i];
                }
                msgButtons[i].show();
                hideToolbar = false;
            } else {
                msgButtons[i].hide();
            }
        }
        
        if (hideToolbar) {
            me.bottomTb.hide();
        } else {
            me.bottomTb.show();
        }
        Ext.resumeLayouts(true);
    },
    
    updateButtonText: function() {
        var me = this,
            buttonText = me.buttonText,
            buttons = 0,
            btnId, btn;
        for (btnId in buttonText) {
            if (buttonText.hasOwnProperty(btnId)) {
                btn = me.msgButtons[btnId];
                if (btn) {
                    if (me.cfg && me.cfg.buttonText) {
                        buttons = buttons | Math.pow(2, Ext.Array.indexOf(me.buttonIds, btnId));
                    }
                    if (btn.text != buttonText[btnId]) {
                        btn.setText(buttonText[btnId]);
                    }
                }
            }
        }
        return buttons;
    },
    
    show: function(cfg) {
        var me = this,
            visibleFocusables;
        cfg = cfg || {};
        
        if (Ext.Component.layoutSuspendCount) {
            Ext.on({
                resumelayouts: function() {
                    me.show(cfg);
                },
                single: true
            });
            return me;
        }
        me.reconfigure(cfg);
        if (cfg.cls) {
            me.addCls(cfg.cls);
        }
        
        
        visibleFocusables = me.query('textfield:not([hidden]),textarea:not([hidden]),button:not([hidden])');
        me.preventFocusOnActivate = !visibleFocusables.length;
        
        
        me.hidden = true;
        me.callParent();
        return me;
    },
    onShow: function() {
        this.callParent(arguments);
        this.center();
    },
    updateText: function(text) {
        this.msg.setHtml(text);
    },
    
    setIcon: function(icon, width, height) {
        var me = this,
            iconCmp = me.iconComponent,
            cls = me.messageIconCls;
        if (cls) {
            iconCmp.removeCls(cls);
        }
        if (icon) {
            iconCmp.show();
            if (width || height) {
                iconCmp.setSize(width || iconCmp.getWidth(), height || iconCmp.getHeight());
            }
            iconCmp.addCls(Ext.baseCSSPrefix + 'dlg-icon');
            iconCmp.addCls(me.messageIconCls = icon);
        } else {
            iconCmp.removeCls(Ext.baseCSSPrefix + 'dlg-icon');
            iconCmp.hide();
        }
        return me;
    },
    
    updateProgress: function(value, progressText, message) {
        this.progressBar.updateProgress(value, progressText);
        if (message) {
            this.updateText(message);
        }
        return this;
    },
    onEsc: function() {
        if (this.closable !== false) {
            this.callParent(arguments);
        }
    },
    
    confirm: function(cfg, message, fn, scope) {
        if (Ext.isString(cfg)) {
            cfg = {
                title: cfg,
                icon: this.QUESTION,
                message: message,
                buttons: this.YESNO,
                callback: fn,
                scope: scope
            };
        }
        return this.show(cfg);
    },
    
    prompt: function(title, message, fn, scope, multiline, value) {
        if (Ext.isString(title)) {
            title = {
                prompt: true,
                title: title,
                minWidth: this.minPromptWidth,
                message: message,
                buttons: this.OKCANCEL,
                callback: fn,
                scope: scope,
                multiline: multiline,
                value: value
            };
        }
        return this.show(title);
    },
    
    wait: function(message, title, config) {
        if (Ext.isString(message)) {
            message = {
                title: title,
                message: message,
                closable: false,
                wait: true,
                modal: true,
                minWidth: this.minProgressWidth,
                waitConfig: config
            };
        }
        return this.show(message);
    },
    
    alert: function(title, message, fn, scope) {
        if (Ext.isString(title)) {
            title = {
                title: title,
                message: message,
                buttons: this.OK,
                fn: fn,
                scope: scope,
                minWidth: this.minWidth
            };
        }
        return this.show(title);
    },
    
    progress: function(title, message, progressText) {
        if (Ext.isString(title)) {
            title = {
                title: title,
                message: message,
                progress: true,
                progressText: progressText
            };
        }
        return this.show(title);
    }
}, function(MessageBox) {
    
    
    
    Ext.onReady(function() {
        Ext.MessageBox = Ext.Msg = new MessageBox();
    });
});


Ext.define('Ext.form.Basic', {
    extend: 'Ext.util.Observable',
    alternateClassName: 'Ext.form.BasicForm',
    requires: [
        'Ext.util.MixedCollection',
        'Ext.form.action.Load',
        'Ext.form.action.Submit',
        'Ext.window.MessageBox',
        'Ext.data.ErrorCollection',
        'Ext.util.DelayedTask'
    ],
    
    
    taskDelay: 10,
    
    
    
    
    
    
    
    constructor: function(owner, config) {
        var me = this,
            reader;
        
        me.owner = owner;
        me.fieldMonitors = {
            validitychange: me.checkValidityDelay,
            enable: me.checkValidityDelay,
            disable: me.checkValidityDelay,
            dirtychange: me.checkDirtyDelay,
            errorchange: me.checkErrorDelay,
            scope: me
        };
        me.checkValidityTask = new Ext.util.DelayedTask(me.checkValidity,me);
        me.checkDirtyTask = new Ext.util.DelayedTask(me.checkDirty,me);
        me.checkErrorTask = new Ext.util.DelayedTask(me.checkError,me);
        
        
        
        me.monitor = new Ext.container.Monitor({
            selector: '[isFormField]:not([excludeForm])',
            scope: me,
            addHandler: me.onFieldAdd,
            removeHandler: me.onFieldRemove,
            invalidateHandler: me.onMonitorInvalidate
        });
        me.monitor.bind(owner);
        Ext.apply(me, config);
        
        if (Ext.isString(me.paramOrder)) {
            me.paramOrder = me.paramOrder.split(/[\s,|]/);
        }
        reader = me.reader;
        if (reader && !reader.isReader) {
            if (typeof reader === 'string') {
                reader = {
                    type: reader
                };
            }
            me.reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        reader = me.errorReader;
        if (reader && !reader.isReader) {
            if (typeof reader === 'string') {
                reader = {
                    type: reader
                };
            }
            me.errorReader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        me.callParent();
    },
    
    initialize: function() {
        this.initialized = true;
        this.onValidityChange(!this.hasInvalidField());
    },
    
    
    
    
    
    
    timeout: 30,
    
    
    
    paramsAsHash: false,
    
    
    
    waitTitle: 'Please Wait...',
    
    
    trackResetOnLoad: false,
    
    
    
    
    wasDirty: false,
    
    destroy: function() {
        var me = this,
            mon = me.monitor;
        if (mon) {
            mon.unbind();
            me.monitor = null;
        }
        me.clearListeners();
        me.checkValidityTask.cancel();
        me.checkDirtyTask.cancel();
        me.checkErrorTask.cancel();
        me.checkValidityTask = me.checkDirtyTask = me.checkErrorTask = null;
        me.isDestroyed = true;
    },
    onFieldAdd: function(field) {
        field.on(this.fieldMonitors);
        this.onMonitorInvalidate();
    },
    onFieldRemove: function(field) {
        field.un(this.fieldMonitors);
        this.onMonitorInvalidate();
    },
    onMonitorInvalidate: function() {
        if (this.initialized) {
            this.checkValidityDelay();
        }
    },
    
    getFields: function() {
        return this.monitor.getItems();
    },
    
    getBoundItems: function() {
        var boundItems = this._boundItems;
        if (!boundItems || boundItems.getCount() === 0) {
            boundItems = this._boundItems = new Ext.util.MixedCollection();
            boundItems.addAll(this.owner.query('[formBind]'));
        }
        return boundItems;
    },
    
    hasInvalidField: function() {
        return !!this.getFields().findBy(function(field) {
            var preventMark = field.preventMark,
                isValid;
            field.preventMark = true;
            isValid = field.isValid();
            field.preventMark = preventMark;
            return !isValid;
        });
    },
    
    isValid: function() {
        var me = this,
            invalid;
        Ext.suspendLayouts();
        invalid = me.getFields().filterBy(function(field) {
            return !field.validate();
        });
        Ext.resumeLayouts(true);
        return invalid.length < 1;
    },
    
    checkValidity: function() {
        var me = this,
            valid;
        if (me.isDestroyed) {
            return;
        }
        valid = !me.hasInvalidField();
        if (valid !== me.wasValid) {
            me.onValidityChange(valid);
            me.fireEvent('validitychange', me, valid);
            me.wasValid = valid;
        }
    },
    checkValidityDelay: function() {
        var timer = this.taskDelay;
        if (timer) {
            this.checkValidityTask.delay(timer);
        } else {
            this.checkValidity();
        }
    },
    checkError: function() {
        
        
        
        
        this.fireEvent('errorchange', this);
    },
    checkErrorDelay: function() {
        var timer = this.taskDelay;
        if (timer) {
            this.checkErrorTask.delay(timer);
        } else {
            this.checkError();
        }
    },
    
    onValidityChange: function(valid) {
        var boundItems = this.getBoundItems(),
            items, i, iLen, cmp;
        if (boundItems) {
            items = boundItems.items;
            iLen = items.length;
            for (i = 0; i < iLen; i++) {
                cmp = items[i];
                if (cmp.disabled === valid) {
                    cmp.setDisabled(!valid);
                }
            }
        }
    },
    
    isDirty: function() {
        return !!this.getFields().findBy(function(f) {
            return f.isDirty();
        });
    },
    checkDirtyDelay: function() {
        var timer = this.taskDelay;
        if (timer) {
            this.checkDirtyTask.delay(timer);
        } else {
            this.checkDirty();
        }
    },
    
    checkDirty: function() {
        var me = this,
            dirty;
        if (me.isDestroyed) {
            return;
        }
        dirty = this.isDirty();
        if (dirty !== this.wasDirty) {
            this.fireEvent('dirtychange', this, dirty);
            this.wasDirty = dirty;
        }
    },
    
    hasUpload: function() {
        return !!this.getFields().findBy(function(f) {
            return f.isFileUpload();
        });
    },
    
    doAction: function(action, options) {
        if (Ext.isString(action)) {
            action = Ext.ClassManager.instantiateByAlias('formaction.' + action, Ext.apply({}, options, {
                form: this
            }));
        }
        if (this.fireEvent('beforeaction', this, action) !== false) {
            this.beforeAction(action);
            Ext.defer(action.run, 100, action);
        }
        return this;
    },
    
    submit: function(options) {
        options = options || {};
        var me = this,
            action;
        if (options.standardSubmit || me.standardSubmit) {
            action = 'standardsubmit';
        } else {
            action = me.api ? 'directsubmit' : 'submit';
        }
        return me.doAction(action, options);
    },
    
    load: function(options) {
        return this.doAction(this.api ? 'directload' : 'load', options);
    },
    
    updateRecord: function(record) {
        record = record || this._record;
        if (!record) {
            Ext.Error.raise("A record is required.");
            return this;
        }
        var fields = record.self.fields,
            values = this.getFieldValues(),
            obj = {},
            i = 0,
            len = fields.length,
            name;
        for (; i < len; ++i) {
            name = fields[i].name;
            if (values.hasOwnProperty(name)) {
                obj[name] = values[name];
            }
        }
        record.beginEdit();
        record.set(obj);
        record.endEdit();
        return this;
    },
    
    loadRecord: function(record) {
        this._record = record;
        return this.setValues(record.getData());
    },
    
    getRecord: function() {
        return this._record;
    },
    
    beforeAction: function(action) {
        var me = this,
            waitMsg = action.waitMsg,
            maskCls = Ext.baseCSSPrefix + 'mask-loading',
            fields = me.getFields().items,
            f,
            fLen = fields.length,
            field, waitMsgTarget;
        
        for (f = 0; f < fLen; f++) {
            field = fields[f];
            if (field.isFormField && field.syncValue) {
                field.syncValue();
            }
        }
        if (waitMsg) {
            waitMsgTarget = me.waitMsgTarget;
            if (waitMsgTarget === true) {
                me.owner.el.mask(waitMsg, maskCls);
            } else if (waitMsgTarget) {
                waitMsgTarget = me.waitMsgTarget = Ext.get(waitMsgTarget);
                waitMsgTarget.mask(waitMsg, maskCls);
            } else {
                me.floatingAncestor = me.owner.up('[floating]');
                
                
                
                
                
                
                
                if (me.floatingAncestor) {
                    me.savePreventFocusOnActivate = me.floatingAncestor.preventFocusOnActivate;
                    me.floatingAncestor.preventFocusOnActivate = true;
                }
                Ext.MessageBox.wait(waitMsg, action.waitTitle || me.waitTitle);
            }
        }
    },
    
    afterAction: function(action, success) {
        var me = this;
        if (action.waitMsg) {
            var messageBox = Ext.MessageBox,
                waitMsgTarget = me.waitMsgTarget;
            if (waitMsgTarget === true) {
                me.owner.el.unmask();
            } else if (waitMsgTarget) {
                waitMsgTarget.unmask();
            } else {
                messageBox.hide();
            }
        }
        
        if (me.floatingAncestor) {
            me.floatingAncestor.preventFocusOnActivate = me.savePreventFocusOnActivate;
        }
        if (success) {
            if (action.reset) {
                me.reset();
            }
            Ext.callback(action.success, action.scope || action, [
                me,
                action
            ]);
            me.fireEvent('actioncomplete', me, action);
        } else {
            Ext.callback(action.failure, action.scope || action, [
                me,
                action
            ]);
            me.fireEvent('actionfailed', me, action);
        }
    },
    
    findField: function(id) {
        return this.getFields().findBy(function(f) {
            return f.id === id || f.name === id || f.dataIndex === id;
        });
    },
    
    markInvalid: function(errors) {
        var me = this,
            e, eLen, error, value, key;
        function mark(fieldId, msg) {
            var field = me.findField(fieldId);
            if (field) {
                field.markInvalid(msg);
            }
        }
        if (Ext.isArray(errors)) {
            eLen = errors.length;
            for (e = 0; e < eLen; e++) {
                error = errors[e];
                mark(error.id, error.msg);
            }
        } else if (errors instanceof Ext.data.ErrorCollection) {
            eLen = errors.items.length;
            for (e = 0; e < eLen; e++) {
                error = errors.items[e];
                mark(error.field, error.message);
            }
        } else {
            for (key in errors) {
                if (errors.hasOwnProperty(key)) {
                    value = errors[key];
                    mark(key, value, errors);
                }
            }
        }
        return this;
    },
    
    setValues: function(values) {
        var me = this,
            v, vLen, val;
        function setVal(fieldId, val) {
            var field = me.findField(fieldId);
            if (field) {
                field.setValue(val);
                if (me.trackResetOnLoad) {
                    field.resetOriginalValue();
                }
            }
        }
        
        
        Ext.suspendLayouts();
        if (Ext.isArray(values)) {
            
            vLen = values.length;
            for (v = 0; v < vLen; v++) {
                val = values[v];
                setVal(val.id, val.value);
            }
        } else {
            
            Ext.iterate(values, setVal);
        }
        Ext.resumeLayouts(true);
        return this;
    },
    
    getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues, isSubmitting) {
        var values = {},
            fields = this.getFields().items,
            fLen = fields.length,
            isArray = Ext.isArray,
            field, data, val, bucket, name, f;
        for (f = 0; f < fLen; f++) {
            field = fields[f];
            if (!dirtyOnly || field.isDirty()) {
                data = field[useDataValues ? 'getModelData' : 'getSubmitData'](includeEmptyText, isSubmitting);
                if (Ext.isObject(data)) {
                    for (name in data) {
                        if (data.hasOwnProperty(name)) {
                            val = data[name];
                            if (includeEmptyText && val === '') {
                                val = field.emptyText || '';
                            }
                            if (!field.isRadio) {
                                if (values.hasOwnProperty(name)) {
                                    bucket = values[name];
                                    if (!isArray(bucket)) {
                                        bucket = values[name] = [
                                            bucket
                                        ];
                                    }
                                    if (isArray(val)) {
                                        values[name] = bucket.concat(val);
                                    } else {
                                        bucket.push(val);
                                    }
                                } else {
                                    values[name] = val;
                                }
                            } else {
                                values[name] = values[name] || val;
                            }
                        }
                    }
                }
            }
        }
        if (asString) {
            values = Ext.Object.toQueryString(values);
        }
        return values;
    },
    
    getFieldValues: function(dirtyOnly) {
        return this.getValues(false, dirtyOnly, false, true);
    },
    
    clearInvalid: function() {
        Ext.suspendLayouts();
        var me = this,
            fields = me.getFields().items,
            f,
            fLen = fields.length;
        for (f = 0; f < fLen; f++) {
            fields[f].clearInvalid();
        }
        Ext.resumeLayouts(true);
        return me;
    },
    
    reset: function(resetRecord) {
        Ext.suspendLayouts();
        var me = this,
            fields = me.getFields().items,
            f,
            fLen = fields.length;
        for (f = 0; f < fLen; f++) {
            fields[f].reset();
        }
        Ext.resumeLayouts(true);
        if (resetRecord === true) {
            delete me._record;
        }
        return me;
    },
    
    applyToFields: function(obj) {
        var fields = this.getFields().items,
            f,
            fLen = fields.length;
        for (f = 0; f < fLen; f++) {
            Ext.apply(fields[f], obj);
        }
        return this;
    },
    
    applyIfToFields: function(obj) {
        var fields = this.getFields().items,
            f,
            fLen = fields.length;
        for (f = 0; f < fLen; f++) {
            Ext.applyIf(fields[f], obj);
        }
        return this;
    }
});


Ext.define('Ext.form.FieldAncestor', {
    extend: 'Ext.Mixin',
    requires: [
        'Ext.container.Monitor'
    ],
    mixinConfig: {
        id: 'fieldAncestor',
        after: {
            initInheritedState: 'initFieldInheritedState'
        },
        before: {
            destroy: 'onBeforeDestroy'
        }
    },
    
    
    
    
    initFieldAncestor: function() {
        var me = this;
        
        
        
        me.monitor = new Ext.container.Monitor({
            scope: me,
            selector: '[isFormField]:not([excludeForm])',
            addHandler: me.onChildFieldAdd,
            removeHandler: me.onChildFieldRemove
        });
        me.initFieldDefaults();
    },
    initMonitor: function() {
        this.monitor.bind(this);
    },
    initFieldInheritedState: function(inheritedState) {
        var inheritedFieldDefaults = inheritedState.fieldDefaults,
            fieldDefaults = this.fieldDefaults;
        if (fieldDefaults) {
            if (inheritedFieldDefaults) {
                inheritedState.fieldDefaults = Ext.apply(Ext.Object.chain(inheritedFieldDefaults), fieldDefaults);
            } else {
                inheritedState.fieldDefaults = fieldDefaults;
            }
        }
    },
    onChildFieldAdd: function(field) {
        var me = this;
        me.mon(field, 'errorchange', me.handleFieldErrorChange, me);
        me.mon(field, 'validitychange', me.handleFieldValidityChange, me);
    },
    onChildFieldRemove: function(field) {
        var me = this;
        me.mun(field, 'errorchange', me.handleFieldErrorChange, me);
        me.mun(field, 'validitychange', me.handleFieldValidityChange, me);
    },
    
    initFieldDefaults: function() {
        if (!this.fieldDefaults) {
            this.fieldDefaults = {};
        }
    },
    
    handleFieldValidityChange: function(field, isValid) {
        var me = this;
        if (field !== me) {
            me.fireEvent('fieldvaliditychange', me, field, isValid);
            me.onFieldValidityChange(field, isValid);
        }
    },
    
    handleFieldErrorChange: function(labelable, activeError) {
        var me = this;
        if (labelable !== me) {
            me.fireEvent('fielderrorchange', me, labelable, activeError);
            me.onFieldErrorChange(labelable, activeError);
        }
    },
    
    onFieldValidityChange: Ext.emptyFn,
    
    onFieldErrorChange: Ext.emptyFn,
    onBeforeDestroy: function() {
        this.monitor.unbind();
    }
});


Ext.define('Ext.layout.component.field.FieldContainer', {
    
    extend: 'Ext.layout.component.Auto',
    alias: 'layout.fieldcontainer',
    
    type: 'fieldcontainer',
    waitForOuterHeightInDom: true,
    waitForOuterWidthInDom: true,
    beginLayout: function(ownerContext) {
        var containerEl = this.owner.containerEl;
        this.callParent(arguments);
        
        ownerContext.hasRawContent = true;
        containerEl.setStyle('width', '');
        containerEl.setStyle('height', '');
        ownerContext.containerElContext = ownerContext.getEl('containerEl');
    },
    measureContentHeight: function(ownerContext) {
        
        
        
        return ownerContext.hasDomProp('containerLayoutDone') ? this.callParent(arguments) : NaN;
    },
    measureContentWidth: function(ownerContext) {
        
        return ownerContext.hasDomProp('containerLayoutDone') ? this.callParent(arguments) : NaN;
    },
    publishInnerHeight: function(ownerContext, height) {
        var owner = this.owner;
        if (owner.labelAlign === 'top' && owner.hasVisibleLabel()) {
            height -= owner.labelEl.getHeight();
        }
        if (owner.msgTarget === 'under' && owner.hasActiveError()) {
            height -= owner.errorWrapEl.getHeight();
        }
        height -= owner.bodyEl.getPadding('tb');
        ownerContext.containerElContext.setHeight(height);
    },
    publishInnerWidth: function(ownerContext, width) {
        var owner = this.owner;
        if (owner.labelAlign !== 'top' && owner.hasVisibleLabel()) {
            width -= (owner.labelWidth + (owner.labelPad || 0));
        }
        if (owner.msgTarget === 'side' && owner.hasActiveError()) {
            width -= owner.errorWrapEl.getWidth();
        }
        width -= owner.bodyEl.getPadding('lr');
        ownerContext.containerElContext.setWidth(width);
    }
});


Ext.define('Ext.form.FieldContainer', {
    extend: 'Ext.container.Container',
    mixins: {
        labelable: 'Ext.form.Labelable',
        fieldAncestor: 'Ext.form.FieldAncestor'
    },
    requires: 'Ext.layout.component.field.FieldContainer',
    alias: 'widget.fieldcontainer',
    componentLayout: 'fieldcontainer',
    componentCls: Ext.baseCSSPrefix + 'form-fieldcontainer',
    shrinkWrap: true,
    childEls: [
        'containerEl'
    ],
    
    combineLabels: false,
    
    
    labelConnector: ', ',
    
    
    combineErrors: false,
    maskOnDisable: false,
    
    
    invalidCls: '',
    fieldSubTpl: [
        '<div id="{id}-containerEl" data-ref="containerEl" class="{containerElCls}" role="presentation">',
        '{%this.renderContainer(out,values)%}',
        '</div>'
    ],
    initComponent: function() {
        var me = this;
        
        me.initLabelable();
        me.initFieldAncestor();
        me.callParent();
        me.initMonitor();
    },
    
    onAdd: function(labelItem) {
        var me = this;
        
        
        
        if (labelItem.isLabelable && Ext.isGecko && me.layout.type === 'absolute' && !me.hideLabel && me.labelAlign !== 'top') {
            labelItem.x += (me.labelWidth + me.labelPad);
        }
        me.callParent(arguments);
        if (labelItem.isLabelable && me.combineLabels) {
            labelItem.oldHideLabel = labelItem.hideLabel;
            labelItem.hideLabel = true;
        }
        me.updateLabel();
    },
    
    onRemove: function(labelItem, isDestroying) {
        var me = this;
        me.callParent(arguments);
        if (!isDestroying) {
            if (labelItem.isLabelable && me.combineLabels) {
                labelItem.hideLabel = labelItem.oldHideLabel;
            }
            me.updateLabel();
        }
    },
    initRenderData: function() {
        var me = this,
            data = me.callParent();
        data.containerElCls = me.containerElCls;
        return Ext.applyIf(data, me.getLabelableRenderData());
    },
    
    getFieldLabel: function() {
        var label = this.fieldLabel || '';
        if (!label && this.combineLabels) {
            label = Ext.Array.map(this.query('[isFieldLabelable]'), function(field) {
                return field.getFieldLabel();
            }).join(this.labelConnector);
        }
        return label;
    },
    getSubTplData: function() {
        var ret = this.initRenderData();
        Ext.apply(ret, this.subTplData);
        return ret;
    },
    getSubTplMarkup: function(fieldData) {
        var me = this,
            tpl = me.getTpl('fieldSubTpl'),
            html;
        if (!tpl.renderContent) {
            me.setupRenderTpl(tpl);
        }
        html = tpl.apply(me.getSubTplData(fieldData));
        return html;
    },
    
    updateLabel: function() {
        var me = this,
            label = me.labelEl;
        if (label) {
            me.setFieldLabel(me.getFieldLabel());
        }
    },
    
    onFieldErrorChange: function() {
        if (this.combineErrors) {
            var me = this,
                oldError = me.getActiveError(),
                invalidFields = Ext.Array.filter(me.query('[isFormField]'), function(field) {
                    return field.hasActiveError();
                }),
                newErrors = me.getCombinedErrors(invalidFields);
            if (newErrors) {
                me.setActiveErrors(newErrors);
            } else {
                me.unsetActiveError();
            }
            if (oldError !== me.getActiveError()) {
                me.updateLayout();
            }
        }
    },
    
    getCombinedErrors: function(invalidFields) {
        var errors = [],
            f,
            fLen = invalidFields.length,
            field, activeErrors, a, aLen, error, label;
        for (f = 0; f < fLen; f++) {
            field = invalidFields[f];
            activeErrors = field.getActiveErrors();
            aLen = activeErrors.length;
            for (a = 0; a < aLen; a++) {
                error = activeErrors[a];
                label = field.getFieldLabel();
                errors.push((label ? label + ': ' : '') + error);
            }
        }
        return errors;
    },
    privates: {
        applyTargetCls: function(targetCls) {
            var containerElCls = this.containerElCls;
            this.containerElCls = containerElCls ? containerElCls + ' ' + targetCls : targetCls;
        },
        getTargetEl: function() {
            return this.containerEl;
        },
        initRenderTpl: function() {
            var me = this;
            if (!me.hasOwnProperty('renderTpl')) {
                me.renderTpl = me.getTpl('labelableRenderTpl');
            }
            return me.callParent();
        }
    }
});


Ext.define('Ext.layout.container.CheckboxGroup', {
    extend: 'Ext.layout.container.Container',
    alias: [
        'layout.checkboxgroup'
    ],
    
    autoFlex: true,
    type: 'checkboxgroup',
    createsInnerCt: true,
    childEls: [
        'innerCt'
    ],
    renderTpl: [
        '<table id="{ownerId}-innerCt" data-ref="innerCt" class="' + Ext.baseCSSPrefix + 'table-plain" cellpadding="0"',
        'role="presentation" style="{tableStyle}"><tr role="presentation">',
        '<tpl for="columns">',
        '<td class="{parent.colCls}" valign="top" style="{style}" role="presentation">',
        '{% this.renderColumn(out,parent,xindex-1) %}',
        '</td>',
        '</tpl>',
        '</tr></table>'
    ],
    lastOwnerItemsGeneration: null,
    beginLayout: function(ownerContext) {
        var me = this,
            columns, numCols, i, width, cwidth,
            totalFlex = 0,
            flexedCols = 0,
            autoFlex = me.autoFlex,
            innerCtStyle = me.innerCt.dom.style;
        me.callParent(arguments);
        columns = me.columnNodes;
        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);
        
        if (!ownerContext.widthModel.shrinkWrap) {
            numCols = columns.length;
            
            if (me.columnsArray) {
                
                for (i = 0; i < numCols; i++) {
                    width = me.owner.columns[i];
                    if (width < 1) {
                        totalFlex += width;
                        flexedCols++;
                    }
                }
                
                for (i = 0; i < numCols; i++) {
                    width = me.owner.columns[i];
                    if (width < 1) {
                        cwidth = ((width / totalFlex) * 100) + '%';
                    } else {
                        cwidth = width + 'px';
                    }
                    columns[i].style.width = cwidth;
                }
            } else 
            {
                for (i = 0; i < numCols; i++) {
                    
                    
                    
                    cwidth = autoFlex ? (1 / numCols * 100) + '%' : '';
                    columns[i].style.width = cwidth;
                    flexedCols++;
                }
            }
            
            if (!flexedCols) {
                innerCtStyle.tableLayout = 'fixed';
                innerCtStyle.width = '';
            }
            
            else if (flexedCols < numCols) {
                innerCtStyle.tableLayout = 'fixed';
                innerCtStyle.width = '100%';
            } else 
            {
                innerCtStyle.tableLayout = 'auto';
                
                if (autoFlex) {
                    innerCtStyle.width = '100%';
                } else {
                    innerCtStyle.width = '';
                }
            }
        } else {
            innerCtStyle.tableLayout = 'auto';
            innerCtStyle.width = '';
        }
    },
    cacheElements: function() {
        var me = this;
        
        me.callParent();
        me.rowEl = me.innerCt.down('tr');
        
        me.columnNodes = me.rowEl.dom.childNodes;
    },
    
    calculate: function(ownerContext) {
        var me = this,
            targetContext, widthShrinkWrap, heightShrinkWrap, shrinkWrap, table, targetPadding;
        
        
        if (!ownerContext.getDomProp('containerChildrenSizeDone')) {
            me.done = false;
        } else {
            targetContext = ownerContext.innerCtContext;
            widthShrinkWrap = ownerContext.widthModel.shrinkWrap;
            heightShrinkWrap = ownerContext.heightModel.shrinkWrap;
            shrinkWrap = heightShrinkWrap || widthShrinkWrap;
            table = targetContext.el.dom;
            targetPadding = shrinkWrap && targetContext.getPaddingInfo();
            if (widthShrinkWrap) {
                ownerContext.setContentWidth(table.offsetWidth + targetPadding.width, true);
            }
            if (heightShrinkWrap) {
                ownerContext.setContentHeight(table.offsetHeight + targetPadding.height, true);
            }
        }
    },
    doRenderColumn: function(out, renderData, columnIndex) {
        
        
        var me = renderData.$layout,
            owner = me.owner,
            columnCount = renderData.columnCount,
            items = owner.items.items,
            itemCount = items.length,
            item, itemIndex, rowCount, increment, tree;
        
        
        
        if (owner.vertical) {
            
            
            
            
            
            
            
            
            
            
            rowCount = Math.ceil(itemCount / columnCount);
            
            itemIndex = columnIndex * rowCount;
            itemCount = Math.min(itemCount, itemIndex + rowCount);
            increment = 1;
        } else {
            
            
            
            
            
            
            
            
            
            
            itemIndex = columnIndex;
            increment = columnCount;
        }
        for (; itemIndex < itemCount; itemIndex += increment) {
            item = items[itemIndex];
            me.configureItem(item);
            tree = item.getRenderTree();
            Ext.DomHelper.generateMarkup(tree, out);
        }
    },
    
    getColumnCount: function() {
        var me = this,
            owner = me.owner,
            ownerColumns = owner.columns;
        
        
        if (me.columnsArray) {
            return ownerColumns.length;
        }
        if (Ext.isNumber(ownerColumns)) {
            return ownerColumns;
        }
        return owner.items.length;
    },
    getItemSizePolicy: function(item) {
        return this.autoSizePolicy;
    },
    getRenderData: function() {
        var me = this,
            data = me.callParent(),
            owner = me.owner,
            i,
            columns = me.getColumnCount(),
            width, column, cwidth,
            autoFlex = me.autoFlex,
            totalFlex = 0,
            flexedCols = 0;
        
        if (me.columnsArray) {
            for (i = 0; i < columns; i++) {
                width = me.owner.columns[i];
                if (width < 1) {
                    totalFlex += width;
                    flexedCols++;
                }
            }
        }
        data.colCls = owner.groupCls;
        data.columnCount = columns;
        data.columns = [];
        for (i = 0; i < columns; i++) {
            column = (data.columns[i] = {});
            if (me.columnsArray) {
                width = me.owner.columns[i];
                if (width < 1) {
                    cwidth = ((width / totalFlex) * 100) + '%';
                } else {
                    cwidth = width + 'px';
                }
                column.style = 'width:' + cwidth;
            } else {
                column.style = 'width:' + (1 / columns * 100) + '%';
                flexedCols++;
            }
        }
        
        data.tableStyle = !flexedCols ? 'table-layout:fixed;' : (flexedCols < columns) ? 'table-layout:fixed;width:100%' : (autoFlex) ? 'table-layout:auto;width:100%' : 'table-layout:auto;';
        return data;
    },
    initLayout: function() {
        var me = this,
            owner = me.owner;
        me.columnsArray = Ext.isArray(owner.columns);
        me.autoColumns = !owner.columns || owner.columns === 'auto';
        me.vertical = owner.vertical;
        me.callParent();
    },
    
    isValidParent: Ext.returnTrue,
    setupRenderTpl: function(renderTpl) {
        this.callParent(arguments);
        renderTpl.renderColumn = this.doRenderColumn;
    },
    renderChildren: function() {
        var me = this,
            generation = me.owner.items.generation;
        if (me.lastOwnerItemsGeneration !== generation) {
            me.lastOwnerItemsGeneration = generation;
            me.renderItems(me.getLayoutItems());
        }
    },
    
    renderItems: function(items) {
        var me = this,
            itemCount = items.length,
            i, item, rowCount, columnCount, rowIndex, columnIndex;
        if (itemCount) {
            Ext.suspendLayouts();
            if (me.autoColumns) {
                me.addMissingColumns(itemCount);
            }
            columnCount = me.columnNodes.length;
            rowCount = Math.ceil(itemCount / columnCount);
            for (i = 0; i < itemCount; i++) {
                item = items[i];
                rowIndex = me.getRenderRowIndex(i, rowCount, columnCount);
                columnIndex = me.getRenderColumnIndex(i, rowCount, columnCount);
                if (!item.rendered) {
                    me.renderItem(item, rowIndex, columnIndex);
                } else if (!me.isItemAtPosition(item, rowIndex, columnIndex)) {
                    me.moveItem(item, rowIndex, columnIndex);
                }
            }
            if (me.autoColumns) {
                me.removeExceedingColumns(itemCount);
            }
            Ext.resumeLayouts(true);
        }
    },
    isItemAtPosition: function(item, rowIndex, columnIndex) {
        return item.el.dom === this.getNodeAt(rowIndex, columnIndex);
    },
    getRenderColumnIndex: function(itemIndex, rowCount, columnCount) {
        if (this.vertical) {
            return Math.floor(itemIndex / rowCount);
        } else {
            return itemIndex % columnCount;
        }
    },
    getRenderRowIndex: function(itemIndex, rowCount, columnCount) {
        var me = this;
        if (me.vertical) {
            return itemIndex % rowCount;
        } else {
            return Math.floor(itemIndex / columnCount);
        }
    },
    getNodeAt: function(rowIndex, columnIndex) {
        return this.columnNodes[columnIndex].childNodes[rowIndex];
    },
    addMissingColumns: function(itemsCount) {
        var me = this,
            existingColumnsCount = me.columnNodes.length,
            missingColumnsCount, row, cls, i;
        if (existingColumnsCount < itemsCount) {
            missingColumnsCount = itemsCount - existingColumnsCount;
            row = me.rowEl;
            cls = me.owner.groupCls;
            for (i = 0; i < missingColumnsCount; i++) {
                row.createChild({
                    cls: cls,
                    tag: 'td',
                    vAlign: 'top',
                    role: 'presentation'
                });
            }
        }
    },
    removeExceedingColumns: function(itemsCount) {
        var me = this,
            existingColumnsCount = me.columnNodes.length,
            exceedingColumnsCount, row, i;
        if (existingColumnsCount > itemsCount) {
            exceedingColumnsCount = existingColumnsCount - itemsCount;
            row = me.rowEl;
            for (i = 0; i < exceedingColumnsCount; i++) {
                row.last().destroy();
            }
        }
    },
    
    renderItem: function(item, rowIndex, columnIndex) {
        var me = this;
        me.configureItem(item);
        item.render(Ext.get(me.columnNodes[columnIndex]), rowIndex);
    },
    
    moveItem: function(item, rowIndex, columnIndex) {
        var me = this,
            column = me.columnNodes[columnIndex],
            targetNode = column.childNodes[rowIndex];
        column.insertBefore(item.el.dom, targetNode || null);
    }
});


Ext.define('Ext.form.CheckboxGroup', {
    extend: 'Ext.form.FieldContainer',
    mixins: {
        field: 'Ext.form.field.Field'
    },
    alias: 'widget.checkboxgroup',
    requires: [
        'Ext.layout.container.CheckboxGroup',
        'Ext.form.field.Checkbox',
        'Ext.form.field.Base'
    ],
    
    
    
    columns: 'auto',
    
    vertical: false,
    
    allowBlank: true,
    
    
    blankText: "You must select at least one item in this group",
    
    
    defaultType: 'checkboxfield',
    
    groupCls: Ext.baseCSSPrefix + 'form-check-group',
    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'form-checkboxgroup-body',
    
    layout: 'checkboxgroup',
    componentCls: Ext.baseCSSPrefix + 'form-checkboxgroup',
    ariaRole: 'group',
    initComponent: function() {
        var me = this;
        me.callParent();
        me.initField();
    },
    
    initValue: function() {
        var me = this,
            valueCfg = me.value;
        me.originalValue = me.lastValue = valueCfg || me.getValue();
        if (valueCfg) {
            me.setValue(valueCfg);
        }
    },
    
    onAdd: function(field) {
        var me = this,
            items, len, i;
        if (field.isCheckbox) {
            me.mon(field, 'change', me.checkChange, me);
        } else if (field.isContainer) {
            items = field.items.items;
            for (i = 0 , len = items.length; i < len; i++) {
                me.onAdd(items[i]);
            }
        }
        me.callParent(arguments);
    },
    onRemove: function(item) {
        var me = this,
            items, len, i;
        if (item.isCheckbox) {
            me.mun(item, 'change', me.checkChange, me);
        } else if (item.isContainer) {
            items = item.items.items;
            for (i = 0 , len = items.length; i < len; i++) {
                me.onRemove(items[i]);
            }
        }
        me.callParent(arguments);
    },
    
    isEqual: function(value1, value2) {
        var toQueryString = Ext.Object.toQueryString;
        return toQueryString(value1) === toQueryString(value2);
    },
    
    getErrors: function() {
        var errors = [];
        if (!this.allowBlank && Ext.isEmpty(this.getChecked())) {
            errors.push(this.blankText);
        }
        return errors;
    },
    
    getBoxes: function(query) {
        return this.query('[isCheckbox]' + (query || ''));
    },
    
    eachBox: function(fn, scope) {
        Ext.Array.forEach(this.getBoxes(), fn, scope || this);
    },
    
    getChecked: function() {
        return this.getBoxes('[checked]');
    },
    
    isDirty: function() {
        var boxes = this.getBoxes(),
            b,
            bLen = boxes.length;
        for (b = 0; b < bLen; b++) {
            if (boxes[b].isDirty()) {
                return true;
            }
        }
    },
    
    setReadOnly: function(readOnly) {
        var boxes = this.getBoxes(),
            b,
            bLen = boxes.length;
        for (b = 0; b < bLen; b++) {
            boxes[b].setReadOnly(readOnly);
        }
        this.readOnly = readOnly;
    },
    
    reset: function() {
        var me = this,
            hadError = me.hasActiveError(),
            preventMark = me.preventMark;
        me.preventMark = true;
        me.batchChanges(function() {
            var boxes = me.getBoxes(),
                b,
                bLen = boxes.length;
            for (b = 0; b < bLen; b++) {
                boxes[b].reset();
            }
        });
        me.preventMark = preventMark;
        me.unsetActiveError();
        if (hadError) {
            me.updateLayout();
        }
    },
    resetOriginalValue: function() {
        var me = this,
            boxes = me.getBoxes(),
            b,
            bLen = boxes.length;
        for (b = 0; b < bLen; b++) {
            boxes[b].resetOriginalValue();
        }
        me.originalValue = me.getValue();
        me.checkDirty();
    },
    
    setValue: function(value) {
        var me = this,
            boxes = me.getBoxes(),
            b,
            bLen = boxes.length,
            box, name, cbValue;
        me.batchChanges(function() {
            Ext.suspendLayouts();
            for (b = 0; b < bLen; b++) {
                box = boxes[b];
                name = box.getName();
                cbValue = false;
                if (value) {
                    if (Ext.isArray(value[name])) {
                        cbValue = Ext.Array.contains(value[name], box.inputValue);
                    } else {
                        
                        cbValue = value[name];
                    }
                }
                box.setValue(cbValue);
            }
            Ext.resumeLayouts(true);
        });
        return me;
    },
    
    getValue: function() {
        var values = {},
            boxes = this.getBoxes(),
            b,
            bLen = boxes.length,
            box, name, inputValue, bucket;
        for (b = 0; b < bLen; b++) {
            box = boxes[b];
            name = box.getName();
            inputValue = box.inputValue;
            if (box.getValue()) {
                if (values.hasOwnProperty(name)) {
                    bucket = values[name];
                    if (!Ext.isArray(bucket)) {
                        bucket = values[name] = [
                            bucket
                        ];
                    }
                    bucket.push(inputValue);
                } else {
                    values[name] = inputValue;
                }
            }
        }
        return values;
    },
    
    getSubmitData: function() {
        return null;
    },
    
    getModelData: function() {
        return null;
    },
    validate: function() {
        var me = this,
            errors, isValid, wasValid;
        if (me.disabled) {
            isValid = true;
        } else {
            errors = me.getErrors();
            isValid = Ext.isEmpty(errors);
            wasValid = me.wasValid;
            if (isValid) {
                me.unsetActiveError();
            } else {
                me.setActiveError(errors);
            }
        }
        if (isValid !== wasValid) {
            me.wasValid = isValid;
            me.fireEvent('validitychange', me, isValid);
            me.updateLayout();
        }
        return isValid;
    }
}, function() {
    this.borrow(Ext.form.field.Base, [
        'markInvalid',
        'clearInvalid',
        'setError'
    ]);
});


Ext.define('Ext.form.FieldSet', {
    extend: 'Ext.container.Container',
    mixins: {
        fieldAncestor: 'Ext.form.FieldAncestor'
    },
    alias: 'widget.fieldset',
    uses: [
        'Ext.form.field.Checkbox',
        'Ext.panel.Tool',
        'Ext.layout.container.Anchor',
        'Ext.layout.component.FieldSet'
    ],
    focusable: true,
    
    
    
    
    checkboxUI: 'default',
    
    
    collapsed: false,
    
    toggleOnTitleClick: true,
    
    
    baseCls: Ext.baseCSSPrefix + 'fieldset',
    
    layout: 'anchor',
    componentLayout: 'fieldset',
    ariaRole: null,
    autoEl: 'fieldset',
    childEls: [
        'body'
    ],
    renderTpl: [
        '{%this.renderLegend(out,values);%}',
        '<div id="{id}-body" data-ref="body" class="{baseCls}-body {baseCls}-body-{ui} {bodyTargetCls}" ',
        'role="presentation"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
        '{%this.renderContainer(out,values);%}',
        '</div>'
    ],
    stateEvents: [
        'collapse',
        'expand'
    ],
    maskOnDisable: false,
    
    
    
    
    beforeDestroy: function() {
        var me = this,
            legend = me.legend;
        if (legend) {
            
            delete legend.ownerCt;
            legend.destroy();
            me.legend = null;
        }
        me.callParent();
    },
    initComponent: function() {
        var me = this,
            baseCls = me.baseCls;
        me.initFieldAncestor();
        me.callParent();
        
        
        
        
        
        
        
        
        
        
        me.layout.managePadding = me.layout.manageOverflow = false;
        if (me.collapsed) {
            me.addCls(baseCls + '-collapsed');
            me.collapse();
        }
        if (me.title || me.checkboxToggle || me.collapsible) {
            me.addTitleClasses();
            me.legend = Ext.widget(me.createLegendCt());
        }
        me.initMonitor();
    },
    
    initRenderData: function() {
        var me = this,
            data = me.callParent();
        data.bodyTargetCls = me.bodyTargetCls;
        me.protoBody.writeTo(data);
        delete me.protoBody;
        return data;
    },
    getState: function() {
        var state = this.callParent();
        state = this.addPropertyToState(state, 'collapsed');
        return state;
    },
    afterCollapse: Ext.emptyFn,
    afterExpand: Ext.emptyFn,
    collapsedHorizontal: function() {
        return true;
    },
    collapsedVertical: function() {
        return true;
    },
    createLegendCt: function() {
        var me = this,
            items = [],
            legend = {
                xtype: 'container',
                baseCls: me.baseCls + '-header',
                
                layout: 'container',
                ui: me.ui,
                id: me.id + '-legend',
                autoEl: 'legend',
                ariaRole: null,
                ariaLabelledBy: '.' + me.baseCls + '-header-text',
                items: items,
                ownerCt: me,
                shrinkWrap: true,
                ownerLayout: me.componentLayout
            };
        
        if (me.checkboxToggle) {
            items.push(me.createCheckboxCmp());
        } else if (me.collapsible) {
            
            items.push(me.createToggleCmp());
        }
        
        items.push(me.createTitleCmp());
        return legend;
    },
    
    createTitleCmp: function() {
        var me = this,
            cfg = {
                xtype: 'component',
                html: me.title,
                ui: me.ui,
                cls: me.baseCls + '-header-text',
                id: me.id + '-legendTitle'
            };
        if (me.collapsible && me.toggleOnTitleClick) {
            cfg.listeners = {
                click: {
                    element: 'el',
                    scope: me,
                    fn: me.toggle
                }
            };
            cfg.cls += ' ' + me.baseCls + '-header-text-collapsible';
        }
        return (me.titleCmp = Ext.widget(cfg));
    },
    
    
    createCheckboxCmp: function() {
        var me = this,
            suffix = '-checkbox',
            cls = me.baseCls + '-header' + suffix;
        cls += ' ' + cls + '-' + me.ui;
        me.checkboxCmp = Ext.widget({
            xtype: 'checkbox',
            hideEmptyLabel: true,
            name: me.checkboxName || me.id + suffix,
            cls: cls,
            id: me.id + '-legendChk',
            ui: me.checkboxUI,
            checked: !me.collapsed,
            msgTarget: 'none',
            listeners: {
                change: me.onCheckChange,
                scope: me
            }
        });
        return me.checkboxCmp;
    },
    
    
    createToggleCmp: function() {
        var me = this;
        me.toggleCmp = Ext.widget({
            xtype: 'tool',
            
            
            
            cacheHeight: false,
            cls: me.baseCls + '-header-tool-' + me.ui,
            type: 'toggle',
            handler: me.toggle,
            id: me.id + '-legendToggle',
            scope: me
        });
        return me.toggleCmp;
    },
    doRenderLegend: function(out, renderData) {
        
        
        var me = renderData.$comp,
            legend = me.legend,
            tree;
        
        if (legend) {
            legend.ownerLayout.configureItem(legend);
            tree = legend.getRenderTree();
            Ext.DomHelper.generateMarkup(tree, out);
        }
    },
    getCollapsed: function() {
        return this.collapsed ? 'top' : false;
    },
    getCollapsedDockedItems: function() {
        var legend = this.legend;
        return legend ? [
            legend
        ] : [];
    },
    
    setTitle: function(title) {
        var me = this,
            legend = me.legend;
        me.title = title;
        if (me.rendered) {
            if (!legend) {
                me.legend = legend = Ext.widget(me.createLegendCt());
                me.addTitleClasses();
                legend.ownerLayout.configureItem(legend);
                legend.render(me.el, 0);
            }
            me.titleCmp.update(title);
        } else if (legend) {
            me.titleCmp.update(title);
        } else {
            me.addTitleClasses();
            me.legend = Ext.widget(me.createLegendCt());
        }
        return me;
    },
    addTitleClasses: function() {
        var me = this,
            title = me.title,
            baseCls = me.baseCls;
        if (title) {
            me.addCls(baseCls + '-with-title');
        }
        if (title || me.checkboxToggle || me.collapsible) {
            me.addCls(baseCls + '-with-legend');
        }
    },
    
    expand: function() {
        return this.setExpanded(true);
    },
    
    collapse: function() {
        return this.setExpanded(false);
    },
    
    setExpanded: function(expanded) {
        var me = this,
            checkboxCmp = me.checkboxCmp,
            operation = expanded ? 'expand' : 'collapse';
        if (!me.rendered || me.fireEvent('before' + operation, me) !== false) {
            expanded = !!expanded;
            if (checkboxCmp) {
                checkboxCmp.setValue(expanded);
            }
            if (expanded) {
                me.removeCls(me.baseCls + '-collapsed');
            } else {
                me.addCls(me.baseCls + '-collapsed');
            }
            me.collapsed = !expanded;
            if (expanded) {
                delete me.getInherited().collapsed;
            } else {
                me.getInherited().collapsed = true;
            }
            if (me.rendered) {
                
                
                
                me.updateLayout({
                    isRoot: false
                });
                me.fireEvent(operation, me);
            }
        }
        return me;
    },
    getRefItems: function(deep) {
        var refItems = this.callParent(arguments),
            legend = this.legend;
        
        if (legend) {
            refItems.unshift(legend);
            if (deep) {
                refItems.unshift.apply(refItems, legend.getRefItems(true));
            }
        }
        return refItems;
    },
    
    toggle: function() {
        this.setExpanded(!!this.collapsed);
    },
    privates: {
        applyTargetCls: function(targetCls) {
            this.bodyTargetCls = targetCls;
        },
        finishRender: function() {
            var legend = this.legend;
            this.callParent();
            if (legend) {
                legend.finishRender();
            }
        },
        getProtoBody: function() {
            var me = this,
                body = me.protoBody;
            if (!body) {
                me.protoBody = body = new Ext.util.ProtoElement({
                    styleProp: 'bodyStyle',
                    styleIsText: true
                });
            }
            return body;
        },
        getDefaultContentTarget: function() {
            return this.body;
        },
        getTargetEl: function() {
            return this.body || this.frameBody || this.el;
        },
        initPadding: function(targetEl) {
            var me = this,
                body = me.getProtoBody(),
                padding = me.padding,
                bodyPadding;
            if (padding !== undefined) {
                if (Ext.isIE8) {
                    
                    
                    padding = me.parseBox(padding);
                    bodyPadding = Ext.Element.parseBox(0);
                    bodyPadding.top = padding.top;
                    padding.top = 0;
                    body.setStyle('padding', me.unitizeBox(bodyPadding));
                }
                targetEl.setStyle('padding', me.unitizeBox(padding));
            }
        },
        
        onCheckChange: function(cmp, checked) {
            this.setExpanded(checked);
        },
        setupRenderTpl: function(renderTpl) {
            this.callParent(arguments);
            renderTpl.renderLegend = this.doRenderLegend;
        }
    }
});


Ext.define('Ext.form.Label', {
    extend: 'Ext.Component',
    alias: 'widget.label',
    requires: [
        'Ext.util.Format'
    ],
    autoEl: 'label',
    
    
    
    maskOnDisable: false,
    getElConfig: function() {
        var me = this;
        me.html = me.text ? Ext.util.Format.htmlEncode(me.text) : (me.html || '');
        return Ext.apply(me.callParent(), {
            htmlFor: me.forId || ''
        });
    },
    
    setText: function(text, encode) {
        var me = this;
        encode = encode !== false;
        if (encode) {
            me.text = text;
            delete me.html;
        } else {
            me.html = text;
            delete me.text;
        }
        if (me.rendered) {
            me.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(text) : text;
            me.updateLayout();
        }
        return me;
    }
});


Ext.define('Ext.form.Panel', {
    extend: 'Ext.panel.Panel',
    mixins: {
        fieldAncestor: 'Ext.form.FieldAncestor'
    },
    alias: 'widget.form',
    alternateClassName: [
        'Ext.FormPanel',
        'Ext.form.FormPanel'
    ],
    requires: [
        'Ext.form.Basic',
        'Ext.util.TaskRunner'
    ],
    
    
    
    layout: 'anchor',
    ariaRole: 'form',
    basicFormConfigs: [
        
        'api',
        
        'baseParams',
        
        'errorReader',
        
        'jsonSubmit',
        
        'method',
        
        'paramOrder',
        
        'paramsAsHash',
        
        'reader',
        
        'standardSubmit',
        
        'timeout',
        
        'trackResetOnLoad',
        
        'url',
        
        'waitMsgTarget',
        
        'waitTitle'
    ],
    initComponent: function() {
        var me = this;
        if (me.frame) {
            me.border = false;
        }
        me.initFieldAncestor();
        me.callParent();
        me.relayEvents(me.form, [
            
            'beforeaction',
            
            'actionfailed',
            
            'actioncomplete',
            
            'validitychange',
            
            'dirtychange'
        ]);
        
        if (me.pollForChanges) {
            me.startPolling(me.pollInterval || 500);
        }
    },
    initItems: function() {
        
        this.callParent();
        this.initMonitor();
        this.form = this.createForm();
    },
    
    afterFirstLayout: function() {
        this.callParent(arguments);
        this.form.initialize();
    },
    
    createForm: function() {
        var cfg = {},
            props = this.basicFormConfigs,
            len = props.length,
            i = 0,
            prop;
        for (; i < len; ++i) {
            prop = props[i];
            cfg[prop] = this[prop];
        }
        return new Ext.form.Basic(this,cfg);
    },
    
    getForm: function() {
        return this.form;
    },
    
    loadRecord: function(record) {
        return this.getForm().loadRecord(record);
    },
    
    getRecord: function() {
        return this.getForm().getRecord();
    },
    
    updateRecord: function(record) {
        return this.getForm().updateRecord(record);
    },
    
    getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
        return this.getForm().getValues(asString, dirtyOnly, includeEmptyText, useDataValues);
    },
    
    isDirty: function() {
        return this.form.isDirty();
    },
    
    isValid: function() {
        return this.form.isValid();
    },
    
    reset: function() {
        this.form.reset();
    },
    
    hasInvalidField: function() {
        return this.form.hasInvalidField();
    },
    beforeDestroy: function() {
        this.stopPolling();
        this.form.destroy();
        this.callParent();
    },
    
    load: function(options) {
        this.form.load(options);
    },
    
    submit: function(options) {
        this.form.submit(options);
    },
    
    startPolling: function(interval) {
        this.stopPolling();
        var task = new Ext.util.TaskRunner(interval);
        task.start({
            interval: 0,
            run: this.checkChange,
            scope: this
        });
        this.pollTask = task;
    },
    
    stopPolling: function() {
        var task = this.pollTask;
        if (task) {
            task.stopAll();
            delete this.pollTask;
        }
    },
    
    checkChange: function() {
        var fields = this.form.getFields().items,
            f,
            fLen = fields.length;
        for (f = 0; f < fLen; f++) {
            fields[f].checkChange();
        }
    }
});


Ext.define('Ext.form.RadioManager', {
    extend: 'Ext.util.MixedCollection',
    singleton: true,
    getByName: function(name, formId) {
        return this.filterBy(function(item) {
            return item.name == name && item.getFormId() == formId;
        });
    },
    getWithValue: function(name, value, formId) {
        return this.filterBy(function(item) {
            return item.name == name && item.inputValue == value && item.getFormId() == formId;
        });
    },
    getChecked: function(name, formId) {
        return this.findBy(function(item) {
            return item.name == name && item.checked && item.getFormId() == formId;
        });
    }
});


Ext.define('Ext.form.field.Radio', {
    extend: 'Ext.form.field.Checkbox',
    alias: [
        'widget.radiofield',
        'widget.radio'
    ],
    alternateClassName: 'Ext.form.Radio',
    requires: [
        'Ext.form.RadioManager'
    ],
    
    isRadio: true,
    
    
    inputType: 'radio',
    ariaRole: 'radio',
    
    
    tabIndex: 0,
    formId: null,
    
    getGroupValue: function() {
        var selected = this.getManager().getChecked(this.name, this.getFormId());
        return selected ? selected.inputValue : null;
    },
    
    onBoxClick: function() {
        var me = this;
        if (!me.disabled && !me.readOnly) {
            this.setValue(true);
        }
    },
    onRemoved: function() {
        this.callParent(arguments);
        this.formId = null;
    },
    
    setValue: function(value) {
        var me = this,
            container, active;
        if (Ext.isBoolean(value)) {
            me.callParent(arguments);
        } else {
            active = me.getManager().getWithValue(me.name, value, me.getFormId()).getAt(0);
            if (active) {
                active.setValue(true);
            }
        }
        return me;
    },
    
    getSubmitValue: function() {
        return this.checked ? this.inputValue : null;
    },
    getModelData: function() {
        var o = this.callParent(arguments);
        if (o) {
            o[this.getName()] = this.getSubmitValue();
        }
        return o;
    },
    
    onChange: function(newVal, oldVal) {
        var me = this,
            r, rLen, radio, radios;
        me.callParent(arguments);
        if (newVal) {
            radios = me.getManager().getByName(me.name, me.getFormId()).items;
            rLen = radios.length;
            for (r = 0; r < rLen; r++) {
                radio = radios[r];
                if (radio !== me) {
                    radio.setValue(false);
                }
            }
        }
    },
    
    getManager: function() {
        return Ext.form.RadioManager;
    }
});


Ext.define('Ext.form.RadioGroup', {
    extend: 'Ext.form.CheckboxGroup',
    alias: 'widget.radiogroup',
    requires: [
        'Ext.form.field.Radio'
    ],
    mixins: [
        'Ext.util.FocusableContainer'
    ],
    
    
    allowBlank: true,
    
    
    blankText: 'You must select one item in this group',
    
    
    defaultType: 'radiofield',
    
    groupCls: Ext.baseCSSPrefix + 'form-radio-group',
    ariaRole: 'radiogroup',
    getBoxes: function(query, root) {
        return (root || this).query('[isRadio]' + (query || ''));
    },
    checkChange: function() {
        var value = this.getValue(),
            key = Ext.Object.getKeys(value)[0];
        
        
        if (Ext.isArray(value[key])) {
            return;
        }
        this.callParent(arguments);
    },
    
    setValue: function(value) {
        var cbValue, first, formId, radios, i, len, name;
        if (Ext.isObject(value)) {
            Ext.suspendLayouts();
            first = this.items.first();
            formId = first ? first.getFormId() : null;
            for (name in value) {
                cbValue = value[name];
                radios = Ext.form.RadioManager.getWithValue(name, cbValue, formId).items;
                len = radios.length;
                for (i = 0; i < len; ++i) {
                    radios[i].setValue(true);
                }
            }
            Ext.resumeLayouts(true);
        }
        return this;
    },
    privates: {
        getFocusables: function() {
            return this.getBoxes();
        },
        initDefaultFocusable: function(beforeRender) {
            var me = this,
                checked, item;
            checked = me.getChecked();
            
            if (checked.length > 1) {
                Ext.log.error("RadioGroup " + me.id + " has more than one checked button");
            }
            
            
            if (checked.length) {
                item = checked[0];
            } else {
                item = me.findNextFocusableChild(null, true, null, beforeRender);
            }
            if (item) {
                me.activateFocusable(item);
            }
            return item;
        },
        onFocusableContainerFocusLeave: function() {
            this.clearFocusables();
            this.initDefaultFocusable();
        },
        doFocusableChildAdd: function(child) {
            var me = this,
                mixin = me.mixins.focusablecontainer,
                boxes, i, len;
            boxes = child.isContainer ? me.getBoxes('', child) : [
                child
            ];
            for (i = 0 , len = boxes.length; i < len; i++) {
                mixin.doFocusableChildAdd.call(me, boxes[i]);
            }
        },
        doFocusableChildRemove: function(child) {
            var me = this,
                mixin = me.mixins.focusablecontainer,
                boxes, i, len;
            boxes = child.isContainer ? me.getBoxes('', child) : [
                child
            ];
            for (i = 0 , len = boxes.length; i < len; i++) {
                mixin.doFocusableChildRemove.call(me, boxes[i]);
            }
        },
        focusChild: function(radio, forward, e) {
            var nextRadio = this.mixins.focusablecontainer.focusChild.apply(this, arguments);
            
            if (!e.ctrlKey) {
                nextRadio.setValue(true);
            }
        }
    }
});


Ext.define('Ext.form.action.DirectAction', {
    extend: 'Ext.Mixin',
    mixinConfig: {
        id: 'directaction'
    },
    resolveMethod: function(type) {
        var me = this,
            form = me.form,
            api = form.api,
            fn;
        if (!api) {
            Ext.Error.raise("Cannot resolve Ext.Direct API method for " + type + " action; form " + form.id + " has no api object defined");
        }
        fn = api[type];
        if (typeof fn !== 'function') {
            var fnName = fn;
            api[type] = fn = Ext.direct.Manager.parseMethod(fn);
            if (!Ext.isFunction(fn)) {
                Ext.Error.raise("Cannot resolve Ext.Direct API method " + fnName + " for " + type + " action");
            }
        }
        return fn;
    }
});


Ext.define('Ext.form.action.DirectLoad', {
    extend: 'Ext.form.action.Load',
    alternateClassName: 'Ext.form.Action.DirectLoad',
    alias: 'formaction.directload',
    requires: [
        'Ext.direct.Manager'
    ],
    mixins: [
        'Ext.form.action.DirectAction'
    ],
    type: 'directload',
    run: function() {
        var me = this,
            form = me.form,
            metadata = me.metadata || form.metadata,
            timeout = me.timeout || form.timeout,
            args, fn;
        fn = me.resolveMethod('load');
        args = fn.directCfg.method.getArgs({
            params: me.getParams(),
            paramOrder: form.paramOrder,
            paramsAsHash: form.paramsAsHash,
            options: timeout != null ? {
                timeout: timeout * 1000
            } : null,
            metadata: metadata,
            callback: me.onComplete,
            scope: me
        });
        fn.apply(window, args);
    },
    
    
    
    processResponse: function(result) {
        return (this.result = result);
    },
    onComplete: function(data) {
        if (data) {
            this.onSuccess(data);
        } else {
            this.onFailure(null);
        }
    }
});


Ext.define('Ext.form.action.DirectSubmit', {
    extend: 'Ext.form.action.Submit',
    alternateClassName: 'Ext.form.Action.DirectSubmit',
    alias: 'formaction.directsubmit',
    requires: [
        'Ext.direct.Manager'
    ],
    mixins: [
        'Ext.form.action.DirectAction'
    ],
    type: 'directsubmit',
    doSubmit: function() {
        var me = this,
            form = me.form,
            metadata = me.metadata || form.metadata,
            timeout = me.timeout || form.timeout,
            fn, formInfo, args;
        fn = me.resolveMethod('submit');
        formInfo = me.buildForm();
        args = fn.directCfg.method.getArgs({
            params: formInfo.formEl,
            options: timeout != null ? {
                timeout: timeout * 1000
            } : null,
            metadata: metadata,
            callback: me.onComplete,
            scope: me
        });
        fn.apply(window, args);
        me.cleanup(formInfo);
    },
    
    
    
    processResponse: function(result) {
        return (this.result = result);
    },
    onComplete: function(data) {
        if (data) {
            this.onSuccess(data);
        } else {
            this.onFailure(null);
        }
    }
});


Ext.define('Ext.form.action.StandardSubmit', {
    extend: 'Ext.form.action.Submit',
    alias: 'formaction.standardsubmit',
    
    
    doSubmit: function() {
        var formInfo = this.buildForm();
        formInfo.formEl.submit();
        this.cleanup(formInfo);
    }
});


Ext.define('Ext.form.field.Picker', {
    extend: 'Ext.form.field.Text',
    alias: 'widget.pickerfield',
    alternateClassName: 'Ext.form.Picker',
    requires: [
        'Ext.util.KeyNav'
    ],
    config: {
        triggers: {
            picker: {
                handler: 'onTriggerClick',
                scope: 'this'
            }
        }
    },
    
    isPickerField: true,
    
    matchFieldWidth: true,
    
    pickerAlign: 'tl-bl?',
    
    
    openCls: Ext.baseCSSPrefix + 'pickerfield-open',
    
    isExpanded: false,
    
    editable: true,
    
    
    
    
    applyTriggers: function(triggers) {
        var me = this,
            picker = triggers.picker;
        if (!picker.cls) {
            picker.cls = me.triggerCls;
        }
        return me.callParent([
            triggers
        ]);
    },
    initEvents: function() {
        var me = this;
        me.callParent();
        
        me.keyNav = new Ext.util.KeyNav(me.inputEl,{
            down: me.onDownArrow,
            esc: {
                handler: me.onEsc,
                scope: me,
                defaultEventAction: false
            },
            scope: me,
            forceKeyDown: true
        });
        
        if (!me.editable) {
            me.mon(me.inputEl, 'click', me.onTriggerClick, me);
        }
        
        if (Ext.isGecko) {
            me.inputEl.dom.setAttribute('autocomplete', 'off');
        }
    },
    
    onEsc: function(e) {
        if (Ext.isIE) {
            
            
            
            
            e.preventDefault();
        }
        if (this.isExpanded) {
            this.collapse();
            e.stopEvent();
        }
    },
    onDownArrow: function(e) {
        if (!this.isExpanded) {
            
            e.stopEvent();
            
            
            this.onTriggerClick();
        }
    },
    
    expand: function() {
        var me = this,
            bodyEl, picker, doc, collapseIf;
        if (me.rendered && !me.isExpanded && !me.isDestroyed) {
            bodyEl = me.bodyEl;
            picker = me.getPicker();
            doc = Ext.getDoc();
            collapseIf = me.collapseIf;
            picker.setMaxHeight(picker.initialConfig.maxHeight);
            if (me.matchFieldWidth) {
                picker.width = me.bodyEl.getWidth();
            }
            
            picker.show();
            me.isExpanded = true;
            me.alignPicker();
            bodyEl.addCls(me.openCls);
            
            me.hideListeners = doc.on({
                mousewheel: me.collapseIf,
                touchstart: me.collapseIf,
                scope: me,
                delegated: false,
                destroyable: true
            });
            Ext.on('resize', me.alignPicker, me);
            me.fireEvent('expand', me);
            me.onExpand();
        }
    },
    onExpand: Ext.emptyFn,
    
    alignPicker: function() {
        var me = this,
            picker = me.getPicker();
        if (picker.isVisible()) {
            if (picker.isFloating()) {
                me.doAlign();
            }
        }
    },
    
    doAlign: function() {
        var me = this,
            picker = me.picker,
            aboveSfx = '-above',
            isAbove;
        
        
        me.picker.alignTo(me.triggerWrap, me.pickerAlign, me.pickerOffset);
        
        
        isAbove = picker.el.getY() < me.inputEl.getY();
        me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx);
        picker[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx);
    },
    
    collapse: function() {
        var me = this;
        if (me.isExpanded && !me.isDestroyed && !me.destroying) {
            var openCls = me.openCls,
                picker = me.picker,
                aboveSfx = '-above';
            
            picker.hide();
            me.isExpanded = false;
            
            me.bodyEl.removeCls([
                openCls,
                openCls + aboveSfx
            ]);
            picker.el.removeCls(picker.baseCls + aboveSfx);
            
            me.hideListeners.destroy();
            Ext.un('resize', me.alignPicker, me);
            me.fireEvent('collapse', me);
            me.onCollapse();
        }
    },
    onCollapse: Ext.emptyFn,
    
    collapseIf: function(e) {
        var me = this;
        
        
        if (!me.isDestroyed && !e.within(me.bodyEl, false, true) && !me.owns(e.target) && !Ext.fly(e.target).isFocusable()) {
            me.collapse();
        }
    },
    
    getPicker: function() {
        var me = this,
            picker = me.picker;
        if (!picker) {
            me.picker = picker = me.createPicker();
            
            picker.ownerCmp = me;
        }
        return me.picker;
    },
    
    
    onFocusLeave: function(e) {
        var me = this;
        me.collapse();
        me.callParent([
            e
        ]);
    },
    
    
    
    
    getRefItems: function() {
        var result = [];
        if (this.picker) {
            result[0] = this.picker;
        }
        return result;
    },
    
    createPicker: Ext.emptyFn,
    
    onTriggerClick: function(e) {
        var me = this;
        if (!me.readOnly && !me.disabled) {
            if (me.isExpanded) {
                me.collapse();
            } else {
                me.expand();
            }
        }
    },
    beforeDestroy: function() {
        var me = this,
            picker = me.picker;
        me.callParent();
        Ext.un('resize', me.alignPicker, me);
        Ext.destroy(me.keyNav, picker);
        if (picker) {
            me.picker = picker.pickerField = null;
        }
    }
});


Ext.define('Ext.view.BoundListKeyNav', {
    extend: 'Ext.view.NavigationModel',
    alias: 'view.navigation.boundlist',
    
    navigateOnSpace: true,
    initKeyNav: function(view) {
        var me = this,
            field = view.pickerField;
        
        
        if (!me.keyNav) {
            me.callParent([
                view
            ]);
        }
        
        if (!field) {
            return;
        }
        if (!field.rendered) {
            field.on('render', Ext.Function.bind(me.initKeyNav, me, [
                view
            ], 0), me, {
                single: true
            });
            return;
        }
        
        me.fieldKeyNav = new Ext.util.KeyNav({
            disabled: true,
            target: field.inputEl,
            forceKeyDown: true,
            up: me.onKeyUp,
            down: me.onKeyDown,
            right: me.onKeyRight,
            left: me.onKeyLeft,
            pageDown: me.onKeyPageDown,
            pageUp: me.onKeyPageUp,
            home: me.onKeyHome,
            end: me.onKeyEnd,
            tab: me.onKeyTab,
            space: me.onKeySpace,
            enter: me.onKeyEnter,
            A: {
                ctrl: true,
                
                
                handler: me.onSelectAllKeyPress
            },
            scope: me
        });
    },
    enable: function() {
        this.fieldKeyNav.enable();
        this.callParent();
    },
    disable: function() {
        this.fieldKeyNav.disable();
        this.callParent();
    },
    onItemMouseDown: function(view, record, item, index, event) {
        this.callParent([
            view,
            record,
            item,
            index,
            event
        ]);
        
        event.preventDefault();
    },
    onKeyUp: function() {
        var me = this,
            boundList = me.view,
            allItems = boundList.all,
            oldItem = boundList.highlightedItem,
            oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1,
            newItemIdx = oldItemIdx > 0 ? oldItemIdx - 1 : allItems.getCount() - 1;
        
        me.setPosition(newItemIdx);
    },
    onKeyDown: function() {
        var me = this,
            boundList = me.view,
            allItems = boundList.all,
            oldItem = boundList.highlightedItem,
            oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1,
            newItemIdx = oldItemIdx < allItems.getCount() - 1 ? oldItemIdx + 1 : 0;
        
        me.setPosition(newItemIdx);
    },
    onKeyLeft: Ext.returnTrue,
    onKeyRight: Ext.returnTrue,
    onKeyTab: function(e) {
        var view = this.view,
            field = view.pickerField;
        if (view.isVisible()) {
            if (field.selectOnTab) {
                this.selectHighlighted(e);
            }
            if (field.collapse) {
                field.collapse();
            }
        }
        
        return true;
    },
    onKeyEnter: function(e) {
        var view = this.view,
            selModel = view.getSelectionModel(),
            field = view.pickerField,
            count = selModel.getCount();
        
        
        e.stopEvent();
        this.selectHighlighted(e);
        
        
        if (!field.multiSelect && count === selModel.getCount() && field.collapse) {
            field.collapse();
        }
    },
    onKeySpace: function() {
        if (this.navigateOnSpace) {
            this.callParent(arguments);
        }
        
        return true;
    },
    
    focusItem: function(item) {
        var me = this,
            boundList = me.view;
        if (typeof item === 'number') {
            item = boundList.all.item(item);
        }
        if (item) {
            item = item.dom;
            boundList.highlightItem(item);
            boundList.getOverflowEl().scrollChildIntoView(item, false);
        }
    },
    
    selectHighlighted: function(e) {
        var me = this,
            boundList = me.view,
            selModel = boundList.getSelectionModel(),
            highlightedRec,
            highlightedPosition = me.recordIndex;
        
        if (boundList.all.getCount()) {
            highlightedRec = me.getRecord();
            if (highlightedRec) {
                
                
                if (e.getKey() === e.ENTER || !selModel.isSelected(highlightedRec)) {
                    selModel.selectWithEvent(highlightedRec, e);
                    
                    
                    if (!boundList.store.data.contains(highlightedRec)) {
                        me.setPosition(Math.min(highlightedPosition, boundList.store.getCount() - 1));
                    }
                }
            }
        }
    },
    destroy: function() {
        Ext.destroy(this.fieldKeyNav);
        this.callParent();
    }
});


Ext.define('Ext.layout.component.BoundList', {
    extend: 'Ext.layout.component.Auto',
    alias: 'layout.boundlist',
    type: 'component',
    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            toolbar = owner.pagingToolbar;
        me.scrollPos = owner.listWrap.getScroll();
        me.callParent(arguments);
        if (owner.floating) {
            ownerContext.savedXY = owner.getXY();
            
            
            owner.setXY([
                0,
                -9999
            ]);
        }
        if (toolbar) {
            ownerContext.toolbarContext = ownerContext.context.getCmp(toolbar);
        }
        ownerContext.listContext = ownerContext.getEl('listWrap');
    },
    beginLayoutCycle: function(ownerContext) {
        var owner = this.owner;
        this.callParent(arguments);
        if (ownerContext.heightModel.auto) {
            
            
            
            owner.el.setHeight('auto');
            owner.listWrap.setHeight('auto');
        }
    },
    getLayoutItems: function() {
        var toolbar = this.owner.pagingToolbar;
        return toolbar ? [
            toolbar
        ] : [];
    },
    isValidParent: function() {
        
        
        return true;
    },
    finishedLayout: function(ownerContext) {
        var me = this,
            xy = ownerContext.savedXY,
            owner = me.owner,
            listWrap = owner.listWrap,
            scrollPos = me.scrollPos;
        me.callParent(arguments);
        if (xy) {
            me.owner.setXY(xy);
        }
        listWrap.setScrollLeft(scrollPos.left);
        listWrap.setScrollTop(scrollPos.top);
    },
    measureContentWidth: function(ownerContext) {
        return this.owner.listWrap.getWidth();
    },
    measureContentHeight: function(ownerContext) {
        return this.owner.listWrap.getHeight();
    },
    publishInnerHeight: function(ownerContext, height) {
        var toolbar = ownerContext.toolbarContext,
            toolbarHeight = 0;
        if (toolbar) {
            toolbarHeight = toolbar.getProp('height');
        }
        if (toolbarHeight === undefined) {
            this.done = false;
        } else {
            ownerContext.listContext.setHeight(height - ownerContext.getFrameInfo().height - toolbarHeight);
        }
    },
    calculateOwnerHeightFromContentHeight: function(ownerContext) {
        var height = this.callParent(arguments),
            toolbar = ownerContext.toolbarContext;
        if (toolbar) {
            height += toolbar.getProp('height');
        }
        return height;
    }
});


Ext.define('Ext.toolbar.Item', {
    extend: 'Ext.Component',
    
    
    requires: [
        'Ext.toolbar.Toolbar'
    ],
    alias: 'widget.tbitem',
    alternateClassName: 'Ext.Toolbar.Item',
    enable: Ext.emptyFn,
    disable: Ext.emptyFn,
    focus: Ext.emptyFn
});


Ext.define('Ext.toolbar.TextItem', {
    extend: 'Ext.toolbar.Item',
    
    
    requires: [
        'Ext.toolbar.Toolbar',
        'Ext.XTemplate'
    ],
    alias: 'widget.tbtext',
    alternateClassName: 'Ext.Toolbar.TextItem',
    
    text: '',
    baseCls: Ext.baseCSSPrefix + 'toolbar-text',
    ariaRole: null,
    beforeRender: function() {
        var text = this.text;
        this.callParent();
        if (text) {
            this.html = text;
        }
    },
    
    setText: function(text) {
        this.update(text);
    }
});


Ext.define('Ext.form.trigger.Spinner', {
    extend: 'Ext.form.trigger.Trigger',
    alias: 'trigger.spinner',
    cls: Ext.baseCSSPrefix + 'form-trigger-spinner',
    spinnerCls: Ext.baseCSSPrefix + 'form-spinner',
    spinnerUpCls: Ext.baseCSSPrefix + 'form-spinner-up',
    spinnerDownCls: Ext.baseCSSPrefix + 'form-spinner-down',
    focusCls: Ext.baseCSSPrefix + 'form-spinner-focus',
    overCls: Ext.baseCSSPrefix + 'form-spinner-over',
    clickCls: Ext.baseCSSPrefix + 'form-spinner-click',
    
    focusFieldOnClick: true,
    
    
    
    vertical: true,
    bodyTpl: '<tpl if="vertical">' + '<div class="{spinnerCls} {spinnerCls}-{ui} {spinnerUpCls} {spinnerUpCls}-{ui}' + ' {childElCls} {upDisabledCls}"></div>' + '</tpl>' + '<div class="{spinnerCls} {spinnerCls}-{ui} {spinnerDownCls} {spinnerDownCls}-{ui}' + ' {childElCls} {downDisabledCls}"></div>' + '<tpl if="!vertical">' + '<div class="{spinnerCls} {spinnerCls}-{ui} {spinnerUpCls} {spinnerUpCls}-{ui}' + ' {childElCls} {upDisabledCls}"></div>' + '</tpl>',
    destroy: function() {
        var me = this;
        if (me.spinnerEl) {
            me.spinnerEl.destroy();
            me.spinnerEl = me.upEl = me.downEl = null;
        }
        me.callParent();
    },
    getBodyRenderData: function() {
        var me = this;
        return {
            vertical: me.vertical,
            upDisabledCls: me.upEnabled ? '' : (me.spinnerUpCls + '-disabled'),
            downDisabledCls: me.downEnabled ? '' : (me.spinnerDownCls + '-disabled'),
            spinnerCls: me.spinnerCls,
            spinnerUpCls: me.spinnerUpCls,
            spinnerDownCls: me.spinnerDownCls
        };
    },
    getStateEl: function() {
        return this.spinnerEl;
    },
    onClick: function(e) {
        var me = this,
            args = arguments,
            e = me.clickRepeater ? args[1] : args[0],
            field = me.field;
        if (!field.readOnly && !field.disabled) {
            if (me.upEl.contains(e.target)) {
                Ext.callback(me.upHandler, me.scope, [
                    field,
                    me,
                    e
                ], 0, field);
            } else if (me.downEl.contains(e.target)) {
                Ext.callback(me.downHandler, me.scope, [
                    field,
                    me,
                    e
                ], 0, field);
            }
        }
        field.inputEl.focus();
    },
    onFieldRender: function() {
        var me = this,
            vertical = me.vertical,
            spinnerEl, elements;
        me.callParent();
        
        spinnerEl = me.spinnerEl = me.el.select('.' + me.spinnerCls, true);
        elements = spinnerEl.elements;
        me.upEl = vertical ? elements[0] : elements[1];
        me.downEl = vertical ? elements[1] : elements[0];
    },
    
    setUpEnabled: function(enabled) {
        this.upEl[enabled ? 'removeCls' : 'addCls'](this.spinnerUpCls + '-disabled');
    },
    
    setDownEnabled: function(enabled) {
        this.downEl[enabled ? 'removeCls' : 'addCls'](this.spinnerDownCls + '-disabled');
    }
});


Ext.define('Ext.form.field.Spinner', {
    extend: 'Ext.form.field.Text',
    alias: 'widget.spinnerfield',
    alternateClassName: 'Ext.form.Spinner',
    requires: [
        'Ext.form.trigger.Spinner',
        'Ext.util.KeyNav'
    ],
    config: {
        triggers: {
            spinner: {
                type: 'spinner',
                upHandler: 'onSpinnerUpClick',
                downHandler: 'onSpinnerDownClick',
                scope: 'this'
            }
        }
    },
    
    spinUpEnabled: true,
    
    spinDownEnabled: true,
    
    keyNavEnabled: true,
    
    mouseWheelEnabled: true,
    
    repeatTriggerClick: true,
    
    onSpinUp: Ext.emptyFn,
    
    onSpinDown: Ext.emptyFn,
    ariaRole: 'spinbutton',
    
    
    
    applyTriggers: function(triggers) {
        var me = this,
            spinnerTrigger = triggers.spinner;
        spinnerTrigger.upEnabled = me.spinUpEnabled;
        spinnerTrigger.downEnabled = me.spinDownEnabled;
        return me.callParent([
            triggers
        ]);
    },
    
    onRender: function() {
        var me = this,
            spinnerTrigger = me.getTrigger('spinner');
        me.callParent();
        
        if (me.keyNavEnabled) {
            me.spinnerKeyNav = new Ext.util.KeyNav(me.inputEl,{
                scope: me,
                up: me.spinUp,
                down: me.spinDown
            });
        }
        
        if (me.mouseWheelEnabled) {
            me.mon(me.bodyEl, 'mousewheel', me.onMouseWheel, me);
        }
        
        
        me.spinUpEl = spinnerTrigger.upEl;
        me.spinDownEl = spinnerTrigger.downEl;
    },
    
    onSpinnerUpClick: function() {
        this.spinUp();
    },
    
    onSpinnerDownClick: function() {
        this.spinDown();
    },
    
    spinUp: function() {
        var me = this;
        if (me.spinUpEnabled && !me.disabled) {
            me.fireEvent('spin', me, 'up');
            me.fireEvent('spinup', me);
            me.onSpinUp();
        }
    },
    
    spinDown: function() {
        var me = this;
        if (me.spinDownEnabled && !me.disabled) {
            me.fireEvent('spin', me, 'down');
            me.fireEvent('spindown', me);
            me.onSpinDown();
        }
    },
    
    setSpinUpEnabled: function(enabled) {
        var me = this,
            wasEnabled = me.spinUpEnabled;
        me.spinUpEnabled = enabled;
        if (wasEnabled !== enabled && me.rendered) {
            me.getTrigger('spinner').setUpEnabled(enabled);
        }
    },
    
    setSpinDownEnabled: function(enabled) {
        var me = this,
            wasEnabled = me.spinDownEnabled;
        me.spinDownEnabled = enabled;
        if (wasEnabled !== enabled && me.rendered) {
            me.getTrigger('spinner').setDownEnabled(enabled);
        }
    },
    
    onMouseWheel: function(e) {
        var me = this,
            delta;
        if (me.hasFocus) {
            delta = e.getWheelDelta();
            if (delta > 0) {
                me.spinUp();
            } else if (delta < 0) {
                me.spinDown();
            }
            e.stopEvent();
        }
    },
    onDestroy: function() {
        Ext.destroyMembers(this, 'spinnerKeyNav');
        this.callParent();
    }
});


Ext.define('Ext.form.field.Number', {
    extend: 'Ext.form.field.Spinner',
    alias: 'widget.numberfield',
    alternateClassName: [
        'Ext.form.NumberField',
        'Ext.form.Number'
    ],
    
    
    
    allowExponential: true,
    
    allowDecimals: true,
    
    
    decimalSeparator: null,
    
    
    
    submitLocaleSeparator: true,
    
    
    
    decimalPrecision: 2,
    
    
    minValue: Number.NEGATIVE_INFINITY,
    
    maxValue: Number.MAX_VALUE,
    
    step: 1,
    
    
    minText: 'The minimum value for this field is {0}',
    
    
    
    maxText: 'The maximum value for this field is {0}',
    
    
    
    nanText: '{0} is not a valid number',
    
    
    
    negativeText: 'The value cannot be negative',
    
    
    baseChars: '0123456789',
    
    autoStripChars: false,
    initComponent: function() {
        var me = this;
        if (me.decimalSeparator === null) {
            me.decimalSeparator = Ext.util.Format.decimalSeparator;
        }
        me.callParent();
        me.setMinValue(me.minValue);
        me.setMaxValue(me.maxValue);
    },
    
    getErrors: function(value) {
        value = arguments.length > 0 ? value : this.processRawValue(this.getRawValue());
        var me = this,
            errors = me.callParent([
                value
            ]),
            format = Ext.String.format,
            num;
        if (value.length < 1) {
            
            return errors;
        }
        value = String(value).replace(me.decimalSeparator, '.');
        if (isNaN(value)) {
            errors.push(format(me.nanText, value));
        }
        num = me.parseValue(value);
        if (me.minValue === 0 && num < 0) {
            errors.push(this.negativeText);
        } else if (num < me.minValue) {
            errors.push(format(me.minText, me.minValue));
        }
        if (num > me.maxValue) {
            errors.push(format(me.maxText, me.maxValue));
        }
        return errors;
    },
    rawToValue: function(rawValue) {
        var value = this.fixPrecision(this.parseValue(rawValue));
        if (value === null) {
            value = rawValue || null;
        }
        return value;
    },
    valueToRaw: function(value) {
        var me = this,
            decimalSeparator = me.decimalSeparator;
        value = me.parseValue(value);
        value = me.fixPrecision(value);
        value = Ext.isNumber(value) ? value : parseFloat(String(value).replace(decimalSeparator, '.'));
        value = isNaN(value) ? '' : String(value).replace('.', decimalSeparator);
        return value;
    },
    getSubmitValue: function() {
        var me = this,
            value = me.callParent();
        if (!me.submitLocaleSeparator) {
            value = value.replace(me.decimalSeparator, '.');
        }
        return value;
    },
    onChange: function() {
        this.toggleSpinners();
        this.callParent(arguments);
    },
    toggleSpinners: function() {
        var me = this,
            value = me.getValue(),
            valueIsNull = value === null,
            enabled;
        
        
        if (me.spinUpEnabled || me.spinUpDisabledByToggle) {
            enabled = valueIsNull || value < me.maxValue;
            me.setSpinUpEnabled(enabled, true);
        }
        if (me.spinDownEnabled || me.spinDownDisabledByToggle) {
            enabled = valueIsNull || value > me.minValue;
            me.setSpinDownEnabled(enabled, true);
        }
    },
    
    setMinValue: function(value) {
        var me = this,
            allowed;
        me.minValue = Ext.Number.from(value, Number.NEGATIVE_INFINITY);
        me.toggleSpinners();
        
        if (me.disableKeyFilter !== true) {
            allowed = me.baseChars + '';
            if (me.allowExponential) {
                allowed += me.decimalSeparator + 'e+-';
            } else {
                if (me.allowDecimals) {
                    allowed += me.decimalSeparator;
                }
                if (me.minValue < 0) {
                    allowed += '-';
                }
            }
            allowed = Ext.String.escapeRegex(allowed);
            me.maskRe = new RegExp('[' + allowed + ']');
            if (me.autoStripChars) {
                me.stripCharsRe = new RegExp('[^' + allowed + ']','gi');
            }
        }
    },
    
    setMaxValue: function(value) {
        this.maxValue = Ext.Number.from(value, Number.MAX_VALUE);
        this.toggleSpinners();
    },
    
    parseValue: function(value) {
        value = parseFloat(String(value).replace(this.decimalSeparator, '.'));
        return isNaN(value) ? null : value;
    },
    
    fixPrecision: function(value) {
        var me = this,
            nan = isNaN(value),
            precision = me.decimalPrecision;
        if (nan || !value) {
            return nan ? '' : value;
        } else if (!me.allowDecimals || precision <= 0) {
            precision = 0;
        }
        return parseFloat(Ext.Number.toFixed(parseFloat(value), precision));
    },
    onBlur: function(e) {
        var me = this,
            v = me.rawToValue(me.getRawValue());
        if (!Ext.isEmpty(v)) {
            me.setValue(v);
        }
        me.callParent([
            e
        ]);
    },
    setSpinUpEnabled: function(enabled, 
    internal) {
        this.callParent(arguments);
        if (!internal) {
            delete this.spinUpDisabledByToggle;
        } else {
            this.spinUpDisabledByToggle = !enabled;
        }
    },
    onSpinUp: function() {
        var me = this;
        if (!me.readOnly) {
            me.setSpinValue(Ext.Number.constrain(me.getValue() + me.step, me.minValue, me.maxValue));
        }
    },
    setSpinDownEnabled: function(enabled, 
    internal) {
        this.callParent(arguments);
        if (!internal) {
            delete this.spinDownDisabledByToggle;
        } else {
            this.spinDownDisabledByToggle = !enabled;
        }
    },
    onSpinDown: function() {
        var me = this;
        if (!me.readOnly) {
            me.setSpinValue(Ext.Number.constrain(me.getValue() - me.step, me.minValue, me.maxValue));
        }
    },
    setSpinValue: function(value) {
        var me = this;
        if (me.enforceMaxLength) {
            
            
            if (me.fixPrecision(value).toString().length > me.maxLength) {
                return;
            }
        }
        me.setValue(value);
    }
});


Ext.define('Ext.toolbar.Paging', {
    extend: 'Ext.toolbar.Toolbar',
    xtype: 'pagingtoolbar',
    alternateClassName: 'Ext.PagingToolbar',
    requires: [
        'Ext.toolbar.TextItem',
        'Ext.form.field.Number'
    ],
    mixins: [
        'Ext.util.StoreHolder'
    ],
    
    
    displayInfo: false,
    
    prependButtons: false,
    
    
    displayMsg: 'Displaying {0} - {1} of {2}',
    
    
    
    emptyMsg: 'No data to display',
    
    
    
    beforePageText: 'Page',
    
    
    
    afterPageText: 'of {0}',
    
    
    
    firstText: 'First Page',
    
    
    
    prevText: 'Previous Page',
    
    
    
    nextText: 'Next Page',
    
    
    
    lastText: 'Last Page',
    
    
    
    refreshText: 'Refresh',
    
    
    inputItemWidth: 30,
    
    
    emptyPageData: {
        total: 0,
        currentPage: 0,
        pageCount: 0,
        toRecord: 0,
        fromRecord: 0
    },
    
    defaultBindProperty: 'store',
    
    getPagingItems: function() {
        var me = this,
            inputListeners = {
                scope: me,
                blur: me.onPagingBlur
            };
        inputListeners[Ext.supports.SpecialKeyDownRepeat ? 'keydown' : 'keypress'] = me.onPagingKeyDown;
        return [
            {
                itemId: 'first',
                tooltip: me.firstText,
                overflowText: me.firstText,
                iconCls: Ext.baseCSSPrefix + 'tbar-page-first',
                disabled: true,
                handler: me.moveFirst,
                scope: me
            },
            {
                itemId: 'prev',
                tooltip: me.prevText,
                overflowText: me.prevText,
                iconCls: Ext.baseCSSPrefix + 'tbar-page-prev',
                disabled: true,
                handler: me.movePrevious,
                scope: me
            },
            '-',
            me.beforePageText,
            {
                xtype: 'numberfield',
                itemId: 'inputItem',
                name: 'inputItem',
                cls: Ext.baseCSSPrefix + 'tbar-page-number',
                allowDecimals: false,
                minValue: 1,
                hideTrigger: true,
                enableKeyEvents: true,
                keyNavEnabled: false,
                selectOnFocus: true,
                submitValue: false,
                
                isFormField: false,
                width: me.inputItemWidth,
                margin: '-1 2 3 2',
                listeners: inputListeners
            },
            {
                xtype: 'tbtext',
                itemId: 'afterTextItem',
                text: Ext.String.format(me.afterPageText, 1)
            },
            '-',
            {
                itemId: 'next',
                tooltip: me.nextText,
                overflowText: me.nextText,
                iconCls: Ext.baseCSSPrefix + 'tbar-page-next',
                disabled: true,
                handler: me.moveNext,
                scope: me
            },
            {
                itemId: 'last',
                tooltip: me.lastText,
                overflowText: me.lastText,
                iconCls: Ext.baseCSSPrefix + 'tbar-page-last',
                disabled: true,
                handler: me.moveLast,
                scope: me
            },
            '-',
            {
                itemId: 'refresh',
                tooltip: me.refreshText,
                overflowText: me.refreshText,
                iconCls: Ext.baseCSSPrefix + 'tbar-loading',
                disabled: me.store.isLoading(),
                handler: me.doRefresh,
                scope: me
            }
        ];
    },
    initComponent: function() {
        var me = this,
            userItems = me.items || me.buttons || [],
            pagingItems;
        me.bindStore(me.store || 'ext-empty-store', true);
        pagingItems = me.getPagingItems();
        if (me.prependButtons) {
            me.items = userItems.concat(pagingItems);
        } else {
            me.items = pagingItems.concat(userItems);
        }
        delete me.buttons;
        if (me.displayInfo) {
            me.items.push('->');
            me.items.push({
                xtype: 'tbtext',
                itemId: 'displayItem'
            });
        }
        me.callParent();
    },
    beforeRender: function() {
        this.callParent(arguments);
        this.updateBarInfo();
    },
    updateBarInfo: function() {
        var me = this;
        if (!me.store.isLoading()) {
            me.calledInternal = true;
            me.onLoad();
            me.calledInternal = false;
        }
    },
    
    updateInfo: function() {
        var me = this,
            displayItem = me.child('#displayItem'),
            store = me.store,
            pageData = me.getPageData(),
            count, msg;
        if (displayItem) {
            count = store.getCount();
            if (count === 0) {
                msg = me.emptyMsg;
            } else {
                msg = Ext.String.format(me.displayMsg, pageData.fromRecord, pageData.toRecord, pageData.total);
            }
            displayItem.setText(msg);
        }
    },
    
    onLoad: function() {
        var me = this,
            pageData, currPage, pageCount, afterText, count, isEmpty, item;
        count = me.store.getCount();
        isEmpty = count === 0;
        if (!isEmpty) {
            pageData = me.getPageData();
            currPage = pageData.currentPage;
            pageCount = pageData.pageCount;
            
            if (currPage > pageCount) {
                me.store.loadPage(pageCount);
                return;
            }
            afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);
        } else {
            currPage = 0;
            pageCount = 0;
            afterText = Ext.String.format(me.afterPageText, 0);
        }
        Ext.suspendLayouts();
        item = me.child('#afterTextItem');
        if (item) {
            item.setText(afterText);
        }
        item = me.getInputItem();
        if (item) {
            item.setDisabled(isEmpty).setValue(currPage);
        }
        me.setChildDisabled('#first', currPage === 1 || isEmpty);
        me.setChildDisabled('#prev', currPage === 1 || isEmpty);
        me.setChildDisabled('#next', currPage === pageCount || isEmpty);
        me.setChildDisabled('#last', currPage === pageCount || isEmpty);
        me.setChildDisabled('#refresh', false);
        me.updateInfo();
        Ext.resumeLayouts(true);
        if (!me.calledInternal) {
            me.fireEvent('change', me, pageData || me.emptyPageData);
        }
    },
    setChildDisabled: function(selector, disabled) {
        var item = this.child(selector);
        if (item) {
            item.setDisabled(disabled);
        }
    },
    
    getPageData: function() {
        var store = this.store,
            totalCount = store.getTotalCount();
        return {
            total: totalCount,
            currentPage: store.currentPage,
            pageCount: Math.ceil(totalCount / store.pageSize),
            fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
            toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
        };
    },
    
    onLoadError: function() {
        this.setChildDisabled('#refresh', false);
    },
    getInputItem: function() {
        return this.child('#inputItem');
    },
    
    readPageFromInput: function(pageData) {
        var inputItem = this.getInputItem(),
            pageNum = false,
            v;
        if (inputItem) {
            v = inputItem.getValue();
            pageNum = parseInt(v, 10);
            if (!v || isNaN(pageNum)) {
                inputItem.setValue(pageData.currentPage);
                return false;
            }
        }
        return pageNum;
    },
    
    onPagingBlur: function(e) {
        var inputItem = this.getInputItem(),
            curPage;
        if (inputItem) {
            curPage = this.getPageData().currentPage;
            inputItem.setValue(curPage);
        }
    },
    
    onPagingKeyDown: function(field, e) {
        this.processKeyEvent(field, e);
    },
    processKeyEvent: function(field, e) {
        var me = this,
            k = e.getKey(),
            pageData = me.getPageData(),
            increment = e.shiftKey ? 10 : 1,
            pageNum;
        if (k == e.RETURN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum !== false) {
                pageNum = Math.min(Math.max(1, pageNum), pageData.pageCount);
                if (pageNum !== pageData.currentPage && me.fireEvent('beforechange', me, pageNum) !== false) {
                    me.store.loadPage(pageNum);
                }
            }
        } else if (k == e.HOME || k == e.END) {
            e.stopEvent();
            pageNum = k == e.HOME ? 1 : pageData.pageCount;
            field.setValue(pageNum);
        } else if (k == e.UP || k == e.PAGE_UP || k == e.DOWN || k == e.PAGE_DOWN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum) {
                if (k == e.DOWN || k == e.PAGE_DOWN) {
                    increment *= -1;
                }
                pageNum += increment;
                if (pageNum >= 1 && pageNum <= pageData.pageCount) {
                    field.setValue(pageNum);
                }
            }
        }
    },
    
    beforeLoad: function() {
        this.setChildDisabled('#refresh', true);
    },
    
    moveFirst: function() {
        if (this.fireEvent('beforechange', this, 1) !== false) {
            this.store.loadPage(1);
            return true;
        }
        return false;
    },
    
    movePrevious: function() {
        var me = this,
            store = me.store,
            prev = store.currentPage - 1;
        if (prev > 0) {
            if (me.fireEvent('beforechange', me, prev) !== false) {
                store.previousPage();
                return true;
            }
        }
        return false;
    },
    
    moveNext: function() {
        var me = this,
            store = me.store,
            total = me.getPageData().pageCount,
            next = store.currentPage + 1;
        if (next <= total) {
            if (me.fireEvent('beforechange', me, next) !== false) {
                store.nextPage();
                return true;
            }
        }
        return false;
    },
    
    moveLast: function() {
        var me = this,
            last = me.getPageData().pageCount;
        if (me.fireEvent('beforechange', me, last) !== false) {
            me.store.loadPage(last);
            return true;
        }
        return false;
    },
    
    doRefresh: function() {
        var me = this,
            store = me.store,
            current = store.currentPage;
        if (me.fireEvent('beforechange', me, current) !== false) {
            store.loadPage(current);
            return true;
        }
        return false;
    },
    getStoreListeners: function() {
        return {
            beforeload: this.beforeLoad,
            load: this.onLoad,
            exception: this.onLoadError
        };
    },
    onBindStore: function() {
        if (this.rendered) {
            this.updateBarInfo();
        }
    },
    
    onDestroy: function() {
        this.bindStore(null);
        this.callParent();
    }
});


Ext.define('Ext.view.BoundList', {
    extend: 'Ext.view.View',
    alias: 'widget.boundlist',
    alternateClassName: 'Ext.BoundList',
    requires: [
        'Ext.view.BoundListKeyNav',
        'Ext.layout.component.BoundList',
        'Ext.toolbar.Paging'
    ],
    mixins: [
        'Ext.mixin.Queryable'
    ],
    
    pageSize: 0,
    
    
    
    baseCls: Ext.baseCSSPrefix + 'boundlist',
    itemCls: Ext.baseCSSPrefix + 'boundlist-item',
    listItemCls: '',
    shadow: false,
    trackOver: true,
    preserveScrollOnRefresh: true,
    enableInitialSelection: false,
    refreshSelmodelOnRefresh: true,
    componentLayout: 'boundlist',
    navigationModel: 'boundlist',
    scrollable: true,
    childEls: [
        'listWrap',
        'listEl'
    ],
    renderTpl: [
        '<div id="{id}-listWrap" data-ref="listWrap" role="presentation" class="{baseCls}-list-ct ',
        Ext.dom.Element.unselectableCls,
        '">',
        '<ul id="{id}-listEl" data-ref="listEl" class="' + Ext.baseCSSPrefix + 'list-plain">',
        '</ul>',
        '</div>',
        '{%',
        'var pagingToolbar=values.$comp.pagingToolbar;',
        'if (pagingToolbar) {',
        'Ext.DomHelper.generateMarkup(pagingToolbar.getRenderTree(), out);',
        '}',
        '%}',
        {
            disableFormats: true
        }
    ],
    
    
    
    
    
    focusOnToFront: false,
    initComponent: function() {
        var me = this,
            baseCls = me.baseCls,
            itemCls = me.itemCls;
        me.selectedItemCls = baseCls + '-selected';
        if (me.trackOver) {
            me.overItemCls = baseCls + '-item-over';
        }
        me.itemSelector = "." + itemCls;
        me.scrollerSelector = 'ul.' + Ext.baseCSSPrefix + 'list-plain';
        if (me.floating) {
            me.addCls(baseCls + '-floating');
        }
        if (!me.tpl) {
            
            
            me.tpl = new Ext.XTemplate('<tpl for=".">','<li role="option" unselectable="on" class="' + itemCls + '">' + me.getInnerTpl(me.displayField) + '</li>','</tpl>');
        } else if (!me.tpl.isTemplate) {
            me.tpl = new Ext.XTemplate(me.tpl);
        }
        if (me.pageSize) {
            me.pagingToolbar = me.createPagingToolbar();
        }
        me.callParent();
    },
    getRefOwner: function() {
        return this.pickerField || this.callParent();
    },
    getRefItems: function() {
        var result = this.callParent(),
            toolbar = this.pagingToolbar;
        if (toolbar) {
            result.push(toolbar);
        }
        return result;
    },
    createPagingToolbar: function() {
        return Ext.widget('pagingtoolbar', {
            id: this.id + '-paging-toolbar',
            pageSize: this.pageSize,
            store: this.dataSource,
            border: false,
            ownerCt: this,
            ownerLayout: this.getComponentLayout()
        });
    },
    getNodeContainer: function() {
        return this.listEl;
    },
    refresh: function() {
        var me = this,
            tpl = me.tpl,
            toolbar = me.pagingToolbar,
            rendered = me.rendered;
        
        tpl.field = me.pickerField;
        tpl.store = me.store;
        me.callParent();
        tpl.field = tpl.store = null;
        
        
        if (rendered && toolbar && toolbar.rendered && !me.preserveScrollOnRefresh) {
            me.el.appendChild(toolbar.el, true);
        }
    },
    bindStore: function(store, initial) {
        var toolbar = this.pagingToolbar;
        this.callParent(arguments);
        if (toolbar) {
            toolbar.bindStore(store, initial);
        }
    },
    
    getInnerTpl: function(displayField) {
        return '{' + displayField + '}';
    },
    onShow: function() {
        this.callParent();
        
        if (Ext.Element.getActiveElement() !== this.pickerField.inputEl.dom) {
            this.focus();
        }
    },
    onHide: function() {
        var inputEl = this.pickerField.inputEl.dom;
        
        
        
        if (Ext.Element.getActiveElement() !== inputEl && Ext.EventObject.pointerType !== 'touch') {
            inputEl.focus();
        }
        
        
        this.callParent(arguments);
    },
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var picker = this.pickerField;
        this.callParent(arguments);
        
        
        if (picker && picker.alignPicker) {
            picker.alignPicker();
        }
    },
    
    onItemClick: function(record) {
        
        var me = this,
            pickerField = me.pickerField,
            valueField = pickerField.valueField,
            selected = me.getSelectionModel().getSelection();
        if (!pickerField.multiSelect && selected.length) {
            selected = selected[0];
            
            if (selected && pickerField.isEqual(record.get(valueField), selected.get(valueField)) && pickerField.collapse) {
                pickerField.collapse();
            }
        }
    },
    onDestroy: function() {
        this.callParent();
        Ext.destroyMembers(this, 'pagingToolbar', 'listWrap', 'listEl');
    },
    privates: {
        getTargetEl: function() {
            return this.listEl;
        },
        getOverflowEl: function() {
            return this.listWrap;
        },
        
        finishRenderChildren: function() {
            var toolbar = this.pagingToolbar;
            this.callParent(arguments);
            if (toolbar) {
                toolbar.finishRender();
            }
        }
    }
});


Ext.define('Ext.form.field.ComboBox', {
    extend: 'Ext.form.field.Picker',
    requires: [
        'Ext.util.DelayedTask',
        'Ext.view.BoundList',
        'Ext.data.StoreManager'
    ],
    alternateClassName: 'Ext.form.ComboBox',
    alias: [
        'widget.combobox',
        'widget.combo'
    ],
    mixins: [
        'Ext.util.StoreHolder'
    ],
    config: {
        filters: null,
        
        selection: null,
        
        valueNotFoundText: null
    },
    publishes: [
        'selection'
    ],
    twoWayBindable: [
        'selection'
    ],
    
    
    triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',
    
    hiddenName: '',
    
    
    collapseOnSelect: false,
    
    
    hiddenDataCls: Ext.baseCSSPrefix + 'hidden-display ' + Ext.baseCSSPrefix + 'form-data-hidden',
    ariaRole: 'combobox',
    childEls: {
        'hiddenDataEl': true
    },
    
    filtered: false,
    afterRender: function() {
        var me = this;
        me.callParent(arguments);
        me.setHiddenValue(me.value);
    },
    
    
    multiSelect: false,
    
    
    delimiter: ', ',
    
    
    displayField: 'text',
    
    
    triggerAction: 'all',
    
    allQuery: '',
    
    queryParam: 'query',
    
    queryMode: 'remote',
    
    queryCaching: true,
    
    autoLoadOnValue: false,
    
    pageSize: 0,
    
    
    
    anyMatch: false,
    
    caseSensitive: false,
    
    autoSelect: true,
    
    typeAhead: false,
    
    typeAheadDelay: 250,
    
    selectOnTab: true,
    
    forceSelection: false,
    
    growToLongestValue: true,
    
    clearFilterOnBlur: true,
    
    
    
    defaultListConfig: {
        loadingHeight: 70,
        minWidth: 70,
        maxHeight: 300,
        shadow: 'sides'
    },
    
    
    transformInPlace: true,
    
    getGrowWidth: function() {
        var me = this,
            value = me.inputEl.dom.value,
            field, store, dataLn, currentLongestLength, i, item, itemLn;
        if (me.growToLongestValue) {
            field = me.displayField;
            store = me.store;
            dataLn = store.data.length;
            currentLongestLength = 0;
            for (i = 0; i < dataLn; i++) {
                item = store.getAt(i).data[field];
                itemLn = item.length;
                
                if (itemLn > currentLongestLength) {
                    currentLongestLength = itemLn;
                    value = item;
                }
            }
        }
        return value;
    },
    
    
    
    
    initComponent: function() {
        var me = this,
            isDefined = Ext.isDefined,
            store = me.store,
            transform = me.transform,
            displayTpl = me.displayTpl,
            transformSelect, isLocalMode;
        if (me.typeAhead && me.multiSelect) {
            Ext.Error.raise('typeAhead and multiSelect are mutually exclusive options -- please remove one of them.');
        }
        if (me.typeAhead && !me.editable) {
            Ext.Error.raise('If typeAhead is enabled the combo must be editable: true -- please change one of those settings.');
        }
        if (me.selectOnFocus && !me.editable) {
            Ext.Error.raise('If selectOnFocus is enabled the combo must be editable: true -- please change one of those settings.');
        }
        
        if ('pinList' in me) {
            me.collapseOnSelect = !me.pinList;
        }
        
        if (transform) {
            transformSelect = Ext.getDom(transform);
            if (transformSelect) {
                if (!me.store) {
                    store = Ext.Array.map(Ext.Array.from(transformSelect.options), function(option) {
                        return [
                            option.value,
                            option.text
                        ];
                    });
                }
                if (!me.name) {
                    me.name = transformSelect.name;
                }
                if (!('value' in me)) {
                    me.value = transformSelect.value;
                }
            }
        }
        me.bindStore(store || 'ext-empty-store', true, true);
        if (!displayTpl) {
            me.displayTpl = new Ext.XTemplate('<tpl for=".">' + '{[typeof values === "string" ? values : values["' + me.displayField + '"]]}' + '<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' + '</tpl>');
        } else if (!displayTpl.isTemplate) {
            me.displayTpl = new Ext.XTemplate(displayTpl);
        }
        isLocalMode = me.queryMode === 'local';
        if (!isDefined(me.queryDelay)) {
            me.queryDelay = isLocalMode ? 10 : 500;
        }
        if (!isDefined(me.minChars)) {
            me.minChars = isLocalMode ? 0 : 4;
        }
        me.callParent();
        me.doQueryTask = new Ext.util.DelayedTask(me.doRawQuery,me);
        
        if (transformSelect) {
            if (me.transformInPlace) {
                me.render(transformSelect.parentNode, transformSelect);
                delete me.renderTo;
            }
            Ext.removeNode(transformSelect);
        }
    },
    getSubTplMarkup: function(fieldData) {
        var me = this,
            hiddenDataElMarkup = '',
            markup = me.callParent(arguments);
        if (me.hiddenName) {
            hiddenDataElMarkup = '<div id="' + fieldData.id + '-hiddenDataEl" data-ref="hiddenDataEl" class="' + me.hiddenDataCls + '" role="presentation"></div>';
        }
        return hiddenDataElMarkup + markup;
    },
    applyFilters: function(filters, collection) {
        var me = this;
        if (filters === null || filters.isFilterCollection) {
            return filters;
        }
        if (filters) {
            if (!collection) {
                collection = this.getFilters();
            }
            collection.beginUpdate();
            collection.splice(0, collection.length, filters);
            collection.each(function(filter) {
                filter.ownerId = me.id;
            });
            collection.endUpdate();
        }
        return collection;
    },
    applyValueNotFoundText: function(v) {
        var me = this,
            valueNotFoundRecord = me.valueNotFoundRecord || (me.valueNotFoundRecord = new Ext.data.Model());
        valueNotFoundRecord.set(me.displayField, v);
        if (me.valueField && me.displayField !== me.valueField) {
            valueNotFoundRecord.set(me.valueField, v);
        }
        return v;
    },
    
    getFilters: function(autoCreate) {
        var ret = this.filters;
        if (!ret && autoCreate !== false) {
            ret = new Ext.util.FilterCollection();
            this.setFilters(ret);
        }
        return ret;
    },
    updateFilters: function(newFilters, oldFilters) {
        var me = this;
        if (oldFilters) {
            oldFilters.un('endupdate', 'onEndUpdateFilters', me);
        }
        if (newFilters) {
            newFilters.on('endupdate', 'onEndUpdateFilters', me);
        }
        me.onEndUpdateFilters(newFilters);
    },
    onEndUpdateFilters: function(filters) {
        var me = this,
            was = me.filtered,
            is = !!filters && (filters.length > 0),
            
            old, storeFilters;
        if (was || is) {
            me.filtered = is;
            old = [];
            storeFilters = me.store.getFilters();
            storeFilters.each(function(filter) {
                if (filter.ownerId === me.id && !filters.contains(filter)) {
                    old.push(filter);
                }
            });
            storeFilters.splice(0, old, filters.items);
        }
    },
    
    getStore: function() {
        return this.store;
    },
    completeEdit: function(e) {
        var me = this,
            filter = me.queryFilter;
        me.doQueryTask.cancel();
        me.assertValue();
        if (filter && me.queryMode === 'local' && me.clearFilterOnBlur) {
            me.getStore().getFilters().remove(filter);
        }
    },
    onFocus: function(e) {
        var me = this;
        me.callParent([
            e
        ]);
        if (me.triggerAction !== 'all' && me.queryFilter && me.queryMode === 'local' && me.clearFilterOnBlur) {
            delete me.lastQuery;
            me.doRawQuery();
        }
    },
    
    assertValue: function() {
        var me = this,
            value = me.getRawValue(),
            rec, currentValue,
            displayValue = me.getDisplayValue();
        if (me.forceSelection) {
            if (me.multiSelect) {
                
                
                if (value !== displayValue) {
                    me.setRawValue(displayValue);
                }
            } else {
                
                
                rec = me.findRecordByDisplay(value);
                if (rec) {
                    currentValue = me.value;
                    
                    
                    if (!me.findRecordByValue(currentValue)) {
                        me.select(rec, true);
                    }
                } else if (me.lastSelection) {
                    me.setRawValue(displayValue);
                }
            }
        }
        me.collapse();
    },
    onTypeAhead: function() {
        var me = this,
            displayField = me.displayField,
            record = me.store.findRecord(displayField, me.getRawValue()),
            boundList = me.getPicker(),
            newValue, len, selStart;
        if (record) {
            newValue = record.get(displayField);
            len = newValue.length;
            selStart = me.getRawValue().length;
            boundList.highlightItem(boundList.getNode(record));
            if (selStart !== 0 && selStart !== len) {
                me.setRawValue(newValue);
                me.selectText(selStart, newValue.length);
            }
        }
    },
    
    
    resetToDefault: Ext.emptyFn,
    beforeReset: function() {
        var filter = this.queryFilter;
        this.callParent();
        if (filter) {
            this.getStore().getFilters().remove(filter);
        }
    },
    onUnbindStore: function() {
        var me = this,
            picker = me.picker,
            filter = me.queryFilter;
        
        if (filter && !me.store.isDestroyed) {
            me.changingFilters = true;
            me.getStore().getFilters().remove(filter);
            me.changingFilters = false;
        }
        me.pickerSelectionModel.destroy();
        if (picker) {
            picker.bindStore(null);
        }
    },
    onBindStore: function(store, initial) {
        var me = this,
            picker = me.picker,
            extraKeySpec, valueCollectionConfig;
        
        if (store) {
            
            if (store.autoCreated) {
                me.queryMode = 'local';
                me.valueField = me.displayField = 'field1';
                if (!store.expanded) {
                    me.displayField = 'field2';
                }
            }
            if (!Ext.isDefined(me.valueField)) {
                me.valueField = me.displayField;
            }
            
            
            
            
            
            
            extraKeySpec = {
                byValue: {
                    rootProperty: 'data',
                    unique: false
                }
            };
            extraKeySpec.byValue.property = me.valueField;
            store.setExtraKeys(extraKeySpec);
            if (me.displayField === me.valueField) {
                store.byText = store.byValue;
            } else {
                extraKeySpec.byText = {
                    rootProperty: 'data',
                    unique: false
                };
                extraKeySpec.byText.property = me.displayField;
                store.setExtraKeys(extraKeySpec);
            }
            
            
            valueCollectionConfig = {
                rootProperty: 'data',
                extraKeys: {
                    byInternalId: {
                        property: 'internalId'
                    },
                    byValue: {
                        property: '',
                        
                        rootProperty: 'data'
                    }
                },
                
                
                listeners: {
                    beginupdate: me.onValueCollectionBeginUpdate,
                    endupdate: me.onValueCollectionEndUpdate,
                    scope: me
                }
            };
            valueCollectionConfig.extraKeys.byValue.property = me.valueField;
            
            me.valueCollection = new Ext.util.Collection(valueCollectionConfig);
            
            
            
            me.pickerSelectionModel = new Ext.selection.DataViewModel({
                mode: me.multiSelect ? 'SIMPLE' : 'SINGLE',
                enableInitialSelection: false,
                pruneRemoved: false,
                selected: me.valueCollection,
                store: store
            });
            if (!initial) {
                me.resetToDefault();
            }
            if (picker) {
                picker.setSelectionModel(me.pickerSelectionModel);
                if (picker.getStore() !== store) {
                    picker.bindStore(store);
                }
            }
        }
    },
    
    bindStore: function(store, preventFilter, 
    initial) {
        var me = this,
            filter = me.queryFilter;
        me.mixins.storeholder.bindStore.call(me, store, initial);
        store = me.getStore();
        if (store && filter && !preventFilter) {
            store.getFilters().add(filter);
        }
        if (!initial && store) {
            me.setValueOnData();
        }
    },
    getStoreListeners: function() {
        var me = this;
        return {
            datachanged: me.onDataChanged,
            load: me.onLoad,
            exception: me.onException,
            update: me.onStoreUpdate,
            remove: me.checkValueOnChange,
            filterchange: me.checkValueOnChange
        };
    },
    onDataChanged: function() {
        var me = this;
        if (me.grow && me.growToLongestValue) {
            me.autoSize();
        }
    },
    checkValueOnChange: function() {
        var me = this;
        
        
        
        
        
        if (me.multiSelect) {} else 
        
        
        {
            if (me.forceSelection && !me.changingFilters && !me.findRecordByValue(me.value)) {
                me.setValue(null);
            }
        }
    },
    onStoreUpdate: function(store, record) {
        if (record.get(this.valueField) === this.value) {
            this.setValue(record);
        }
    },
    onException: function() {
        this.collapse();
    },
    onLoad: function(store, records, success) {
        
        if (success && !(store.lastOptions && 'rawQuery' in store.lastOptions)) {
            this.setValueOnData();
        }
    },
    setValueOnData: function() {
        var me = this;
        
        if (me.value == null) {
            
            if (me.getStore().getCount()) {
                me.doAutoSelect();
            } else {
                
                me.setValue(me.value);
            }
        } else {
            me.setValue(me.value);
        }
    },
    
    doRawQuery: function() {
        var me = this,
            rawValue = me.inputEl.dom.value;
        
        if (me.multiSelect) {
            rawValue = rawValue.split(me.delimiter).pop();
        }
        me.doQuery(rawValue, false, true);
    },
    
    doQuery: function(queryString, forceAll, rawQuery) {
        var me = this,
            
            queryPlan = me.beforeQuery({
                query: queryString || '',
                rawQuery: rawQuery,
                forceAll: forceAll,
                combo: me,
                cancel: false
            });
        
        if (queryPlan !== false && !queryPlan.cancel) {
            
            if (me.queryCaching && queryPlan.query === me.lastQuery) {
                me.expand();
                if (me.queryMode === 'local') {
                    me.doAutoSelect();
                }
            } else 
            {
                me.lastQuery = queryPlan.query;
                if (me.queryMode === 'local') {
                    me.doLocalQuery(queryPlan);
                } else {
                    me.doRemoteQuery(queryPlan);
                }
            }
        }
        return true;
    },
    
    beforeQuery: function(queryPlan) {
        var me = this;
        
        if (me.fireEvent('beforequery', queryPlan) === false) {
            queryPlan.cancel = true;
        }
        
        else if (!queryPlan.cancel) {
            
            if (queryPlan.query.length < me.minChars && !queryPlan.forceAll) {
                queryPlan.cancel = true;
            }
        }
        return queryPlan;
    },
    doLocalQuery: function(queryPlan) {
        var me = this,
            queryString = queryPlan.query,
            filters = me.getStore().getFilters(),
            filter = me.queryFilter;
        me.queryFilter = null;
        me.changingFilters = true;
        filters.beginUpdate();
        if (filter) {
            filters.remove(filter);
        }
        
        if (queryString) {
            filter = me.queryFilter = new Ext.util.Filter({
                id: me.id + '-filter',
                anyMatch: me.anyMatch,
                caseSensitive: me.caseSensitive,
                root: 'data',
                property: me.displayField,
                value: me.enableRegEx ? new RegExp(queryString) : queryString
            });
            filters.add(filter);
        }
        filters.endUpdate();
        me.changingFilters = false;
        
        if (me.store.getCount() || me.getPicker().emptyText) {
            me.expand();
        } else {
            me.collapse();
        }
        me.afterQuery(queryPlan);
    },
    doRemoteQuery: function(queryPlan) {
        var me = this,
            loadCallback = function() {
                if (!me.isDestroyed) {
                    me.afterQuery(queryPlan);
                }
            };
        
        me.expand();
        
        
        if (me.pageSize) {
            
            me.loadPage(1, {
                rawQuery: queryPlan.rawQuery,
                callback: loadCallback
            });
        } else {
            me.store.load({
                params: me.getParams(queryPlan.query),
                rawQuery: queryPlan.rawQuery,
                callback: loadCallback
            });
        }
    },
    
    afterQuery: function(queryPlan) {
        var me = this;
        if (me.store.getCount()) {
            if (me.typeAhead) {
                me.doTypeAhead();
            }
            if (queryPlan.rawQuery) {
                if (me.picker && !me.picker.getSelectionModel().hasSelection()) {
                    me.doAutoSelect();
                }
            } else {
                me.doAutoSelect();
            }
        }
        
        me.checkChange();
    },
    loadPage: function(pageNum, options) {
        this.store.loadPage(pageNum, Ext.apply({
            params: this.getParams(this.lastQuery)
        }, options));
    },
    onPageChange: function(toolbar, newPage) {
        
        this.loadPage(newPage);
        return false;
    },
    
    getParams: function(queryString) {
        var params = {},
            param = this.queryParam;
        if (param) {
            params[param] = queryString;
        }
        return params;
    },
    
    doAutoSelect: function() {
        var me = this,
            picker = me.picker;
        if (picker && me.autoSelect && me.store.getCount() > 0) {
            
            picker.getNavigationModel().setPosition(me.picker.getSelectionModel().lastSelected || 0);
        }
    },
    doTypeAhead: function() {
        var me = this,
            Event = Ext.event.Event;
        if (!me.typeAheadTask) {
            me.typeAheadTask = new Ext.util.DelayedTask(me.onTypeAhead,me);
        }
        if (me.lastKey !== Event.BACKSPACE && me.lastKey !== Event.DELETE) {
            me.typeAheadTask.delay(me.typeAheadDelay);
        }
    },
    onTriggerClick: function() {
        var me = this;
        if (!me.readOnly && !me.disabled) {
            if (me.isExpanded) {
                me.collapse();
            } else {
                if (me.triggerAction === 'all') {
                    me.doQuery(me.allQuery, true);
                } else if (me.triggerAction === 'last') {
                    me.doQuery(me.lastQuery, true);
                } else {
                    me.doQuery(me.getRawValue(), false, true);
                }
            }
        }
    },
    onFieldMutation: function(e) {
        var me = this,
            key = e.getKey(),
            isDelete = key === e.BACKSPACE || key === e.DELETE,
            rawValue = me.inputEl.dom.value,
            len = rawValue.length;
        
        
        
        
        if ((rawValue !== me.lastMutatedValue || isDelete) && key !== e.TAB) {
            me.lastMutatedValue = rawValue;
            me.lastKey = key;
            if (len && (e.type !== 'keyup' || (!e.isSpecialKey() || isDelete))) {
                me.doQueryTask.delay(me.queryDelay);
            } else {
                
                if (!len && (!key || isDelete)) {
                    
                    
                    if (!me.multiSelect) {
                        me.value = null;
                        me.displayTplData = undefined;
                    }
                    
                    me.collapse();
                    
                    
                    if (me.queryFilter) {
                        me.changingFilters = true;
                        me.store.removeFilter(me.queryFilter, true);
                        me.changingFilters = false;
                    }
                }
                me.callParent([
                    e
                ]);
            }
        }
    },
    onDestroy: function() {
        var me = this;
        me.doQueryTask.cancel();
        if (me.typeAheadTask) {
            me.typeAheadTask.cancel();
            me.typeAheadTask = null;
        }
        me.bindStore(null);
        me.valueCollection = Ext.destroy(me.valueCollection);
        me.callParent();
    },
    
    
    onAdded: function() {
        var me = this;
        me.callParent(arguments);
        if (me.picker) {
            me.picker.ownerCt = me.up('[floating]');
            me.picker.registerWithOwnerCt();
        }
    },
    createPicker: function() {
        var me = this,
            picker,
            pickerCfg = Ext.apply({
                xtype: 'boundlist',
                pickerField: me,
                selectionModel: me.pickerSelectionModel,
                floating: true,
                hidden: true,
                store: me.getPickerStore(),
                displayField: me.displayField,
                preserveScrollOnRefresh: true,
                pageSize: me.pageSize,
                tpl: me.tpl
            }, me.listConfig, me.defaultListConfig);
        picker = me.picker = Ext.widget(pickerCfg);
        if (me.pageSize) {
            picker.pagingToolbar.on('beforechange', me.onPageChange, me);
        }
        
        
        if (!picker.initialConfig.maxHeight) {
            picker.on({
                beforeshow: me.onBeforePickerShow,
                scope: me
            });
        }
        picker.getSelectionModel().on({
            beforeselect: me.onBeforeSelect,
            beforedeselect: me.onBeforeDeselect,
            scope: me
        });
        picker.getNavigationModel().navigateOnSpace = false;
        return picker;
    },
    getPickerStore: function() {
        return this.store;
    },
    onBeforePickerShow: function(picker) {
        
        
        var me = this,
            heightAbove = me.getPosition()[1] - Ext.getBody().getScroll().top,
            heightBelow = Ext.Element.getViewportHeight() - heightAbove - me.getHeight();
        
        picker.maxHeight = Math.max(heightAbove, heightBelow) - 5;
    },
    
    onBeforeSelect: function(list, record, recordIndex) {
        return this.fireEvent('beforeselect', this, record, recordIndex);
    },
    onBeforeDeselect: function(list, record, recordIndex) {
        return this.fireEvent('beforedeselect', this, record, recordIndex);
    },
    getSelection: function() {
        var selModel = this.getPicker().getSelectionModel(),
            selection = selModel.getSelection();
        return selection.length ? selModel.getLastSelected() : null;
    },
    updateSelection: function(selection) {
        var me = this,
            sm;
        if (!me.ignoreNextSelection) {
            me.ignoreNextSelection = true;
            sm = me.getPicker().getSelectionModel();
            if (selection) {
                sm.select(selection);
            } else {
                sm.deselectAll();
            }
            me.ignoreNextSelection = false;
        }
    },
    updateBindSelection: function(selModel, selection) {
        var me = this,
            selected = null;
        if (!me.ignoreNextSelection) {
            me.ignoreNextSelection = true;
            if (selection.length) {
                selected = selModel.getLastSelected();
                me.hasHadSelection = true;
            }
            if (me.hasHadSelection) {
                me.setSelection(selected);
            }
            me.ignoreNextSelection = false;
        }
    },
    onValueCollectionBeginUpdate: Ext.emptyFn,
    onValueCollectionEndUpdate: function() {
        var me = this,
            store = me.store,
            selectedRecords = me.valueCollection.getRange(),
            selectedRecord = selectedRecords[0],
            selectionCount = selectedRecords.length;
        me.updateBindSelection(me.pickerSelectionModel, selectedRecords);
        if (me.isSelectionUpdating()) {
            return;
        }
        Ext.suspendLayouts();
        me.updateValue();
        
        
        if (selectionCount && ((!me.multiSelect && store.contains(selectedRecord)) || me.collapseOnSelect || !store.getCount())) {
            me.updatingValue = true;
            me.collapse();
            me.updatingValue = false;
        }
        Ext.resumeLayouts(true);
        if (selectionCount && !me.suspendCheckChange) {
            if (!me.multiSelect) {
                selectedRecords = selectedRecord;
            }
            me.fireEvent('select', me, selectedRecords);
        }
    },
    isSelectionUpdating: function() {
        var selModel = this.pickerSelectionModel;
        return selModel.deselectingDuringSelect || selModel.refreshing;
    },
    
    onExpand: function() {
        var keyNav = this.getPicker().getNavigationModel();
        if (keyNav) {
            keyNav.enable();
        }
    },
    
    onCollapse: function() {
        var keyNav = this.getPicker().getNavigationModel();
        if (keyNav) {
            keyNav.disable();
        }
        if (this.updatingValue) {
            this.doQueryTask.cancel();
        }
    },
    
    select: function(r, 
    assert) {
        var me = this,
            picker = me.picker,
            fireSelect;
        if (r && r.isModel && assert === true && picker) {
            fireSelect = !picker.getSelectionModel().isSelected(r);
        }
        if (!fireSelect) {
            me.suspendEvent('select');
        }
        me.setValue(r);
        me.resumeEvent('select');
    },
    
    findRecord: function(field, value) {
        var ds = this.store,
            idx = ds.findExact(field, value);
        return idx !== -1 ? ds.getAt(idx) : false;
    },
    getSelectedRecord: function() {
        return this.findRecordByValue(this.value) || null;
    },
    
    findRecordByValue: function(value) {
        var result = this.store.byValue.get(value);
        
        if (result) {
            return result[0] || result;
        }
    },
    
    findRecordByDisplay: function(value) {
        var result = this.store.byText.get(value);
        
        if (result) {
            return result[0] || result;
        }
    },
    
    addValue: function(value) {
        return this.doSetValue(value, true);
    },
    
    setValue: function(value) {
        return this.doSetValue(value);
    },
    
    doSetValue: function(value, 
    add) {
        var me = this,
            store = me.getStore(),
            Model = store.getModel(),
            matchedRecords = [],
            valueArray = [],
            key,
            autoLoadOnValue = me.autoLoadOnValue,
            isLoaded = store.getCount() > 0 || store.isLoaded(),
            pendingLoad = store.hasPendingLoad(),
            unloaded = autoLoadOnValue && !isLoaded && !pendingLoad,
            forceSelection = me.forceSelection,
            selModel = me.pickerSelectionModel,
            displayTplData = me.displayTplData || (me.displayTplData = []),
            displayIsValue = me.displayField === me.valueField,
            i, len, record, dataObj, raw;
        if (add && !me.multiSelect) {
            Ext.Error.raise('Cannot add values to non muiltiSelect ComboBox');
        }
        
        
        
        
        if (value != null && !displayIsValue && (pendingLoad || unloaded || !isLoaded || store.isEmptyStore)) {
            if (value.isModel) {
                displayTplData.length = 0;
                displayTplData.push(value.data);
                raw = me.getDisplayValue();
            }
            if (add) {
                me.value = Ext.Array.from(me.value).concat(value);
            } else {
                if (value.isModel) {
                    value = value.get(me.valueField);
                }
                me.value = value;
            }
            me.setHiddenValue(me.value);
            
            
            me.setRawValue(raw || '');
            if (unloaded && store.getProxy().isRemote) {
                store.load();
            }
            return me;
        }
        
        value = add ? Ext.Array.from(me.value).concat(value) : Ext.Array.from(value);
        
        for (i = 0 , len = value.length; i < len; i++) {
            record = value[i];
            
            if (!record || !record.isModel) {
                record = me.findRecordByValue(key = record);
                
                
                if (!record) {
                    record = me.valueCollection.find(me.valueField, key);
                }
            }
            
            
            if (!record) {
                
                
                if (!forceSelection) {
                    if (!record) {
                        dataObj = {};
                        dataObj[me.displayField] = value[i];
                        if (me.valueField && me.displayField !== me.valueField) {
                            dataObj[me.valueField] = value[i];
                        }
                        record = new Model(dataObj);
                    }
                }
                
                else if (me.valueNotFoundRecord) {
                    record = me.valueNotFoundRecord;
                }
            }
            
            if (record) {
                matchedRecords.push(record);
                valueArray.push(record.get(me.valueField));
            }
        }
        me.lastSelection = matchedRecords;
        
        me.suspendEvent('select');
        me.valueCollection.beginUpdate();
        if (matchedRecords.length) {
            selModel.select(matchedRecords, false);
        } else {
            selModel.deselectAll();
        }
        me.valueCollection.endUpdate();
        me.resumeEvent('select');
        return me;
    },
    
    
    updateValue: function() {
        var me = this,
            selectedRecords = me.valueCollection.getRange(),
            len = selectedRecords.length,
            valueArray = [],
            displayTplData = me.displayTplData || (me.displayTplData = []),
            inputEl = me.inputEl,
            i, record;
        
        displayTplData.length = 0;
        for (i = 0; i < len; i++) {
            record = selectedRecords[i];
            displayTplData.push(record.data);
            
            if (record !== me.valueNotFoundRecord) {
                valueArray.push(record.get(me.valueField));
            }
        }
        
        me.setHiddenValue(valueArray);
        me.value = me.multiSelect ? valueArray : valueArray[0];
        if (!Ext.isDefined(me.value)) {
            me.value = undefined;
        }
        me.displayTplData = displayTplData;
        
        if (inputEl && me.emptyText && !Ext.isEmpty(me.value)) {
            inputEl.removeCls(me.emptyCls);
        }
        
        me.setRawValue(me.getDisplayValue());
        me.checkChange();
        me.applyEmptyText();
    },
    
    setHiddenValue: function(values) {
        var me = this,
            name = me.hiddenName,
            i, dom, childNodes, input, valueCount, childrenCount;
        if (!me.hiddenDataEl || !name) {
            return;
        }
        values = Ext.Array.from(values);
        dom = me.hiddenDataEl.dom;
        childNodes = dom.childNodes;
        input = childNodes[0];
        valueCount = values.length;
        childrenCount = childNodes.length;
        if (!input && valueCount > 0) {
            me.hiddenDataEl.setHtml(Ext.DomHelper.markup({
                tag: 'input',
                type: 'hidden',
                name: name
            }));
            childrenCount = 1;
            input = dom.firstChild;
        }
        while (childrenCount > valueCount) {
            dom.removeChild(childNodes[0]);
            --childrenCount;
        }
        while (childrenCount < valueCount) {
            dom.appendChild(input.cloneNode(true));
            ++childrenCount;
        }
        for (i = 0; i < valueCount; i++) {
            childNodes[i].value = values[i];
        }
    },
    
    getDisplayValue: function() {
        return this.displayTpl.apply(this.displayTplData);
    },
    getValue: function() {
        
        
        
        var me = this,
            picker = me.picker,
            rawValue = me.getRawValue(),
            
            value = me.value;
        
        if (me.getDisplayValue() !== rawValue) {
            me.displayTplData = undefined;
            if (picker) {
                
                
                me.valueCollection.suspendEvents();
                picker.getSelectionModel().deselectAll();
                me.valueCollection.resumeEvents();
            }
            
            
            
            if (me.multiSelect || me.forceSelection) {
                value = me.value = undefined;
            } else {
                value = me.value = rawValue;
            }
        }
        
        me.value = value == null ? null : value;
        return me.value;
    },
    getSubmitValue: function() {
        var value = this.getValue();
        
        
        if (Ext.isEmpty(value)) {
            value = '';
        }
        return value;
    },
    isEqual: function(v1, v2) {
        var fromArray = Ext.Array.from,
            i, len;
        v1 = fromArray(v1);
        v2 = fromArray(v2);
        len = v1.length;
        if (len !== v2.length) {
            return false;
        }
        for (i = 0; i < len; i++) {
            if (v2[i] !== v1[i]) {
                return false;
            }
        }
        return true;
    },
    
    clearValue: function() {
        this.setValue([]);
    },
    onEditorTab: function(e) {
        var keyNav = this.getPicker().getNavigationModel();
        if (this.selectOnTab && keyNav && this.isExpanded) {
            keyNav.selectHighlighted(e);
        }
    }
});


Ext.define('Ext.picker.Month', {
    extend: 'Ext.Component',
    requires: [
        'Ext.XTemplate',
        'Ext.util.ClickRepeater',
        'Ext.Date',
        'Ext.button.Button'
    ],
    alias: 'widget.monthpicker',
    alternateClassName: 'Ext.MonthPicker',
    isMonthPicker: true,
    focusable: true,
    childEls: [
        'bodyEl',
        'prevEl',
        'nextEl',
        'monthEl',
        'yearEl'
    ],
    renderTpl: [
        '<div id="{id}-bodyEl" data-ref="bodyEl" class="{baseCls}-body">',
        '<div id="{id}-monthEl" data-ref="monthEl" class="{baseCls}-months">',
        '<tpl for="months">',
        '<div class="{parent.baseCls}-item {parent.baseCls}-month">',
        '<a style="{parent.monthStyle}" role="button" hidefocus="on" class="{parent.baseCls}-item-inner">{.}</a>',
        '</div>',
        '</tpl>',
        '</div>',
        '<div id="{id}-yearEl" data-ref="yearEl" class="{baseCls}-years">',
        '<div class="{baseCls}-yearnav">',
        '<div class="{baseCls}-yearnav-button-ct">',
        '<a id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-prev" hidefocus="on" role="button"></a>',
        '</div>',
        '<div class="{baseCls}-yearnav-button-ct">',
        '<a id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-next" hidefocus="on" role="button"></a>',
        '</div>',
        '</div>',
        '<tpl for="years">',
        '<div class="{parent.baseCls}-item {parent.baseCls}-year">',
        '<a hidefocus="on" class="{parent.baseCls}-item-inner" role="button">{.}</a>',
        '</div>',
        '</tpl>',
        '</div>',
        '<div class="' + Ext.baseCSSPrefix + 'clear"></div>',
        '<tpl if="showButtons">',
        '<div class="{baseCls}-buttons">{%',
        'var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;',
        'okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;',
        'okBtn.ownerCt = cancelBtn.ownerCt = me;',
        'Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);',
        'Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);',
        '%}</div>',
        '</tpl>',
        '</div>'
    ],
    
    
    okText: 'OK',
    
    
    
    cancelText: 'Cancel',
    
    
    baseCls: Ext.baseCSSPrefix + 'monthpicker',
    
    showButtons: true,
    
    
    measureWidth: 35,
    measureMaxHeight: 20,
    
    smallCls: Ext.baseCSSPrefix + 'monthpicker-small',
    
    totalYears: 10,
    yearOffset: 5,
    
    monthOffset: 6,
    
    
    
    
    
    
    
    
    
    
    initComponent: function() {
        var me = this;
        me.selectedCls = me.baseCls + '-selected';
        if (me.small) {
            me.addCls(me.smallCls);
        }
        me.setValue(me.value);
        me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);
        if (me.showButtons) {
            me.okBtn = new Ext.button.Button({
                text: me.okText,
                handler: me.onOkClick,
                scope: me
            });
            me.cancelBtn = new Ext.button.Button({
                text: me.cancelText,
                handler: me.onCancelClick,
                scope: me
            });
        }
        this.callParent();
    },
    
    
    beforeRender: function() {
        var me = this,
            i = 0,
            months = [],
            shortName = Ext.Date.getShortMonthName,
            monthLen = me.monthOffset,
            margin = me.monthMargin,
            style = '';
        if (me.padding && !me.width) {
            me.cacheWidth();
        }
        me.callParent();
        for (; i < monthLen; ++i) {
            months.push(shortName(i), shortName(i + monthLen));
        }
        if (Ext.isDefined(margin)) {
            style = 'margin: 0 ' + margin + 'px;';
        }
        Ext.apply(me.renderData, {
            months: months,
            years: me.getYears(),
            showButtons: me.showButtons,
            monthStyle: style
        });
    },
    cacheWidth: function() {
        var me = this,
            padding = me.parseBox(me.padding),
            widthEl = Ext.getBody().createChild({
                cls: me.baseCls + ' ' + me.borderBoxCls,
                style: 'position:absolute;top:-1000px;left:-1000px;',
                html: '&nbsp;'
            });
        
        me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right;
        widthEl.destroy();
    },
    
    
    afterRender: function() {
        var me = this,
            body = me.bodyEl;
        me.callParent();
        me.mon(body, 'click', me.onBodyClick, me);
        me.mon(body, 'dblclick', me.onBodyClick, me);
        
        me.years = body.select('.' + me.baseCls + '-year a');
        me.months = body.select('.' + me.baseCls + '-month a');
        me.backRepeater = new Ext.util.ClickRepeater(me.prevEl,{
            handler: Ext.Function.bind(me.adjustYear, me, [
                -me.totalYears
            ])
        });
        me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over');
        me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl,{
            handler: Ext.Function.bind(me.adjustYear, me, [
                me.totalYears
            ])
        });
        me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over');
        me.updateBody();
        if (!Ext.isDefined(me.monthMargin)) {
            Ext.picker.Month.prototype.monthMargin = me.calculateMonthMargin();
        }
    },
    calculateMonthMargin: function() {
        
        
        
        
        var me = this,
            monthEl = me.monthEl,
            months = me.months,
            first = months.first(),
            itemMargin = first.getMargin('l');
        while (itemMargin && me.getLargest() > me.measureMaxHeight) {
            --itemMargin;
            months.setStyle('margin', '0 ' + itemMargin + 'px');
        }
        return itemMargin;
    },
    getLargest: function(months) {
        var largest = 0;
        this.months.each(function(item) {
            var h = item.getHeight();
            if (h > largest) {
                largest = h;
            }
        });
        return largest;
    },
    
    setValue: function(value) {
        var me = this,
            active = me.activeYear,
            offset = me.monthOffset,
            year, index;
        if (!value) {
            me.value = [
                null,
                null
            ];
        } else if (Ext.isDate(value)) {
            me.value = [
                value.getMonth(),
                value.getFullYear()
            ];
        } else {
            me.value = [
                value[0],
                value[1]
            ];
        }
        if (me.rendered) {
            year = me.value[1];
            if (year !== null) {
                if ((year < active || year > active + me.yearOffset)) {
                    me.activeYear = year - me.yearOffset + 1;
                }
            }
            me.updateBody();
        }
        return me;
    },
    
    getValue: function() {
        return this.value;
    },
    
    hasSelection: function() {
        var value = this.value;
        return value[0] !== null && value[1] !== null;
    },
    
    getYears: function() {
        var me = this,
            offset = me.yearOffset,
            start = me.activeYear,
            
            end = start + offset,
            i = start,
            years = [];
        for (; i < end; ++i) {
            years.push(i, i + offset);
        }
        return years;
    },
    
    updateBody: function() {
        var me = this,
            years = me.years,
            months = me.months,
            yearNumbers = me.getYears(),
            cls = me.selectedCls,
            value = me.getYear(null),
            month = me.value[0],
            monthOffset = me.monthOffset,
            year, yearItems, y, yLen, el;
        if (me.rendered) {
            years.removeCls(cls);
            months.removeCls(cls);
            yearItems = years.elements;
            yLen = yearItems.length;
            for (y = 0; y < yLen; y++) {
                el = Ext.fly(yearItems[y]);
                year = yearNumbers[y];
                el.dom.innerHTML = year;
                if (year == value) {
                    el.addCls(cls);
                }
            }
            if (month !== null) {
                if (month < monthOffset) {
                    month = month * 2;
                } else {
                    month = (month - monthOffset) * 2 + 1;
                }
                months.item(month).addCls(cls);
            }
        }
    },
    
    getYear: function(defaultValue, offset) {
        var year = this.value[1];
        offset = offset || 0;
        return year === null ? defaultValue : year + offset;
    },
    
    onBodyClick: function(e, t) {
        var me = this,
            isDouble = e.type == 'dblclick';
        if (e.getTarget('.' + me.baseCls + '-month')) {
            e.stopEvent();
            me.onMonthClick(t, isDouble);
        } else if (e.getTarget('.' + me.baseCls + '-year')) {
            e.stopEvent();
            me.onYearClick(t, isDouble);
        }
    },
    
    adjustYear: function(offset) {
        if (typeof offset != 'number') {
            offset = this.totalYears;
        }
        this.activeYear += offset;
        this.updateBody();
    },
    
    onOkClick: function() {
        this.fireEvent('okclick', this, this.value);
    },
    
    onCancelClick: function() {
        this.fireEvent('cancelclick', this);
    },
    
    onMonthClick: function(target, isDouble) {
        var me = this;
        me.value[0] = me.resolveOffset(me.months.indexOf(target), me.monthOffset);
        me.updateBody();
        me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);
    },
    
    onYearClick: function(target, isDouble) {
        var me = this;
        me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset);
        me.updateBody();
        me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);
    },
    
    resolveOffset: function(index, offset) {
        if (index % 2 === 0) {
            return (index / 2);
        } else {
            return offset + Math.floor(index / 2);
        }
    },
    
    
    beforeDestroy: function() {
        var me = this;
        me.years = me.months = null;
        Ext.destroyMembers(me, 'backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');
        me.callParent();
    },
    onDestroy: function() {
        Ext.destroyMembers(this, 'okBtn', 'cancelBtn');
        this.callParent();
    },
    privates: {
        
        
        finishRenderChildren: function() {
            var me = this;
            this.callParent(arguments);
            if (this.showButtons) {
                me.okBtn.finishRender();
                me.cancelBtn.finishRender();
            }
        }
    }
});


Ext.define('Ext.picker.Date', {
    extend: 'Ext.Component',
    requires: [
        'Ext.XTemplate',
        'Ext.button.Button',
        'Ext.button.Split',
        'Ext.util.ClickRepeater',
        'Ext.util.KeyNav',
        'Ext.fx.Manager',
        'Ext.picker.Month'
    ],
    alias: 'widget.datepicker',
    alternateClassName: 'Ext.DatePicker',
    isDatePicker: true,
    focusable: true,
    childEls: [
        'innerEl',
        'eventEl',
        'prevEl',
        'nextEl',
        'middleBtnEl',
        'footerEl'
    ],
    border: true,
    renderTpl: [
        '<div id="{id}-innerEl" data-ref="innerEl">',
        '<div class="{baseCls}-header">',
        '<div id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-prev {baseCls}-arrow" role="button" title="{prevText}"></div>',
        '<div id="{id}-middleBtnEl" data-ref="middleBtnEl" class="{baseCls}-month" role="heading">{%this.renderMonthBtn(values, out)%}</div>',
        '<div id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-next {baseCls}-arrow" role="button" title="{nextText}"></div>',
        '</div>',
        '<table role="grid" id="{id}-eventEl" data-ref="eventEl" class="{baseCls}-inner" {%',
        
        
        
        
        'if (values.$comp.focusable) {out.push("tabindex=\\"0\\"");}',
        '%} cellspacing="0">',
        '<thead><tr role="row">',
        '<tpl for="dayNames">',
        '<th role="columnheader" class="{parent.baseCls}-column-header" aria-label="{.}">',
        '<div role="presentation" class="{parent.baseCls}-column-header-inner">{.:this.firstInitial}</div>',
        '</th>',
        '</tpl>',
        '</tr></thead>',
        '<tbody><tr role="row">',
        '<tpl for="days">',
        '{#:this.isEndOfWeek}',
        '<td role="gridcell">',
        '<div hidefocus="on" class="{parent.baseCls}-date"></div>',
        '</td>',
        '</tpl>',
        '</tr></tbody>',
        '</table>',
        '<tpl if="showToday">',
        '<div id="{id}-footerEl" data-ref="footerEl" role="presentation" class="{baseCls}-footer">{%this.renderTodayBtn(values, out)%}</div>',
        '</tpl>',
        '</div>',
        {
            firstInitial: function(value) {
                return Ext.picker.Date.prototype.getDayInitial(value);
            },
            isEndOfWeek: function(value) {
                
                
                value--;
                var end = value % 7 === 0 && value !== 0;
                return end ? '</tr><tr role="row">' : '';
            },
            renderTodayBtn: function(values, out) {
                Ext.DomHelper.generateMarkup(values.$comp.todayBtn.getRenderTree(), out);
            },
            renderMonthBtn: function(values, out) {
                Ext.DomHelper.generateMarkup(values.$comp.monthBtn.getRenderTree(), out);
            }
        }
    ],
    
    
    todayText: 'Today',
    
    
    
    ariaTitle: 'Date Picker: {0}',
    
    
    
    ariaTitleDateFormat: 'F d',
    
    
    
    
    
    todayTip: '{0} (Spacebar)',
    
    
    
    minText: 'This date is before the minimum date',
    
    
    
    maxText: 'This date is after the maximum date',
    
    
    
    
    disabledDaysText: 'Disabled',
    
    
    
    disabledDatesText: 'Disabled',
    
    
    
    
    
    nextText: 'Next Month (Control+Right)',
    
    
    
    prevText: 'Previous Month (Control+Left)',
    
    
    
    monthYearText: 'Choose a month (Control+Up/Down to move years)',
    
    
    
    monthYearFormat: 'F Y',
    
    
    
    startDay: 0,
    
    
    
    showToday: true,
    
    
    
    
    
    
    
    disableAnim: false,
    
    baseCls: Ext.baseCSSPrefix + 'datepicker',
    
    
    
    
    longDayFormat: 'F d, Y',
    
    
    
    
    initHour: 12,
    
    numDays: 42,
    
    
    initComponent: function() {
        var me = this,
            clearTime = Ext.Date.clearTime;
        me.selectedCls = me.baseCls + '-selected';
        me.disabledCellCls = me.baseCls + '-disabled';
        me.prevCls = me.baseCls + '-prevday';
        me.activeCls = me.baseCls + '-active';
        me.cellCls = me.baseCls + '-cell';
        me.nextCls = me.baseCls + '-prevday';
        me.todayCls = me.baseCls + '-today';
        if (!me.format) {
            me.format = Ext.Date.defaultFormat;
        }
        if (!me.dayNames) {
            me.dayNames = Ext.Date.dayNames;
        }
        me.dayNames = me.dayNames.slice(me.startDay).concat(me.dayNames.slice(0, me.startDay));
        me.callParent();
        me.value = me.value ? clearTime(me.value, true) : clearTime(new Date());
        me.initDisabledDays();
    },
    
    getRefOwner: function() {
        return this.pickerField || this.callParent();
    },
    getRefItems: function() {
        var results = [],
            monthBtn = this.monthBtn,
            todayBtn = this.todayBtn;
        if (monthBtn) {
            results.push(monthBtn);
        }
        if (todayBtn) {
            results.push(todayBtn);
        }
        return results;
    },
    beforeRender: function() {
        
        var me = this,
            days = new Array(me.numDays),
            today = Ext.Date.format(new Date(), me.format);
        if (me.padding && !me.width) {
            me.cacheWidth();
        }
        me.monthBtn = new Ext.button.Split({
            ownerCt: me,
            ownerLayout: me.getComponentLayout(),
            text: '',
            tooltip: me.monthYearText,
            listeners: {
                click: me.doShowMonthPicker,
                arrowclick: me.doShowMonthPicker,
                scope: me
            }
        });
        if (me.showToday) {
            me.todayBtn = new Ext.button.Button({
                ownerCt: me,
                ownerLayout: me.getComponentLayout(),
                text: Ext.String.format(me.todayText, today),
                tooltip: Ext.String.format(me.todayTip, today),
                tooltipType: 'title',
                handler: me.selectToday,
                scope: me
            });
        }
        me.callParent();
        Ext.applyIf(me, {
            renderData: {}
        });
        Ext.apply(me.renderData, {
            dayNames: me.dayNames,
            showToday: me.showToday,
            prevText: me.prevText,
            nextText: me.nextText,
            days: days
        });
        me.protoEl.unselectable();
    },
    cacheWidth: function() {
        var me = this,
            padding = me.parseBox(me.padding),
            widthEl = Ext.getBody().createChild({
                cls: me.baseCls + ' ' + me.borderBoxCls,
                style: 'position:absolute;top:-1000px;left:-1000px;'
            });
        me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right;
        widthEl.destroy();
    },
    
    
    onRender: function(container, position) {
        var me = this;
        me.callParent(arguments);
        me.cells = me.eventEl.select('tbody td');
        me.textNodes = me.eventEl.query('tbody td div');
        me.eventEl.set({
            'aria-labelledby': me.monthBtn.id
        });
        me.mon(me.eventEl, {
            scope: me,
            mousewheel: me.handleMouseWheel,
            click: {
                fn: me.handleDateClick,
                delegate: 'div.' + me.baseCls + '-date'
            }
        });
    },
    
    
    initEvents: function() {
        var me = this,
            pickerField = me.pickerField,
            eDate = Ext.Date,
            day = eDate.DAY;
        me.callParent();
        
        
        if (!me.focusable) {
            me.el.on({
                mousedown: me.onMouseDown
            });
        }
        me.prevRepeater = new Ext.util.ClickRepeater(me.prevEl,{
            handler: me.showPrevMonth,
            scope: me,
            preventDefault: true,
            stopDefault: true
        });
        me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl,{
            handler: me.showNextMonth,
            scope: me,
            preventDefault: true,
            stopDefault: true
        });
        
        me.keyNav = new Ext.util.KeyNav(pickerField ? pickerField.inputEl : me.eventEl,Ext.apply({
            scope: me,
            
            capture: true,
            left: function(e) {
                if (e.ctrlKey) {
                    me.showPrevMonth();
                } else {
                    me.update(eDate.add(me.activeDate, day, -1));
                }
            },
            right: function(e) {
                if (e.ctrlKey) {
                    me.showNextMonth();
                } else {
                    me.update(eDate.add(me.activeDate, day, 1));
                }
            },
            up: function(e) {
                if (e.ctrlKey) {
                    me.showNextYear();
                } else {
                    me.update(eDate.add(me.activeDate, day, -7));
                }
            },
            down: function(e) {
                if (e.ctrlKey) {
                    me.showPrevYear();
                } else {
                    me.update(eDate.add(me.activeDate, day, 7));
                }
            },
            pageUp: function(e) {
                if (e.ctrlKey) {
                    me.showPrevYear();
                } else {
                    me.showPrevMonth();
                }
            },
            pageDown: function(e) {
                if (e.ctrlKey) {
                    me.showNextYear();
                } else {
                    me.showNextMonth();
                }
            },
            tab: function(e) {
                me.handleTabClick(e);
                
                return true;
            },
            enter: function(e) {
                me.handleDateClick(e, me.activeCell.firstChild);
            },
            space: function() {
                me.setValue(new Date(me.activeCell.firstChild.dateValue));
                var startValue = me.startValue,
                    value = me.value,
                    pickerValue;
                if (pickerField) {
                    pickerValue = pickerField.getValue();
                    if (pickerValue && startValue && pickerValue.getTime() === value.getTime()) {
                        pickerField.setValue(startValue);
                    } else {
                        pickerField.setValue(value);
                    }
                }
            },
            home: function(e) {
                me.update(eDate.getFirstDateOfMonth(me.activeDate));
            },
            end: function(e) {
                me.update(eDate.getLastDateOfMonth(me.activeDate));
            }
        }, me.keyNavConfig));
        if (me.disabled) {
            me.syncDisabled(true);
        }
        me.update(me.value);
    },
    onMouseDown: function(e) {
        e.preventDefault();
    },
    handleTabClick: function(e) {
        var me = this,
            t = me.getSelectedDate(me.activeDate),
            handler = me.handler;
        
        if (!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
            me.setValue(new Date(t.dateValue));
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            me.onSelect();
        }
    },
    getSelectedDate: function(date) {
        var me = this,
            t = date.getTime(),
            cells = me.cells,
            cls = me.selectedCls,
            cellItems = cells.elements,
            cLen = cellItems.length,
            cell, c;
        cells.removeCls(cls);
        for (c = 0; c < cLen; c++) {
            cell = cellItems[c].firstChild;
            if (cell.dateValue == t) {
                return cell;
            }
        }
        return null;
    },
    
    initDisabledDays: function() {
        var me = this,
            dd = me.disabledDates,
            re = '(?:',
            len, d, dLen, dI;
        if (!me.disabledDatesRE && dd) {
            len = dd.length - 1;
            dLen = dd.length;
            for (d = 0; d < dLen; d++) {
                dI = dd[d];
                re += Ext.isDate(dI) ? '^' + Ext.String.escapeRegex(Ext.Date.dateFormat(dI, me.format)) + '$' : dI;
                if (d !== len) {
                    re += '|';
                }
            }
            me.disabledDatesRE = new RegExp(re + ')');
        }
    },
    
    setDisabledDates: function(dd) {
        var me = this;
        if (Ext.isArray(dd)) {
            me.disabledDates = dd;
            me.disabledDatesRE = null;
        } else {
            me.disabledDatesRE = dd;
        }
        me.initDisabledDays();
        me.update(me.value, true);
        return me;
    },
    
    setDisabledDays: function(dd) {
        this.disabledDays = dd;
        return this.update(this.value, true);
    },
    
    setMinDate: function(dt) {
        this.minDate = dt;
        return this.update(this.value, true);
    },
    
    setMaxDate: function(dt) {
        this.maxDate = dt;
        return this.update(this.value, true);
    },
    
    setValue: function(value) {
        
        this.value = Ext.Date.clearTime(value || new Date(), true);
        return this.update(this.value);
    },
    
    getValue: function() {
        return this.value;
    },
    
    
    getDayInitial: function(value) {
        return value.substr(0, 1);
    },
    
    
    
    onEnable: function() {
        this.callParent();
        this.syncDisabled(false);
        this.update(this.activeDate);
    },
    
    
    onShow: function() {
        this.callParent();
        this.syncDisabled(false);
        if (this.pickerField) {
            this.startValue = this.pickerField.getValue();
        }
    },
    
    
    onHide: function() {
        this.callParent();
        this.syncDisabled(true);
    },
    
    
    onDisable: function() {
        this.callParent();
        this.syncDisabled(true);
    },
    
    getActive: function() {
        return this.activeDate || this.value;
    },
    
    runAnimation: function(isHide) {
        var picker = this.monthPicker,
            options = {
                duration: 200,
                callback: function() {
                    picker.setVisible(!isHide);
                }
            };
        if (isHide) {
            picker.el.slideOut('t', options);
        } else {
            picker.el.slideIn('t', options);
        }
    },
    
    hideMonthPicker: function(animate) {
        var me = this,
            picker = me.monthPicker;
        if (picker && picker.isVisible()) {
            if (me.shouldAnimate(animate)) {
                me.runAnimation(true);
            } else {
                picker.hide();
            }
        }
        return me;
    },
    doShowMonthPicker: function() {
        
        
        this.showMonthPicker();
    },
    doHideMonthPicker: function() {
        
        
        this.hideMonthPicker();
    },
    
    showMonthPicker: function(animate) {
        var me = this,
            el = me.el,
            picker;
        if (me.rendered && !me.disabled) {
            picker = me.createMonthPicker();
            if (!picker.isVisible()) {
                picker.setValue(me.getActive());
                picker.setSize(el.getSize());
                
                picker.floatParent = null;
                picker.setPosition(-el.getBorderWidth('l'), -el.getBorderWidth('t'));
                if (me.shouldAnimate(animate)) {
                    me.runAnimation(false);
                } else {
                    picker.show();
                }
            }
        }
        return me;
    },
    
    shouldAnimate: function(animate) {
        return Ext.isDefined(animate) ? animate : !this.disableAnim;
    },
    
    createMonthPicker: function() {
        var me = this,
            picker = me.monthPicker;
        if (!picker) {
            me.monthPicker = picker = new Ext.picker.Month({
                renderTo: me.el,
                
                
                
                ownerCmp: me,
                floating: true,
                padding: me.padding,
                shadow: false,
                small: me.showToday === false,
                listeners: {
                    scope: me,
                    cancelclick: me.onCancelClick,
                    okclick: me.onOkClick,
                    yeardblclick: me.onOkClick,
                    monthdblclick: me.onOkClick
                }
            });
            if (!me.disableAnim) {
                
                picker.el.setStyle('display', 'none');
            }
            picker.hide();
            me.on('beforehide', me.doHideMonthPicker, me);
        }
        return picker;
    },
    
    onOkClick: function(picker, value) {
        var me = this,
            month = value[0],
            year = value[1],
            date = new Date(year,month,me.getActive().getDate());
        if (date.getMonth() !== month) {
            
            date = Ext.Date.getLastDateOfMonth(new Date(year,month,1));
        }
        me.setValue(date);
        me.hideMonthPicker();
    },
    
    onCancelClick: function() {
        this.selectedUpdate(this.activeDate);
        this.hideMonthPicker();
    },
    
    showPrevMonth: function(e) {
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, -1));
    },
    
    showNextMonth: function(e) {
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, 1));
    },
    
    showPrevYear: function() {
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, -1));
    },
    
    showNextYear: function() {
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, 1));
    },
    
    handleMouseWheel: function(e) {
        e.stopEvent();
        if (!this.disabled) {
            var delta = e.getWheelDelta();
            if (delta > 0) {
                this.showPrevMonth();
            } else if (delta < 0) {
                this.showNextMonth();
            }
        }
    },
    
    handleDateClick: function(e, t) {
        var me = this,
            handler = me.handler;
        e.stopEvent();
        if (!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
            me.setValue(new Date(t.dateValue));
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            
            
            
            
            me.onSelect();
        }
    },
    
    onSelect: function() {
        if (this.hideOnSelect) {
            this.hide();
        }
    },
    
    selectToday: function() {
        var me = this,
            btn = me.todayBtn,
            handler = me.handler;
        if (btn && !btn.disabled) {
            me.setValue(Ext.Date.clearTime(new Date()));
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            me.onSelect();
        }
        return me;
    },
    
    selectedUpdate: function(date) {
        var me = this,
            t = date.getTime(),
            cells = me.cells,
            cls = me.selectedCls,
            c,
            cLen = cells.getCount(),
            cell;
        cell = me.activeCell;
        if (cell) {
            Ext.fly(cell).removeCls(cls);
            cell.setAttribute('aria-selected', false);
        }
        for (c = 0; c < cLen; c++) {
            cell = cells.item(c);
            if (me.textNodes[c].dateValue == t) {
                me.activeCell = cell.dom;
                me.eventEl.dom.setAttribute('aria-activedescendant', cell.dom.id);
                cell.dom.setAttribute('aria-selected', true);
                cell.addCls(cls);
                me.fireEvent('highlightitem', me, cell);
                break;
            }
        }
    },
    
    fullUpdate: function(date) {
        var me = this,
            cells = me.cells.elements,
            textNodes = me.textNodes,
            disabledCls = me.disabledCellCls,
            eDate = Ext.Date,
            i = 0,
            extraDays = 0,
            newDate = +eDate.clearTime(date, true),
            today = +eDate.clearTime(new Date()),
            min = me.minDate ? eDate.clearTime(me.minDate, true) : Number.NEGATIVE_INFINITY,
            max = me.maxDate ? eDate.clearTime(me.maxDate, true) : Number.POSITIVE_INFINITY,
            ddMatch = me.disabledDatesRE,
            ddText = me.disabledDatesText,
            ddays = me.disabledDays ? me.disabledDays.join('') : false,
            ddaysText = me.disabledDaysText,
            format = me.format,
            days = eDate.getDaysInMonth(date),
            firstOfMonth = eDate.getFirstDateOfMonth(date),
            startingPos = firstOfMonth.getDay() - me.startDay,
            previousMonth = eDate.add(date, eDate.MONTH, -1),
            ariaTitleDateFormat = me.ariaTitleDateFormat,
            prevStart, current, disableToday, tempDate, setCellClass, html, cls, formatValue, value;
        if (startingPos < 0) {
            startingPos += 7;
        }
        days += startingPos;
        prevStart = eDate.getDaysInMonth(previousMonth) - startingPos;
        current = new Date(previousMonth.getFullYear(),previousMonth.getMonth(),prevStart,me.initHour);
        if (me.showToday) {
            tempDate = eDate.clearTime(new Date());
            disableToday = (tempDate < min || tempDate > max || (ddMatch && format && ddMatch.test(eDate.dateFormat(tempDate, format))) || (ddays && ddays.indexOf(tempDate.getDay()) != -1));
            if (!me.disabled) {
                me.todayBtn.setDisabled(disableToday);
            }
        }
        setCellClass = function(cellIndex, cls) {
            var cell = cells[cellIndex];
            value = +eDate.clearTime(current, true);
            cell.setAttribute('aria-label', eDate.format(current, ariaTitleDateFormat));
            
            cell.firstChild.dateValue = value;
            if (value == today) {
                cls += ' ' + me.todayCls;
                cell.firstChild.title = me.todayText;
                
                me.todayElSpan = Ext.DomHelper.append(cell.firstChild, {
                    tag: 'span',
                    cls: Ext.baseCSSPrefix + 'hidden-clip',
                    html: me.todayText
                }, true);
            }
            if (value == newDate) {
                me.activeCell = cell;
                me.eventEl.dom.setAttribute('aria-activedescendant', cell.id);
                cell.setAttribute('aria-selected', true);
                cls += ' ' + me.selectedCls;
                me.fireEvent('highlightitem', me, cell);
            } else {
                cell.setAttribute('aria-selected', false);
            }
            if (value < min) {
                cls += ' ' + disabledCls;
                cell.setAttribute('aria-label', me.minText);
            } else if (value > max) {
                cls += ' ' + disabledCls;
                cell.setAttribute('aria-label', me.maxText);
            } else if (ddays && ddays.indexOf(current.getDay()) !== -1) {
                cell.setAttribute('aria-label', ddaysText);
                cls += ' ' + disabledCls;
            } else if (ddMatch && format) {
                formatValue = eDate.dateFormat(current, format);
                if (ddMatch.test(formatValue)) {
                    cell.setAttribute('aria-label', ddText.replace('%0', formatValue));
                    cls += ' ' + disabledCls;
                }
            }
            cell.className = cls + ' ' + me.cellCls;
        };
        for (; i < me.numDays; ++i) {
            if (i < startingPos) {
                html = (++prevStart);
                cls = me.prevCls;
            } else if (i >= days) {
                html = (++extraDays);
                cls = me.nextCls;
            } else {
                html = i - startingPos + 1;
                cls = me.activeCls;
            }
            textNodes[i].innerHTML = html;
            current.setDate(current.getDate() + 1);
            setCellClass(i, cls);
        }
        me.monthBtn.setText(Ext.Date.format(date, me.monthYearFormat));
    },
    
    update: function(date, forceRefresh) {
        var me = this,
            active = me.activeDate;
        if (me.rendered) {
            me.activeDate = date;
            if (!forceRefresh && active && me.el && active.getMonth() == date.getMonth() && active.getFullYear() == date.getFullYear()) {
                me.selectedUpdate(date, active);
            } else {
                me.fullUpdate(date, active);
            }
        }
        return me;
    },
    
    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            Ext.destroy(me.keyNav, me.monthPicker, me.monthBtn, me.nextRepeater, me.prevRepeater, me.todayBtn, me.todayElSpan);
            delete me.textNodes;
            delete me.cells.elements;
        }
        me.callParent();
    },
    privates: {
        
        
        finishRenderChildren: function() {
            var me = this;
            me.callParent();
            me.monthBtn.finishRender();
            if (me.showToday) {
                me.todayBtn.finishRender();
            }
        },
        getFocusEl: function() {
            return this.eventEl;
        },
        
        syncDisabled: function(disabled) {
            var me = this,
                keyNav = me.keyNav;
            
            if (keyNav) {
                keyNav.setDisabled(disabled);
                me.prevRepeater.setDisabled(disabled);
                me.nextRepeater.setDisabled(disabled);
                if (me.todayBtn) {
                    me.todayBtn.setDisabled(disabled);
                }
            }
        }
    }
});


Ext.define('Ext.form.field.Date', {
    extend: 'Ext.form.field.Picker',
    alias: 'widget.datefield',
    requires: [
        'Ext.picker.Date'
    ],
    alternateClassName: [
        'Ext.form.DateField',
        'Ext.form.Date'
    ],
    
    
    format: "m/d/Y",
    
    
    
    altFormats: "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d|n-j|n/j",
    
    
    
    disabledDaysText: "Disabled",
    
    
    
    disabledDatesText: "Disabled",
    
    
    
    minText: "The date in this field must be equal to or after {0}",
    
    
    
    maxText: "The date in this field must be equal to or before {0}",
    
    
    
    invalidText: "{0} is not a valid date - it must be in the format {1}",
    
    
    triggerCls: Ext.baseCSSPrefix + 'form-date-trigger',
    
    showToday: true,
    
    
    
    
    
    
    useStrict: undefined,
    
    
    initTime: '12',
    
    initTimeFormat: 'H',
    matchFieldWidth: false,
    
    
    startDay: 0,
    
    
    valuePublishEvent: [
        'select',
        'blur'
    ],
    initComponent: function() {
        var me = this,
            isString = Ext.isString,
            min, max;
        min = me.minValue;
        max = me.maxValue;
        if (isString(min)) {
            me.minValue = me.parseDate(min);
        }
        if (isString(max)) {
            me.maxValue = me.parseDate(max);
        }
        me.disabledDatesRE = null;
        me.initDisabledDays();
        me.callParent();
    },
    initValue: function() {
        var me = this,
            value = me.value;
        
        if (Ext.isString(value)) {
            me.value = me.rawToValue(value);
        }
        me.callParent();
    },
    
    initDisabledDays: function() {
        if (this.disabledDates) {
            var dd = this.disabledDates,
                len = dd.length - 1,
                re = "(?:",
                d,
                dLen = dd.length,
                date;
            for (d = 0; d < dLen; d++) {
                date = dd[d];
                re += Ext.isDate(date) ? '^' + Ext.String.escapeRegex(date.dateFormat(this.format)) + '$' : date;
                if (d !== len) {
                    re += '|';
                }
            }
            this.disabledDatesRE = new RegExp(re + ')');
        }
    },
    
    setDisabledDates: function(disabledDates) {
        var me = this,
            picker = me.picker;
        me.disabledDates = disabledDates;
        me.initDisabledDays();
        if (picker) {
            picker.setDisabledDates(me.disabledDatesRE);
        }
    },
    
    setDisabledDays: function(disabledDays) {
        var picker = this.picker;
        this.disabledDays = disabledDays;
        if (picker) {
            picker.setDisabledDays(disabledDays);
        }
    },
    
    setMinValue: function(value) {
        var me = this,
            picker = me.picker,
            minValue = (Ext.isString(value) ? me.parseDate(value) : value);
        me.minValue = minValue;
        if (picker) {
            picker.minText = Ext.String.format(me.minText, me.formatDate(me.minValue));
            picker.setMinDate(minValue);
        }
    },
    
    setMaxValue: function(value) {
        var me = this,
            picker = me.picker,
            maxValue = (Ext.isString(value) ? me.parseDate(value) : value);
        me.maxValue = maxValue;
        if (picker) {
            picker.maxText = Ext.String.format(me.maxText, me.formatDate(me.maxValue));
            picker.setMaxDate(maxValue);
        }
    },
    
    getErrors: function(value) {
        value = arguments.length > 0 ? value : this.formatDate(this.processRawValue(this.getRawValue()));
        var me = this,
            format = Ext.String.format,
            clearTime = Ext.Date.clearTime,
            errors = me.callParent([
                value
            ]),
            disabledDays = me.disabledDays,
            disabledDatesRE = me.disabledDatesRE,
            minValue = me.minValue,
            maxValue = me.maxValue,
            len = disabledDays ? disabledDays.length : 0,
            i = 0,
            svalue, fvalue, day, time;
        if (value === null || value.length < 1) {
            
            return errors;
        }
        svalue = value;
        value = me.parseDate(value);
        if (!value) {
            errors.push(format(me.invalidText, svalue, Ext.Date.unescapeFormat(me.format)));
            return errors;
        }
        time = value.getTime();
        if (minValue && time < clearTime(minValue).getTime()) {
            errors.push(format(me.minText, me.formatDate(minValue)));
        }
        if (maxValue && time > clearTime(maxValue).getTime()) {
            errors.push(format(me.maxText, me.formatDate(maxValue)));
        }
        if (disabledDays) {
            day = value.getDay();
            for (; i < len; i++) {
                if (day === disabledDays[i]) {
                    errors.push(me.disabledDaysText);
                    break;
                }
            }
        }
        fvalue = me.formatDate(value);
        if (disabledDatesRE && disabledDatesRE.test(fvalue)) {
            errors.push(format(me.disabledDatesText, fvalue));
        }
        return errors;
    },
    rawToValue: function(rawValue) {
        return this.parseDate(rawValue) || rawValue || null;
    },
    valueToRaw: function(value) {
        return this.formatDate(this.parseDate(value));
    },
    
    
    safeParse: function(value, format) {
        var me = this,
            utilDate = Ext.Date,
            result = null,
            strict = me.useStrict,
            parsedDate;
        if (utilDate.formatContainsHourInfo(format)) {
            
            result = utilDate.parse(value, format, strict);
        } else {
            
            parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat, strict);
            if (parsedDate) {
                result = utilDate.clearTime(parsedDate);
            }
        }
        return result;
    },
    
    getSubmitValue: function() {
        var format = this.submitFormat || this.format,
            value = this.getValue();
        return value ? Ext.Date.format(value, format) : '';
    },
    
    parseDate: function(value) {
        if (!value || Ext.isDate(value)) {
            return value;
        }
        var me = this,
            val = me.safeParse(value, me.format),
            altFormats = me.altFormats,
            altFormatsArray = me.altFormatsArray,
            i = 0,
            len;
        if (!val && altFormats) {
            altFormatsArray = altFormatsArray || altFormats.split('|');
            len = altFormatsArray.length;
            for (; i < len && !val; ++i) {
                val = me.safeParse(value, altFormatsArray[i]);
            }
        }
        return val;
    },
    
    formatDate: function(date) {
        return Ext.isDate(date) ? Ext.Date.dateFormat(date, this.format) : date;
    },
    createPicker: function() {
        var me = this,
            format = Ext.String.format;
        
        
        
        return new Ext.picker.Date({
            pickerField: me,
            floating: true,
            focusable: false,
            
            hidden: true,
            minDate: me.minValue,
            maxDate: me.maxValue,
            disabledDatesRE: me.disabledDatesRE,
            disabledDatesText: me.disabledDatesText,
            disabledDays: me.disabledDays,
            disabledDaysText: me.disabledDaysText,
            format: me.format,
            showToday: me.showToday,
            startDay: me.startDay,
            minText: format(me.minText, me.formatDate(me.minValue)),
            maxText: format(me.maxText, me.formatDate(me.maxValue)),
            listeners: {
                scope: me,
                select: me.onSelect
            },
            keyNavConfig: {
                esc: function() {
                    me.collapse();
                }
            }
        });
    },
    onSelect: function(m, d) {
        var me = this;
        me.setValue(d);
        me.fireEvent('select', me, d);
        me.collapse();
    },
    
    onExpand: function() {
        var value = this.getValue();
        this.picker.setValue(Ext.isDate(value) ? value : new Date());
    },
    
    onBlur: function(e) {
        var me = this,
            v = me.rawToValue(me.getRawValue());
        if (Ext.isDate(v)) {
            me.setValue(v);
        }
        me.callParent([
            e
        ]);
    }
});


Ext.define('Ext.form.field.FileButton', {
    extend: 'Ext.button.Button',
    alias: 'widget.filebutton',
    childEls: [
        'fileInputEl'
    ],
    inputCls: Ext.baseCSSPrefix + 'form-file-input',
    cls: Ext.baseCSSPrefix + 'form-file-btn',
    preventDefault: false,
    
    
    tabIndex: null,
    autoEl: {
        tag: 'div',
        unselectable: 'on'
    },
    
    afterTpl: [
        '<input id="{id}-fileInputEl" data-ref="fileInputEl" class="{childElCls} {inputCls}" ',
        'type="file" size="1" name="{inputName}" role="{role}" ',
        '<tpl if="tabIndex != null">tabindex="{tabIndex}"</tpl>',
        '>'
    ],
    
    getAfterMarkup: function(values) {
        return this.getTpl('afterTpl').apply(values);
    },
    getTemplateArgs: function() {
        var args = this.callParent();
        args.inputCls = this.inputCls;
        args.inputName = this.inputName;
        args.tabIndex = this.ownerCt.tabIndex;
        return args;
    },
    afterRender: function() {
        var me = this;
        me.callParent(arguments);
        
        
        me.fileInputEl.on({
            scope: me,
            change: me.fireChange,
            focus: me.onFocus,
            blur: me.onBlur
        });
    },
    fireChange: function(e) {
        this.fireEvent('change', this, e, this.fileInputEl.dom.value);
    },
    
    createFileInput: function(isTemporary) {
        var me = this,
            fileInputEl = me.fileInputEl = me.el.createChild({
                name: me.inputName,
                id: !isTemporary ? me.id + '-fileInputEl' : undefined,
                cls: me.inputCls,
                tag: 'input',
                type: 'file',
                size: 1,
                role: 'button'
            });
        
        fileInputEl.dom.setAttribute(Ext.Component.componentIdAttribute, me.id);
        
        
        fileInputEl.on({
            scope: me,
            change: me.fireChange,
            focus: me.onFocus,
            blur: me.onBlur
        });
    },
    reset: function(remove) {
        
        
        var me = this;
        if (remove) {
            me.fileInputEl.destroy();
        }
        me.createFileInput(!remove);
    },
    restoreInput: function(el) {
        
        
        var me = this;
        me.fileInputEl.destroy();
        el = Ext.get(el);
        me.el.appendChild(el);
        me.fileInputEl = el;
    },
    onDisable: function() {
        this.callParent();
        this.fileInputEl.dom.disabled = true;
    },
    onEnable: function() {
        this.callParent();
        this.fileInputEl.dom.disabled = false;
    },
    privates: {
        getFocusEl: function() {
            return this.fileInputEl;
        },
        getFocusClsEl: function() {
            return this.el;
        }
    }
});


Ext.define('Ext.form.trigger.Component', {
    extend: 'Ext.form.trigger.Trigger',
    alias: 'trigger.component',
    cls: Ext.baseCSSPrefix + 'form-trigger-cmp',
    
    
    onFieldRender: function() {
        var me = this,
            component = me.component;
        this.callParent();
        if (!component.isComponent && !component.isWidget) {
            component = Ext.widget(component);
        }
        me.component = component;
        component.render(me.el);
    },
    destroy: function() {
        var component = this.component;
        if (component.isComponent || component.isWidget) {
            component.destroy();
        }
        this.callParent();
    }
});


Ext.define('Ext.form.field.File', {
    extend: 'Ext.form.field.Text',
    alias: [
        'widget.filefield',
        'widget.fileuploadfield'
    ],
    alternateClassName: [
        'Ext.form.FileUploadField',
        'Ext.ux.form.FileUploadField',
        'Ext.form.File'
    ],
    requires: [
        'Ext.form.field.FileButton',
        'Ext.form.trigger.Component'
    ],
    needArrowKeys: false,
    triggers: {
        filebutton: {
            type: 'component',
            hideOnReadOnly: false,
            
            
            
            
            
            
            preventMouseDown: false
        }
    },
    
    
    buttonText: 'Browse...',
    
    
    buttonOnly: false,
    
    buttonMargin: 3,
    
    clearOnSubmit: true,
    
    
    
    
    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'form-file-wrap',
    
    inputCls: Ext.baseCSSPrefix + 'form-text-file',
    
    readOnly: true,
    
    editable: false,
    submitValue: false,
    
    triggerNoEditCls: '',
    
    
    childEls: [
        'browseButtonWrap'
    ],
    
    applyTriggers: function(triggers) {
        var me = this,
            triggerCfg = (triggers || {}).filebutton;
        if (triggerCfg) {
            triggerCfg.component = Ext.apply({
                xtype: 'filebutton',
                ownerCt: me,
                id: me.id + '-button',
                ui: me.ui,
                disabled: me.disabled,
                text: me.buttonText,
                style: me.buttonOnly ? '' : me.getButtonMarginProp() + me.buttonMargin + 'px',
                inputName: me.getName(),
                listeners: {
                    scope: me,
                    change: me.onFileChange
                }
            }, me.buttonConfig);
            return me.callParent([
                triggers
            ]);
        } else {
            Ext.Error.raise(me.$className + ' requires a valid trigger config containing "filebutton" specification');
        }
    },
    getSubTplData: function(fieldData) {
        var data = this.callParent([
                fieldData
            ]);
        
        
        
        data.tabIdx = -1;
        return data;
    },
    
    onRender: function() {
        var me = this,
            inputEl, button, buttonEl, trigger;
        me.callParent(arguments);
        inputEl = me.inputEl;
        inputEl.dom.name = '';
        
        trigger = me.getTrigger('filebutton');
        button = me.button = trigger.component;
        me.fileInputEl = button.fileInputEl;
        buttonEl = button.el;
        if (me.buttonOnly) {
            me.inputWrap.setDisplayed(false);
            me.shrinkWrap = 3;
        }
        
        trigger.el.setWidth(buttonEl.getWidth() + buttonEl.getMargin('lr'));
        if (Ext.isIE) {
            me.button.getEl().repaint();
        }
    },
    
    getTriggerMarkup: function() {
        return '<td id="' + this.id + '-browseButtonWrap" data-ref="browseButtonWrap" role="presentation"></td>';
    },
    
    onFileChange: function(button, e, value) {
        this.duringFileSelect = true;
        Ext.form.field.File.superclass.setValue.call(this, value);
        delete this.duringFileSelect;
    },
    didValueChange: function() {
        
        
        
        
        return !!this.duringFileSelect;
    },
    
    setValue: Ext.emptyFn,
    reset: function() {
        var me = this,
            clear = me.clearOnSubmit;
        if (me.rendered) {
            me.button.reset(clear);
            me.fileInputEl = me.button.fileInputEl;
            if (clear) {
                me.inputEl.dom.value = '';
                
                Ext.form.field.File.superclass.setValue.call(this, null);
            }
        }
        me.callParent();
    },
    onShow: function() {
        this.callParent();
        
        
        this.button.updateLayout();
    },
    onDisable: function() {
        this.callParent();
        this.button.disable();
    },
    onEnable: function() {
        this.callParent();
        this.button.enable();
    },
    
    isFileUpload: Ext.returnTrue,
    extractFileInput: function() {
        var me = this,
            fileInput;
        if (me.rendered) {
            fileInput = me.button.fileInputEl.dom;
            me.reset();
        } else {
            
            
            fileInput = document.createElement('input');
            fileInput.type = 'file';
            fileInput.className = Ext.baseCSSPrefix + 'hidden-display';
            fileInput.name = me.getName();
        }
        return fileInput;
    },
    restoreInput: function(el) {
        
        
        if (this.rendered) {
            var button = this.button;
            button.restoreInput(el);
            this.fileInputEl = button.fileInputEl;
        }
    },
    onDestroy: function() {
        this.fileInputEl = this.button = null;
        this.callParent();
    },
    getButtonMarginProp: function() {
        return 'margin-left:';
    },
    privates: {
        getFocusEl: function() {
            return this.button;
        }
    }
});


Ext.define('Ext.form.field.Hidden', {
    extend: 'Ext.form.field.Base',
    alias: [
        'widget.hiddenfield',
        'widget.hidden'
    ],
    alternateClassName: 'Ext.form.Hidden',
    
    focusable: false,
    inputType: 'hidden',
    isTextInput: false,
    hideLabel: true,
    hidden: true,
    ariaRole: 'presentation',
    initComponent: function() {
        this.formItemCls += '-hidden';
        this.callParent();
    },
    
    isEqual: function(value1, value2) {
        return this.isEqualAsString(value1, value2);
    },
    
    initEvents: Ext.emptyFn,
    setSize: Ext.emptyFn,
    setWidth: Ext.emptyFn,
    setHeight: Ext.emptyFn,
    setPosition: Ext.emptyFn,
    setPagePosition: Ext.emptyFn,
    markInvalid: Ext.emptyFn,
    clearInvalid: Ext.emptyFn
});


Ext.define('Ext.picker.Color', {
    extend: 'Ext.Component',
    requires: 'Ext.XTemplate',
    alias: 'widget.colorpicker',
    alternateClassName: 'Ext.ColorPalette',
    focusable: true,
    
    componentCls: Ext.baseCSSPrefix + 'color-picker',
    
    selectedCls: Ext.baseCSSPrefix + 'color-picker-selected',
    
    itemCls: Ext.baseCSSPrefix + 'color-picker-item',
    
    value: null,
    
    clickEvent: 'click',
    
    allowReselect: false,
    
    colors: [
        '000000',
        '993300',
        '333300',
        '003300',
        '003366',
        '000080',
        '333399',
        '333333',
        '800000',
        'FF6600',
        '808000',
        '008000',
        '008080',
        '0000FF',
        '666699',
        '808080',
        'FF0000',
        'FF9900',
        '99CC00',
        '339966',
        '33CCCC',
        '3366FF',
        '800080',
        '969696',
        'FF00FF',
        'FFCC00',
        'FFFF00',
        '00FF00',
        '00FFFF',
        '00CCFF',
        '993366',
        'C0C0C0',
        'FF99CC',
        'FFCC99',
        'FFFF99',
        'CCFFCC',
        'CCFFFF',
        '99CCFF',
        'CC99FF',
        'FFFFFF'
    ],
    
    
    colorRe: /(?:^|\s)color-(.{6})(?:\s|$)/,
    renderTpl: [
        '<tpl for="colors">',
        '<a href="#" role="button" class="color-{.} {parent.itemCls}" hidefocus="on">',
        '<span class="{parent.itemCls}-inner" style="background:#{.}">&#160;</span>',
        '</a>',
        '</tpl>'
    ],
    
    
    initComponent: function() {
        var me = this;
        me.callParent(arguments);
        if (me.handler) {
            me.on('select', me.handler, me.scope, true);
        }
    },
    
    initRenderData: function() {
        var me = this;
        return Ext.apply(me.callParent(), {
            itemCls: me.itemCls,
            colors: me.colors
        });
    },
    onRender: function() {
        var me = this,
            clickEvent = me.clickEvent;
        me.callParent(arguments);
        me.mon(me.el, clickEvent, me.handleClick, me, {
            delegate: 'a'
        });
        
        if (clickEvent !== 'click') {
            me.mon(me.el, 'click', Ext.emptyFn, me, {
                delegate: 'a',
                stopEvent: true
            });
        }
    },
    
    afterRender: function() {
        var me = this,
            value;
        me.callParent(arguments);
        if (me.value) {
            value = me.value;
            me.value = null;
            me.select(value, true);
        }
    },
    
    handleClick: function(event) {
        var me = this,
            color;
        event.stopEvent();
        if (!me.disabled) {
            color = event.currentTarget.className.match(me.colorRe)[1];
            me.select(color.toUpperCase());
        }
    },
    
    select: function(color, suppressEvent) {
        var me = this,
            selectedCls = me.selectedCls,
            value = me.value,
            el, item;
        color = color.replace('#', '');
        if (!me.rendered) {
            me.value = color;
            return;
        }
        if (color != value || me.allowReselect) {
            el = me.el;
            if (me.value) {
                item = el.down('a.color-' + value, true);
                Ext.fly(item).removeCls(selectedCls);
            }
            item = el.down('a.color-' + color, true);
            Ext.fly(item).addCls(selectedCls);
            me.value = color;
            if (suppressEvent !== true) {
                me.fireEvent('select', me, color);
            }
        }
    },
    
    clear: function() {
        var me = this,
            value = me.value,
            el;
        if (value && me.rendered) {
            el = me.el.down('a.color-' + value, true);
            Ext.fly(el).removeCls(me.selectedCls);
        }
        me.value = null;
    },
    
    getValue: function() {
        return this.value || null;
    }
});


Ext.define('Ext.layout.component.field.HtmlEditor', {
    extend: 'Ext.layout.component.field.FieldContainer',
    alias: [
        'layout.htmleditor'
    ],
    type: 'htmleditor',
    naturalHeight: 150,
    naturalWidth: 300,
    beginLayout: function(ownerContext) {
        var owner = this.owner,
            dom;
        
        
        
        if (Ext.isGecko) {
            dom = owner.textareaEl.dom;
            this.lastValue = dom.value;
            dom.value = '';
        }
        this.callParent(arguments);
        ownerContext.toolbarContext = ownerContext.context.getCmp(owner.toolbar);
        ownerContext.inputCmpContext = ownerContext.context.getCmp(owner.inputCmp);
        ownerContext.bodyCellContext = ownerContext.getEl('bodyEl');
        ownerContext.textAreaContext = ownerContext.getEl('textareaEl');
        ownerContext.iframeContext = ownerContext.getEl('iframeEl');
    },
    beginLayoutCycle: function(ownerContext) {
        var me = this,
            widthModel = ownerContext.widthModel,
            heightModel = ownerContext.heightModel,
            owner = me.owner,
            iframeEl = owner.iframeEl,
            textareaEl = owner.textareaEl,
            height = (heightModel.natural || heightModel.shrinkWrap) ? me.naturalHeight : '';
        me.callParent(arguments);
        if (widthModel.shrinkWrap) {
            iframeEl.setStyle('width', '');
            textareaEl.setStyle('width', '');
        } else if (widthModel.natural) {
            ownerContext.bodyCellContext.setWidth(me.naturalWidth);
        }
        iframeEl.setStyle('height', height);
        textareaEl.setStyle('height', height);
    },
    finishedLayout: function() {
        var owner = this.owner;
        this.callParent(arguments);
        if (Ext.isGecko) {
            owner.textareaEl.dom.value = this.lastValue;
        }
    }
});


Ext.define('Ext.toolbar.Separator', {
    extend: 'Ext.toolbar.Item',
    
    
    requires: [
        'Ext.toolbar.Toolbar'
    ],
    alias: 'widget.tbseparator',
    alternateClassName: 'Ext.Toolbar.Separator',
    baseCls: Ext.baseCSSPrefix + 'toolbar-separator',
    ariaRole: 'separator'
});


Ext.define('Ext.layout.container.boxOverflow.Menu', {
    
    extend: 'Ext.layout.container.boxOverflow.None',
    requires: [
        'Ext.toolbar.Separator',
        'Ext.button.Button'
    ],
    alternateClassName: 'Ext.layout.boxOverflow.Menu',
    alias: [
        'box.overflow.menu',
        'box.overflow.Menu'
    ],
    
    
    
    noItemsMenuText: '<div class="' + Ext.baseCSSPrefix + 'toolbar-no-items" role="menuitem">(None)</div>',
    menuCls: Ext.baseCSSPrefix + 'box-menu',
    constructor: function(config) {
        var me = this;
        me.callParent(arguments);
        
        me.menuItems = [];
    },
    beginLayout: function(ownerContext) {
        this.callParent(arguments);
        
        
        this.clearOverflow(ownerContext);
    },
    beginLayoutCycle: function(ownerContext, firstCycle) {
        this.callParent(arguments);
        if (!firstCycle) {
            
            
            this.clearOverflow(ownerContext);
            this.layout.cacheChildItems(ownerContext);
        }
    },
    onRemove: function(comp) {
        Ext.Array.remove(this.menuItems, comp);
    },
    
    getSuffixConfig: function() {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            oid = owner.id;
        
        me.menu = new Ext.menu.Menu({
            listeners: {
                scope: me,
                beforeshow: me.beforeMenuShow
            }
        });
        
        me.menuTrigger = new Ext.button.Button({
            id: oid + '-menu-trigger',
            cls: me.menuCls + '-after ' + Ext.baseCSSPrefix + 'toolbar-item',
            plain: owner.usePlainButtons,
            ownerCt: owner,
            
            ownerLayout: layout,
            iconCls: Ext.baseCSSPrefix + me.getOwnerType(owner) + '-more-icon',
            ui: owner.defaultButtonUI || 'default',
            menu: me.menu,
            
            showEmptyMenu: true,
            getSplitCls: function() {
                return '';
            }
        });
        return me.menuTrigger.getRenderTree();
    },
    getOverflowCls: function(direction) {
        return this.menuCls + '-body-' + direction;
    },
    handleOverflow: function(ownerContext) {
        var me = this,
            layout = me.layout;
        me.showTrigger(ownerContext);
        
        if (layout.direction !== 'vertical') {
            me.menuTrigger.setLocalY((ownerContext.state.boxPlan.maxSize - me.menuTrigger[layout.names.getHeight]()) / 2);
        }
        return {
            reservedSpace: me.triggerTotalWidth
        };
    },
    
    captureChildElements: function() {
        var me = this,
            menuTrigger = me.menuTrigger,
            names = me.layout.names;
        
        if (menuTrigger.rendering) {
            menuTrigger.finishRender();
            me.triggerTotalWidth = menuTrigger[names.getWidth]() + menuTrigger.el.getMargin(names.parallelMargins);
        }
    },
    
    clearOverflow: function(ownerContext) {
        var me = this,
            items = me.menuItems,
            item,
            i = 0,
            length = items.length,
            owner = me.layout.owner,
            asLayoutRoot = owner._asLayoutRoot;
        owner.suspendLayouts();
        me.captureChildElements();
        me.hideTrigger();
        owner.resumeLayouts();
        for (; i < length; i++) {
            item = items[i];
            
            
            item.suspendLayouts();
            item.show();
            item.resumeLayouts(asLayoutRoot);
        }
        items.length = 0;
    },
    
    showTrigger: function(ownerContext) {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            names = layout.names,
            startProp = names.x,
            sizeProp = names.width,
            plan = ownerContext.state.boxPlan,
            available = plan.targetSize[sizeProp],
            childItems = ownerContext.childItems,
            len = childItems.length,
            menuTrigger = me.menuTrigger,
            childContext, comp, i, props;
        
        
        menuTrigger.suspendLayouts();
        menuTrigger.show();
        menuTrigger.resumeLayouts(me._asLayoutRoot);
        available -= me.triggerTotalWidth;
        owner.suspendLayouts();
        
        
        me.menuItems.length = 0;
        for (i = 0; i < len; i++) {
            childContext = childItems[i];
            props = childContext.props;
            if (props[startProp] + props[sizeProp] > available) {
                comp = childContext.target;
                me.menuItems.push(comp);
                comp.hide();
            }
        }
        owner.resumeLayouts();
    },
    
    hideTrigger: function() {
        var menuTrigger = this.menuTrigger;
        if (menuTrigger) {
            menuTrigger.hide();
        }
    },
    
    beforeMenuShow: function(menu) {
        var me = this,
            items = me.menuItems,
            i = 0,
            len = items.length,
            item, prev,
            needsSep = function(group, prev) {
                return group.isXType('buttongroup') && !(prev instanceof Ext.toolbar.Separator);
            };
        menu.suspendLayouts();
        menu.removeAll(false);
        for (; i < len; i++) {
            item = items[i];
            
            if (!i && (item instanceof Ext.toolbar.Separator)) {
                
                continue;
            }
            if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
                menu.add('-');
            }
            me.addComponentToMenu(menu, item);
            prev = item;
        }
        
        if (menu.items.length < 1) {
            menu.add(me.noItemsMenuText);
        }
        menu.resumeLayouts();
    },
    
    createMenuConfig: function(component, hideOnClick) {
        var config = Ext.apply({}, component.initialConfig),
            group = component.toggleGroup;
        Ext.copyTo(config, component, [
            'iconCls',
            'icon',
            'itemId',
            'disabled',
            'handler',
            'scope',
            'menu',
            'tabIndex'
        ]);
        Ext.applyIf(config, {
            text: component.overflowText || component.text,
            hideOnClick: hideOnClick,
            destroyMenu: false,
            listeners: null
        });
        
        if (component.isFormField) {
            config.value = component.getValue();
            
            
            
            
            
            config.listeners = {
                change: function(c, newVal, oldVal) {
                    component.setValue(newVal);
                }
            };
        }
        
        else if (group || component.enableToggle) {
            Ext.apply(config, {
                hideOnClick: false,
                group: group,
                checked: component.pressed,
                handler: function(item, e) {
                    component.onClick(e);
                }
            });
        }
        
        if (component.isButton && !component.changeListenersAdded) {
            component.on({
                textchange: this.onButtonAttrChange,
                iconchange: this.onButtonAttrChange,
                toggle: this.onButtonToggle
            });
            component.changeListenersAdded = true;
        }
        
        
        
        delete config.margin;
        delete config.ownerCt;
        delete config.xtype;
        delete config.id;
        delete config.itemId;
        return config;
    },
    onButtonAttrChange: function(btn) {
        var clone = btn.overflowClone;
        clone.suspendLayouts();
        clone.setText(btn.text);
        clone.setIcon(btn.icon);
        clone.setIconCls(btn.iconCls);
        clone.resumeLayouts(true);
    },
    onButtonToggle: function(btn, state) {
        
        if (btn.overflowClone.checked !== state) {
            btn.overflowClone.setChecked(state);
        }
    },
    
    addComponentToMenu: function(menu, component) {
        var me = this,
            i, items, iLen;
        
        if (component instanceof Ext.toolbar.Fill) {
            return;
        }
        
        else if (component instanceof Ext.toolbar.Separator) {
            menu.add('-');
        } else if (component.overflowClone) {
            menu.add(component.overflowClone);
        }
        
        else if (component.isComponent) {
            if (component.isXType('splitbutton')) {
                component.overflowClone = menu.add(me.createMenuConfig(component, true));
            } else if (component.isXType('button')) {
                component.overflowClone = menu.add(me.createMenuConfig(component, !component.menu));
            } else if (component.isXType('buttongroup')) {
                items = component.items.items;
                iLen = items.length;
                for (i = 0; i < iLen; i++) {
                    me.addComponentToMenu(menu, items[i]);
                }
            } else {
                component.overflowClone = menu.add(Ext.create(Ext.getClassName(component), me.createMenuConfig(component)));
            }
        }
    },
    
    destroy: function() {
        var trigger = this.menuTrigger;
        if (trigger && !this.layout.owner.items.contains(trigger)) {
            
            
            delete trigger.ownerCt;
        }
        Ext.destroy(this.menu, trigger);
    }
});


Ext.define('Ext.form.field.HtmlEditor', {
    extend: 'Ext.form.FieldContainer',
    mixins: {
        field: 'Ext.form.field.Field'
    },
    alias: 'widget.htmleditor',
    alternateClassName: 'Ext.form.HtmlEditor',
    requires: [
        'Ext.tip.QuickTipManager',
        'Ext.picker.Color',
        'Ext.layout.container.VBox',
        'Ext.toolbar.Item',
        'Ext.toolbar.Toolbar',
        'Ext.util.Format',
        'Ext.layout.component.field.HtmlEditor',
        'Ext.util.TaskManager',
        'Ext.layout.container.boxOverflow.Menu'
    ],
    focusable: true,
    componentLayout: 'htmleditor',
    
    textareaCls: Ext.baseCSSPrefix + 'htmleditor-textarea',
    componentTpl: [
        '{beforeTextAreaTpl}',
        '<textarea id="{id}-textareaEl" data-ref="textareaEl" name="{name}" tabindex="-1" {inputAttrTpl}',
        ' class="{textareaCls}" autocomplete="off">',
        '{[Ext.util.Format.htmlEncode(values.value)]}',
        '</textarea>',
        '{afterTextAreaTpl}',
        '{beforeIFrameTpl}',
        '<iframe id="{id}-iframeEl" data-ref="iframeEl" name="{iframeName}" frameBorder="0" {iframeAttrTpl}',
        ' src="{iframeSrc}" class="{iframeCls}"></iframe>',
        '{afterIFrameTpl}',
        {
            disableFormats: true
        }
    ],
    stretchInputElFixed: true,
    subTplInsertions: [
        
        'beforeTextAreaTpl',
        
        'afterTextAreaTpl',
        
        'beforeIFrameTpl',
        
        'afterIFrameTpl',
        
        'iframeAttrTpl',
        
        'inputAttrTpl'
    ],
    
    enableFormat: true,
    
    enableFontSize: true,
    
    enableColors: true,
    
    enableAlignments: true,
    
    enableLists: true,
    
    enableSourceEdit: true,
    
    enableLinks: true,
    
    enableFont: true,
    
    
    createLinkText: 'Please enter the URL for the link:',
    
    
    defaultLinkValue: 'http:/' + '/',
    
    fontFamilies: [
        'Arial',
        'Courier New',
        'Tahoma',
        'Times New Roman',
        'Verdana'
    ],
    
    defaultValue: Ext.isOpera ? '&#160;' : '&#8203;',
    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'html-editor-wrap',
    
    defaultButtonUI: 'default-toolbar',
    
    initialized: false,
    
    activated: false,
    
    sourceEditMode: false,
    
    iframePad: 3,
    
    hideMode: 'offsets',
    maskOnDisable: true,
    containerElCls: Ext.baseCSSPrefix + 'html-editor-container',
    
    reStripQuotes: /^['"]*|['"]*$/g,
    textAlignRE: /text-align:(.*?);/i,
    safariNonsenseRE: /\sclass="(?:Apple-style-span|Apple-tab-span|khtml-block-placeholder)"/gi,
    nonDigitsRE: /\D/g,
    
    
    
    
    
    
    
    
    initComponent: function() {
        var me = this;
        me.items = [
            me.createToolbar(),
            me.createInputCmp()
        ];
        me.layout = {
            type: 'vbox',
            align: 'stretch'
        };
        me.callParent(arguments);
        me.initField();
    },
    createInputCmp: function() {
        this.inputCmp = Ext.widget(this.getInputCmpCfg());
        return this.inputCmp;
    },
    getInputCmpCfg: function() {
        var me = this,
            id = me.id + '-inputCmp',
            data = {
                id: id,
                name: me.name,
                textareaCls: me.textareaCls + ' ' + Ext.baseCSSPrefix + 'hidden',
                value: me.value,
                iframeName: Ext.id(),
                iframeSrc: Ext.SSL_SECURE_URL,
                iframeCls: Ext.baseCSSPrefix + 'htmleditor-iframe'
            };
        me.getInsertionRenderData(data, me.subTplInsertions);
        return {
            flex: 1,
            xtype: 'component',
            tpl: me.getTpl('componentTpl'),
            childEls: [
                'iframeEl',
                'textareaEl'
            ],
            id: id,
            cls: Ext.baseCSSPrefix + 'html-editor-input',
            data: data
        };
    },
    
    createToolbar: function() {
        this.toolbar = Ext.widget(this.getToolbarCfg());
        return this.toolbar;
    },
    getToolbarCfg: function() {
        var me = this,
            items = [],
            i,
            tipsEnabled = Ext.quickTipsActive && Ext.tip.QuickTipManager.isEnabled(),
            baseCSSPrefix = Ext.baseCSSPrefix,
            fontSelectItem, undef;
        function btn(id, toggle, handler) {
            return {
                itemId: id,
                cls: baseCSSPrefix + 'btn-icon',
                iconCls: baseCSSPrefix + 'edit-' + id,
                enableToggle: toggle !== false,
                scope: me,
                handler: handler || me.relayBtnCmd,
                clickEvent: 'mousedown',
                tooltip: tipsEnabled ? me.buttonTips[id] || undef : undef,
                overflowText: me.buttonTips[id].title || undef,
                tabIndex: -1
            };
        }
        if (me.enableFont && !Ext.isSafari2) {
            fontSelectItem = Ext.widget('component', {
                itemId: 'fontSelect',
                renderTpl: [
                    '<select id="{id}-selectEl" data-ref="selectEl" class="' + baseCSSPrefix + 'font-select">',
                    '</select>'
                ],
                childEls: [
                    'selectEl'
                ],
                afterRender: function() {
                    me.fontSelect = this.selectEl;
                    Ext.Component.prototype.afterRender.apply(this, arguments);
                },
                onDisable: function() {
                    var selectEl = this.selectEl;
                    if (selectEl) {
                        selectEl.dom.disabled = true;
                    }
                    Ext.Component.prototype.onDisable.apply(this, arguments);
                },
                onEnable: function() {
                    var selectEl = this.selectEl;
                    if (selectEl) {
                        selectEl.dom.disabled = false;
                    }
                    Ext.Component.prototype.onEnable.apply(this, arguments);
                },
                listeners: {
                    change: function() {
                        me.win.focus();
                        me.relayCmd('fontName', me.fontSelect.dom.value);
                        me.deferFocus();
                    },
                    element: 'selectEl'
                }
            });
            items.push(fontSelectItem, '-');
        }
        if (me.enableFormat) {
            items.push(btn('bold'), btn('italic'), btn('underline'));
        }
        if (me.enableFontSize) {
            items.push('-', btn('increasefontsize', false, me.adjustFont), btn('decreasefontsize', false, me.adjustFont));
        }
        if (me.enableColors) {
            items.push('-', {
                itemId: 'forecolor',
                cls: baseCSSPrefix + 'btn-icon',
                iconCls: baseCSSPrefix + 'edit-forecolor',
                overflowText: me.buttonTips.forecolor.title,
                tooltip: tipsEnabled ? me.buttonTips.forecolor || undef : undef,
                tabIndex: -1,
                menu: Ext.widget('menu', {
                    plain: true,
                    items: [
                        {
                            xtype: 'colorpicker',
                            allowReselect: true,
                            focus: Ext.emptyFn,
                            value: '000000',
                            plain: true,
                            clickEvent: 'mousedown',
                            handler: function(cp, color) {
                                me.relayCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#' + color : color);
                                this.up('menu').hide();
                            }
                        }
                    ]
                })
            }, {
                itemId: 'backcolor',
                cls: baseCSSPrefix + 'btn-icon',
                iconCls: baseCSSPrefix + 'edit-backcolor',
                overflowText: me.buttonTips.backcolor.title,
                tooltip: tipsEnabled ? me.buttonTips.backcolor || undef : undef,
                tabIndex: -1,
                menu: Ext.widget('menu', {
                    plain: true,
                    items: [
                        {
                            xtype: 'colorpicker',
                            focus: Ext.emptyFn,
                            value: 'FFFFFF',
                            plain: true,
                            allowReselect: true,
                            clickEvent: 'mousedown',
                            handler: function(cp, color) {
                                if (Ext.isGecko) {
                                    me.execCmd('useCSS', false);
                                    me.execCmd('hilitecolor', '#' + color);
                                    me.execCmd('useCSS', true);
                                    me.deferFocus();
                                } else {
                                    me.relayCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE || Ext.isOpera ? '#' + color : color);
                                }
                                this.up('menu').hide();
                            }
                        }
                    ]
                })
            });
        }
        if (me.enableAlignments) {
            items.push('-', btn('justifyleft'), btn('justifycenter'), btn('justifyright'));
        }
        if (!Ext.isSafari2) {
            if (me.enableLinks) {
                items.push('-', btn('createlink', false, me.createLink));
            }
            if (me.enableLists) {
                items.push('-', btn('insertorderedlist'), btn('insertunorderedlist'));
            }
            if (me.enableSourceEdit) {
                items.push('-', btn('sourceedit', true, function() {
                    me.toggleSourceEdit(!me.sourceEditMode);
                }));
            }
        }
        
        for (i = 0; i < items.length; i++) {
            if (items[i].itemId !== 'sourceedit') {
                items[i].disabled = true;
            }
        }
        
        
        return {
            xtype: 'toolbar',
            defaultButtonUI: me.defaultButtonUI,
            cls: Ext.baseCSSPrefix + 'html-editor-tb',
            enableOverflow: true,
            items: items,
            
            listeners: {
                click: function(e) {
                    e.preventDefault();
                },
                element: 'el'
            }
        };
    },
    getMaskTarget: function() {
        
        
        return Ext.isGecko ? this.inputCmp.el : this.bodyEl;
    },
    
    setReadOnly: function(readOnly) {
        var me = this,
            textareaEl = me.textareaEl,
            iframeEl = me.iframeEl,
            body;
        me.readOnly = readOnly;
        if (textareaEl) {
            textareaEl.dom.readOnly = readOnly;
        }
        if (me.initialized) {
            body = me.getEditorBody();
            if (Ext.isIE) {
                
                iframeEl.setDisplayed(false);
                body.contentEditable = !readOnly;
                iframeEl.setDisplayed(true);
            } else {
                me.setDesignMode(!readOnly);
            }
            if (body) {
                body.style.cursor = readOnly ? 'default' : 'text';
            }
            me.disableItems(readOnly);
        }
    },
    
    getDocMarkup: function() {
        var me = this,
            h = me.iframeEl.getHeight() - me.iframePad * 2;
        
        
        
        return Ext.String.format('<!DOCTYPE html>' + '<html><head><style type="text/css">' + (Ext.isOpera ? 'p{margin:0;}' : '') + 'body{border:0;margin:0;padding:{0}px;direction:' + (me.rtl ? 'rtl;' : 'ltr;') + (Ext.isIE8 ? Ext.emptyString : 'min-') + 'height:{1}px;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;cursor:text;background-color:white;' + (Ext.isIE ? '' : 'font-size:12px;font-family:{2}') + '}</style></head><body></body></html>', me.iframePad, h, me.defaultFont);
    },
    
    getEditorBody: function() {
        var doc = this.getDoc();
        return doc.body || doc.documentElement;
    },
    
    getDoc: function() {
        return this.iframeEl.dom.contentDocument || this.getWin().document;
    },
    
    getWin: function() {
        
        
        
        
        
        
        return this.iframeEl.dom.contentWindow || window.frames[this.iframeEl.dom.name];
    },
    initDefaultFont: function() {
        
        
        
        
        var me = this,
            selIdx = 0,
            fonts, font, select, option, i, len, lower;
        if (!me.defaultFont) {
            font = me.textareaEl.getStyle('font-family');
            font = Ext.String.capitalize(font.split(',')[0]);
            fonts = Ext.Array.clone(me.fontFamilies);
            Ext.Array.include(fonts, font);
            fonts.sort();
            me.defaultFont = font;
            select = me.down('#fontSelect').selectEl.dom;
            for (i = 0 , len = fonts.length; i < len; ++i) {
                font = fonts[i];
                lower = font.toLowerCase();
                option = new Option(font,lower);
                if (font === me.defaultFont) {
                    selIdx = i;
                }
                option.style.fontFamily = lower;
                if (Ext.isIE) {
                    select.add(option);
                } else {
                    select.options.add(option);
                }
            }
            
            
            select.options[selIdx].selected = true;
        }
    },
    isEqual: function(value1, value2) {
        return this.isEqualAsString(value1, value2);
    },
    
    afterRender: function() {
        var me = this,
            inputCmp = me.inputCmp;
        me.callParent(arguments);
        me.iframeEl = inputCmp.iframeEl;
        me.textareaEl = inputCmp.textareaEl;
        
        
        me.inputEl = me.iframeEl;
        if (me.enableFont) {
            me.initDefaultFont();
        }
        
        me.monitorTask = Ext.TaskManager.start({
            run: me.checkDesignMode,
            scope: me,
            interval: 100
        });
    },
    initFrameDoc: function() {
        var me = this,
            doc, task;
        Ext.TaskManager.stop(me.monitorTask);
        doc = me.getDoc();
        me.win = me.getWin();
        doc.open();
        doc.write(me.getDocMarkup());
        doc.close();
        task = {
            
            run: function() {
                var doc = me.getDoc();
                if (doc.body || doc.readyState === 'complete') {
                    Ext.TaskManager.stop(task);
                    me.setDesignMode(true);
                    Ext.defer(me.initEditor, 10, me);
                }
            },
            interval: 10,
            duration: 10000,
            scope: me
        };
        Ext.TaskManager.start(task);
    },
    checkDesignMode: function() {
        var me = this,
            doc = me.getDoc();
        if (doc && (!doc.editorInitialized || me.getDesignMode() !== 'on')) {
            me.initFrameDoc();
        }
    },
    
    setDesignMode: function(mode) {
        var me = this,
            doc = me.getDoc();
        if (doc) {
            if (me.readOnly) {
                mode = false;
            }
            doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ? 'on' : 'off';
        }
    },
    
    getDesignMode: function() {
        var doc = this.getDoc();
        return !doc ? '' : String(doc.designMode).toLowerCase();
    },
    disableItems: function(disabled) {
        var items = this.getToolbar().items.items,
            i,
            iLen = items.length,
            item;
        for (i = 0; i < iLen; i++) {
            item = items[i];
            if (item.getItemId() !== 'sourceedit') {
                item.setDisabled(disabled);
            }
        }
    },
    
    toggleSourceEdit: function(sourceEditMode) {
        var me = this,
            iframe = me.iframeEl,
            textarea = me.textareaEl,
            hiddenCls = Ext.baseCSSPrefix + 'hidden',
            btn = me.getToolbar().getComponent('sourceedit');
        if (!Ext.isBoolean(sourceEditMode)) {
            sourceEditMode = !me.sourceEditMode;
        }
        me.sourceEditMode = sourceEditMode;
        if (btn.pressed !== sourceEditMode) {
            btn.toggle(sourceEditMode);
        }
        if (sourceEditMode) {
            me.disableItems(true);
            me.syncValue();
            iframe.addCls(hiddenCls);
            textarea.removeCls(hiddenCls);
            textarea.dom.removeAttribute('tabindex');
            textarea.focus();
            me.inputEl = textarea;
        } else {
            if (me.initialized) {
                me.disableItems(me.readOnly);
            }
            me.pushValue();
            iframe.removeCls(hiddenCls);
            textarea.addCls(hiddenCls);
            textarea.dom.setAttribute('tabindex', -1);
            me.deferFocus();
            me.inputEl = iframe;
        }
        me.fireEvent('editmodechange', me, sourceEditMode);
        me.updateLayout();
    },
    
    createLink: function() {
        var url = prompt(this.createLinkText, this.defaultLinkValue);
        if (url && url !== 'http:/' + '/') {
            this.relayCmd('createlink', url);
        }
    },
    clearInvalid: Ext.emptyFn,
    setValue: function(value) {
        var me = this,
            textarea = me.textareaEl;
        if (value === null || value === undefined) {
            value = '';
        }
        
        if (me.value !== value) {
            if (textarea) {
                textarea.dom.value = value;
            }
            me.pushValue();
            if (!me.rendered && me.inputCmp) {
                me.inputCmp.data.value = value;
            }
            me.mixins.field.setValue.call(me, value);
        }
        return me;
    },
    
    cleanHtml: function(html) {
        html = String(html);
        if (Ext.isWebKit) {
            
            html = html.replace(this.safariNonsenseRE, '');
        }
        
        if (html.charCodeAt(0) === parseInt(this.defaultValue.replace(this.nonDigitsRE, ''), 10)) {
            html = html.substring(1);
        }
        return html;
    },
    
    syncValue: function() {
        var me = this,
            body, changed, html, bodyStyle, match, textElDom;
        if (me.initialized) {
            body = me.getEditorBody();
            html = body.innerHTML;
            textElDom = me.textareaEl.dom;
            if (Ext.isWebKit) {
                bodyStyle = body.getAttribute('style');
                
                match = bodyStyle.match(me.textAlignRE);
                if (match && match[1]) {
                    html = '<div style="' + match[0] + '">' + html + '</div>';
                }
            }
            html = me.cleanHtml(html);
            if (me.fireEvent('beforesync', me, html) !== false) {
                
                
                if (Ext.isGecko && textElDom.value === '' && html === '<br>') {
                    html = '';
                }
                if (textElDom.value !== html) {
                    textElDom.value = html;
                    changed = true;
                }
                me.fireEvent('sync', me, html);
                if (changed) {
                    
                    
                    me.checkChange();
                }
            }
        }
    },
    getValue: function() {
        var me = this,
            value;
        if (!me.sourceEditMode) {
            me.syncValue();
        }
        value = me.rendered ? me.textareaEl.dom.value : me.value;
        me.value = value;
        return value;
    },
    
    pushValue: function() {
        var me = this,
            v;
        if (me.initialized) {
            v = me.textareaEl.dom.value || '';
            if (!me.activated && v.length < 1) {
                v = me.defaultValue;
            }
            if (me.fireEvent('beforepush', me, v) !== false) {
                me.getEditorBody().innerHTML = v;
                if (Ext.isGecko) {
                    
                    me.setDesignMode(false);
                    
                    me.setDesignMode(true);
                }
                me.fireEvent('push', me, v);
            }
        }
    },
    focus: function(selectText, delay) {
        var me = this,
            value, focusEl;
        if (delay) {
            if (!me.focusTask) {
                me.focusTask = new Ext.util.DelayedTask(me.focus);
            }
            me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [
                selectText,
                false
            ]);
        } else {
            if (selectText) {
                if (me.textareaEl && me.textareaEl.dom) {
                    value = me.textareaEl.dom.value;
                }
                if (value && value.length) {
                    
                    me.execCmd('selectall', true);
                }
            }
            focusEl = me.getFocusEl();
            if (focusEl && focusEl.focus) {
                focusEl.focus();
            }
        }
        return me;
    },
    
    initEditor: function() {
        var me = this,
            dbody, ss, doc, docEl, fn;
        
        if (me.destroying || me.isDestroyed) {
            return;
        }
        dbody = me.getEditorBody();
        ss = me.textareaEl.getStyle([
            'font-size',
            'font-family',
            'background-image',
            'background-repeat',
            'background-color',
            'color'
        ]);
        ss['background-attachment'] = 'fixed';
        
        dbody.bgProperties = 'fixed';
        
        Ext.DomHelper.applyStyles(dbody, ss);
        doc = me.getDoc();
        docEl = Ext.get(doc);
        if (docEl) {
            try {
                docEl.clearListeners();
            } catch (e) {}
            
            fn = me.onEditorEvent.bind(me);
            docEl.on({
                mousedown: fn,
                dblclick: fn,
                click: fn,
                keyup: fn,
                delegated: false,
                buffer: 100
            });
            
            
            
            
            
            fn = me.onRelayedEvent;
            docEl.on({
                mousedown: fn,
                
                mousemove: fn,
                
                mouseup: fn,
                
                click: fn,
                
                dblclick: fn,
                
                delegated: false,
                scope: me
            });
            if (Ext.isGecko) {
                docEl.on('keypress', me.applyCommand, me);
            }
            if (me.fixKeys) {
                docEl.on('keydown', me.fixKeys, me);
            }
            if (me.fixKeysAfter) {
                docEl.on('keyup', me.fixKeysAfter, me);
            }
            if (Ext.isIE9) {
                Ext.get(doc.documentElement).on('focus', me.focus, me);
            }
            
            
            
            if (Ext.isIE8) {
                docEl.on('focusout', function() {
                    me.savedSelection = doc.selection.type !== 'None' ? doc.selection.createRange() : null;
                }, me);
                docEl.on('focusin', function() {
                    if (me.savedSelection) {
                        me.savedSelection.select();
                    }
                }, me);
            }
            
            Ext.getWin().on('beforeunload', me.beforeDestroy, me);
            doc.editorInitialized = true;
            me.initialized = true;
            me.pushValue();
            me.setReadOnly(me.readOnly);
            me.fireEvent('initialize', me);
        }
    },
    
    beforeDestroy: function() {
        var me = this,
            monitorTask = me.monitorTask,
            doc, prop;
        if (monitorTask) {
            Ext.TaskManager.stop(monitorTask);
        }
        if (me.rendered) {
            Ext.getWin().un(me.beforeDestroy, me);
            doc = me.getDoc();
            if (doc) {
                
                
                
                
                
                Ext.get(doc).destroy();
                if (doc.hasOwnProperty) {
                    for (prop in doc) {
                        try {
                            if (doc.hasOwnProperty(prop)) {
                                delete doc[prop];
                            }
                        } catch (e) {}
                    }
                }
            }
            
            delete me.iframeEl;
            delete me.textareaEl;
            delete me.toolbar;
            delete me.inputCmp;
        }
        me.callParent();
    },
    
    onRelayedEvent: function(event) {
        
        var iframeEl = this.iframeEl,
            iframeXY = Ext.fly(iframeEl).getTrueXY(),
            originalEventXY = event.getXY(),
            eventXY = event.getXY();
        
        
        event.xy = [
            iframeXY[0] + eventXY[0],
            iframeXY[1] + eventXY[1]
        ];
        event.injectEvent(iframeEl);
        
        event.xy = originalEventXY;
    },
    
    
    onFirstFocus: function() {
        var me = this,
            selection, range;
        me.activated = true;
        me.disableItems(me.readOnly);
        if (Ext.isGecko) {
            
            me.win.focus();
            selection = me.win.getSelection();
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (selection.focusNode && !me.getValue().length) {
                range = selection.getRangeAt(0);
                range.selectNodeContents(me.getEditorBody());
                range.collapse(true);
                me.deferFocus();
            }
            try {
                me.execCmd('useCSS', true);
                me.execCmd('styleWithCSS', false);
            } catch (e) {}
        }
        
        me.fireEvent('activate', me);
    },
    
    adjustFont: function(btn) {
        var adjust = btn.getItemId() === 'increasefontsize' ? 1 : -1,
            size = this.getDoc().queryCommandValue('FontSize') || '2',
            isPxSize = Ext.isString(size) && size.indexOf('px') !== -1,
            isSafari;
        size = parseInt(size, 10);
        if (isPxSize) {
            
            
            if (size <= 10) {
                size = 1 + adjust;
            } else if (size <= 13) {
                size = 2 + adjust;
            } else if (size <= 16) {
                size = 3 + adjust;
            } else if (size <= 18) {
                size = 4 + adjust;
            } else if (size <= 24) {
                size = 5 + adjust;
            } else {
                size = 6 + adjust;
            }
            size = Ext.Number.constrain(size, 1, 6);
        } else {
            isSafari = Ext.isSafari;
            if (isSafari) {
                
                adjust *= 2;
            }
            size = Math.max(1, size + adjust) + (isSafari ? 'px' : 0);
        }
        this.relayCmd('FontSize', size);
    },
    
    onEditorEvent: function() {
        this.updateToolbar();
    },
    
    updateToolbar: function() {
        var me = this,
            i, l, btns, doc, name, queriedName, fontSelect, toolbarSubmenus;
        if (me.readOnly) {
            return;
        }
        if (!me.activated) {
            me.onFirstFocus();
            return;
        }
        btns = me.getToolbar().items.map;
        doc = me.getDoc();
        if (me.enableFont && !Ext.isSafari2) {
            
            
            queriedName = doc.queryCommandValue('fontName');
            name = (queriedName ? queriedName.split(",")[0].replace(me.reStripQuotes, '') : me.defaultFont).toLowerCase();
            fontSelect = me.fontSelect.dom;
            if (name !== fontSelect.value || name !== queriedName) {
                fontSelect.value = name;
            }
        }
        function updateButtons() {
            var state;
            for (i = 0 , l = arguments.length , name; i < l; i++) {
                name = arguments[i];
                
                
                try {
                    state = doc.queryCommandState(name);
                } catch (e) {
                    state = false;
                }
                btns[name].toggle(state);
            }
        }
        if (me.enableFormat) {
            updateButtons('bold', 'italic', 'underline');
        }
        if (me.enableAlignments) {
            updateButtons('justifyleft', 'justifycenter', 'justifyright');
        }
        if (!Ext.isSafari2 && me.enableLists) {
            updateButtons('insertorderedlist', 'insertunorderedlist');
        }
        
        
        toolbarSubmenus = me.toolbar.query('menu');
        for (i = 0; i < toolbarSubmenus.length; i++) {
            toolbarSubmenus[i].hide();
        }
        me.syncValue();
    },
    
    relayBtnCmd: function(btn) {
        this.relayCmd(btn.getItemId());
    },
    
    relayCmd: function(cmd, value) {
        Ext.defer(function() {
            var me = this;
            if (!this.isDestroyed) {
                me.win.focus();
                me.execCmd(cmd, value);
                me.updateToolbar();
            }
        }, 10, this);
    },
    
    execCmd: function(cmd, value) {
        var me = this,
            doc = me.getDoc();
        doc.execCommand(cmd, false, (value === undefined ? null : value));
        me.syncValue();
    },
    
    applyCommand: function(e) {
        if (e.ctrlKey) {
            var me = this,
                c = e.getCharCode(),
                cmd;
            if (c > 0) {
                c = String.fromCharCode(c);
                switch (c) {
                    case 'b':
                        cmd = 'bold';
                        break;
                    case 'i':
                        cmd = 'italic';
                        break;
                    case 'u':
                        cmd = 'underline';
                        break;
                }
                if (cmd) {
                    me.win.focus();
                    me.execCmd(cmd);
                    me.deferFocus();
                    e.preventDefault();
                }
            }
        }
    },
    
    insertAtCursor: function(text) {
        
        var me = this,
            win = me.getWin(),
            doc = me.getDoc(),
            sel, range, el, frag, node, lastNode, firstNode;
        if (me.activated) {
            win.focus();
            if (win.getSelection) {
                sel = win.getSelection();
                if (sel.getRangeAt && sel.rangeCount) {
                    range = sel.getRangeAt(0);
                    range.deleteContents();
                    
                    
                    
                    el = doc.createElement("div");
                    el.innerHTML = text;
                    frag = doc.createDocumentFragment();
                    while ((node = el.firstChild)) {
                        lastNode = frag.appendChild(node);
                    }
                    firstNode = frag.firstChild;
                    range.insertNode(frag);
                    
                    if (lastNode) {
                        range = range.cloneRange();
                        range.setStartAfter(lastNode);
                        range.collapse(true);
                        sel.removeAllRanges();
                        sel.addRange(range);
                    }
                }
            } else if (doc.selection && sel.type !== 'Control') {
                sel = doc.selection;
                range = sel.createRange();
                range.collapse(true);
                sel.createRange().pasteHTML(text);
            }
            me.deferFocus();
        }
    },
    
    fixKeys: (function() {
        
        var tag;
        if (Ext.isIE) {
            return function(e) {
                var me = this,
                    k = e.getKey(),
                    doc = me.getDoc(),
                    readOnly = me.readOnly,
                    range, target;
                if (k === e.TAB) {
                    e.stopEvent();
                    if (!readOnly) {
                        range = doc.selection.createRange();
                        if (range) {
                            if (range.collapse) {
                                range.collapse(true);
                                range.pasteHTML('&#160;&#160;&#160;&#160;');
                            }
                            me.deferFocus();
                        }
                    }
                } else if (k === e.ENTER) {
                    if (!readOnly) {
                        if (Ext.isIE10m) {
                            range = doc.selection.createRange();
                            if (range) {
                                target = range.parentElement();
                                if (!target || target.tagName.toLowerCase() !== 'li') {
                                    e.stopEvent();
                                    range.pasteHTML('<br />');
                                    range.collapse(false);
                                    range.select();
                                }
                            }
                        } else {
                            
                            range = doc.getSelection().getRangeAt(0);
                            if (range && range.commonAncestorContainer.parentNode.tagName.toLowerCase() !== 'li') {
                                
                                e.stopEvent();
                                tag = doc.createElement('div');
                                range.insertNode(tag);
                            }
                        }
                    }
                }
            };
        }
        if (Ext.isOpera) {
            return function(e) {
                var me = this,
                    k = e.getKey(),
                    readOnly = me.readOnly;
                if (k === e.TAB) {
                    e.stopEvent();
                    if (!readOnly) {
                        me.win.focus();
                        me.execCmd('InsertHTML', '&#160;&#160;&#160;&#160;');
                        me.deferFocus();
                    }
                }
            };
        }
        return null;
    }()),
    
    
    fixKeysAfter: (function() {
        if (Ext.isIE) {
            return function(e) {
                var me = this,
                    k = e.getKey(),
                    doc = me.getDoc(),
                    readOnly = me.readOnly,
                    innerHTML;
                if (!readOnly && (k === e.BACKSPACE || k === e.DELETE)) {
                    innerHTML = doc.body.innerHTML;
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    if (innerHTML === '<p>&nbsp;</p>' || innerHTML === '<P>&nbsp;</P>') {
                        doc.body.innerHTML = '';
                    }
                }
            };
        }
        return null;
    }()),
    
    getToolbar: function() {
        return this.toolbar;
    },
    
    
    buttonTips: {
        bold: {
            title: 'Bold (Ctrl+B)',
            text: 'Make the selected text bold.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        italic: {
            title: 'Italic (Ctrl+I)',
            text: 'Make the selected text italic.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        underline: {
            title: 'Underline (Ctrl+U)',
            text: 'Underline the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        increasefontsize: {
            title: 'Grow Text',
            text: 'Increase the font size.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        decreasefontsize: {
            title: 'Shrink Text',
            text: 'Decrease the font size.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        backcolor: {
            title: 'Text Highlight Color',
            text: 'Change the background color of the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        forecolor: {
            title: 'Font Color',
            text: 'Change the color of the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifyleft: {
            title: 'Align Text Left',
            text: 'Align text to the left.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifycenter: {
            title: 'Center Text',
            text: 'Center text in the editor.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifyright: {
            title: 'Align Text Right',
            text: 'Align text to the right.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        insertunorderedlist: {
            title: 'Bullet List',
            text: 'Start a bulleted list.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        insertorderedlist: {
            title: 'Numbered List',
            text: 'Start a numbered list.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        createlink: {
            title: 'Hyperlink',
            text: 'Make the selected text a hyperlink.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        sourceedit: {
            title: 'Source Edit',
            text: 'Switch to source editing mode.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        }
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    privates: {
        deferFocus: function() {
            this.focus(false, true);
        },
        getFocusEl: function() {
            return this.sourceEditMode ? this.textareaEl : this.iframeEl;
        }
    }
});


Ext.define('Ext.form.field.Tag', {
    extend: 'Ext.form.field.ComboBox',
    requires: [
        'Ext.selection.Model',
        'Ext.data.Store',
        'Ext.data.ChainedStore'
    ],
    xtype: 'tagfield',
    
    
    multiSelect: true,
    
    delimiter: ',',
    
    
    forceSelection: true,
    
    createNewOnEnter: false,
    
    createNewOnBlur: false,
    
    encodeSubmitValue: false,
    
    triggerOnClick: true,
    
    stacked: false,
    
    filterPickList: false,
    
    grow: true,
    
    growMin: false,
    
    growMax: false,
    
    selectOnFocus: true,
    
    
    
    
    fieldSubTpl: [
        '<div id="{cmpId}-listWrapper" data-ref="listWrapper" class="' + Ext.baseCSSPrefix + 'tagfield {fieldCls} {typeCls} {typeCls}-{ui} style="{wrapperStyle}">',
        '<ul id="{cmpId}-itemList" data-ref="itemList" class="' + Ext.baseCSSPrefix + 'tagfield-list{itemListCls}">',
        '<li id="{cmpId}-inputElCt" data-ref="inputElCt" class="' + Ext.baseCSSPrefix + 'tagfield-input">',
        '<div id="{cmpId}-emptyEl" data-ref="emptyEl" class="{emptyCls}">{emptyText}</div>',
        '<input id="{cmpId}-inputEl" data-ref="inputEl" type="{type}" ',
        '<tpl if="name">name="{name}" </tpl>',
        '<tpl if="value"> value="{[Ext.util.Format.htmlEncode(values.value)]}"</tpl>',
        '<tpl if="size">size="{size}" </tpl>',
        '<tpl if="tabIdx != null">tabindex="{tabIdx}" </tpl>',
        '<tpl if="disabled"> disabled="disabled"</tpl>',
        'class="' + Ext.baseCSSPrefix + 'tagfield-input-field {inputElCls}" autocomplete="off">',
        '</li>',
        '</ul>',
        '</div>',
        {
            disableFormats: true
        }
    ],
    extraFieldBodyCls: Ext.baseCSSPrefix + 'tagfield-body',
    
    childEls: [
        'listWrapper',
        'itemList',
        'inputEl',
        'inputElCt',
        'emptyEl'
    ],
    
    emptyInputCls: Ext.baseCSSPrefix + 'tagfield-emptyinput',
    tagItemSelector: '.' + Ext.baseCSSPrefix + 'tagfield-item',
    tagItemCloseSelector: '.' + Ext.baseCSSPrefix + 'tagfield-item-close',
    tagSelectedCls: Ext.baseCSSPrefix + 'tagfield-item-selected',
    initComponent: function() {
        var me = this,
            typeAhead = me.typeAhead,
            delimiter = me.delimiter;
        if (typeAhead && !me.editable) {
            Ext.Error.raise('If typeAhead is enabled the combo must be editable: true -- please change one of those settings.');
        }
        
        if (me.createNewOnEnter || me.createNewOnBlur) {
            me.forceSelection = false;
        }
        me.typeAhead = false;
        
        
        
        me.selectionModel = new Ext.selection.Model({
            mode: 'MULTI',
            onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
                commitFn();
            },
            
            listeners: {
                scope: me,
                selectionchange: me.onSelectionChange,
                focuschange: me.onFocusChange
            }
        });
        me.callParent();
        me.typeAhead = typeAhead;
        if (delimiter && me.multiSelect) {
            me.delimiterRegexp = new RegExp(Ext.String.escapeRegex(delimiter));
        }
    },
    initEvents: function() {
        var me = this,
            inputEl = me.inputEl;
        me.callParent(arguments);
        if (!me.enableKeyEvents) {
            inputEl.on('keydown', me.onKeyDown, me);
            inputEl.on('keyup', me.onKeyUp, me);
        }
        me.listWrapper.on('click', me.onItemListClick, me);
    },
    isValid: function() {
        var me = this,
            disabled = me.disabled,
            validate = me.forceValidation || !disabled;
        return validate ? me.validateValue(me.getValue()) : disabled;
    },
    onBindStore: function(store) {
        var me = this;
        me.callParent([
            store
        ]);
        if (store) {
            
            me.valueStore = new Ext.data.Store({
                model: store.getModel()
            });
            me.selectionModel.bindStore(me.valueStore);
            
            if (me.filterPickList) {
                me.listFilter = new Ext.util.Filter({
                    scope: me,
                    filterFn: me.filterPicked
                });
                me.changingFilters = true;
                store.filter(me.listFilter);
                me.changingFilters = false;
            }
        }
    },
    filterPicked: function(rec) {
        return !this.valueCollection.contains(rec);
    },
    onUnbindStore: function(store) {
        var me = this,
            valueStore = me.valueStore,
            picker = me.picker;
        if (picker) {
            picker.bindStore(null);
        }
        if (valueStore) {
            valueStore.destroy();
            me.valueStore = null;
        }
        if (me.filterPickList) {
            me.changingFilters = true;
            store.removeFilter(me.listFilter);
            me.changingFilters = false;
        }
        me.callParent(arguments);
    },
    onValueCollectionEndUpdate: function() {
        var me = this,
            pickedRecords = me.valueCollection.items,
            valueStore = me.valueStore;
        if (me.isSelectionUpdating()) {
            return;
        }
        
        if (me.filterPickList) {
            me.changingFilters = true;
            me.store.filter(me.listFilter);
            me.changingFilters = false;
        }
        me.callParent();
        Ext.suspendLayouts();
        if (valueStore) {
            valueStore.suspendEvents();
            valueStore.loadRecords(pickedRecords);
            valueStore.resumeEvents();
        }
        Ext.resumeLayouts(true);
    },
    checkValueOnDataChange: Ext.emptyFn,
    onSelectionChange: function(selModel, selectedRecs) {
        this.applyMultiselectItemMarkup();
        this.fireEvent('valueselectionchange', this, selectedRecs);
    },
    onFocusChange: function(selectionModel, oldFocused, newFocused) {
        this.fireEvent('valuefocuschange', this, oldFocused, newFocused);
    },
    onDestroy: function() {
        this.selectionModel = Ext.destroy(this.selectionModel);
        
        this.callParent(arguments);
    },
    getSubTplData: function(fieldData) {
        var me = this,
            data = me.callParent(arguments),
            emptyText = me.emptyText,
            emptyInputCls = me.emptyInputCls,
            isEmpty = emptyText && data.value.length < 1,
            growMin = me.growMin,
            growMax = me.growMax;
        data.value = '';
        data.emptyText = isEmpty ? emptyText : '';
        data.emptyCls = isEmpty ? me.emptyCls : emptyInputCls;
        data.inputElCls = isEmpty ? emptyInputCls : '';
        data.wrapperStyle = '';
        data.itemListCls = '';
        if (me.grow) {
            if (Ext.isNumber(growMin) && growMin > 0) {
                wrapperStyle += 'min-height:' + growMin + 'px;';
            }
            if (Ext.isNumber(growMax) && growMax > 0) {
                wrapperStyle += 'max-height:' + growMax + 'px;';
            }
        }
        if (me.stacked === true) {
            data.itemListCls += ' ' + Ext.baseCSSPrefix + 'tagfield-stacked';
        }
        if (!me.multiSelect) {
            data.itemListCls += ' ' + Ext.baseCSSPrefix + 'tagfield-singleselect';
        }
        return data;
    },
    afterRender: function() {
        var me = this,
            inputEl = me.inputEl;
        if (Ext.supports.Placeholder && inputEl && me.emptyText) {
            inputEl.dom.removeAttribute('placeholder');
        }
        me.applyMultiselectItemMarkup();
        me.callParent(arguments);
    },
    findRecord: function(field, value) {
        var matches = this.getStore().queryRecords(field, value);
        return matches.length ? matches[0] : false;
    },
    
    getCursorPosition: function() {
        var cursorPos;
        if (document.selection) {
            cursorPos = document.selection.createRange();
            cursorPos.collapse(true);
            cursorPos.moveStart('character', -this.inputEl.dom.value.length);
            cursorPos = cursorPos.text.length;
        } else {
            cursorPos = this.inputEl.dom.selectionStart;
        }
        return cursorPos;
    },
    
    hasSelectedText: function() {
        var inputEl = this.inputEl.dom,
            sel, range;
        if (document.selection) {
            sel = document.selection;
            range = sel.createRange();
            return (range.parentElement() === inputEl);
        } else {
            return inputEl.selectionStart !== inputEl.selectionEnd;
        }
    },
    
    onKeyDown: function(e) {
        var me = this,
            key = e.getKey(),
            inputEl = me.inputEl,
            rawValue = inputEl.dom.value,
            valueCollection = me.valueCollection,
            selModel = me.selectionModel,
            stopEvent = false,
            lastSelectionIndex;
        if (me.readOnly || me.disabled || !me.editable) {
            return;
        }
        if (valueCollection.getCount() > 0 && (rawValue === '' || (me.getCursorPosition() === 0 && !me.hasSelectedText()))) {
            
            lastSelectionIndex = (selModel.getCount() > 0) ? valueCollection.indexOf(selModel.getLastSelected()) : -1;
            if (key === e.BACKSPACE || key === e.DELETE) {
                
                if (lastSelectionIndex > -1) {
                    if (selModel.getCount() > 1) {
                        lastSelectionIndex = -1;
                    }
                    valueCollection.remove(selModel.getSelection());
                } else {
                    valueCollection.remove(valueCollection.last());
                }
                selModel.clearSelections();
                if (lastSelectionIndex > 0) {
                    selModel.select(lastSelectionIndex - 1);
                } else if (valueCollection.getCount()) {
                    selModel.select(valueCollection.last());
                }
                stopEvent = true;
            } else if (key === e.RIGHT || key === e.LEFT) {
                
                if (lastSelectionIndex === -1 && key === e.LEFT) {
                    selModel.select(valueCollection.last());
                    stopEvent = true;
                } else if (lastSelectionIndex > -1) {
                    if (key === e.RIGHT) {
                        if (lastSelectionIndex < (valueCollection.getCount() - 1)) {
                            selModel.select(lastSelectionIndex + 1, e.shiftKey);
                            stopEvent = true;
                        } else if (!e.shiftKey) {
                            selModel.deselectAll();
                            stopEvent = true;
                        }
                    } else if (key === e.LEFT && (lastSelectionIndex > 0)) {
                        selModel.select(lastSelectionIndex - 1, e.shiftKey);
                        stopEvent = true;
                    }
                }
            } else if (key === e.A && e.ctrlKey) {
                
                selModel.selectAll();
                stopEvent = e.A;
            }
        }
        if (stopEvent) {
            me.preventKeyUpEvent = stopEvent;
            e.stopEvent();
            return;
        }
        
        if (me.isExpanded && key === e.ENTER && me.picker.highlightedItem) {
            me.preventKeyUpEvent = true;
        }
        if (me.enableKeyEvents) {
            me.callParent(arguments);
        }
        if (!e.isSpecialKey() && !e.hasModifier()) {
            selModel.deselectAll();
        }
    },
    
    onKeyUp: function(e, t) {
        var me = this,
            inputEl = me.inputEl,
            rawValue = inputEl.dom.value,
            preventKeyUpEvent = me.preventKeyUpEvent;
        if (me.preventKeyUpEvent) {
            e.stopEvent();
            if (preventKeyUpEvent === true || e.getKey() === preventKeyUpEvent) {
                delete me.preventKeyUpEvent;
            }
            return;
        }
        if (me.multiSelect && me.delimiterRegexp && me.delimiterRegexp.test(rawValue) || (me.createNewOnEnter && e.getKey() === e.ENTER)) {
            rawValue = Ext.Array.clean(rawValue.split(me.delimiterRegexp));
            inputEl.dom.value = '';
            me.setValue(me.valueStore.getRange().concat(rawValue));
            inputEl.focus();
        }
        me.callParent([
            e,
            t
        ]);
    },
    
    onTypeAhead: function() {
        var me = this,
            displayField = me.displayField,
            inputElDom = me.inputEl.dom,
            boundList = me.getPicker(),
            record = me.getStore().findRecord(displayField, inputElDom.value),
            newValue, len, selStart;
        if (record) {
            newValue = record.get(displayField);
            len = newValue.length;
            selStart = inputElDom.value.length;
            boundList.highlightItem(boundList.getNode(record));
            if (selStart !== 0 && selStart !== len) {
                inputElDom.value = newValue;
                me.selectText(selStart, newValue.length);
            }
        }
    },
    
    onItemListClick: function(e) {
        var me = this,
            selectionModel = me.selectionModel,
            itemEl = e.getTarget(me.tagItemSelector),
            closeEl = itemEl ? e.getTarget(me.tagItemCloseSelector) : false;
        if (me.readOnly || me.disabled) {
            return;
        }
        e.stopPropagation();
        if (itemEl) {
            if (closeEl) {
                me.removeByListItemNode(itemEl);
                if (me.valueStore.getCount() > 0) {
                    me.fireEvent('select', me, me.valueStore.getRange());
                }
            } else {
                me.toggleSelectionByListItemNode(itemEl, e.shiftKey);
            }
            
            if (!Ext.supports.TouchEvents) {
                me.inputEl.focus();
            }
        } else {
            if (selectionModel.getCount() > 0) {
                selectionModel.deselectAll();
            }
            me.inputEl.focus();
            if (me.triggerOnClick) {
                me.onTriggerClick();
            }
        }
    },
    
    getMultiSelectItemMarkup: function() {
        var me = this,
            cssPrefix = Ext.baseCSSPrefix,
            valueField = me.valueField;
        if (!me.multiSelectItemTpl) {
            if (!me.labelTpl) {
                me.labelTpl = '{' + me.displayField + '}';
            }
            me.labelTpl = me.getTpl('labelTpl');
            me.multiSelectItemTpl = new Ext.XTemplate([
                '<tpl for=".">',
                '<li data-selectionIndex="{[xindex - 1]}" data-recordId="{internalId}" data-value="{[this.getItemValue(values)]}" class="' + cssPrefix + 'tagfield-item',
                '<tpl if="this.isSelected(values)">',
                ' ' + me.tagSelectedCls,
                '</tpl>',
                '{%',
                'values = values.data;',
                '%}',
                '" qtip="{' + me.displayField + '}">',
                '<div class="' + cssPrefix + 'tagfield-item-text">{[this.getItemLabel(values)]}</div>',
                '<div class="' + cssPrefix + 'tagfield-item-close"></div>',
                '</li>',
                '</tpl>',
                {
                    isSelected: function(rec) {
                        return me.selectionModel.isSelected(rec);
                    },
                    getItemLabel: function(values) {
                        return me.labelTpl.apply(values);
                    },
                    getItemValue: function(rec) {
                        return rec.get(valueField);
                    },
                    strict: true
                }
            ]);
        }
        if (!me.multiSelectItemTpl.isTemplate) {
            me.multiSelectItemTpl = this.getTpl('multiSelectItemTpl');
        }
        return me.multiSelectItemTpl.apply(me.valueCollection.getRange());
    },
    
    applyMultiselectItemMarkup: function() {
        var me = this,
            itemList = me.itemList;
        if (itemList) {
            itemList.select('.' + Ext.baseCSSPrefix + 'tagfield-item').destroy();
            me.inputElCt.insertHtml('beforeBegin', me.getMultiSelectItemMarkup());
            me.autoSize();
        }
    },
    
    getRecordByListItemNode: function(itemEl) {
        return this.valueCollection.items[Number(itemEl.getAttribute('data-selectionIndex'))];
    },
    
    toggleSelectionByListItemNode: function(itemEl, keepExisting) {
        var me = this,
            rec = me.getRecordByListItemNode(itemEl),
            selModel = me.selectionModel;
        if (rec) {
            if (selModel.isSelected(rec)) {
                selModel.deselect(rec);
            } else {
                selModel.select(rec, keepExisting);
            }
        }
    },
    
    removeByListItemNode: function(itemEl) {
        var me = this,
            rec = me.getRecordByListItemNode(itemEl);
        if (rec) {
            me.pickerSelectionModel.deselect(rec);
        }
    },
    
    
    
    getDisplayValue: function() {
        return this.getRawValue();
    },
    
    getRawValue: function() {
        var me = this,
            inputEl = me.inputEl,
            result;
        me.inputEl = false;
        result = me.callParent(arguments);
        me.inputEl = inputEl;
        return result;
    },
    
    setRawValue: function(value) {
        var me = this,
            inputEl = me.inputEl,
            result;
        me.inputEl = false;
        result = me.callParent([
            value
        ]);
        me.inputEl = inputEl;
        return result;
    },
    
    removeValue: function(value) {
        var me = this,
            valueCollection = me.valueCollection,
            len, i, item,
            toRemove = [];
        if (value) {
            value = Ext.Array.from(value);
            
            for (i = 0 , len = value.length; i < len; ++i) {
                item = value[i];
                
                if (!item.isModel) {
                    item = valueCollection.byValue.get(item);
                }
                if (item) {
                    toRemove.push(item);
                }
            }
            me.valueCollection.beginUpdate();
            me.pickerSelectionModel.deselect(toRemove);
            me.valueCollection.endUpdate();
        }
    },
    
    setValue: function(value, 
    add, skipLoad) {
        var me = this,
            valueStore = me.valueStore,
            valueField = me.valueField,
            unknownValues = [],
            store = me.store,
            record, len, i, valueRecord, cls, params;
        if (Ext.isEmpty(value)) {
            value = null;
        }
        if (Ext.isString(value) && me.multiSelect) {
            value = value.split(me.delimiter);
        }
        value = Ext.Array.from(value, true);
        for (i = 0 , len = value.length; i < len; i++) {
            record = value[i];
            if (!record || !record.isModel) {
                valueRecord = valueStore.findExact(valueField, record);
                if (valueRecord > -1) {
                    value[i] = valueStore.getAt(valueRecord);
                } else {
                    valueRecord = me.findRecord(valueField, record);
                    if (!valueRecord) {
                        if (me.forceSelection) {
                            unknownValues.push(record);
                        } else {
                            valueRecord = {};
                            valueRecord[me.valueField] = record;
                            valueRecord[me.displayField] = record;
                            cls = me.valueStore.getModel();
                            valueRecord = new cls(valueRecord);
                        }
                    }
                    if (valueRecord) {
                        value[i] = valueRecord;
                    }
                }
            }
        }
        if (!store.isEmptyStore && skipLoad !== true && unknownValues.length > 0 && me.queryMode === 'remote') {
            params = {};
            params[me.valueParam || me.valueField] = unknownValues.join(me.delimiter);
            store.load({
                params: params,
                callback: function() {
                    if (me.itemList) {
                        me.itemList.unmask();
                    }
                    me.setValue(value, add, true);
                    me.autoSize();
                    me.lastQuery = false;
                }
            });
            return false;
        }
        
        if (!me.multiSelect && value.length > 0) {
            for (i = value.length - 1; i >= 0; i--) {
                if (value[i].isModel) {
                    value = value[i];
                    break;
                }
            }
            if (Ext.isArray(value)) {
                value = value[value.length - 1];
            }
        }
        return me.callParent([
            value,
            add
        ]);
    },
    
    
    updateValue: function() {
        var me = this,
            valueArray = me.valueCollection.getRange(),
            len = valueArray.length,
            i;
        for (i = 0; i < len; i++) {
            valueArray[i] = valueArray[i].get(me.valueField);
        }
        
        me.setHiddenValue(valueArray);
        me.value = me.multiSelect ? valueArray : valueArray[0];
        if (!Ext.isDefined(me.value)) {
            me.value = undefined;
        }
        me.applyMultiselectItemMarkup();
        me.checkChange();
    },
    
    getValueRecords: function() {
        return this.valueCollection.getRange();
    },
    
    getSubmitData: function() {
        var me = this,
            val = me.callParent(arguments);
        if (me.multiSelect && me.encodeSubmitValue && val && val[me.name]) {
            val[me.name] = Ext.encode(val[me.name]);
        }
        return val;
    },
    
    assertValue: function() {
        var me = this,
            rawValue = me.inputEl.dom.value,
            rec = !Ext.isEmpty(rawValue) ? me.findRecordByDisplay(rawValue) : false,
            value = false;
        if (!rec && !me.forceSelection && me.createNewOnBlur && !Ext.isEmpty(rawValue)) {
            value = rawValue;
        } else if (rec) {
            value = rec;
        }
        if (value) {
            me.addValue(value);
        }
        me.inputEl.dom.value = '';
        me.collapse();
    },
    
    isEqual: function(v1, v2) {
        var fromArray = Ext.Array.from,
            valueField = this.valueField,
            i, len, t1, t2;
        v1 = fromArray(v1);
        v2 = fromArray(v2);
        len = v1.length;
        if (len !== v2.length) {
            return false;
        }
        for (i = 0; i < len; i++) {
            t1 = v1[i].isModel ? v1[i].get(valueField) : v1[i];
            t2 = v2[i].isModel ? v2[i].get(valueField) : v2[i];
            if (t1 !== t2) {
                return false;
            }
        }
        return true;
    },
    
    applyEmptyText: function() {
        var me = this,
            emptyText = me.emptyText,
            emptyEl = me.emptyEl,
            inputEl = me.inputEl,
            listWrapper = me.listWrapper,
            emptyCls = me.emptyCls,
            emptyInputCls = me.emptyInputCls,
            isEmpty;
        if (me.rendered && emptyText) {
            isEmpty = Ext.isEmpty(me.value) && !me.hasFocus;
            if (isEmpty) {
                inputEl.dom.value = '';
                emptyEl.setHtml(emptyText);
                emptyEl.addCls(emptyCls);
                emptyEl.removeCls(emptyInputCls);
                listWrapper.addCls(emptyCls);
                inputEl.addCls(emptyInputCls);
            } else {
                emptyEl.addCls(emptyInputCls);
                emptyEl.removeCls(emptyCls);
                listWrapper.removeCls(emptyCls);
                inputEl.removeCls(emptyInputCls);
            }
            me.autoSize();
        }
    },
    
    preFocus: function() {
        var me = this,
            inputEl = me.inputEl,
            isEmpty = inputEl.dom.value === '';
        me.emptyEl.addCls(me.emptyInputCls);
        me.emptyEl.removeCls(me.emptyCls);
        me.listWrapper.removeCls(me.emptyCls);
        me.inputEl.removeCls(me.emptyInputCls);
        if (me.selectOnFocus || isEmpty) {
            inputEl.dom.select();
        }
    },
    
    onFocus: function() {
        var me = this,
            focusCls = me.focusCls,
            itemList = me.itemList;
        if (focusCls && itemList) {
            itemList.addCls(focusCls);
        }
        me.callParent(arguments);
    },
    
    onBlur: function() {
        var me = this,
            focusCls = me.focusCls,
            itemList = me.itemList;
        if (focusCls && itemList) {
            itemList.removeCls(focusCls);
        }
        me.callParent(arguments);
    },
    
    renderActiveError: function() {
        var me = this,
            invalidCls = me.invalidCls,
            itemList = me.itemList,
            hasError = me.hasActiveError();
        if (invalidCls && itemList) {
            itemList[hasError ? 'addCls' : 'removeCls'](me.invalidCls + '-field');
        }
        me.callParent(arguments);
    },
    
    autoSize: function() {
        var me = this;
        if (me.grow && me.rendered) {
            me.autoSizing = true;
            me.updateLayout();
        }
        return me;
    },
    
    afterComponentLayout: function() {
        var me = this,
            height;
        if (me.autoSizing) {
            height = me.getHeight();
            if (height !== me.lastInputHeight) {
                if (me.isExpanded) {
                    me.alignPicker();
                }
                me.fireEvent('autosize', me, height);
                me.lastInputHeight = height;
                me.autoSizing = false;
            }
        }
    }
});


Ext.define('Ext.picker.Time', {
    extend: 'Ext.view.BoundList',
    alias: 'widget.timepicker',
    requires: [
        'Ext.data.Store',
        'Ext.Date'
    ],
    config: {
        
        store: true
    },
    statics: {
        
        createStore: function(format, increment) {
            var dateUtil = Ext.Date,
                clearTime = dateUtil.clearTime,
                initDate = this.prototype.initDate,
                times = [],
                min = clearTime(new Date(initDate[0],initDate[1],initDate[2])),
                max = dateUtil.add(clearTime(new Date(initDate[0],initDate[1],initDate[2])), 'mi', (24 * 60) - 1);
            while (min <= max) {
                times.push({
                    disp: dateUtil.dateFormat(min, format),
                    date: min
                });
                min = dateUtil.add(min, 'mi', increment);
            }
            return new Ext.data.Store({
                model: Ext.picker.Time.prototype.modelType,
                data: times
            });
        }
    },
    
    
    
    increment: 15,
    
    
    format: "g:i A",
    
    
    displayField: 'disp',
    
    initDate: [
        2008,
        0,
        1
    ],
    componentCls: Ext.baseCSSPrefix + 'timepicker',
    
    loadMask: false,
    initComponent: function() {
        var me = this,
            dateUtil = Ext.Date,
            clearTime = dateUtil.clearTime,
            initDate = me.initDate;
        
        me.absMin = clearTime(new Date(initDate[0],initDate[1],initDate[2]));
        me.absMax = dateUtil.add(clearTime(new Date(initDate[0],initDate[1],initDate[2])), 'mi', (24 * 60) - 1);
        
        me.updateList();
        me.callParent();
    },
    applyStore: function(store, oldStore) {
        
        
        if (store === true) {
            store = Ext.picker.Time.createStore(this.format, this.increment);
        }
        return store;
    },
    
    setMinValue: function(value) {
        this.minValue = value;
        this.updateList();
    },
    
    setMaxValue: function(value) {
        this.maxValue = value;
        this.updateList();
    },
    
    normalizeDate: function(date) {
        var initDate = this.initDate;
        date.setFullYear(initDate[0], initDate[1], initDate[2]);
        return date;
    },
    
    updateList: function() {
        var me = this,
            min = me.normalizeDate(me.minValue || me.absMin),
            max = me.normalizeDate(me.maxValue || me.absMax),
            filters = me.getStore().getFilters(),
            filter = me.rangeFilter;
        filters.beginUpdate();
        if (filter) {
            filters.remove(filter);
        }
        filter = me.rangeFilter = new Ext.util.Filter({
            filterFn: function(record) {
                var date = record.get('date');
                return date >= min && date <= max;
            }
        });
        filters.add(filter);
        filters.endUpdate();
    }
}, function() {
    this.prototype.modelType = Ext.define(null, {
        extend: 'Ext.data.Model',
        fields: [
            'disp',
            'date'
        ]
    });
});


Ext.define('Ext.form.field.Time', {
    extend: 'Ext.form.field.ComboBox',
    alias: 'widget.timefield',
    requires: [
        'Ext.form.field.Date',
        'Ext.picker.Time',
        'Ext.view.BoundListKeyNav',
        'Ext.Date'
    ],
    alternateClassName: [
        'Ext.form.TimeField',
        'Ext.form.Time'
    ],
    
    triggerCls: Ext.baseCSSPrefix + 'form-time-trigger',
    
    
    
    
    minText: "The time in this field must be equal to or after {0}",
    
    
    
    maxText: "The time in this field must be equal to or before {0}",
    
    
    
    invalidText: "{0} is not a valid time",
    
    
    
    format: "g:i A",
    
    
    
    
    
    
    altFormats: "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A",
    
    
    increment: 15,
    
    pickerMaxHeight: 300,
    
    selectOnTab: true,
    
    snapToIncrement: false,
    
    valuePublishEvent: [
        'select',
        'blur'
    ],
    
    initDate: '1/1/2008',
    initDateParts: [
        2008,
        0,
        1
    ],
    initDateFormat: 'j/n/Y',
    queryMode: 'local',
    displayField: 'disp',
    valueField: 'date',
    initComponent: function() {
        var me = this,
            min = me.minValue,
            max = me.maxValue;
        if (min) {
            me.setMinValue(min);
        }
        if (max) {
            me.setMaxValue(max);
        }
        me.displayTpl = new Ext.XTemplate('<tpl for=".">' + '{[typeof values === "string" ? values : this.formatDate(values["' + me.displayField + '"])]}' + '<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' + '</tpl>',{
            formatDate: me.formatDate.bind(me)
        });
        
        me.store = Ext.picker.Time.createStore(me.format, me.increment);
        me.callParent();
    },
    
    isEqual: function(v1, v2) {
        var fromArray = Ext.Array.from,
            isEqual = Ext.Date.isEqual,
            i, len;
        v1 = fromArray(v1);
        v2 = fromArray(v2);
        len = v1.length;
        if (len !== v2.length) {
            return false;
        }
        for (i = 0; i < len; i++) {
            if (!isEqual(v2[i], v1[i])) {
                return false;
            }
        }
        return true;
    },
    
    setMinValue: function(value) {
        var me = this,
            picker = me.picker;
        me.setLimit(value, true);
        if (picker) {
            picker.setMinValue(me.minValue);
        }
    },
    
    setMaxValue: function(value) {
        var me = this,
            picker = me.picker;
        me.setLimit(value, false);
        if (picker) {
            picker.setMaxValue(me.maxValue);
        }
    },
    
    setLimit: function(value, isMin) {
        var me = this,
            d, val;
        if (Ext.isString(value)) {
            d = me.parseDate(value);
        } else if (Ext.isDate(value)) {
            d = value;
        }
        if (d) {
            val = me.getInitDate();
            val.setHours(d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
        } else 
        {
            val = null;
        }
        me[isMin ? 'minValue' : 'maxValue'] = val;
    },
    getInitDate: function(hours, minutes, seconds) {
        var parts = this.initDateParts;
        return new Date(parts[0],parts[1],parts[2],hours || 0,minutes || 0,seconds || 0,0);
    },
    valueToRaw: function(value) {
        return this.formatDate(this.parseDate(value));
    },
    
    getErrors: function(value) {
        value = arguments.length > 0 ? value : this.getRawValue();
        var me = this,
            format = Ext.String.format,
            errors = me.callParent([
                value
            ]),
            minValue = me.minValue,
            maxValue = me.maxValue,
            data = me.displayTplData,
            raw = me.getRawValue(),
            i, len, date, item;
        if (data && data.length > 0) {
            for (i = 0 , len = data.length; i < len; i++) {
                item = data[i];
                item = item.date || item.disp;
                date = me.parseDate(item);
                if (!date) {
                    errors.push(format(me.invalidText, item, Ext.Date.unescapeFormat(me.format)));
                    
                    continue;
                }
                if (minValue && date < minValue) {
                    errors.push(format(me.minText, me.formatDate(minValue)));
                }
                if (maxValue && date > maxValue) {
                    errors.push(format(me.maxText, me.formatDate(maxValue)));
                }
            }
        } else if (raw.length && !me.parseDate(raw)) {
            
            errors.push(format(me.invalidText, raw, Ext.Date.unescapeFormat(me.format)));
        }
        return errors;
    },
    formatDate: function(items) {
        var formatted = [],
            i, len;
        items = Ext.Array.from(items);
        for (i = 0 , len = items.length; i < len; i++) {
            formatted.push(Ext.form.field.Date.prototype.formatDate.call(this, items[i]));
        }
        return formatted.join(this.delimiter);
    },
    
    parseDate: function(value) {
        var me = this,
            val = value,
            altFormats = me.altFormats,
            altFormatsArray = me.altFormatsArray,
            i = 0,
            len;
        if (value && !Ext.isDate(value)) {
            val = me.safeParse(value, me.format);
            if (!val && altFormats) {
                altFormatsArray = altFormatsArray || altFormats.split('|');
                len = altFormatsArray.length;
                for (; i < len && !val; ++i) {
                    val = me.safeParse(value, altFormatsArray[i]);
                }
            }
        }
        
        if (val && me.snapToIncrement) {
            val = new Date(Ext.Number.snap(val.getTime(), me.increment * 60 * 1000));
        }
        return val;
    },
    safeParse: function(value, format) {
        var me = this,
            utilDate = Ext.Date,
            parsedDate,
            result = null;
        if (utilDate.formatContainsDateInfo(format)) {
            
            result = utilDate.parse(value, format);
        } else {
            
            parsedDate = utilDate.parse(me.initDate + ' ' + value, me.initDateFormat + ' ' + format);
            if (parsedDate) {
                result = parsedDate;
            }
        }
        return result;
    },
    
    getSubmitValue: function() {
        var me = this,
            format = me.submitFormat || me.format,
            value = me.getValue();
        return value ? Ext.Date.format(value, format) : null;
    },
    
    createPicker: function() {
        var me = this;
        me.listConfig = Ext.apply({
            xtype: 'timepicker',
            pickerField: me,
            cls: undefined,
            minValue: me.minValue,
            maxValue: me.maxValue,
            increment: me.increment,
            format: me.format,
            maxHeight: me.pickerMaxHeight
        }, me.listConfig);
        return me.callParent();
    },
    completeEdit: function() {
        var me = this,
            val = me.getValue();
        me.callParent(arguments);
        
        if (me.validateValue(val)) {
            me.setValue(val);
        }
    },
    
    findRecordByValue: function(value) {
        if (typeof value === 'string') {
            value = this.parseDate(value);
        }
        return this.callParent([
            value
        ]);
    },
    rawToValue: function(item) {
        var me = this,
            items, values, i, len;
        if (me.multiSelect) {
            values = [];
            items = Ext.Array.from(item);
            for (i = 0 , len = items.length; i < len; i++) {
                values.push(me.parseDate(items[i]));
            }
            return values;
        }
        return me.parseDate(item);
    },
    setValue: function(v) {
        
        this.getPicker();
        if (Ext.isDate(v)) {
            v = this.getInitDate(v.getHours(), v.getMinutes(), v.getSeconds());
        }
        return this.callParent([
            v
        ]);
    },
    getValue: function() {
        return this.rawToValue(this.callParent(arguments));
    }
});


Ext.define('Ext.form.field.Trigger', {
    extend: 'Ext.form.field.Text',
    alias: [
        'widget.triggerfield',
        'widget.trigger'
    ],
    requires: [
        'Ext.dom.Helper',
        'Ext.util.ClickRepeater'
    ],
    alternateClassName: [
        'Ext.form.TriggerField',
        'Ext.form.TwinTriggerField',
        'Ext.form.Trigger'
    ],
    
    triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',
    inheritableStatics: {
        warnDeprecated: function() {
            
            Ext.log.warn('Ext.form.field.Trigger is deprecated. Use Ext.form.field.Text instead.');
        }
    },
    onClassExtended: function() {
        this.warnDeprecated();
    },
    constructor: function(config) {
        this.self.warnDeprecated();
        this.callParent([
            config
        ]);
    }
});


Ext.define('Ext.grid.CellEditor', {
    extend: 'Ext.Editor',
    alignment: 'l-l?',
    hideEl: false,
    cls: Ext.baseCSSPrefix + 'small-editor ' + Ext.baseCSSPrefix + 'grid-editor ' + Ext.baseCSSPrefix + 'grid-cell-editor',
    treeNodeSelector: '.' + Ext.baseCSSPrefix + 'tree-node-text',
    shim: false,
    shadow: false,
    constructor: function(config) {
        var field;
        
        this.y = 0;
        config = Ext.apply({}, config);
        field = config.field;
        if (field) {
            field.monitorTab = false;
        }
        this.callParent([
            config
        ]);
    },
    
    
    
    
    
    setGrid: function(grid) {
        var me = this,
            oldGrid = me.grid,
            view, viewListeners;
        if (grid !== oldGrid) {
            viewListeners = {
                beforerefresh: me.beforeViewRefresh,
                refresh: me.onViewRefresh,
                scope: me
            };
            
            if (oldGrid) {
                oldGrid.getView().un(viewListeners);
            }
            
            view = grid.getView();
            me.renderTo = view.getTargetEl().dom;
            me.grid = grid;
            
            view.on(viewListeners);
        }
    },
    afterFirstLayout: function(width, height) {
        
        
        delete this.y;
        this.callParent([
            width,
            height
        ]);
    },
    beforeViewRefresh: function() {
        var me = this,
            dom = me.el && me.el.dom;
        if (dom) {
            if (me.editing && !(me.field.column && me.field.column.sorting)) {
                
                
                me.grid.view.cellFocused = false;
                
                me.wasAllowBlur = me.allowBlur;
                me.allowBlur = false;
            }
            
            if (dom.parentNode) {
                dom.parentNode.removeChild(dom);
            }
        }
    },
    onViewRefresh: function() {
        var me = this,
            dom = me.el && me.el.dom,
            sorting;
        if (dom) {
            sorting = me.field.column && me.field.column.sorting;
            
            if (me.editing && !sorting) {
                me.allowBlur = me.wasAllowBlur;
                me.renderTo.appendChild(dom);
                
                
                me.grid.view.cellFocused = true;
                me.field.focus();
            } else if (!sorting) {
                Ext.getDetachedBody().dom.appendChild(dom);
            }
            
            
            if (me.editing && sorting) {
                me.completeEdit();
            }
        }
    },
    startEdit: function(record, columnHeader) {
        this.context = this.editingPlugin.context;
        this.callParent([
            record,
            columnHeader
        ]);
    },
    
    onShow: function() {
        var me = this,
            innerCell = me.boundEl.first();
        
        
        
        if (me.el.dom.parentNode !== me.renderTo) {
            me.renderTo.appendChild(me.el.dom);
        }
        if (innerCell) {
            if (me.isForTree) {
                innerCell = innerCell.child(me.treeNodeSelector);
            }
            innerCell.hide();
        }
        me.callParent(arguments);
    },
    onEditComplete: function(remainVisible) {
        
        if (remainVisible) {
            this.restoreCell();
        }
        this.callParent(arguments);
    },
    
    onHide: function() {
        var me = this,
            context = me.context,
            focusContext;
        me.restoreCell();
        if (context && me.el.contains(Ext.Element.getActiveElement())) {
            focusContext = context.clone();
            
            
            
            Ext.on({
                idle: function() {
                    if (Ext.Element.getActiveElement() === document.body) {
                        context.view.getNavigationModel().setPosition(focusContext, null, null, null, true);
                    }
                },
                single: true
            });
            me.context = null;
        }
        me.callParent(arguments);
    },
    restoreCell: function() {
        var me = this,
            innerCell = me.boundEl.first();
        if (innerCell) {
            if (me.isForTree) {
                innerCell = innerCell.child(me.treeNodeSelector);
            }
            innerCell.show();
        }
    },
    
    afterRender: function() {
        var me = this,
            field = me.field;
        me.callParent(arguments);
        if (field.isCheckbox) {
            field.mon(field.inputEl, {
                mousedown: me.onCheckBoxMouseDown,
                click: me.onCheckBoxClick,
                scope: me
            });
        }
    },
    
    onCheckBoxMouseDown: function() {
        this.completeEdit = Ext.emptyFn;
    },
    
    onCheckBoxClick: function() {
        delete this.completeEdit;
        this.field.focus(false, 10);
    },
    
    realign: function(autoSize) {
        var me = this,
            boundEl = me.boundEl,
            innerCell = boundEl.first(),
            innerCellTextNode = innerCell.dom.firstChild,
            width = boundEl.getWidth(),
            offsets = Ext.Array.clone(me.offsets),
            grid = me.grid,
            xOffset,
            v = '',
            
            isEmpty = !innerCellTextNode || (innerCellTextNode.nodeType === 3 && !(Ext.String.trim(v = innerCellTextNode.data).length));
        if (me.isForTree) {
            
            
            xOffset = me.getTreeNodeOffset(innerCell);
            width -= Math.abs(xOffset);
            offsets[0] += xOffset;
        }
        if (grid.columnLines) {
            
            
            
            width -= boundEl.getBorderWidth('rl');
        }
        if (autoSize === true) {
            me.field.setWidth(width);
        }
        
        if (isEmpty) {
            innerCell.dom.innerHTML = 'X';
        }
        me.alignTo(innerCell, me.alignment, offsets);
        if (isEmpty) {
            innerCell.dom.firstChild.data = v;
        }
    },
    
    getTreeNodeOffset: function(innerCell) {
        return innerCell.child(this.treeNodeSelector).getOffsetsTo(innerCell)[0];
    },
    onEditorTab: function(e) {
        var field = this.field;
        if (field.onEditorTab) {
            field.onEditorTab(e);
        }
    },
    onFocusLeave: function(e) {
        
        
        this.previousFocus = null;
        this.callParent([
            e
        ]);
        
        
        this.selectSameEditor = false;
    }
});


Ext.define('Ext.grid.ColumnManager', {
    alternateClassName: [
        'Ext.grid.ColumnModel'
    ],
    columns: null,
    constructor: function(visibleOnly, headerCt, secondHeaderCt) {
        if (!headerCt.isRootHeader && !headerCt.isGroupHeader) {
            Ext.Error.raise('ColumnManager must be passed an instantiated HeaderContainer or group header');
        }
        this.headerCt = headerCt;
        
        if (secondHeaderCt) {
            if (!headerCt.isRootHeader && !headerCt.isGroupHeader) {
                Ext.Error.raise('ColumnManager must be passed an instantiated HeaderContainer or group header');
            }
            this.secondHeaderCt = secondHeaderCt;
        }
        this.visibleOnly = !!visibleOnly;
    },
    getColumns: function() {
        if (!this.columns) {
            this.cacheColumns();
        }
        return this.columns;
    },
    hasVariableRowHeight: function() {
        var me = this,
            columns = me.getColumns(),
            len = columns.length,
            i;
        if (me.variableRowHeight == null) {
            me.variableRowHeight = false;
            for (i = 0; !me.variableRowHeight && i < len; i++) {
                me.variableRowHeight = !!columns[i].variableRowHeight;
            }
        }
        return me.variableRowHeight;
    },
    
    getHeaderIndex: function(header) {
        if (header.isGroupHeader) {
            
            
            header = this.getHeaderColumns(header)[0];
        }
        return Ext.Array.indexOf(this.getColumns(), header);
    },
    
    getHeaderAtIndex: function(index) {
        var columns = this.getColumns(),
            col = columns[index];
        return col || null;
    },
    getPreviousSibling: function(header) {
        var index = this.getHeaderIndex(header),
            col = null;
        if (index > 0) {
            col = this.getColumns()[index - 1];
        }
        return col;
    },
    getNextSibling: function(header) {
        var index = this.getHeaderIndex(header),
            col;
        if (index !== -1) {
            col = this.getColumns()[index + 1];
        }
        return col || null;
    },
    
    getFirst: function() {
        var columns = this.getColumns();
        return columns.length > 0 ? columns[0] : null;
    },
    
    getLast: function() {
        var columns = this.getColumns(),
            len = columns.length;
        return len > 0 ? columns[len - 1] : null;
    },
    
    getHeaderByDataIndex: function(dataIndex) {
        var columns = this.getColumns(),
            len = columns.length,
            i, header;
        for (i = 0; i < len; ++i) {
            header = columns[i];
            if (header.dataIndex === dataIndex) {
                return header;
            }
        }
        return null;
    },
    
    getHeaderById: function(id) {
        var columns = this.getColumns(),
            len = columns.length,
            i, header;
        for (i = 0; i < len; ++i) {
            header = columns[i];
            if (header.getItemId() === id) {
                return header;
            }
        }
        return null;
    },
    
    getVisibleHeaderClosestToIndex: function(index) {
        var result = this.getHeaderAtIndex(index);
        if (result && result.hidden) {
            result = result.next(':not([hidden])') || result.prev(':not([hidden])');
        }
        return result;
    },
    cacheColumns: function() {
        var columns = this.getHeaderColumns(this.headerCt),
            second = this.secondHeaderCt;
        if (second) {
            columns = columns.concat(this.getHeaderColumns(second));
        }
        this.columns = columns;
    },
    getHeaderColumns: function(header) {
        var result = this.visibleOnly ? header.getVisibleGridColumns() : header.getGridColumns();
        return Ext.Array.clone(result);
    },
    invalidate: function() {
        var root = this.rootColumns;
        this.columns = this.variableRowHeight = null;
        
        if (root) {
            root.invalidate();
        }
    },
    destroy: function() {
        this.columns = this.rootColumns = null;
    }
}, function() {
    this.createAlias('indexOf', 'getHeaderIndex');
});


Ext.define('Ext.grid.RowEditorButtons', {
    extend: 'Ext.container.Container',
    alias: 'widget.roweditorbuttons',
    frame: true,
    shrinkWrap: true,
    position: 'bottom',
    constructor: function(config) {
        var me = this,
            rowEditor = config.rowEditor,
            cssPrefix = Ext.baseCSSPrefix,
            plugin = rowEditor.editingPlugin;
        config = Ext.apply({
            baseCls: cssPrefix + 'grid-row-editor-buttons',
            defaults: {
                xtype: 'button',
                ui: rowEditor.buttonUI,
                scope: plugin,
                flex: 1,
                minWidth: Ext.panel.Panel.prototype.minButtonWidth
            },
            items: [
                {
                    cls: cssPrefix + 'row-editor-update-button',
                    itemId: 'update',
                    handler: plugin.completeEdit,
                    text: rowEditor.saveBtnText,
                    disabled: rowEditor.updateButtonDisabled
                },
                {
                    cls: cssPrefix + 'row-editor-cancel-button',
                    handler: plugin.cancelEdit,
                    text: rowEditor.cancelBtnText
                }
            ]
        }, config);
        me.callParent([
            config
        ]);
        me.addClsWithUI(me.position);
    },
    setButtonPosition: function(position) {
        var me = this,
            rowEditor = this.rowEditor,
            rowEditorHeight = rowEditor.getHeight(),
            rowEditorBody = rowEditor.body,
            bottom = '',
            top = '';
        me.removeClsWithUI(me.position);
        me.position = position;
        me.addClsWithUI(position);
        
        
        
        
        if (position === 'top') {
            bottom = (rowEditorHeight - rowEditorBody.getBorderWidth('t')) + 'px';
        } else {
            top = (rowEditorHeight - rowEditorBody.getBorderWidth('b')) + 'px';
        }
        me.el.setStyle({
            top: top,
            bottom: bottom
        });
    },
    privates: {
        getFramingInfoCls: function() {
            return this.baseCls + '-' + this.ui + '-' + this.position;
        },
        getFrameInfo: function() {
            var frameInfo = this.callParent();
            
            
            
            frameInfo.top = true;
            return frameInfo;
        }
    }
});









Ext.define('Ext.grid.RowEditor', {
    extend: 'Ext.form.Panel',
    alias: 'widget.roweditor',
    requires: [
        'Ext.tip.ToolTip',
        'Ext.util.KeyNav',
        'Ext.grid.RowEditorButtons'
    ],
    
    saveBtnText: 'Update',
    
    
    cancelBtnText: 'Cancel',
    
    
    errorsText: 'Errors',
    
    
    dirtyText: 'You need to commit or cancel your changes',
    
    lastScrollLeft: 0,
    lastScrollTop: 0,
    border: false,
    _wrapCls: Ext.baseCSSPrefix + 'grid-row-editor-wrap',
    errorCls: Ext.baseCSSPrefix + 'grid-row-editor-errors-item',
    buttonUI: 'default',
    
    
    hideMode: 'offsets',
    initComponent: function() {
        var me = this,
            grid = me.editingPlugin.grid,
            Container = Ext.container.Container,
            form, normalCt, lockedCt;
        me.cls = Ext.baseCSSPrefix + 'grid-editor ' + Ext.baseCSSPrefix + 'grid-row-editor';
        me.layout = {
            type: 'hbox',
            align: 'middle'
        };
        me.lockable = grid.lockable;
        
        if (me.lockable) {
            me.items = [
                
                lockedCt = me.lockedColumnContainer = new Container({
                    id: grid.id + '-locked-editor-cells',
                    scrollable: {
                        x: false,
                        y: false
                    },
                    layout: {
                        type: 'hbox',
                        align: 'middle'
                    },
                    
                    margin: '0 1 0 0'
                }),
                
                normalCt = me.normalColumnContainer = new Container({
                    
                    scrollable: {
                        x: false,
                        y: false
                    },
                    flex: 1,
                    id: grid.id + '-normal-editor-cells',
                    layout: {
                        type: 'hbox',
                        align: 'middle'
                    }
                })
            ];
            
            lockedCt.getScrollable().addPartner(grid.lockedGrid.view.getScrollable(), 'x');
            normalCt.getScrollable().addPartner(grid.normalGrid.view.getScrollable(), 'x');
        } else {
            
            me.setScrollable({
                x: false,
                y: false
            });
            
            me.getScrollable().addPartner(grid.view.getScrollable(), 'x');
            me.lockedColumnContainer = me.normalColumnContainer = me;
        }
        me.callParent(arguments);
        if (me.fields) {
            me.addFieldsForColumn(me.fields, true);
            me.insertColumnEditor(me.fields);
            delete me.fields;
        }
        me.mon(Ext.GlobalEvents, {
            scope: me,
            show: me.repositionIfVisible
        });
        form = me.getForm();
        form.on('validitychange', me.onValidityChange, me);
        form.on('errorchange', me.onErrorChange, me);
    },
    
    
    
    
    onGridResize: function() {
        var me = this,
            clientWidth = me.getClientWidth(),
            grid = me.editingPlugin.grid,
            gridBody = grid.body,
            btns = me.getFloatingButtons();
        me.wrapEl.setLocalX(gridBody.getOffsetsTo(grid)[0] + gridBody.getBorderWidth('l') - grid.el.getBorderWidth('l'));
        me.setWidth(clientWidth);
        btns.setLocalX((clientWidth - btns.getWidth()) / 2);
        if (me.lockable) {
            me.lockedColumnContainer.setWidth(grid.lockedGrid.view.el.dom.clientWidth);
        }
    },
    syncAllFieldWidths: function() {
        var me = this;
        
        
        Ext.Array.each(me.query('[isEditorComponent]'), function(editorComponent) {
            if (editorComponent.column.isVisible()) {
                me.onColumnShow(editorComponent.column);
            }
        }, me);
    },
    syncFieldWidth: function(column) {
        var field = column.getEditor(),
            width;
        field._marginWidth = (field._marginWidth || field.el.getMargin('lr'));
        width = column.getWidth() - field._marginWidth;
        field.setWidth(width);
        if (field.xtype === 'displayfield') {
            
            field.inputWidth = width;
        }
    },
    onValidityChange: function(form, valid) {
        this.updateButton(valid);
        this.isValid = valid;
    },
    onErrorChange: function() {
        var me = this,
            valid;
        if (me.errorSummary && me.isVisible()) {
            valid = me.getForm().isValid();
            me[valid ? 'hideToolTip' : 'showToolTip']();
        }
    },
    updateButton: function(valid) {
        var buttons = this.floatingButtons;
        if (buttons) {
            buttons.child('#update').setDisabled(!valid);
        } else {
            
            this.updateButtonDisabled = !valid;
        }
    },
    afterRender: function() {
        var me = this,
            plugin = me.editingPlugin,
            grid = plugin.grid,
            view = grid.lockable ? grid.normalGrid.view : grid.view;
        me.callParent(arguments);
        
        me.scrollingView = view;
        me.scrollingViewEl = view.el;
        view.on('scroll', me.onViewScroll, me);
        
        me.mon(me.el, {
            click: Ext.emptyFn,
            stopPropagation: true
        });
        
        me.mon(grid, 'resize', me.onGridResize, me);
        if (me.lockable) {
            grid.lockedGrid.view.on('resize', 'onGridResize', me);
        }
        me.el.swallowEvent([
            'keypress',
            'keydown'
        ]);
        me.initKeyNav();
        me.mon(plugin.view, {
            beforerefresh: me.onBeforeViewRefresh,
            refresh: me.onViewRefresh,
            itemremove: me.onViewItemRemove,
            scope: me
        });
        
        me.preventReposition = true;
        me.syncAllFieldWidths();
        delete me.preventReposition;
    },
    initKeyNav: function() {
        var me = this,
            plugin = me.editingPlugin;
        me.keyNav = new Ext.util.KeyNav(me.el,{
            enter: plugin.onEnterKey,
            esc: plugin.onEscKey,
            scope: plugin
        });
    },
    onBeforeViewRefresh: function(view) {
        var me = this,
            viewDom = view.el.dom;
        if (me.el.dom.parentNode === viewDom) {
            viewDom.removeChild(me.el.dom);
        }
    },
    onViewRefresh: function(view) {
        var me = this,
            context = me.context,
            row;
        
        if (context && (row = view.getRow(context.record))) {
            context.row = row;
            me.reposition();
            if (me.tooltip && me.tooltip.isVisible()) {
                me.tooltip.setTarget(context.row);
            }
        } else {
            me.editingPlugin.cancelEdit();
        }
    },
    onViewItemRemove: function(record, index, item, view) {
        
        
        
        if (!view.refreshing) {
            var context = this.context;
            if (context && record === context.record) {
                
                this.editingPlugin.cancelEdit();
            }
        }
    },
    onViewScroll: function() {
        var me = this,
            viewEl = me.editingPlugin.view.el,
            scrollingView = me.scrollingView,
            scrollTop = scrollingView.getScrollY(),
            scrollLeft = scrollingView.getScrollX(),
            scrollTopChanged = scrollTop !== me.lastScrollTop,
            row;
        me.lastScrollTop = scrollTop;
        me.lastScrollLeft = scrollLeft;
        if (me.isVisible()) {
            row = Ext.getDom(me.context.row);
            
            if (row && viewEl.contains(row)) {
                if (scrollTopChanged) {
                    
                    me.context.row = row;
                    me.reposition(null, true);
                    if ((me.tooltip && me.tooltip.isVisible()) || me.hiddenTip) {
                        me.repositionTip();
                    }
                    me.syncEditorClip();
                }
            } else 
            {
                me.setLocalY(-400);
            }
        }
    },
    onColumnResize: function(column, width) {
        var me = this;
        if (me.rendered && !me.editingPlugin.reconfiguring) {
            
            me.onGridResize();
            me.onViewScroll();
            if (!column.isGroupHeader) {
                me.syncFieldWidth(column);
                me.repositionIfVisible();
            }
        }
    },
    onColumnHide: function(column) {
        if (!this.editingPlugin.reconfiguring && !column.isGroupHeader) {
            column.getEditor().hide();
            this.repositionIfVisible();
        }
    },
    onColumnShow: function(column) {
        var me = this;
        if (me.rendered && !me.editingPlugin.reconfiguring && !column.isGroupHeader && column.getEditor) {
            column.getEditor().show();
            me.syncFieldWidth(column);
            if (!me.preventReposition) {
                this.repositionIfVisible();
            }
        }
    },
    onColumnMove: function(column, fromIdx, toIdx) {
        var me = this,
            locked = column.isLocked(),
            fieldContainer = locked ? me.lockedColumnContainer : me.normalColumnContainer,
            columns, i, len, after, offset;
        
        if (column.isGroupHeader) {
            Ext.suspendLayouts();
            after = toIdx > fromIdx;
            offset = after ? 1 : 0;
            columns = column.getGridColumns();
            for (i = 0 , len = columns.length; i < len; ++i) {
                column = columns[i];
                toIdx = column.getIndex();
                if (after) {
                    ++offset;
                }
                me.setColumnEditor(column, toIdx + offset, fieldContainer);
            }
            Ext.resumeLayouts(true);
        } else {
            me.setColumnEditor(column, column.getIndex(), fieldContainer);
        }
    },
    setColumnEditor: function(column, idx, fieldContainer) {
        this.addFieldsForColumn(column);
        fieldContainer.insert(idx, column.getEditor());
    },
    onColumnAdd: function(column) {
        
        if (column.isGroupHeader) {
            column = column.getGridColumns();
        }
        
        this.addFieldsForColumn(column);
        this.insertColumnEditor(column);
        this.preventReposition = false;
    },
    insertColumnEditor: function(column) {
        var me = this,
            fieldContainer, len, i;
        if (Ext.isArray(column)) {
            for (i = 0 , len = column.length; i < len; i++) {
                me.insertColumnEditor(column[i]);
            }
            return;
        }
        if (!column.getEditor) {
            return;
        }
        fieldContainer = column.isLocked() ? me.lockedColumnContainer : me.normalColumnContainer;
        
        fieldContainer.insert(column.getIndex(), column.getEditor());
        me.needsSyncFieldWidths = true;
    },
    destroyColumnEditor: function(column) {
        var me = this,
            field, len, i;
        if (Ext.isArray(column)) {
            for (i = 0 , len = column.length; i < len; i++) {
                me.removeColumnEditor(column[i]);
            }
            return;
        }
        if (column.hasEditor() && (field = column.getEditor())) {
            field.destroy();
        }
    },
    getFloatingButtons: function() {
        var me = this,
            btns = me.floatingButtons;
        if (!btns) {
            me.floatingButtons = btns = new Ext.grid.RowEditorButtons({
                rowEditor: me
            });
        }
        return btns;
    },
    repositionIfVisible: function(c) {
        var me = this,
            view = me.view;
        
        
        if (c && (c === me || !c.el.isAncestor(view.el))) {
            return;
        }
        if (me.isVisible() && view.isVisible(true)) {
            me.reposition();
        }
    },
    isLayoutChild: function(ownerCandidate) {
        
        return false;
    },
    getRefOwner: function() {
        return this.editingPlugin.grid;
    },
    getRefItems: function(deep) {
        var me = this,
            result;
        if (me.lockable) {
            
            
            result = [
                me.lockedColumnContainer
            ];
            result.push.apply(result, me.lockedColumnContainer.getRefItems(deep));
            result.push(me.normalColumnContainer);
            result.push.apply(result, me.normalColumnContainer.getRefItems(deep));
        } else {
            result = me.callParent(arguments);
        }
        result.push.apply(result, me.getFloatingButtons().getRefItems(deep));
        return result;
    },
    reposition: function(animateConfig, fromScrollHandler) {
        var me = this,
            context = me.context,
            row = context && context.row,
            yOffset = 0,
            wrapEl = me.wrapEl,
            rowTop, localY, deltaY, afterPosition;
        
        if (row && Ext.isElement(row)) {
            deltaY = me.syncButtonPosition(me.getScrollDelta());
            if (!me.editingPlugin.grid.rowLines) {
                
                
                
                
                yOffset = -parseInt(Ext.fly(row).first().getStyle('border-bottom-width'), 10);
            }
            rowTop = me.calculateLocalRowTop(row);
            localY = me.calculateEditorTop(rowTop) + yOffset;
            
            
            
            
            if (!fromScrollHandler) {
                afterPosition = function() {
                    if (deltaY) {
                        me.scrollingViewEl.scrollBy(0, deltaY, true);
                    }
                    me.focusColumnField(context.column);
                };
            }
            me.syncEditorClip();
            
            
            
            if (animateConfig) {
                wrapEl.animate(Ext.applyIf({
                    to: {
                        top: localY
                    },
                    duration: animateConfig.duration || 125,
                    callback: afterPosition
                }, animateConfig));
            } else {
                wrapEl.setLocalY(localY);
                if (afterPosition) {
                    afterPosition();
                }
            }
        }
    },
    
    getScrollDelta: function() {
        var me = this,
            scrollingViewDom = me.scrollingViewEl.dom,
            context = me.context,
            body = me.body,
            deltaY = 0;
        if (context) {
            deltaY = Ext.fly(context.row).getOffsetsTo(scrollingViewDom)[1];
            if (deltaY < 0) {
                deltaY -= body.getBorderPadding().beforeY;
            } else if (deltaY > 0) {
                deltaY = Math.max(deltaY + me.getHeight() + me.floatingButtons.getHeight() - scrollingViewDom.clientHeight - body.getBorderWidth('b'), 0);
                if (deltaY > 0) {
                    deltaY -= body.getBorderPadding().afterY;
                }
            }
        }
        return deltaY;
    },
    
    
    
    
    calculateLocalRowTop: function(row) {
        var grid = this.editingPlugin.grid;
        return Ext.fly(row).getOffsetsTo(grid)[1] - grid.el.getBorderWidth('t') + this.lastScrollTop;
    },
    
    
    
    calculateEditorTop: function(rowTop) {
        return rowTop - this.body.getBorderPadding().beforeY - this.lastScrollTop;
    },
    getClientWidth: function() {
        var me = this,
            grid = me.editingPlugin.grid,
            result;
        if (me.lockable) {
            result = grid.lockedGrid.getWidth() + grid.normalGrid.view.el.dom.clientWidth;
        } else {
            result = grid.view.el.dom.clientWidth;
        }
        return result;
    },
    getEditor: function(fieldInfo) {
        var me = this;
        if (Ext.isNumber(fieldInfo)) {
            
            
            return me.query('[isEditorComponent]')[fieldInfo];
        } else if (fieldInfo.isHeader && !fieldInfo.isGroupHeader) {
            return fieldInfo.getEditor();
        }
    },
    addFieldsForColumn: function(column, initial) {
        var me = this,
            i, length, field;
        if (Ext.isArray(column)) {
            for (i = 0 , length = column.length; i < length; i++) {
                me.addFieldsForColumn(column[i], initial);
            }
            return;
        }
        if (column.getEditor) {
            
            field = column.getEditor(null, me.getDefaultFieldCfg());
            if (column.align === 'right') {
                field.fieldStyle = 'text-align:right';
            }
            if (column.xtype === 'actioncolumn') {
                field.fieldCls += ' ' + Ext.baseCSSPrefix + 'form-action-col-field';
            }
            if (me.isVisible() && me.context) {
                if (field.is('displayfield')) {
                    me.renderColumnData(field, me.context.record, column);
                } else {
                    field.suspendEvents();
                    field.setValue(me.context.record.get(column.dataIndex));
                    field.resumeEvents();
                }
            }
            if (column.hidden) {
                me.onColumnHide(column);
            } else if (column.rendered && !initial) {
                
                me.onColumnShow(column);
            }
        }
    },
    getDefaultFieldCfg: function() {
        return {
            xtype: 'displayfield',
            
            
            getModelData: function() {
                return null;
            }
        };
    },
    loadRecord: function(record) {
        var me = this,
            form = me.getForm(),
            fields = form.getFields(),
            items = fields.items,
            length = items.length,
            i, displayFields, isValid, item;
        
        for (i = 0; i < length; i++) {
            item = items[i];
            item.suspendEvents();
            item.reset();
        }
        form.loadRecord(record);
        for (i = 0; i < length; i++) {
            items[i].resumeEvents();
        }
        
        
        if (form.hasInvalidField() === form.wasValid) {
            delete form.wasValid;
        }
        isValid = form.isValid();
        if (me.errorSummary) {
            if (isValid) {
                me.hideToolTip();
            } else {
                me.showToolTip();
            }
        }
        me.updateButton(isValid);
        
        displayFields = me.query('>displayfield');
        length = displayFields.length;
        for (i = 0; i < length; i++) {
            me.renderColumnData(displayFields[i], record);
        }
    },
    renderColumnData: function(field, record, activeColumn) {
        var me = this,
            grid = me.editingPlugin.grid,
            headerCt = grid.headerCt,
            view = me.scrollingView,
            store = view.dataSource,
            column = activeColumn || field.column,
            value = record.get(column.dataIndex),
            renderer = column.editRenderer || column.renderer,
            metaData, rowIdx, colIdx,
            scope = (column.usingDefaultRenderer && !column.scope) ? column : column.scope;
        
        if (renderer) {
            metaData = {
                tdCls: '',
                style: ''
            };
            rowIdx = store.indexOf(record);
            colIdx = headerCt.getHeaderIndex(column);
            value = renderer.call(scope || headerCt.ownerCt, value, metaData, record, rowIdx, colIdx, store, view);
        }
        field.setRawValue(value);
    },
    beforeEdit: function() {
        var me = this,
            scrollDelta;
        if (me.isVisible() && me.errorSummary && !me.autoCancel && me.isDirty()) {
            
            scrollDelta = me.getScrollDelta();
            if (scrollDelta) {
                me.scrollingViewEl.scrollBy(0, scrollDelta, true);
            }
            me.showToolTip();
            return false;
        }
    },
    
    startEdit: function(record, columnHeader) {
        var me = this,
            editingPlugin = me.editingPlugin,
            grid = editingPlugin.grid,
            context = me.context = editingPlugin.context,
            alreadyVisible = me.isVisible(),
            wrapEl = me.wrapEl;
        
        
        Ext.suspendLayouts();
        if (!me.rendered) {
            me.width = me.getClientWidth();
            me.render(grid.el, grid.el.dom.firstChild);
            
            
            
            
            wrapEl = me.wrapEl = me.el.wrap();
            
            
            wrapEl.setVisibilityMode(3);
            wrapEl.addCls(me._wrapCls);
            me.getFloatingButtons().render(wrapEl);
            
            me.onViewScroll();
        }
        
        context.grid.getSelectionModel().selectByPosition({
            row: record,
            column: columnHeader
        });
        
        me.onGridResize();
        
        me.loadRecord(record);
        
        
        Ext.resumeLayouts(alreadyVisible);
        if (alreadyVisible) {
            me.reposition(true);
        } else {
            me.show();
        }
    },
    
    
    
    
    syncButtonPosition: function(scrollDelta) {
        var me = this,
            floatingButtons = me.getFloatingButtons(),
            scrollingView = me.scrollingView,
            overflow = me.getScrollDelta() - (scrollingView.getScrollable().getSize().y - scrollingView.getScrollY() - me.scrollingViewEl.dom.clientHeight);
        if (overflow > 0) {
            if (!me._buttonsOnTop) {
                floatingButtons.setButtonPosition('top');
                me._buttonsOnTop = true;
            }
            scrollDelta = 0;
        } else if (me._buttonsOnTop !== false) {
            floatingButtons.setButtonPosition('bottom');
            me._buttonsOnTop = false;
        }
        return scrollDelta;
    },
    
    
    
    
    
    
    
    
    syncEditorClip: function() {
        var me = this,
            overflow = me.getScrollDelta(),
            el = me.el,
            floatingButtons = me.floatingButtons,
            btnEl = floatingButtons.el,
            max = Math.max,
            body, btnHeight, editorHeight;
        if (overflow) {
            
            me.isOverflowing = true;
            body = me.body;
            btnHeight = floatingButtons.getHeight();
            editorHeight = me.getHeight();
            max = Math.max;
            if (overflow > 0) {
                
                if (me._buttonsOnTop) {
                    overflow -= (btnHeight - body.getBorderWidth('b'));
                    me.clipBottom(el, max(editorHeight - overflow), 0);
                    overflow -= (editorHeight - body.getBorderWidth('t'));
                    if (overflow > 0) {
                        me.clipBottom(btnEl, max(btnHeight - overflow, 0));
                    } else {
                        me.clearClip(btnEl);
                    }
                } else {
                    me.clipBottom(btnEl, max(btnHeight - overflow, 0));
                    overflow -= (btnHeight - body.getBorderWidth('b'));
                    if (overflow > 0) {
                        me.clipBottom(el, max(editorHeight - overflow, 0));
                    } else {
                        me.clearClip(el);
                    }
                }
            } else if (overflow < 0) {
                
                overflow = Math.abs(overflow);
                me.clipTop(el, overflow);
                overflow -= (editorHeight - body.getBorderWidth('b'));
                if (overflow > 0) {
                    me.clipTop(btnEl, overflow);
                } else {
                    me.clearClip(btnEl);
                }
            }
        } else if (me.isOverflowing) {
            me.clearClip(btnEl);
            me.clearClip(el);
            me.isOverflowing = false;
        }
    },
    focusColumnField: function(column) {
        var field, didFocus;
        if (column && !column.isDestroyed) {
            if (column.isVisible()) {
                field = this.getEditor(column);
                if (field && field.isFocusable(true)) {
                    didFocus = true;
                    field.focus();
                }
            }
            if (!didFocus) {
                this.focusColumnField(column.next());
            }
        }
    },
    cancelEdit: function() {
        var me = this,
            form = me.getForm(),
            fields = form.getFields(),
            items = fields.items,
            length = items.length,
            i;
        me.hide();
        form.clearInvalid();
        
        for (i = 0; i < length; i++) {
            items[i].suspendEvents();
        }
        form.reset();
        for (i = 0; i < length; i++) {
            items[i].resumeEvents();
        }
    },
    completeEdit: function() {
        var me = this,
            form = me.getForm();
        if (!form.isValid()) {
            return false;
        }
        form.updateRecord(me.context.record);
        me.hide();
        return true;
    },
    onShow: function() {
        var me = this;
        me.wrapEl.show();
        me.previousFocus = Ext.Element.getActiveElement();
        me.callParent(arguments);
        if (me.needsSyncFieldWidths) {
            me.suspendLayouts();
            me.syncAllFieldWidths();
            me.resumeLayouts(true);
        }
        delete me.needsSyncFieldWidths;
        me.reposition();
    },
    onHide: function() {
        var me = this;
        
        if (me.el.contains(Ext.Element.getActiveElement())) {
            if (me.context) {
                me.context.view.grid.focus();
                me.context = null;
            } else {
                me.previousFocus.focus();
            }
        }
        me.wrapEl.hide();
        me.callParent(arguments);
        if (me.tooltip) {
            me.hideToolTip();
        }
    },
    onResize: function(width, height) {
        this.wrapEl.setSize(width, height);
    },
    isDirty: function() {
        return this.getForm().isDirty();
    },
    getToolTip: function() {
        var me = this,
            tip = me.tooltip,
            grid = me.editingPlugin.grid;
        if (!tip) {
            me.tooltip = tip = new Ext.tip.ToolTip({
                cls: Ext.baseCSSPrefix + 'grid-row-editor-errors',
                title: me.errorsText,
                autoHide: false,
                closable: true,
                closeAction: 'disable',
                anchor: 'left',
                anchorToTarget: true,
                constrainPosition: true,
                constrainTo: document.body
            });
            grid.add(tip);
            
            me.mon(grid, {
                afterlayout: me.onGridLayout,
                scope: me
            });
        }
        return tip;
    },
    hideToolTip: function() {
        var me = this,
            tip = me.getToolTip();
        if (tip.rendered) {
            tip.disable();
        }
        me.hiddenTip = false;
    },
    showToolTip: function() {
        var me = this,
            tip = me.getToolTip();
        tip.update(me.getErrors());
        me.repositionTip();
        tip.enable();
    },
    onGridLayout: function() {
        if (this.tooltip && this.tooltip.isVisible()) {
            this.repositionTip();
        }
    },
    repositionTip: function() {
        var me = this,
            tip = me.getToolTip(),
            context = me.context,
            row = Ext.get(context.row),
            viewEl = me.scrollingViewEl,
            viewHeight = viewEl.dom.clientHeight,
            viewTop = me.lastScrollTop,
            viewBottom = viewTop + viewHeight,
            rowHeight = row.getHeight(),
            rowTop = row.getOffsetsTo(me.context.view.body)[1],
            rowBottom = rowTop + rowHeight;
        if (rowBottom > viewTop && rowTop < viewBottom) {
            
            
            tip.anchorTarget = me.editingPlugin.grid.view.el;
            tip.mouseOffset = [
                0,
                row.getOffsetsTo(viewEl)[1]
            ];
            
            tip.show();
            me.hiddenTip = false;
        } else {
            tip.hide();
            me.hiddenTip = true;
        }
    },
    getErrors: function() {
        var me = this,
            errors = [],
            fields = me.query('>[isFormField]'),
            length = fields.length,
            i, fieldErrors, field;
        for (i = 0; i < length; i++) {
            field = fields[i];
            fieldErrors = field.getErrors();
            if (fieldErrors.length) {
                errors.push(me.createErrorListItem(fieldErrors[0], field.column.text));
            }
        }
        
        if (!errors.length && !me.autoCancel && me.isDirty()) {
            errors[0] = me.createErrorListItem(me.dirtyText);
        }
        return '<ul class="' + Ext.baseCSSPrefix + 'list-plain">' + errors.join('') + '</ul>';
    },
    createErrorListItem: function(e, name) {
        e = name ? name + ': ' + e : e;
        return '<li class="' + this.errorCls + '">' + e + '</li>';
    },
    beforeDestroy: function() {
        Ext.destroy(this.floatingButtons, this.tooltip);
        this.callParent();
    },
    clipBottom: function(el, value) {
        el.setStyle('clip', 'rect(0 auto ' + value + 'px 0)');
    },
    clipTop: function(el, value) {
        el.setStyle('clip', 'rect(' + value + 'px, auto, auto, 0)');
    },
    clearClip: function(el) {
        el.setStyle('clip', Ext.isIE8 ? 'rect(-1000px auto 1000px auto)' : 'auto');
    }
});

Ext.define('Ext.grid.Scroller', {
    constructor: Ext.deprecated()
});


Ext.define('Ext.view.DropZone', {
    extend: 'Ext.dd.DropZone',
    indicatorCls: Ext.baseCSSPrefix + 'grid-drop-indicator',
    indicatorHtml: [
        '<div class="',
        Ext.baseCSSPrefix,
        'grid-drop-indicator-left" role="presentation"></div>',
        '<div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-right" role="presentation"></div>'
    ].join(''),
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);
        
        
        
        
        
        if (!me.ddGroup) {
            me.ddGroup = 'view-dd-zone-' + me.view.id;
        }
        
        
        
        me.callParent([
            me.view.el
        ]);
    },
    
    
    fireViewEvent: function() {
        var me = this,
            result;
        me.lock();
        result = me.view.fireEvent.apply(me.view, arguments);
        me.unlock();
        return result;
    },
    getTargetFromEvent: function(e) {
        var node = e.getTarget(this.view.getItemSelector()),
            mouseY, nodeList, testNode, i, len, box;
        
        
        if (!node) {
            mouseY = e.getY();
            for (i = 0 , nodeList = this.view.getNodes() , len = nodeList.length; i < len; i++) {
                testNode = nodeList[i];
                box = Ext.fly(testNode).getBox();
                if (mouseY <= box.bottom) {
                    return testNode;
                }
            }
        }
        return node;
    },
    getIndicator: function() {
        var me = this;
        if (!me.indicator) {
            me.indicator = new Ext.Component({
                ariaRole: 'presentation',
                html: me.indicatorHtml,
                cls: me.indicatorCls,
                ownerCt: me.view,
                floating: true,
                shadow: false
            });
        }
        return me.indicator;
    },
    getPosition: function(e, node) {
        var y = e.getXY()[1],
            region = Ext.fly(node).getRegion(),
            pos;
        if ((region.bottom - y) >= (region.bottom - region.top) / 2) {
            pos = "before";
        } else {
            pos = "after";
        }
        return pos;
    },
    
    containsRecordAtOffset: function(records, record, offset) {
        if (!record) {
            return false;
        }
        var view = this.view,
            recordIndex = view.indexOf(record),
            nodeBefore = view.getNode(recordIndex + offset),
            recordBefore = nodeBefore ? view.getRecord(nodeBefore) : null;
        return recordBefore && Ext.Array.contains(records, recordBefore);
    },
    positionIndicator: function(node, data, e) {
        var me = this,
            view = me.view,
            pos = me.getPosition(e, node),
            overRecord = view.getRecord(node),
            draggingRecords = data.records,
            indicatorY;
        if (!Ext.Array.contains(draggingRecords, overRecord) && (pos === 'before' && !me.containsRecordAtOffset(draggingRecords, overRecord, -1) || pos === 'after' && !me.containsRecordAtOffset(draggingRecords, overRecord, 1))) {
            me.valid = true;
            if (me.overRecord !== overRecord || me.currentPosition !== pos) {
                indicatorY = Ext.fly(node).getY() - view.el.getY() - 1;
                if (pos === 'after') {
                    indicatorY += Ext.fly(node).getHeight();
                }
                
                if (view.touchScroll === 2) {
                    indicatorY += view.getScrollY();
                }
                me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, indicatorY);
                
                me.overRecord = overRecord;
                me.currentPosition = pos;
            }
        } else {
            me.invalidateDrop();
        }
    },
    invalidateDrop: function() {
        if (this.valid) {
            this.valid = false;
            this.getIndicator().hide();
        }
    },
    
    onNodeOver: function(node, dragZone, e, data) {
        var me = this;
        if (!Ext.Array.contains(data.records, me.view.getRecord(node))) {
            me.positionIndicator(node, data, e);
        }
        return me.valid ? me.dropAllowed : me.dropNotAllowed;
    },
    
    
    notifyOut: function(node, dragZone, e, data) {
        var me = this;
        me.callParent(arguments);
        me.overRecord = me.currentPosition = null;
        me.valid = false;
        if (me.indicator) {
            me.indicator.hide();
        }
    },
    
    onContainerOver: function(dd, e, data) {
        var me = this,
            view = me.view,
            count = view.dataSource.getCount();
        
        if (count) {
            me.positionIndicator(view.all.last(), data, e);
        } else 
        {
            me.overRecord = me.currentPosition = null;
            me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, 0);
            me.valid = true;
        }
        return me.dropAllowed;
    },
    onContainerDrop: function(dd, e, data) {
        return this.onNodeDrop(dd, null, e, data);
    },
    onNodeDrop: function(targetNode, dragZone, e, data) {
        var me = this,
            dropHandled = false,
            
            
            
            
            
            dropHandlers = {
                wait: false,
                processDrop: function() {
                    me.invalidateDrop();
                    me.handleNodeDrop(data, me.overRecord, me.currentPosition);
                    dropHandled = true;
                    me.fireViewEvent('drop', targetNode, data, me.overRecord, me.currentPosition);
                },
                cancelDrop: function() {
                    me.invalidateDrop();
                    dropHandled = true;
                }
            },
            performOperation = false;
        if (me.valid) {
            performOperation = me.fireViewEvent('beforedrop', targetNode, data, me.overRecord, me.currentPosition, dropHandlers);
            if (dropHandlers.wait) {
                return;
            }
            if (performOperation !== false) {
                
                if (!dropHandled) {
                    dropHandlers.processDrop();
                }
            }
        }
        return performOperation;
    },
    destroy: function() {
        Ext.destroy(this.indicator);
        delete this.indicator;
        this.callParent();
    }
});


Ext.define('Ext.grid.ViewDropZone', {
    extend: 'Ext.view.DropZone',
    indicatorHtml: '<div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-left" role="presentation"></div><div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-right" role="presentation"></div>',
    indicatorCls: Ext.baseCSSPrefix + 'grid-drop-indicator',
    handleNodeDrop: function(data, record, position) {
        var view = this.view,
            store = view.getStore(),
            index, records, i, len;
        
        if (data.copy) {
            records = data.records;
            data.records = [];
            for (i = 0 , len = records.length; i < len; i++) {
                data.records.push(records[i].copy());
            }
        } else {
            
            data.view.store.remove(data.records, data.view === view);
        }
        if (record && position) {
            index = store.indexOf(record);
            
            if (position !== 'before') {
                index++;
            }
            store.insert(index, data.records);
        } else 
        {
            store.add(data.records);
        }
        
        view.getSelectionModel().select(data.records);
        
        view.getNavigationModel().setPosition(data.records[0]);
    }
});


Ext.define('Ext.grid.column.Action', {
    extend: 'Ext.grid.column.Column',
    alias: [
        'widget.actioncolumn'
    ],
    alternateClassName: 'Ext.grid.ActionColumn',
    
    
    
    
    
    
    
    stopSelection: true,
    
    
    
    
    
    actionIdRe: new RegExp(Ext.baseCSSPrefix + 'action-col-(\\d+)'),
    
    altText: '',
    
    menuText: '<i>Actions</i>',
    sortable: false,
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-action-col',
    actionIconCls: Ext.baseCSSPrefix + 'action-col-icon',
    constructor: function(config) {
        var me = this,
            cfg = Ext.apply({}, config),
            
            items = cfg.items || me.items || [
                me
            ],
            hasGetClass, i, len;
        me.origRenderer = cfg.renderer || me.renderer;
        me.origScope = cfg.scope || me.scope;
        me.renderer = me.scope = cfg.renderer = cfg.scope = null;
        
        cfg.items = null;
        me.callParent([
            cfg
        ]);
        
        me.items = items;
        for (i = 0 , len = items.length; i < len; ++i) {
            if (items[i].getClass) {
                hasGetClass = true;
                break;
            }
        }
        
        if (me.origRenderer || hasGetClass) {
            me.hasCustomRenderer = true;
        }
    },
    initComponent: function() {
        var me = this;
        me.callParent();
        if (me.sortable && !me.dataIndex) {
            me.sortable = false;
        }
    },
    
    
    defaultRenderer: function(v, cellValues, record, rowIdx, colIdx, store, view) {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            scope = me.origScope || me,
            items = me.items,
            len = items.length,
            i = 0,
            item, ret, disabled, tooltip;
        
        
        
        ret = Ext.isFunction(me.origRenderer) ? me.origRenderer.apply(scope, arguments) || '' : '';
        cellValues.tdCls += ' ' + Ext.baseCSSPrefix + 'action-col-cell';
        for (; i < len; i++) {
            item = items[i];
            disabled = item.disabled || (item.isDisabled ? item.isDisabled.call(item.scope || scope, view, rowIdx, colIdx, item, record) : false);
            tooltip = disabled ? null : (item.tooltip || (item.getTip ? item.getTip.apply(item.scope || scope, arguments) : null));
            
            if (!item.hasActionConfiguration) {
                
                item.stopSelection = me.stopSelection;
                item.disable = Ext.Function.bind(me.disableAction, me, [
                    i
                ], 0);
                item.enable = Ext.Function.bind(me.enableAction, me, [
                    i
                ], 0);
                item.hasActionConfiguration = true;
            }
            ret += '<img role="button" alt="' + (item.altText || me.altText) + '" src="' + (item.icon || Ext.BLANK_IMAGE_URL) + '" class="' + me.actionIconCls + ' ' + prefix + 'action-col-' + String(i) + ' ' + (disabled ? prefix + 'item-disabled' : ' ') + (Ext.isFunction(item.getClass) ? item.getClass.apply(item.scope || scope, arguments) : (item.iconCls || me.iconCls || '')) + '"' + (tooltip ? ' data-qtip="' + tooltip + '"' : '') + ' />';
        }
        return ret;
    },
    updater: function(cell, value, record, view, dataSource) {
        var cellValues = {};
        cell.firstChild.innerHTML = this.defaultRenderer(value, cellValues, record, null, null, dataSource, view);
        Ext.fly(cell).addCls(cellValues.tdCls);
    },
    
    enableAction: function(index, silent) {
        var me = this;
        if (!index) {
            index = 0;
        } else if (!Ext.isNumber(index)) {
            index = Ext.Array.indexOf(me.items, index);
        }
        me.items[index].disabled = false;
        me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).removeCls(me.disabledCls);
        if (!silent) {
            me.fireEvent('enable', me);
        }
    },
    
    disableAction: function(index, silent) {
        var me = this;
        if (!index) {
            index = 0;
        } else if (!Ext.isNumber(index)) {
            index = Ext.Array.indexOf(me.items, index);
        }
        me.items[index].disabled = true;
        me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).addCls(me.disabledCls);
        if (!silent) {
            me.fireEvent('disable', me);
        }
    },
    beforeDestroy: function() {
        
        
        this.renderer = this.items = null;
        return this.callParent(arguments);
    },
    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {
        var me = this,
            target = e.getTarget(),
            key = type === 'keydown' && e.getKey(),
            match, item, disabled;
        
        
        
        if (key && !Ext.fly(target).findParent(view.getCellSelector())) {
            target = Ext.fly(cell).down('.' + Ext.baseCSSPrefix + 'action-col-icon', true);
        }
        
        if (target && (match = target.className.match(me.actionIdRe))) {
            item = me.items[parseInt(match[1], 10)];
            disabled = item.disabled || (item.isDisabled ? item.isDisabled.call(item.scope || me.origScope || me, view, recordIndex, cellIndex, item, record) : false);
            if (item && !disabled) {
                
                if (type === 'mousedown') {
                    if (item.stopSelection) {
                        e.preventDefault();
                    }
                    return false;
                }
                if (type === 'click' || (key === e.ENTER || key === e.SPACE)) {
                    Ext.callback(item.handler || me.handler, item.scope || me.origScope, [
                        view,
                        recordIndex,
                        cellIndex,
                        item,
                        e,
                        record,
                        row
                    ], undefined, me);
                    
                    
                    if (item.stopSelection !== false) {
                        return false;
                    }
                }
            }
        }
        return me.callParent(arguments);
    },
    cascade: function(fn, scope) {
        fn.call(scope || this, this);
    },
    
    getRefItems: function() {
        return [];
    },
    privates: {
        getFocusables: function() {
            
            
            return [];
        }
    }
});


Ext.define('Ext.grid.column.Boolean', {
    extend: 'Ext.grid.column.Column',
    alias: [
        'widget.booleancolumn'
    ],
    alternateClassName: 'Ext.grid.BooleanColumn',
    
    
    trueText: 'true',
    
    
    
    falseText: 'false',
    
    
    undefinedText: '&#160;',
    defaultFilterType: 'boolean',
    
    
    
    producesHTML: false,
    defaultRenderer: function(value) {
        if (value === undefined) {
            return this.undefinedText;
        }
        if (!value || value === 'false') {
            return this.falseText;
        }
        return this.trueText;
    },
    updater: function(cell, value) {
        cell.firstChild.innerHTML = Ext.grid.column.Boolean.prototype.defaultRenderer.call(this, value);
    }
});


Ext.define('Ext.grid.column.Check', {
    extend: 'Ext.grid.column.Column',
    alternateClassName: [
        'Ext.ux.CheckColumn',
        'Ext.grid.column.CheckColumn'
    ],
    alias: 'widget.checkcolumn',
    
    align: 'center',
    
    stopSelection: true,
    tdCls: Ext.baseCSSPrefix + 'grid-cell-checkcolumn',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-checkcolumn',
    clickTargetName: 'el',
    defaultFilterType: 'boolean',
    
    
    constructor: function() {
        this.scope = this;
        this.callParent(arguments);
    },
    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {
        var me = this,
            key = type === 'keydown' && e.getKey(),
            mousedown = type == 'mousedown';
        if (!me.disabled && (mousedown || (key == e.ENTER || key == e.SPACE))) {
            var checked = !me.isRecordChecked(record);
            
            if (me.fireEvent('beforecheckchange', me, recordIndex, checked) !== false) {
                me.setRecordCheck(record, checked, cell, row, e);
                me.fireEvent('checkchange', me, recordIndex, checked);
                
                if (mousedown) {
                    e.stopEvent();
                }
                
                
                if (!me.stopSelection) {
                    view.selModel.selectByPosition({
                        row: recordIndex,
                        column: cellIndex
                    });
                }
                
                return false;
            } else {
                
                return !me.stopSelection;
            }
        } else {
            return me.callParent(arguments);
        }
    },
    
    onEnable: function() {
        this.callParent(arguments);
        this._setDisabled(false);
    },
    
    onDisable: function() {
        this._setDisabled(true);
    },
    
    _setDisabled: function(disabled) {
        var me = this,
            cls = me.disabledCls,
            items;
        items = me.up('tablepanel').el.select(me.getCellSelector());
        if (disabled) {
            items.addCls(cls);
        } else {
            items.removeCls(cls);
        }
    },
    
    
    defaultRenderer: function(value, cellValues) {
        var cssPrefix = Ext.baseCSSPrefix,
            cls = cssPrefix + 'grid-checkcolumn';
        if (this.disabled) {
            cellValues.tdCls += ' ' + this.disabledCls;
        }
        if (value) {
            cls += ' ' + cssPrefix + 'grid-checkcolumn-checked';
        }
        return '<img class="' + cls + '" src="' + Ext.BLANK_IMAGE_URL + '"/>';
    },
    isRecordChecked: function(record) {
        var prop = this.property;
        if (prop) {
            return record[prop];
        }
        return record.get(this.dataIndex);
    },
    setRecordCheck: function(record, checked, cell, row, e) {
        var me = this,
            prop = me.property;
        if (prop) {
            record[prop] = checked;
            me.updater(cell, checked);
        } else {
            record.set(me.dataIndex, checked);
        }
    },
    updater: function(cell, value) {
        var cellValues = {};
        cell.firstChild.innerHTML = this.defaultRenderer(value, cellValues);
        Ext.fly(cell).addCls(cellValues.tdCls);
    }
});


Ext.define('Ext.grid.column.Date', {
    extend: 'Ext.grid.column.Column',
    alias: [
        'widget.datecolumn'
    ],
    requires: [
        'Ext.Date'
    ],
    alternateClassName: 'Ext.grid.DateColumn',
    isDateColumn: true,
    defaultFilterType: 'date',
    
    
    
    
    producesHTML: false,
    initComponent: function() {
        if (!this.format) {
            this.format = Ext.Date.defaultFormat;
        }
        this.callParent(arguments);
    },
    defaultRenderer: function(value) {
        return Ext.util.Format.date(value, this.format);
    },
    updater: function(cell, value) {
        cell.firstChild.innerHTML = Ext.grid.column.Date.prototype.defaultRenderer.call(this, value);
    }
});


Ext.define('Ext.grid.column.Number', {
    extend: 'Ext.grid.column.Column',
    alias: [
        'widget.numbercolumn'
    ],
    requires: [
        'Ext.util.Format'
    ],
    alternateClassName: 'Ext.grid.NumberColumn',
    defaultFilterType: 'number',
    
    
    format: '0,000.00',
    
    
    
    
    producesHTML: false,
    defaultRenderer: function(value) {
        return Ext.util.Format.number(value, this.format);
    },
    updater: function(cell, value) {
        cell.firstChild.innerHTML = Ext.grid.column.Number.prototype.defaultRenderer.call(this, value);
    }
});


Ext.define('Ext.grid.column.RowNumberer', {
    extend: 'Ext.grid.column.Column',
    alternateClassName: 'Ext.grid.RowNumberer',
    alias: 'widget.rownumberer',
    
    text: "&#160;",
    
    width: 23,
    
    sortable: false,
    
    draggable: false,
    
    autoLock: true,
    
    lockable: false,
    align: 'right',
    
    producesHTML: false,
    constructor: function(config) {
        var me = this;
        
        
        me.width = me.width;
        me.callParent(arguments);
        
        me.sortable = false;
        me.scope = me;
    },
    
    resizable: false,
    hideable: false,
    menuDisabled: true,
    dataIndex: '',
    cls: Ext.baseCSSPrefix + 'row-numberer',
    tdCls: Ext.baseCSSPrefix + 'grid-cell-row-numberer ' + Ext.baseCSSPrefix + 'grid-cell-special',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-row-numberer',
    rowspan: undefined,
    
    defaultRenderer: function(value, metaData, record, rowIdx, colIdx, dataSource, view) {
        var rowspan = this.rowspan,
            page = dataSource.currentPage,
            result = view.store.indexOf(record);
        if (metaData && rowspan) {
            metaData.tdAttr = 'rowspan="' + rowspan + '"';
        }
        if (page > 1) {
            result += (page - 1) * dataSource.pageSize;
        }
        return result + 1;
    },
    updater: function(cell, value, record, view, dataSource) {
        cell.firstChild.innerHTML = this.defaultRenderer(value, null, record, null, null, dataSource, view);
    }
});


Ext.define('Ext.grid.column.Template', {
    extend: 'Ext.grid.column.Column',
    alias: [
        'widget.templatecolumn'
    ],
    requires: [
        'Ext.XTemplate'
    ],
    alternateClassName: 'Ext.grid.TemplateColumn',
    
    
    
    initComponent: function() {
        var me = this;
        me.tpl = (!Ext.isPrimitive(me.tpl) && me.tpl.compile) ? me.tpl : new Ext.XTemplate(me.tpl);
        
        
        me.hasCustomRenderer = true;
        me.callParent(arguments);
    },
    defaultRenderer: function(value, meta, record) {
        var data = Ext.apply({}, record.data, record.getAssociatedData());
        return this.tpl.apply(data);
    },
    updater: function(cell, value) {
        cell.firstChild.innerHTML = Ext.grid.column.CheckColumn.prototype.defaultRenderer.call(this, value);
    }
});


Ext.define('Ext.grid.column.Widget', {
    extend: 'Ext.grid.column.Column',
    alias: 'widget.widgetcolumn',
    config: {
        
        defaultWidgetUI: {}
    },
    
    sortable: false,
    
    
    
    
    onWidgetAttach: null,
    
    stopSelection: true,
    preventUpdate: true,
    initComponent: function() {
        var me = this,
            widget;
        me.callParent(arguments);
        widget = me.widget;
        if (!widget || widget.isComponent) {
            Ext.Error.raise('column.Widget requires a widget configuration.');
        }
        
        if (!widget.ui) {
            widget.ui = me.getDefaultWidgetUI()[widget.xtype] || 'default';
        }
        me.isFixedSize = Ext.isNumber(widget.width);
    },
    processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {
        var selector = view.innerSelector,
            target;
        if (this.stopSelection && type === 'click') {
            
            
            
            
            target = e.getTarget(selector);
            if (target && target !== e.target) {
                return false;
            }
        }
    },
    beforeRender: function() {
        var me = this,
            widget;
        me.listenerScopeFn = function(defaultScope) {
            if (defaultScope === 'this') {
                return this;
            }
            return me.resolveListenerScope(defaultScope);
        };
        me.liveWidgets = {};
        me.cachedStyles = {};
        me.freeWidgetStack = [
            widget = me.getFreeWidget()
        ];
        me.tdCls = widget.getTdCls();
        me.setupViewListeners(me.getView());
        me.callParent(arguments);
    },
    afterRender: function() {
        var view = this.getView();
        this.callParent(arguments);
        
        if (view && view.viewReady) {
            this.onViewRefresh(view, view.getViewRange());
        }
    },
    
    defaultRenderer: Ext.emptyFn,
    updater: function(cell, value, record) {
        this.updateWidget(record);
    },
    onResize: function(newWidth) {
        var me = this,
            liveWidgets = me.liveWidgets,
            view = me.getView(),
            id, cell;
        if (!me.isFixedSize && me.rendered && view && view.viewReady) {
            cell = view.getEl().down(me.getCellInnerSelector());
            if (cell) {
                
                newWidth -= parseInt(me.getCachedStyle(cell, 'padding-left'), 10) + parseInt(me.getCachedStyle(cell, 'padding-right'), 10);
                for (id in liveWidgets) {
                    liveWidgets[id].setWidth(newWidth);
                }
            }
        }
    },
    onAdded: function() {
        var me = this,
            view;
        me.callParent(arguments);
        view = me.getView();
        
        if (view) {
            me.setupViewListeners(view);
            if (view && view.viewReady && me.rendered && view.getEl().down(me.getCellSelector())) {
                
                
                
                
                me.onViewRefresh(view, view.getViewRange());
            }
        }
    },
    onRemoved: function(isDestroying) {
        var me = this,
            liveWidgets = me.liveWidgets,
            viewListeners = me.viewListeners,
            id, widget;
        if (me.rendered) {
            me.viewListeners = Ext.destroy(me.viewListeners);
            
            if (!isDestroying) {
                for (id in liveWidgets) {
                    widget = liveWidgets[id];
                    Ext.detachedBodyEl.dom.appendChild((widget.el || widget.element).dom);
                }
            }
        }
        me.callParent(arguments);
    },
    onDestroy: function() {
        var me = this,
            oldWidgetMap = me.liveWidgets,
            freeWidgetStack = me.freeWidgetStack,
            id, widget, i, len;
        if (me.rendered) {
            for (id in oldWidgetMap) {
                widget = oldWidgetMap[id];
                widget.$widgetRecord = widget.$widgetColumn = null;
                delete widget.getWidgetRecord;
                delete widget.getWidgetColumn;
                widget.destroy();
            }
            for (i = 0 , len = freeWidgetStack.length; i < len; ++i) {
                freeWidgetStack[i].destroy();
            }
        }
        me.freeWidgetStack = me.liveWidgets = null;
        me.callParent();
    },
    getWidget: function(record) {
        var liveWidgets = this.liveWidgets,
            widget;
        if (record && liveWidgets) {
            widget = liveWidgets[record.internalId];
        }
        return widget || null;
    },
    privates: {
        getCachedStyle: function(el, style) {
            var cachedStyles = this.cachedStyles;
            return cachedStyles[style] || (cachedStyles[style] = Ext.fly(el).getStyle(style));
        },
        getFreeWidget: function() {
            var me = this,
                result = me.freeWidgetStack ? me.freeWidgetStack.pop() : null;
            if (!result) {
                result = Ext.widget(me.widget);
                result.resolveListenerScope = me.listenerScopeFn;
                result.getWidgetRecord = me.widgetRecordDecorator;
                result.getWidgetColumn = me.widgetColumnDecorator;
                result.dataIndex = me.dataIndex;
                result.measurer = me;
                result.ownerCmp = me;
            }
            return result;
        },
        onBeforeRefresh: function() {
            var liveWidgets = this.liveWidgets,
                id;
            
            
            
            for (id in liveWidgets) {
                Ext.detachedBodyEl.appendChild(liveWidgets[id].el.dom);
            }
        },
        onItemAdd: function(records, index, items) {
            var me = this,
                view = me.getView(),
                hasAttach = !!me.onWidgetAttach,
                dataIndex = me.dataIndex,
                isFixedSize = me.isFixedSize,
                len = records.length,
                i, record, row, cell, widget, el, width;
            
            
            if (me.rendered && !me.hidden) {
                for (i = 0; i < len; i++) {
                    record = records[i];
                    if (record.isNonData) {
                        
                        continue;
                    }
                    row = view.getRowFromItem(items[i]);
                    
                    if (row) {
                        cell = row.cells[me.getVisibleIndex()].firstChild;
                        if (!isFixedSize && !width) {
                            width = me.lastBox.width - parseInt(me.getCachedStyle(cell, 'padding-left'), 10) - parseInt(me.getCachedStyle(cell, 'padding-right'), 10);
                        }
                        widget = me.liveWidgets[record.internalId] = me.getFreeWidget();
                        
                        Ext.fly(cell).empty();
                        
                        if (widget.defaultBindProperty && me.dataIndex) {
                            widget.setConfig(widget.defaultBindProperty, record.get(me.dataIndex));
                        }
                        widget.$widgetColumn = me;
                        widget.$widgetRecord = record;
                        if (hasAttach) {
                            Ext.callback(me.onWidgetAttach, me.scope, [
                                me,
                                widget,
                                record
                            ], 0, me);
                        }
                        if (el = (widget.el || widget.element)) {
                            cell.appendChild(el.dom);
                            if (!isFixedSize) {
                                widget.setWidth(width);
                            }
                        } else {
                            if (!isFixedSize) {
                                widget.width = width;
                            }
                            widget.render(cell);
                        }
                    }
                }
            }
        },
        onItemRemove: function(record, index, item) {
            var me = this,
                liveWidgets = me.liveWidgets,
                widget;
            
            
            if (me.rendered && record && (widget = liveWidgets[record.internalId])) {
                delete liveWidgets[record.internalId];
                me.freeWidgetStack.unshift(widget);
                widget.$widgetRecord = widget.$widgetColumn = null;
                Ext.detachedBodyEl.dom.appendChild((widget.el || widget.element).dom);
            }
        },
        onItemUpdate: function(record, recordIndex, oldItemDom) {
            this.updateWidget(record);
        },
        onViewRefresh: function(view, records) {
            var me = this,
                rows = view.all,
                hasAttach = !!me.onWidgetAttach,
                oldWidgetMap = me.liveWidgets,
                dataIndex = me.dataIndex,
                isFixedSize = me.isFixedSize,
                cell, widget, el, width, recordId, itemIndex, recordIndex, record, id, lastBox, dom;
            if (me.rendered && !me.hidden) {
                me.liveWidgets = {};
                Ext.suspendLayouts();
                for (itemIndex = rows.startIndex , recordIndex = 0; itemIndex <= rows.endIndex; itemIndex++ , recordIndex++) {
                    record = records[recordIndex];
                    if (record.isNonData) {
                        
                        continue;
                    }
                    recordId = record.internalId;
                    cell = view.getRow(rows.item(itemIndex)).cells[me.getVisibleIndex()].firstChild;
                    
                    widget = me.liveWidgets[recordId] = oldWidgetMap[recordId] || me.getFreeWidget();
                    delete oldWidgetMap[recordId];
                    lastBox = me.lastBox;
                    if (lastBox && !isFixedSize && width === undefined) {
                        width = lastBox.width - parseInt(me.getCachedStyle(cell, 'padding-left'), 10) - parseInt(me.getCachedStyle(cell, 'padding-right'), 10);
                    }
                    
                    if (widget.defaultBindProperty && dataIndex) {
                        widget.setConfig(widget.defaultBindProperty, records[recordIndex].get(dataIndex));
                    }
                    widget.$widgetRecord = record;
                    widget.$widgetColumn = me;
                    if (hasAttach) {
                        Ext.callback(me.onWidgetAttach, me.scope, [
                            me,
                            widget,
                            record
                        ], 0, me);
                    }
                    if (el = (widget.el || widget.element)) {
                        dom = el.dom;
                        if (dom.parentNode !== cell) {
                            Ext.fly(cell).empty();
                            cell.appendChild(el.dom);
                        }
                        if (!isFixedSize) {
                            widget.setWidth(width);
                        }
                    } else {
                        if (!isFixedSize) {
                            widget.width = width;
                        }
                        Ext.fly(cell).empty();
                        widget.render(cell);
                    }
                }
                Ext.resumeLayouts(true);
                
                
                for (id in oldWidgetMap) {
                    widget = oldWidgetMap[id];
                    widget.$widgetRecord = widget.$widgetColumn = null;
                    me.freeWidgetStack.unshift(widget);
                    Ext.detachedBodyEl.dom.appendChild((widget.el || widget.element).dom);
                }
            }
        },
        setupViewListeners: function(view) {
            var me = this;
            me.viewListeners = view.on({
                refresh: me.onViewRefresh,
                itemupdate: me.onItemUpdate,
                itemadd: me.onItemAdd,
                itemremove: me.onItemRemove,
                scope: me,
                destroyable: true
            });
            if (Ext.isIE8) {
                view.on('beforerefresh', me.onBeforeRefresh, me);
            }
        },
        updateWidget: function(record) {
            var dataIndex = this.dataIndex,
                widget;
            if (this.rendered) {
                widget = this.liveWidgets[record.internalId];
                
                if (widget && widget.defaultBindProperty && dataIndex) {
                    widget.setConfig(widget.defaultBindProperty, record.get(dataIndex));
                }
            }
        },
        widgetRecordDecorator: function() {
            return this.$widgetRecord;
        },
        widgetColumnDecorator: function() {
            return this.$widgetColumn;
        }
    }
});


Ext.define('Ext.grid.feature.Feature', {
    extend: 'Ext.util.Observable',
    alias: 'feature.feature',
    wrapsItem: false,
    
    isFeature: true,
    
    disabled: false,
    
    hasFeatureEvent: true,
    
    eventPrefix: null,
    
    eventSelector: null,
    
    view: null,
    
    grid: null,
    constructor: function(config) {
        this.initialConfig = config;
        this.callParent(arguments);
    },
    clone: function() {
        return new this.self(this.initialConfig);
    },
    init: Ext.emptyFn,
    destroy: function() {
        this.clearListeners();
    },
    
    getFireEventArgs: function(eventName, view, featureTarget, e) {
        return [
            eventName,
            view,
            featureTarget,
            e
        ];
    },
    vetoEvent: Ext.emptyFn,
    
    enable: function() {
        this.disabled = false;
    },
    
    disable: function() {
        this.disabled = true;
    }
});


Ext.define('Ext.grid.feature.AbstractSummary', {
    extend: 'Ext.grid.feature.Feature',
    alias: 'feature.abstractsummary',
    summaryRowCls: Ext.baseCSSPrefix + 'grid-row-summary',
    summaryRowSelector: '.' + Ext.baseCSSPrefix + 'grid-row-summary',
    readDataOptions: {
        recordCreator: Ext.identityFn
    },
    
    
    summaryRowTpl: {
        fn: function(out, values, parent) {
            
            if (values.record.isSummary && this.summaryFeature.showSummaryRow) {
                this.summaryFeature.outputSummaryRecord(values.record, values, out, parent);
            } else {
                this.nextTpl.applyOut(values, out, parent);
            }
        },
        priority: 1000
    },
    
    showSummaryRow: true,
    
    init: function() {
        var me = this;
        me.view.summaryFeature = me;
        me.rowTpl = me.view.self.prototype.rowTpl;
        
        
        me.view.addRowTpl(me.summaryRowTpl).summaryFeature = me;
        
        me.summaryData = {};
        me.groupInfo = {};
        
        
        if (!me.summaryTableCls) {
            me.summaryTableCls = Ext.baseCSSPrefix + 'grid-item';
        }
    },
    
    toggleSummaryRow: function(visible, 
    fromLockingPartner) {
        var me = this,
            prev = me.showSummaryRow,
            doRefresh;
        visible = visible != null ? !!visible : !me.showSummaryRow;
        me.showSummaryRow = visible;
        if (visible && visible !== prev) {
            
            
            me.updateNext = true;
        }
        
        
        if (me.lockingPartner) {
            if (!fromLockingPartner) {
                me.lockingPartner.toggleSummaryRow(visible, true);
                doRefresh = true;
            }
        } else {
            doRefresh = true;
        }
        if (doRefresh) {
            me.grid.ownerGrid.getView().refresh();
        }
    },
    createRenderer: function(column, record) {
        var me = this,
            ownerGroup = record.ownerGroup,
            summaryData = ownerGroup ? me.summaryData[ownerGroup] : me.summaryData,
            
            dataIndex = column.dataIndex || column.getItemId();
        return function(value, metaData) {
            return column.summaryRenderer ? column.summaryRenderer(record.data[dataIndex], summaryData, dataIndex, metaData) : 
            record.data[dataIndex];
        };
    },
    outputSummaryRecord: function(summaryRecord, contextValues, out) {
        var view = contextValues.view,
            savedRowValues = view.rowValues,
            columns = contextValues.columns || view.headerCt.getVisibleGridColumns(),
            colCount = columns.length,
            i, column,
            
            
            values = {
                view: view,
                record: summaryRecord,
                rowStyle: '',
                rowClasses: [
                    this.summaryRowCls
                ],
                itemClasses: [],
                recordIndex: -1,
                rowId: view.getRowId(summaryRecord),
                columns: columns
            };
        
        for (i = 0; i < colCount; i++) {
            column = columns[i];
            column.savedRenderer = column.renderer;
            if (column.summaryType || column.summaryRenderer) {
                column.renderer = this.createRenderer(column, summaryRecord);
            } else {
                column.renderer = Ext.emptyFn;
            }
        }
        
        view.rowValues = values;
        view.self.prototype.rowTpl.applyOut(values, out, parent);
        view.rowValues = savedRowValues;
        
        for (i = 0; i < colCount; i++) {
            column = columns[i];
            column.renderer = column.savedRenderer;
            column.savedRenderer = null;
        }
    },
    
    getSummary: function(store, type, field, group) {
        var isGrouped = !!group,
            item = isGrouped ? group : store;
        if (type) {
            if (Ext.isFunction(type)) {
                if (isGrouped) {
                    return item.aggregate(field, type);
                } else {
                    return item.aggregate(type, null, false, [
                        field
                    ]);
                }
            }
            switch (type) {
                case 'count':
                    return item.count(field);
                case 'min':
                    return item.min(field);
                case 'max':
                    return item.max(field);
                case 'sum':
                    return item.sum(field);
                case 'average':
                    return item.average(field);
                default:
                    return '';
            }
        }
    },
    generateSummaryData: function(groupField) {
        var me = this,
            reader = me.view.store.getProxy().getReader(),
            convertedSummaryRow = {},
            remoteData = {},
            i, len, root, summaryRows;
        
        root = reader.getRootProperty();
        reader.setRootProperty(me.remoteRoot);
        reader.buildExtractors(true);
        summaryRows = reader.getRoot(reader.rawData);
        if (summaryRows) {
            if (!Ext.isArray(summaryRows)) {
                summaryRows = [
                    summaryRows
                ];
            }
            len = summaryRows.length;
            for (i = 0; i < len; ++i) {
                
                convertedSummaryRow = reader.extractRecordData(summaryRows[i], me.readDataOptions);
                if (groupField) {
                    remoteData[convertedSummaryRow[groupField]] = convertedSummaryRow;
                }
            }
        }
        
        reader.setRootProperty(root);
        reader.buildExtractors(true);
        return groupField ? remoteData : convertedSummaryRow;
    },
    setSummaryData: function(record, colId, summaryValue, groupName) {
        var summaryData = this.summaryData;
        if (groupName) {
            if (!summaryData[groupName]) {
                summaryData[groupName] = {};
            }
            summaryData[groupName][colId] = summaryValue;
        } else {
            summaryData[colId] = summaryValue;
        }
    },
    populateRecord: function(group, groupInfo, remoteData) {
        var me = this,
            view = me.grid.ownerLockable ? me.grid.ownerLockable.view : me.view,
            store = me.view.getStore(),
            record = me.getAggregateRecord(group),
            
            columns = view.headerCt.getGridColumns(),
            len = columns.length,
            groupName = group.getGroupKey(),
            groupData, field, i, column, fieldName, summaryValue;
        record.beginEdit();
        if (remoteData) {
            
            
            
            
            groupData = remoteData[groupName];
            for (field in groupData) {
                if (groupData.hasOwnProperty(field)) {
                    if (field !== record.idProperty) {
                        record.set(field, groupData[field]);
                    }
                }
            }
        }
        
        
        
        
        for (i = 0; i < len; ++i) {
            column = columns[i];
            
            fieldName = column.dataIndex || column.getItemId();
            
            
            if (!remoteData) {
                summaryValue = me.getSummary(store, column.summaryType, fieldName, group);
                record.set(fieldName, summaryValue);
            } else {
                
                summaryValue = record.get(column.dataIndex);
            }
            
            me.setSummaryData(record, column.getItemId(), summaryValue, groupName);
        }
        
        record.ownerGroup = groupName;
        record.endEdit(true);
        record.commit();
        return record;
    },
    getGroupInfo: function(group) {
        var groupInfo = this.groupInfo,
            key = group.getGroupKey(),
            item = groupInfo[key];
        if (!item) {
            item = groupInfo[key] = {
                lastGeneration: null,
                aggregateRecord: new Ext.data.Model()
            };
        }
        return item;
    },
    getAggregateRecord: function(group, forceNew) {
        var rec;
        if (forceNew === true || group.dirty || !group.aggregateRecord) {
            rec = new Ext.data.Model();
            group.aggregateRecord = rec;
            rec.isNonData = rec.isSummary = true;
        }
        return group.aggregateRecord;
    }
});


Ext.define('Ext.grid.feature.GroupStore', {
    extend: 'Ext.util.Observable',
    isStore: true,
    
    defaultViewSize: 100,
    
    
    isFeatureStore: true,
    constructor: function(groupingFeature, store) {
        var me = this;
        me.callParent();
        me.groupingFeature = groupingFeature;
        me.bindStore(store);
    },
    bindStore: function(store) {
        var me = this;
        if (!store || me.store !== store) {
            Ext.destroy(me.storeListeners);
            me.store = null;
        }
        if (store) {
            me.storeListeners = store.on({
                groupchange: me.onGroupChange,
                remove: me.onRemove,
                add: me.onAdd,
                idchanged: me.onIdChanged,
                update: me.onUpdate,
                refresh: me.onRefresh,
                clear: me.onClear,
                scope: me,
                destroyable: true
            });
            me.store = store;
            me.processStore(store);
        }
    },
    processStore: function(store) {
        var me = this,
            groups = store.getGroups(),
            groupCount = groups ? groups.length : 0,
            i, group, groupPlaceholder,
            data = me.data,
            oldGroupCache = me.groupingFeature.groupCache,
            groupCache = me.groupingFeature.clearGroupCache(),
            collapseAll = me.groupingFeature.startCollapsed,
            groupField = store.getGroupField(),
            key, modelData, Model;
        if (data) {
            data.clear();
        } else {
            data = me.data = new Ext.util.Collection({
                rootProperty: 'data',
                extraKeys: {
                    byInternalId: {
                        property: 'internalId',
                        rootProperty: ''
                    }
                }
            });
        }
        if (store.getCount()) {
            
            me.groupingFeature.startCollapsed = false;
            if (groupCount > 0) {
                for (i = 0; i < groupCount; i++) {
                    group = groups.getAt(i);
                    
                    key = group.getGroupKey();
                    groupCache[key] = group;
                    group.isCollapsed = collapseAll || (oldGroupCache[key] && oldGroupCache[key].isCollapsed);
                    
                    
                    if (group.isCollapsed) {
                        Model = store.getModel();
                        modelData = {};
                        modelData[groupField] = key;
                        group.placeholder = groupPlaceholder = new Model(modelData);
                        groupPlaceholder.isNonData = groupPlaceholder.isCollapsedPlaceholder = true;
                        groupPlaceholder.group = group;
                        data.add(groupPlaceholder);
                    } else 
                    {
                        data.insert(me.data.length, group.items);
                    }
                }
            } else {
                data.add(store.getRange());
            }
        }
    },
    isCollapsed: function(name) {
        return this.groupingFeature.groupCache[name].isCollapsed;
    },
    isInCollapsedGroup: function(record) {
        var store = this.store,
            groupData;
        if (store.isGrouped() && (groupData = this.groupingFeature.groupCache[record.get(store.getGroupField())])) {
            return groupData.isCollapsed || false;
        }
        return false;
    },
    isLoading: function() {
        return false;
    },
    getData: function() {
        return this.data;
    },
    getCount: function() {
        return this.data.getCount();
    },
    getTotalCount: function() {
        return this.data.getCount();
    },
    
    rangeCached: function(start, end) {
        return end < this.getCount();
    },
    getRange: function(start, end, options) {
        
        var result = this.data.getRange(start, Ext.isNumber(end) ? end + 1 : end);
        if (options && options.callback) {
            options.callback.call(options.scope || this, result, start, end, options);
        }
        return result;
    },
    getAt: function(index) {
        return this.data.getAt(index);
    },
    
    getById: function(id) {
        return this.store.getById(id);
    },
    
    getByInternalId: function(internalId) {
        
        
        return this.store.getByInternalId(internalId) || this.data.byInternalId.get(internalId);
    },
    expandGroup: function(group) {
        var me = this,
            startIdx, items;
        if (typeof group === 'string') {
            group = me.groupingFeature.groupCache[group];
        }
        if (group) {
            items = group.items;
        }
        if (items.length && (startIdx = me.data.indexOf(group.placeholder)) !== -1) {
            
            group.isCollapsed = false;
            me.isExpandingOrCollapsing = 1;
            
            me.data.removeAt(startIdx);
            
            me.data.insert(startIdx, group.items);
            
            me.fireEvent('replace', me, startIdx, [
                group.placeholder
            ], group.items);
            me.fireEvent('groupexpand', me, group);
            me.isExpandingOrCollapsing = 0;
        }
    },
    collapseGroup: function(group) {
        var me = this,
            startIdx, placeholder, len, items;
        if (typeof group === 'string') {
            group = me.groupingFeature.groupCache[group];
        }
        if (group) {
            items = group.items;
        }
        if (items && (len = items.length) && (startIdx = me.data.indexOf(items[0])) !== -1) {
            
            group.isCollapsed = true;
            me.isExpandingOrCollapsing = 2;
            
            me.data.removeAt(startIdx, len);
            
            me.data.insert(startIdx, placeholder = me.getGroupPlaceholder(group));
            
            me.fireEvent('replace', me, startIdx, items, [
                placeholder
            ]);
            me.fireEvent('groupcollapse', me, group);
            me.isExpandingOrCollapsing = 0;
        }
    },
    getGroupPlaceholder: function(group) {
        if (!group.placeholder) {
            var store = this.store,
                Model = store.getModel(),
                modelData = {},
                key = group.getGroupKey(),
                groupPlaceholder;
            modelData[store.getGroupField()] = key;
            groupPlaceholder = group.placeholder = new Model(modelData);
            groupPlaceholder.isNonData = groupPlaceholder.isCollapsedPlaceholder = true;
            groupPlaceholder.group = group;
        }
        return group.placeholder;
    },
    
    
    indexOf: function(record) {
        if (!record.isCollapsedPlaceholder) {
            return this.data.indexOf(record);
        }
        return -1;
    },
    
    indexOfId: function(id) {
        return this.data.indexOfKey(id);
    },
    
    indexOfTotal: function(record) {
        return this.store.indexOf(record);
    },
    onRefresh: function(store) {
        this.processStore(this.store);
        this.fireEvent('refresh', this);
    },
    onRemove: function(store, records, index, isMove) {
        var me = this;
        
        
        if (store.isMoving()) {
            return;
        }
        me.processStore(me.store);
        me.fireEvent('refresh', me);
    },
    onClear: function(store, records, startIndex) {
        var me = this;
        me.processStore(me.store);
        me.fireEvent('clear', me);
    },
    onAdd: function(store, records, startIndex) {
        var me = this;
        me.processStore(me.store);
        
        
        me.fireEvent('replace', me, me.indexOf(records[0]), [], records);
    },
    onIdChanged: function(store, rec, oldId, newId) {
        this.data.updateKey(rec, oldId);
    },
    onUpdate: function(store, record, operation, modifiedFieldNames) {
        var me = this,
            groupInfo, firstRec, lastRec, items;
        
        
        
        if (store.isGrouped()) {
            
            groupInfo = record.group = me.groupingFeature.getRecordGroup(record);
            if (modifiedFieldNames && Ext.Array.contains(modifiedFieldNames, me.groupingFeature.getGroupField())) {
                return me.onRefresh(me.store);
            }
            
            if (groupInfo.isCollapsed) {
                me.fireEvent('update', me, groupInfo.placeholder);
            } else 
            
            {
                Ext.suspendLayouts();
                
                me.fireEvent('update', me, record, operation, modifiedFieldNames);
                
                
                items = groupInfo.items;
                firstRec = items[0];
                lastRec = items[items.length - 1];
                
                
                
                if (firstRec !== record) {
                    firstRec.group = groupInfo;
                    me.fireEvent('update', me, firstRec, 'edit', modifiedFieldNames);
                    delete firstRec.group;
                }
                if (lastRec !== record && lastRec !== firstRec && me.groupingFeature.showSummaryRow) {
                    lastRec.group = groupInfo;
                    me.fireEvent('update', me, lastRec, 'edit', modifiedFieldNames);
                    delete lastRec.group;
                }
                Ext.resumeLayouts(true);
            }
            delete record.group;
        } else {
            
            me.fireEvent('update', me, record, operation, modifiedFieldNames);
        }
    },
    
    onGroupChange: function(store, grouper) {
        if (!grouper) {
            this.processStore(store);
        }
        this.fireEvent('groupchange', store, grouper);
    },
    destroy: function() {
        var me = this;
        me.bindStore(null);
        me.clearListeners();
        Ext.destroyMembers(me, 'data', 'groupingFeature');
    }
});


Ext.define('Ext.grid.feature.Grouping', {
    extend: 'Ext.grid.feature.Feature',
    mixins: {
        summary: 'Ext.grid.feature.AbstractSummary'
    },
    requires: [
        'Ext.grid.feature.GroupStore'
    ],
    alias: 'feature.grouping',
    eventPrefix: 'group',
    eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd',
    refreshData: {},
    groupInfo: {},
    wrapsItem: true,
    
    
    
    
    
    
    groupHeaderTpl: '{columnName}: {name}',
    
    depthToIndent: 17,
    collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed',
    hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed',
    hdNotCollapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-not-collapsible',
    collapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsible',
    ctCls: Ext.baseCSSPrefix + 'group-hd-container',
    
    
    groupByText: 'Group by this field',
    
    
    
    showGroupsText: 'Show in groups',
    
    
    hideGroupedHeader: false,
    
    startCollapsed: false,
    
    enableGroupingMenu: true,
    
    enableNoGroups: true,
    
    collapsible: true,
    
    expandTip: 'Click to expand. CTRL key collapses all others',
    
    
    collapseTip: 'Click to collapse. CTRL/click collapses all others',
    
    showSummaryRow: false,
    outerTpl: [
        '{%',
        
        'if (!(this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary)) {',
        'this.groupingFeature.setup(values.rows, values.view.rowValues);',
        '}',
        
        'this.nextTpl.applyOut(values, out, parent);',
        
        'if (!(this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary)) {',
        'this.groupingFeature.cleanup(values.rows, values.view.rowValues);',
        '}',
        '%}',
        {
            priority: 200
        }
    ],
    groupRowTpl: [
        '{%',
        'var me = this.groupingFeature,',
        'colspan = "colspan=" + values.columns.length;',
        
        'if (me.disabled || parent.rows.length === 1 && parent.rows[0].isSummary) {',
        'values.needsWrap = false;',
        '} else {',
        
        'me.setupRowData(values.record, values.rowIndex, values);',
        '}',
        '%}',
        '<tpl if="needsWrap">',
        '<tpl if="isFirstRow">',
        
        
        '{% values.view.renderColumnSizer(values, out); %}',
        '<tr data-boundView="{view.id}" data-recordId="{record.internalId:htmlEncode}" data-recordIndex="{[values.isCollapsedGroup ? -1 : values.recordIndex]}" class="{groupHeaderCls}">',
        '<td class="{[me.ctCls]}" {[colspan]}>',
        '{%',
        
        
        'var groupTitleStyle = (!values.view.lockingPartner || (values.view.ownerCt === values.view.ownerCt.ownerLockable.lockedGrid) || (values.view.lockingPartner.headerCt.getVisibleGridColumns().length === 0)) ? "" : "visibility:hidden";',
        '%}',
        
        
        '<div data-groupname="{groupName:htmlEncode}" class="',
        Ext.baseCSSPrefix,
        'grid-group-hd {collapsibleCls}" nottabindex="0" hidefocus="on" {ariaCellInnerAttr}>',
        '<div class="',
        Ext.baseCSSPrefix,
        'grid-group-title" style="{[groupTitleStyle]}" {ariaGroupTitleAttr}>',
        '{[values.groupHeaderTpl.apply(values.groupInfo, parent) || "&#160;"]}',
        '</div>',
        '</div>',
        '</td>',
        '</tr>',
        '</tpl>',
        
        '<tpl if="!isCollapsedGroup">',
        '{%',
        'values.itemClasses.length = 0;',
        'this.nextTpl.applyOut(values, out, parent);',
        '%}',
        '</tpl>',
        '<tpl if="summaryRecord">',
        '{%me.outputSummaryRecord(values.summaryRecord, values, out, parent);%}',
        '</tpl>',
        '<tpl else>',
        '{%this.nextTpl.applyOut(values, out, parent);%}',
        '</tpl>',
        {
            priority: 200,
            beginRowSync: function(rowSync) {
                var owner = this.owner;
                rowSync.add('header', owner.eventSelector);
                rowSync.add('summary', owner.summaryRowSelector);
            },
            syncContent: function(destRow, sourceRow, columnsToUpdate) {
                destRow = Ext.fly(destRow, 'syncDest');
                sourceRow = Ext.fly(sourceRow, 'sycSrc');
                var owner = this.owner,
                    destHd = destRow.down(owner.eventSelector, true),
                    sourceHd = sourceRow.down(owner.eventSelector, true),
                    destSummaryRow = destRow.down(owner.summaryRowSelector, true),
                    sourceSummaryRow = sourceRow.down(owner.summaryRowSelector, true);
                
                if (destHd && sourceHd) {
                    Ext.fly(destHd).syncContent(sourceHd);
                }
                
                if (destSummaryRow && sourceSummaryRow) {
                    
                    if (columnsToUpdate) {
                        this.groupingFeature.view.updateColumns(destSummaryRow, sourceSummaryRow, columnsToUpdate);
                    } else 
                    {
                        Ext.fly(destSummaryRow).syncContent(sourceSummaryRow);
                    }
                }
            }
        }
    ],
    constructor: function() {
        this.groupCache = {};
        this.callParent(arguments);
    },
    init: function(grid) {
        var me = this,
            view = me.view,
            store = view.getStore(),
            lockPartner, dataSource;
        view.isGrouping = !!store.getGrouper();
        
        if (me.lockingPartner && me.lockingPartner.groupCache) {
            me.groupCache = me.lockingPartner.groupCache;
        }
        me.mixins.summary.init.call(me);
        me.callParent(arguments);
        view.headerCt.on({
            columnhide: me.onColumnHideShow,
            columnshow: me.onColumnHideShow,
            columnmove: me.onColumnMove,
            scope: me
        });
        
        view.addTpl(Ext.XTemplate.getTpl(me, 'outerTpl')).groupingFeature = me;
        
        view.addRowTpl(Ext.XTemplate.getTpl(me, 'groupRowTpl')).groupingFeature = me;
        view.preserveScrollOnRefresh = true;
        
        if (store.isBufferedStore) {
            me.collapsible = false;
        } else 
        {
            
            lockPartner = me.lockingPartner;
            if (lockPartner && lockPartner.dataSource) {
                me.dataSource = view.dataSource = dataSource = lockPartner.dataSource;
            } else {
                me.dataSource = view.dataSource = dataSource = new Ext.grid.feature.GroupStore(me,store);
            }
        }
        grid = grid.ownerLockable || grid;
        
        grid.on({
            beforeReconfigure: me.beforeReconfigure,
            scope: me
        });
        view.on({
            afterrender: me.afterViewRender,
            scope: me,
            single: true
        });
        if (dataSource) {
            
            
            dataSource.on('groupchange', me.onGroupChange, me);
        } else {
            me.storeListeners = view.store.on({
                groupchange: me.onGroupChange,
                scope: me,
                destroyable: true
            });
        }
    },
    indexOf: function(record) {
        return this.dataSource.indexOf(record);
    },
    isInCollapsedGroup: function(record) {
        var groupData,
            store = this.view.getStore();
        if (store.isGrouped() && (groupData = this.getGroup(record))) {
            return groupData.isCollapsed || false;
        }
        return false;
    },
    clearGroupCache: function() {
        var me = this,
            groupCache = me.groupCache = {};
        if (me.lockingPartner) {
            me.lockingPartner.groupCache = groupCache;
        }
        return groupCache;
    },
    vetoEvent: function(record, row, rowIndex, e) {
        
        if (e.type !== 'mouseover' && e.type !== 'mouseout' && e.type !== 'mouseenter' && e.type !== 'mouseleave' && e.getTarget(this.eventSelector)) {
            return false;
        }
    },
    enable: function() {
        var me = this,
            view = me.view,
            store = view.getStore(),
            groupToggleMenuItem;
        view.isGrouping = true;
        if (view.lockingPartner) {
            view.lockingPartner.isGrouping = true;
        }
        me.callParent();
        if (me.lastGrouper) {
            store.group(me.lastGrouper);
            me.lastGrouper = null;
        }
        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
        if (groupToggleMenuItem) {
            groupToggleMenuItem.setChecked(true, true);
        }
    },
    disable: function() {
        var me = this,
            view = me.view,
            store = view.getStore(),
            groupToggleMenuItem,
            lastGrouper = store.getGrouper();
        view.isGrouping = false;
        if (view.lockingPartner) {
            view.lockingPartner.isGrouping = false;
        }
        me.callParent();
        if (lastGrouper) {
            me.lastGrouper = lastGrouper;
            store.clearGrouping();
        }
        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
        if (groupToggleMenuItem) {
            groupToggleMenuItem.setChecked(false, true);
        }
    },
    
    afterViewRender: function() {
        var me = this,
            view = me.view;
        view.on({
            scope: me,
            groupclick: me.onGroupClick
        });
        if (me.enableGroupingMenu) {
            me.injectGroupingMenu();
        }
        me.pruneGroupedHeader();
        me.lastGrouper = me.view.getStore().getGrouper();
        
        
        if (me.disabled) {
            me.disable();
        }
    },
    injectGroupingMenu: function() {
        var me = this,
            headerCt = me.view.headerCt;
        headerCt.showMenuBy = me.showMenuBy;
        headerCt.getMenuItems = me.getMenuItems();
    },
    onColumnHideShow: function(headerOwnerCt, header) {
        var view = this.view,
            headerCt = view.headerCt,
            menu = headerCt.getMenu(),
            activeHeader = menu.activeHeader,
            groupMenuItem = menu.down('#groupMenuItem'),
            groupMenuMeth,
            colCount = this.grid.getVisibleColumnManager().getColumns().length,
            items, len, i;
        
        if (activeHeader && groupMenuItem) {
            groupMenuMeth = activeHeader.groupable === false || !activeHeader.dataIndex || this.view.headerCt.getVisibleGridColumns().length < 2 ? 'disable' : 'enable';
            groupMenuItem[groupMenuMeth]();
        }
        
        
        if (view.rendered && colCount) {
            items = view.el.query('.' + this.ctCls);
            for (i = 0 , len = items.length; i < len; ++i) {
                items[i].colSpan = colCount;
            }
        }
    },
    
    
    onColumnMove: function() {
        var me = this,
            store = me.view.getStore(),
            groups, groupName, group, firstRec, lastRec;
        if (store.isGrouped()) {
            groups = me.groupCache;
            Ext.suspendLayouts();
            for (groupName in groups) {
                if (groups.hasOwnProperty(groupName)) {
                    group = groups[groupName];
                    firstRec = group.items[0];
                    lastRec = group.items[group.items.length - 1];
                    
                    
                    store.fireEvent('update', store, firstRec, 'edit', null);
                    if (lastRec !== firstRec && me.showSummaryRow) {
                        store.fireEvent('update', store, lastRec, 'edit', null);
                    }
                }
            }
            Ext.resumeLayouts(true);
        }
    },
    showMenuBy: function(clickEvent, t, header) {
        var menu = this.getMenu(),
            groupMenuItem = menu.down('#groupMenuItem'),
            groupMenuMeth = header.groupable === false || !header.dataIndex || this.view.headerCt.getVisibleGridColumns().length < 2 ? 'disable' : 'enable',
            groupToggleMenuItem = menu.down('#groupToggleMenuItem'),
            isGrouped = this.view.store.isGrouped();
        groupMenuItem[groupMenuMeth]();
        if (groupToggleMenuItem) {
            groupToggleMenuItem.setChecked(isGrouped, true);
            groupToggleMenuItem[isGrouped ? 'enable' : 'disable']();
        }
        Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);
    },
    getMenuItems: function() {
        var me = this,
            groupByText = me.groupByText,
            disabled = me.disabled || !me.getGroupField(),
            showGroupsText = me.showGroupsText,
            enableNoGroups = me.enableNoGroups,
            getMenuItems = me.view.headerCt.getMenuItems;
        
        return function() {
            
            
            var o = getMenuItems.call(this);
            o.push('-', {
                iconCls: Ext.baseCSSPrefix + 'group-by-icon',
                itemId: 'groupMenuItem',
                text: groupByText,
                handler: me.onGroupMenuItemClick,
                scope: me
            });
            if (enableNoGroups) {
                o.push({
                    itemId: 'groupToggleMenuItem',
                    text: showGroupsText,
                    checked: !disabled,
                    checkHandler: me.onGroupToggleMenuItemClick,
                    scope: me
                });
            }
            return o;
        };
    },
    
    onGroupMenuItemClick: function(menuItem, e) {
        var me = this,
            menu = menuItem.parentMenu,
            hdr = menu.activeHeader,
            view = me.view,
            store = view.store;
        if (me.disabled) {
            me.lastGrouper = null;
            me.block();
            me.enable();
            me.unblock();
        }
        view.isGrouping = true;
        store.group(hdr.dataIndex);
        me.pruneGroupedHeader();
    },
    block: function(fromPartner) {
        this.blockRefresh = this.view.blockRefresh = true;
        if (this.lockingPartner && !fromPartner) {
            this.lockingPartner.block(true);
        }
    },
    unblock: function(fromPartner) {
        this.blockRefresh = this.view.blockRefresh = false;
        if (this.lockingPartner && !fromPartner) {
            this.lockingPartner.unblock(true);
        }
    },
    
    onGroupToggleMenuItemClick: function(menuItem, checked) {
        this[checked ? 'enable' : 'disable']();
    },
    
    pruneGroupedHeader: function() {
        var me = this,
            header = me.getGroupedHeader();
        if (me.hideGroupedHeader && header) {
            Ext.suspendLayouts();
            if (me.prunedHeader && me.prunedHeader !== header) {
                me.prunedHeader.show();
            }
            me.prunedHeader = header;
            if (header.rendered) {
                header.hide();
            }
            Ext.resumeLayouts(true);
        }
    },
    getHeaderNode: function(groupName) {
        var el = this.view.getEl(),
            nodes, i, len, result, node;
        if (el) {
            groupName = Ext.htmlEncode(groupName);
            nodes = el.query(this.eventSelector);
            for (i = 0 , len = nodes.length; i < len; ++i) {
                node = nodes[i];
                if (node.getAttribute('data-groupName') === groupName) {
                    return node;
                }
            }
        }
    },
    getGroup: function(name) {
        if (name.isModel) {
            name = name.get(this.view.getStore().getGroupField());
        }
        var cache = this.groupCache,
            item = cache[name];
        if (!item) {
            item = cache[name] = {
                isCollapsed: false
            };
        }
        return item;
    },
    
    isExpanded: function(groupName) {
        return !this.getGroup(groupName).isCollapsed;
    },
    
    expand: function(groupName, focus) {
        this.doCollapseExpand(false, groupName, focus);
    },
    
    expandAll: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName,
            lockingPartner = me.lockingPartner;
        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                groupCache[groupName].isCollapsed = false;
            }
        }
        Ext.suspendLayouts();
        me.dataSource.onRefresh();
        Ext.resumeLayouts(true);
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                me.afterCollapseExpand(false, groupName);
                if (lockingPartner) {
                    lockingPartner.afterCollapseExpand(false, groupName);
                }
            }
        }
    },
    
    collapse: function(groupName, focus) {
        this.doCollapseExpand(true, groupName, focus);
    },
    
    
    isAllCollapsed: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName;
        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                if (!groupCache[groupName].isCollapsed) {
                    return false;
                }
            }
        }
        return true;
    },
    
    
    isAllExpanded: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName;
        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                if (groupCache[groupName].isCollapsed) {
                    return false;
                }
            }
        }
        return true;
    },
    
    collapseAll: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName,
            lockingPartner = me.lockingPartner;
        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                groupCache[groupName].isCollapsed = true;
            }
        }
        Ext.suspendLayouts();
        me.dataSource.onRefresh();
        if (lockingPartner && !lockingPartner.isAllCollapsed()) {
            lockingPartner.collapseAll();
        }
        Ext.resumeLayouts(true);
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                me.afterCollapseExpand(true, groupName);
                if (lockingPartner) {
                    lockingPartner.afterCollapseExpand(true, groupName);
                }
            }
        }
    },
    doCollapseExpand: function(collapsed, groupName, focus) {
        var me = this,
            lockingPartner = me.lockingPartner,
            group = me.groupCache[groupName];
        
        if (group.isCollapsed !== collapsed) {
            
            
            Ext.suspendLayouts();
            if (collapsed) {
                me.dataSource.collapseGroup(group);
            } else {
                me.dataSource.expandGroup(group);
            }
            Ext.resumeLayouts(true);
            
            me.afterCollapseExpand(collapsed, groupName, focus);
            
            
            if (lockingPartner) {
                lockingPartner.afterCollapseExpand(collapsed, groupName, false);
            }
        }
    },
    afterCollapseExpand: function(collapsed, groupName, focus) {
        var me = this,
            view = me.view,
            header;
        header = me.getHeaderNode(groupName);
        view.fireEvent(collapsed ? 'groupcollapse' : 'groupexpand', view, header, groupName);
        if (focus) {
            if (header) {
                view.scrollElIntoView(Ext.fly(header).up(view.getItemSelector()), false, true);
            }
            
            else if (view.bufferedRenderer) {
                
                view.bufferedRenderer.scrollTo(me.dataSource.indexOf(me.getGroup(groupName).getAt(0)));
            }
        }
    },
    onGroupChange: function(store, grouper) {
        var me = this,
            ownerCt = me.grid.ownerCt,
            view = me.view;
        
        
        if (!grouper) {
            
            if (ownerCt && ownerCt.lockable) {
                ownerCt.view.refresh();
            } else {
                view.refresh();
            }
        } else {
            me.lastGrouper = grouper;
        }
    },
    
    getMenuItem: function(dataIndex) {
        var view = this.view,
            header = view.headerCt.down('gridcolumn[dataIndex=' + dataIndex + ']'),
            menu = view.headerCt.getMenu();
        return header ? menu.down('menuitem[headerId=' + header.id + ']') : null;
    },
    onGroupKey: function(keyCode, event) {
        var me = this,
            groupName = me.getGroupName(event.target);
        if (groupName) {
            me.onGroupClick(me.view, event.target, groupName, event);
        }
    },
    
    onGroupClick: function(view, rowElement, groupName, e) {
        var me = this,
            groupCache = me.groupCache,
            groupIsCollapsed = !me.isExpanded(groupName),
            g;
        if (me.collapsible) {
            
            if (e.ctrlKey) {
                Ext.suspendLayouts();
                for (g in groupCache) {
                    if (g === groupName) {
                        if (groupIsCollapsed) {
                            me.expand(groupName);
                        }
                    } else if (!groupCache[g].isCollapsed) {
                        me.doCollapseExpand(true, g, false);
                    }
                }
                Ext.resumeLayouts(true);
                return;
            }
            if (groupIsCollapsed) {
                me.expand(groupName);
            } else {
                me.collapse(groupName);
            }
        }
    },
    setupRowData: function(record, idx, rowValues) {
        var me = this,
            recordIndex = rowValues.recordIndex,
            data = me.refreshData,
            groupInfo = me.groupInfo,
            header = data.header,
            groupField = data.groupField,
            store = me.view.getStore(),
            dataSource = me.view.dataSource,
            grouper, groupName, prev, next, items;
        rowValues.isCollapsedGroup = false;
        rowValues.summaryRecord = rowValues.groupHeaderCls = null;
        if (data.doGrouping) {
            grouper = store.getGrouper();
            
            
            if (record.isCollapsedPlaceholder) {
                groupName = record.group.getGroupKey();
                items = record.group.items;
                rowValues.isFirstRow = rowValues.isLastRow = true;
                rowValues.groupHeaderCls = me.hdCollapsedCls;
                rowValues.isCollapsedGroup = rowValues.needsWrap = true;
                rowValues.groupName = groupName;
                rowValues.groupInfo = groupInfo;
                groupInfo.groupField = groupField;
                groupInfo.name = groupName;
                groupInfo.groupValue = items[0].get(groupField);
                groupInfo.columnName = header ? header.text : groupField;
                rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls;
                groupInfo.rows = groupInfo.children = items;
                if (me.showSummaryRow) {
                    rowValues.summaryRecord = data.summaryData[groupName];
                }
                return;
            }
            groupName = grouper.getGroupString(record);
            
            if (record.group) {
                items = record.group.items;
                rowValues.isFirstRow = record === items[0];
                rowValues.isLastRow = record === items[items.length - 1];
            } else {
                
                rowValues.isFirstRow = recordIndex === 0;
                if (!rowValues.isFirstRow) {
                    prev = store.getAt(recordIndex - 1);
                    
                    if (prev) {
                        
                        rowValues.isFirstRow = !prev.isEqual(grouper.getGroupString(prev), groupName);
                    }
                }
                
                rowValues.isLastRow = recordIndex == (store.isBufferedStore ? store.getTotalCount() : store.getCount()) - 1;
                if (!rowValues.isLastRow) {
                    next = store.getAt(recordIndex + 1);
                    if (next) {
                        
                        rowValues.isLastRow = !next.isEqual(grouper.getGroupString(next), groupName);
                    }
                }
            }
            if (rowValues.isFirstRow) {
                groupInfo.groupField = groupField;
                groupInfo.name = groupName;
                groupInfo.groupValue = record.get(groupField);
                groupInfo.columnName = header ? header.text : groupField;
                rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls;
                rowValues.groupName = groupName;
                if (!me.isExpanded(groupName)) {
                    rowValues.itemClasses.push(me.hdCollapsedCls);
                    rowValues.isCollapsedGroup = true;
                }
                
                if (dataSource.isBufferedStore) {
                    groupInfo.rows = groupInfo.children = [];
                } else {
                    groupInfo.rows = groupInfo.children = me.getRecordGroup(record).items;
                }
                rowValues.groupInfo = groupInfo;
            }
            if (rowValues.isLastRow) {
                
                if (me.showSummaryRow) {
                    rowValues.summaryRecord = data.summaryData[groupName];
                    rowValues.itemClasses.push(Ext.baseCSSPrefix + 'grid-group-last');
                }
            }
            rowValues.needsWrap = (rowValues.isFirstRow || rowValues.summaryRecord);
        }
    },
    setup: function(rows, rowValues) {
        var me = this,
            data = me.refreshData,
            view = rowValues.view,
            
            isGrouping = view.isGrouping = !me.disabled && view.store.isGrouped();
        me.skippedRows = 0;
        if (view.bufferedRenderer) {
            view.bufferedRenderer.variableRowHeight = view.bufferedRenderer.variableRowHeight || isGrouping;
        }
        data.groupField = me.getGroupField();
        data.header = me.getGroupedHeader(data.groupField);
        data.doGrouping = isGrouping;
        rowValues.groupHeaderTpl = Ext.XTemplate.getTpl(me, 'groupHeaderTpl');
        if (isGrouping && me.showSummaryRow) {
            data.summaryData = me.generateSummaryData();
        }
    },
    cleanup: function(rows, rowValues) {
        var data = this.refreshData;
        rowValues.groupInfo = rowValues.groupHeaderTpl = rowValues.isFirstRow = null;
        data.groupField = data.header = null;
    },
    
    generateSummaryData: function() {
        var me = this,
            store = me.view.store,
            groups = store.getGroups().items,
            reader = store.getProxy().getReader(),
            groupField = me.getGroupField(),
            lockingPartner = me.lockingPartner,
            updateNext = me.updateNext,
            data = {},
            i, len, group, groupInfo, record, hasRemote, remoteData;
        
        if (me.remoteRoot && reader.rawData) {
            hasRemote = true;
            remoteData = me.mixins.summary.generateSummaryData.call(me, groupField);
        }
        for (i = 0 , len = groups.length; i < len; ++i) {
            group = groups[i];
            groupInfo = me.getGroupInfo(group);
            
            if (updateNext || hasRemote || store.updating || groupInfo.lastGeneration !== group.generation) {
                record = me.populateRecord(group, groupInfo, remoteData);
                
                if (!lockingPartner || (me.view.ownerCt === me.view.ownerCt.ownerLockable.normalGrid)) {
                    groupInfo.lastGeneration = group.generation;
                }
            } else {
                record = me.getAggregateRecord(group);
            }
            data[group.getGroupKey()] = record;
        }
        me.updateNext = false;
        return data;
    },
    getGroupName: function(element) {
        var me = this,
            view = me.view,
            eventSelector = me.eventSelector,
            parts, targetEl, row;
        
        targetEl = Ext.fly(element).findParent(eventSelector);
        if (!targetEl) {
            
            row = Ext.fly(element).findParent(view.itemSelector);
            if (row) {
                targetEl = row.down(eventSelector, true);
            }
        }
        if (targetEl) {
            return Ext.htmlDecode(targetEl.getAttribute('data-groupname'));
        }
    },
    
    getRecordGroup: function(record) {
        var grouper = this.view.getStore().getGrouper();
        if (grouper) {
            return this.groupCache[grouper.getGroupString(record)];
        }
    },
    getGroupField: function() {
        return this.view.store.getGroupField();
    },
    getGroupedHeader: function(groupField) {
        var me = this,
            headerCt = me.view.headerCt,
            partner = me.lockingPartner,
            selector, header;
        groupField = groupField || this.getGroupField();
        if (groupField) {
            selector = '[dataIndex=' + groupField + ']';
            header = headerCt.down(selector);
            
            if (!header && partner) {
                header = partner.view.headerCt.down(selector);
            }
        }
        return header || null;
    },
    getFireEventArgs: function(type, view, targetEl, e) {
        return [
            type,
            view,
            targetEl,
            this.getGroupName(targetEl),
            e
        ];
    },
    destroy: function() {
        var me = this,
            dataSource = me.dataSource;
        Ext.destroy(me.storeListeners);
        me.view = me.prunedHeader = me.grid = me.groupCache = me.dataSource = null;
        me.callParent();
        if (dataSource) {
            dataSource.bindStore(null);
            Ext.destroy(dataSource);
        }
    },
    beforeReconfigure: function(grid, store, columns, oldStore, oldColumns) {
        var me = this,
            view = me.view,
            dataSource = me.dataSource,
            bufferedStore;
        if (store && store !== oldStore) {
            bufferedStore = store.isBufferedStore;
            if (!dataSource) {
                Ext.destroy(me.storeListeners);
                me.storeListeners = store.on({
                    groupchange: me.onGroupChange,
                    scope: me,
                    destroyable: true
                });
            }
            
            if (bufferedStore !== oldStore.isBufferedStore) {
                Ext.Error.raise('Cannot reconfigure grouping switching between buffered and non-buffered stores');
            }
            view.isGrouping = !!store.getGrouper();
            dataSource.bindStore(store);
        }
    }
});


Ext.define('Ext.grid.feature.GroupingSummary', {
    extend: 'Ext.grid.feature.Grouping',
    alias: 'feature.groupingsummary',
    showSummaryRow: true,
    vetoEvent: function(record, row, rowIndex, e) {
        var result = this.callParent(arguments);
        if (result !== false && e.getTarget(this.summaryRowSelector)) {
            result = false;
        }
        return result;
    }
});


Ext.define('Ext.grid.feature.RowBody', {
    extend: 'Ext.grid.feature.Feature',
    alias: 'feature.rowbody',
    rowBodyCls: Ext.baseCSSPrefix + 'grid-row-body',
    rowBodyHiddenCls: Ext.baseCSSPrefix + 'grid-row-body-hidden',
    rowBodyTdSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell-rowbody',
    eventPrefix: 'rowbody',
    eventSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-rowbody-tr',
    colSpanDecrement: 0,
    
    bodyBefore: false,
    outerTpl: {
        fn: function(out, values, parent) {
            var view = values.view,
                rowValues = view.rowValues;
            this.rowBody.setup(values.rows, rowValues);
            this.nextTpl.applyOut(values, out, parent);
            this.rowBody.cleanup(values.rows, rowValues);
        },
        priority: 100
    },
    extraRowTpl: [
        '{%',
        'if(this.rowBody.bodyBefore) {',
        
        
        'values.view.renderColumnSizer(values, out);',
        '} else {',
        'this.nextTpl.applyOut(values, out, parent);',
        '}',
        'values.view.rowBodyFeature.setupRowData(values.record, values.recordIndex, values);',
        '%}',
        '<tr class="' + Ext.baseCSSPrefix + 'grid-rowbody-tr {rowBodyCls}" {ariaRowAttr}>',
        '<td class="' + Ext.baseCSSPrefix + 'grid-td ' + Ext.baseCSSPrefix + 'grid-cell-rowbody" colspan="{rowBodyColspan}" {ariaCellAttr}>',
        '<div class="' + Ext.baseCSSPrefix + 'grid-rowbody {rowBodyDivCls}" {ariaCellInnerAttr}>{rowBody}</div>',
        '</td>',
        '</tr>',
        '{%',
        'if(this.rowBody.bodyBefore) {',
        'this.nextTpl.applyOut(values, out, parent);',
        '}',
        '%}',
        {
            priority: 100,
            beginRowSync: function(rowSync) {
                rowSync.add('rowBody', this.owner.eventSelector);
            },
            syncContent: function(destRow, sourceRow, columnsToUpdate) {
                var owner = this.owner,
                    destRowBody = Ext.fly(destRow).down(owner.eventSelector, true),
                    sourceRowBody;
                
                if (destRowBody && (sourceRowBody = Ext.fly(sourceRow).down(owner.eventSelector, true))) {
                    Ext.fly(destRowBody).syncContent(sourceRowBody);
                }
            }
        }
    ],
    init: function(grid) {
        var me = this,
            view = me.view = grid.getView();
        
        grid.variableRowHeight = view.variableRowHeight = true;
        view.rowBodyFeature = me;
        grid.mon(view, {
            element: 'el',
            click: me.onClick,
            scope: me
        });
        view.headerCt.on({
            columnschanged: me.onColumnsChanged,
            scope: me
        });
        view.addTpl(me.outerTpl).rowBody = me;
        view.addRowTpl(Ext.XTemplate.getTpl(this, 'extraRowTpl')).rowBody = me;
        me.callParent(arguments);
    },
    
    onClick: function(e) {
        var me = this,
            tableRow = e.getTarget(me.eventSelector);
        
        
        if (tableRow && Ext.fly(tableRow = (tableRow.previousSibling || tableRow.nextSibling)).is(me.view.rowSelector)) {
            e.target = tableRow;
            me.view.handleEvent(e);
        }
    },
    getSelectedRow: function(view, rowIndex) {
        var selectedRow = view.getNode(rowIndex);
        if (selectedRow) {
            return Ext.fly(selectedRow).down(this.eventSelector);
        }
        return null;
    },
    
    onColumnsChanged: function(headerCt) {
        var items = this.view.el.query(this.rowBodyTdSelector),
            colspan = headerCt.getVisibleGridColumns().length,
            len = items.length,
            i;
        for (i = 0; i < len; ++i) {
            items[i].setAttribute('colSpan', colspan);
        }
    },
    
    setupRowData: function(record, rowIndex, rowValues) {
        if (this.getAdditionalData) {
            Ext.apply(rowValues, this.getAdditionalData(record.data, rowIndex, record, rowValues));
        }
    },
    setup: function(rows, rowValues) {
        rowValues.rowBodyCls = this.rowBodyCls;
        rowValues.rowBodyColspan = this.view.headerCt.visibleColumnManager.getColumns().length - this.colSpanDecrement;
    },
    cleanup: function(rows, rowValues) {
        rowValues.rowBodyCls = rowValues.rowBodyColspan = rowValues.rowBody = null;
    }
});


Ext.define('Ext.grid.feature.Summary', {
    
    extend: 'Ext.grid.feature.AbstractSummary',
    alias: 'feature.summary',
    
    dock: undefined,
    dockedSummaryCls: Ext.baseCSSPrefix + 'docked-summary',
    summaryItemCls: Ext.baseCSSPrefix + 'grid-item-summary',
    panelBodyCls: Ext.baseCSSPrefix + 'summary-',
    scrollPadProperty: 'padding-right',
    
    hasFeatureEvent: false,
    init: function(grid) {
        var me = this,
            view = me.view;
        me.callParent(arguments);
        if (me.dock) {
            grid.headerCt.on({
                add: me.onStoreUpdate,
                afterlayout: me.onStoreUpdate,
                scope: me
            });
            grid.on({
                beforerender: function() {
                    var tableCls = [
                            me.summaryTableCls
                        ];
                    if (view.columnLines) {
                        tableCls[tableCls.length] = view.ownerCt.colLinesCls;
                    }
                    me.summaryBar = grid.addDocked({
                        childEls: [
                            'innerCt',
                            'item'
                        ],
                        renderTpl: [
                            '<div id="{id}-innerCt" data-ref="innerCt" role="presentation">',
                            '<table id="{id}-item" data-ref="item" cellPadding="0" cellSpacing="0" class="' + tableCls.join(' ') + '">',
                            '<tr class="' + me.summaryRowCls + '"></tr>',
                            '</table>',
                            '</div>'
                        ],
                        scrollable: {
                            x: false,
                            y: false
                        },
                        hidden: !me.showSummaryRow,
                        itemId: 'summaryBar',
                        cls: [
                            me.dockedSummaryCls,
                            me.dockedSummaryCls + '-' + me.dock
                        ],
                        xtype: 'component',
                        dock: me.dock,
                        weight: 10000000
                    })[0];
                },
                afterrender: function() {
                    grid.body.addCls(me.panelBodyCls + me.dock);
                    view.on('scroll', me.onViewScroll, me);
                    me.onStoreUpdate();
                },
                single: true
            });
            
            grid.headerCt.afterComponentLayout = Ext.Function.createSequence(grid.headerCt.afterComponentLayout, function() {
                var width = this.getTableWidth(),
                    innerCt = me.summaryBar.innerCt;
                me.summaryBar.item.setWidth(width);
                
                
                if (this.tooNarrow) {
                    width += Ext.getScrollbarSize().width;
                }
                innerCt.setWidth(width);
            });
        } else {
            me.view.addFooterFn(me.renderSummaryRow);
        }
        grid.on({
            columnmove: me.onStoreUpdate,
            scope: me
        });
        
        view.mon(view.store, {
            update: me.onStoreUpdate,
            datachanged: me.onStoreUpdate,
            scope: me
        });
    },
    renderSummaryRow: function(values, out, parent) {
        var view = values.view,
            me = view.findFeature('summary'),
            record;
        if (me.showSummaryRow) {
            record = me.summaryRecord;
            out.push('<table class="' + Ext.baseCSSPrefix + 'table-plain ' + me.summaryItemCls + '">');
            me.outputSummaryRecord((record && record.isModel) ? record : me.createSummaryRecord(view), values, out, parent);
            out.push('</table>');
        }
    },
    toggleSummaryRow: function(visible, 
    fromLockingPartner) {
        var me = this,
            bar = me.summaryBar;
        me.callParent([
            visible,
            fromLockingPartner
        ]);
        if (bar) {
            bar.setVisible(me.showSummaryRow);
            me.onViewScroll();
        }
    },
    getSummaryBar: function() {
        return this.summaryBar;
    },
    vetoEvent: function(record, row, rowIndex, e) {
        return !e.getTarget(this.summaryRowSelector);
    },
    onViewScroll: function() {
        this.summaryBar.setScrollX(this.view.getScrollX());
    },
    createSummaryRecord: function(view) {
        var me = this,
            columns = view.headerCt.getVisibleGridColumns(),
            remoteRoot = me.remoteRoot,
            summaryRecord = me.summaryRecord,
            colCount = columns.length,
            i, column, dataIndex, summaryValue, modelData;
        if (!summaryRecord) {
            modelData = {
                id: view.id + '-summary-record'
            };
            summaryRecord = me.summaryRecord = new Ext.data.Model(modelData);
        }
        
        summaryRecord.beginEdit();
        if (remoteRoot && view.store.proxy.reader.rawData) {
            summaryRecord.set(me.generateSummaryData());
        } else if (!remoteRoot) {
            for (i = 0; i < colCount; i++) {
                column = columns[i];
                
                
                dataIndex = column.dataIndex || column.getItemId();
                
                
                summaryValue = me.getSummary(view.store, column.summaryType, dataIndex);
                summaryRecord.set(dataIndex, summaryValue);
                
                me.setSummaryData(summaryRecord, column.getItemId(), summaryValue);
            }
        }
        summaryRecord.endEdit(true);
        
        summaryRecord.commit(true);
        summaryRecord.isSummary = true;
        return summaryRecord;
    },
    onStoreUpdate: function() {
        var me = this,
            view = me.view,
            record = me.createSummaryRecord(view),
            newRowDom = view.createRowElement(record, -1).firstChild.firstChild,
            oldRowDom, p;
        if (!view.rendered) {
            return;
        }
        
        if (me.dock) {
            p = me.summaryBar.item.dom.firstChild;
            oldRowDom = p.firstChild;
        } else 
        
        {
            oldRowDom = me.view.getRow(record);
            p = oldRowDom ? oldRowDom.parentNode : null;
        }
        if (p) {
            p.insertBefore(newRowDom, oldRowDom);
            p.removeChild(oldRowDom);
        }
        
        if (me.dock) {
            me.onColumnHeaderLayout();
        }
    },
    
    onColumnHeaderLayout: function() {
        var view = this.view,
            columns = view.headerCt.getVisibleGridColumns(),
            column,
            len = columns.length,
            i,
            summaryEl = this.summaryBar.el,
            el;
        for (i = 0; i < len; i++) {
            column = columns[i];
            el = summaryEl.down(view.getCellSelector(column));
            if (el) {
                el.setWidth(column.width || (column.lastBox ? column.lastBox.width : 100));
            }
        }
    }
});


Ext.define('Ext.menu.Item', {
    extend: 'Ext.Component',
    alias: 'widget.menuitem',
    alternateClassName: 'Ext.menu.TextItem',
    
    isMenuItem: true,
    mixins: [
        'Ext.mixin.Queryable'
    ],
    
    
    
    activeCls: Ext.baseCSSPrefix + 'menu-item-active',
    
    ariaRole: 'menuitem',
    
    
    clickHideDelay: 0,
    
    destroyMenu: true,
    
    disabledCls: Ext.baseCSSPrefix + 'menu-item-disabled',
    
    
    
    hideOnClick: true,
    
    
    
    
    
    
    menuAlign: 'tl-tr?',
    
    menuExpandDelay: 200,
    
    menuHideDelay: 200,
    
    
    
    tooltipType: 'qtip',
    focusable: true,
    baseCls: Ext.baseCSSPrefix + 'menu-item',
    arrowCls: Ext.baseCSSPrefix + 'menu-item-arrow',
    baseIconCls: Ext.baseCSSPrefix + 'menu-item-icon',
    textCls: Ext.baseCSSPrefix + 'menu-item-text',
    indentCls: Ext.baseCSSPrefix + 'menu-item-indent',
    indentNoSeparatorCls: Ext.baseCSSPrefix + 'menu-item-indent-no-separator',
    indentRightIconCls: Ext.baseCSSPrefix + 'menu-item-indent-right-icon',
    indentRightArrowCls: Ext.baseCSSPrefix + 'menu-item-indent-right-arrow',
    linkCls: Ext.baseCSSPrefix + 'menu-item-link',
    linkHrefCls: Ext.baseCSSPrefix + 'menu-item-link-href',
    childEls: [
        'itemEl',
        'iconEl',
        'textEl',
        'arrowEl'
    ],
    renderTpl: '<tpl if="plain">' + '{text}' + '<tpl else>' + '<a id="{id}-itemEl" data-ref="itemEl"' + ' class="{linkCls}<tpl if="hasHref"> {linkHrefCls}</tpl>{childElCls}"' + ' href="{href}" role="menuitem" ' + '<tpl if="hrefTarget"> target="{hrefTarget}"</tpl>' + ' hidefocus="true"' + 
    ' unselectable="on"' + '<tpl if="tabIndex != null">' + ' tabindex="{tabIndex}"' + '</tpl>' + '>' + '<span id="{id}-textEl" data-ref="textEl" class="{textCls} {textCls}-{ui} {indentCls}{childElCls}" unselectable="on">{text}</span>' + '<tpl if="hasIcon">' + '<div role="presentation" id="{id}-iconEl" data-ref="iconEl" class="{baseIconCls}-{ui} {baseIconCls}' + '{[values.rightIcon ? "-right" : ""]} {iconCls}' + '{childElCls} {glyphCls}" style="<tpl if="icon">background-image:url({icon});</tpl>' + '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">' + '<tpl if="glyph">&#{glyph};</tpl>' + '</div>' + '</tpl>' + '<tpl if="showCheckbox">' + '<div role="presentation" id="{id}-checkEl" data-ref="checkEl" class="{baseIconCls}-{ui} {baseIconCls}' + '{[(values.hasIcon && !values.rightIcon) ? "-right" : ""]} ' + '{groupCls} {checkboxCls}{childElCls}">' + '</div>' + '</tpl>' + '<tpl if="hasMenu">' + '<div role="presentation" id="{id}-arrowEl" data-ref="arrowEl" class="{arrowCls} {arrowCls}-{ui}{childElCls}"></div>' + '</tpl>' + '</a>' + '</tpl>',
    maskOnDisable: false,
    iconAlign: 'left',
    
    
    
    
    
    
    
    canFocus: function() {
        var me = this;
        
        
        
        
        
        return me.focusable && me.rendered && me.canActivate !== false && !me.destroying && !me.isDestroyed && me.isVisible(true);
    },
    onFocus: function(e) {
        var me = this;
        me.callParent([
            e
        ]);
        if (!me.plain) {
            me.addCls(me.activeCls);
        }
        me.activated = true;
        if (me.hasListeners.activate) {
            me.fireEvent('activate', me);
        }
    },
    onFocusLeave: function(e) {
        var me = this;
        me.callParent([
            e
        ]);
        if (me.activated) {
            if (!me.plain) {
                me.removeCls(me.activeCls);
            }
            me.doHideMenu();
            me.activated = false;
            if (me.hasListeners.deactivate) {
                me.fireEvent('deactivate', me);
            }
        }
    },
    doHideMenu: function() {
        var menu = this.menu;
        if (menu && menu.isVisible()) {
            menu.hide();
        }
    },
    cancelDeferHide: function() {
        clearTimeout(this.hideMenuTimer);
    },
    
    deferHideParentMenus: function() {
        var topMenu = this.getRefOwner(),
            owner;
        if (topMenu.floating) {
            topMenu.bubble(function(parent) {
                if (!parent.floating && !parent.isMenuItem) {
                    return false;
                }
                if (parent.isMenu) {
                    topMenu = parent;
                }
            });
            topMenu.hide();
        }
    },
    expandMenu: function(event, delay) {
        var me = this;
        if (me.menu) {
            
            me.hideOnClick = false;
            me.cancelDeferHide();
            if (delay === 0) {
                me.doExpandMenu(event);
            } else {
                clearTimeout(me.expandMenuTimer);
                
                me.expandMenuTimer = Ext.defer(me.doExpandMenu, delay || me.menuExpandDelay, me, [
                    event
                ]);
            }
        }
    },
    doExpandMenu: function(clickEvent) {
        var me = this,
            menu = me.menu;
        if (me.activated && (!menu.rendered || !menu.isVisible())) {
            me.parentMenu.activeChild = menu;
            menu.ownerCmp = me;
            menu.parentMenu = me.parentMenu;
            menu.constrainTo = document.body;
            
            menu.autoFocus = !clickEvent || !clickEvent.pointerType;
            menu.showBy(me, me.menuAlign);
        }
    },
    getRefItems: function(deep) {
        var menu = this.menu,
            items;
        if (menu) {
            items = menu.getRefItems(deep);
            items.unshift(menu);
        }
        return items || [];
    },
    getValue: function() {
        return this.value;
    },
    hideMenu: function(delay) {
        var me = this;
        if (me.menu) {
            clearTimeout(me.expandMenuTimer);
            me.hideMenuTimer = Ext.defer(me.doHideMenu, Ext.isNumber(delay) ? delay : me.menuHideDelay, me);
        }
    },
    initComponent: function() {
        var me = this,
            cls = me.cls ? [
                me.cls
            ] : [],
            menu;
        
        if (me.hasOwnProperty('canActivate')) {
            me.focusable = me.canActivate;
        }
        if (me.plain) {
            cls.push(Ext.baseCSSPrefix + 'menu-item-plain');
        }
        if (cls.length) {
            me.cls = cls.join(' ');
        }
        if (me.menu) {
            menu = me.menu;
            me.menu = null;
            me.setMenu(menu);
        }
        me.callParent(arguments);
    },
    onClick: function(e) {
        var me = this,
            clickHideDelay = me.clickHideDelay,
            browserEvent = e.browserEvent,
            clickResult, preventDefault;
        if (!me.href || me.disabled) {
            e.stopEvent();
            if (me.disabled) {
                return false;
            }
        }
        if (me.disabled || me.handlingClick) {
            return;
        }
        if (me.hideOnClick) {
            
            
            
            
            
            if (!clickHideDelay) {
                me.deferHideParentMenus();
            } else {
                me.deferHideParentMenusTimer = Ext.defer(me.deferHideParentMenus, clickHideDelay, me);
            }
        }
        
        clickResult = me.fireEvent('click', me, e);
        if (me.isDestroyed) {
            return;
        }
        if (clickResult !== false && me.handler) {
            Ext.callback(me.handler, me.scope, [
                me,
                e
            ], 0, me);
        }
        
        
        
        
        
        
        
        
        if (Ext.isIE9m) {
            
            preventDefault = browserEvent.returnValue === false ? true : false;
        } else {
            preventDefault = !!browserEvent.defaultPrevented;
        }
        
        if (me.href && e.type !== 'click' && !preventDefault) {
            me.handlingClick = true;
            me.itemEl.dom.click();
            me.handlingClick = false;
        }
        if (!me.hideOnClick) {
            me.focus();
        }
        return clickResult;
    },
    onRemoved: function() {
        var me = this;
        
        if (me.activated && me.parentMenu.activeItem === me) {
            me.parentMenu.deactivateActiveItem();
        }
        me.callParent(arguments);
        me.parentMenu = me.ownerCmp = null;
    },
    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.clearTip();
        }
        me.callParent();
    },
    onDestroy: function() {
        var me = this;
        clearTimeout(me.expandMenuTimer);
        me.cancelDeferHide();
        clearTimeout(me.deferHideParentMenusTimer);
        me.setMenu(null);
        me.callParent(arguments);
    },
    beforeRender: function() {
        var me = this,
            glyph = me.glyph,
            glyphFontFamily = Ext._glyphFontFamily,
            hasIcon = !!(me.icon || me.iconCls || glyph),
            hasMenu = !!me.menu,
            rightIcon = ((me.iconAlign === 'right') && !hasMenu),
            isCheckItem = me.isMenuCheckItem,
            indentCls = [],
            ownerCt = me.ownerCt,
            isOwnerPlain = ownerCt.plain,
            glyphParts;
        me.callParent();
        if (hasIcon) {
            if (hasMenu && me.showCheckbox) {
                
                
                hasIcon = false;
            }
        }
        if (typeof glyph === 'string') {
            glyphParts = glyph.split('@');
            glyph = glyphParts[0];
            glyphFontFamily = glyphParts[1];
        }
        if (!isOwnerPlain || (hasIcon && !rightIcon) || isCheckItem) {
            if (ownerCt.showSeparator && !isOwnerPlain) {
                indentCls.push(me.indentCls);
            } else {
                indentCls.push(me.indentNoSeparatorCls);
            }
        }
        if (hasMenu) {
            indentCls.push(me.indentRightArrowCls);
        } else if (hasIcon && (rightIcon || isCheckItem)) {
            indentCls.push(me.indentRightIconCls);
        }
        Ext.applyIf(me.renderData, {
            hasHref: !!me.href,
            href: me.href || '#',
            hrefTarget: me.hrefTarget,
            icon: me.icon,
            iconCls: me.iconCls,
            glyph: glyph,
            glyphCls: glyph ? Ext.baseCSSPrefix + 'menu-item-glyph' : undefined,
            glyphFontFamily: glyphFontFamily,
            hasIcon: hasIcon,
            hasMenu: hasMenu,
            indent: !isOwnerPlain || hasIcon || isCheckItem,
            isCheckItem: isCheckItem,
            rightIcon: rightIcon,
            plain: me.plain,
            text: me.text,
            arrowCls: me.arrowCls,
            baseIconCls: me.baseIconCls,
            textCls: me.textCls,
            indentCls: indentCls.join(' '),
            linkCls: me.linkCls,
            linkHrefCls: me.linkHrefCls,
            groupCls: me.group ? me.groupCls : '',
            tabIndex: me.tabIndex
        });
    },
    onRender: function() {
        var me = this;
        me.callParent(arguments);
        if (me.tooltip) {
            me.setTooltip(me.tooltip, true);
        }
    },
    
    setMenu: function(menu, destroyMenu) {
        var me = this,
            oldMenu = me.menu,
            arrowEl = me.arrowEl;
        if (oldMenu) {
            oldMenu.ownerCmp = oldMenu.parentMenu = null;
            if (destroyMenu === true || (destroyMenu !== false && me.destroyMenu)) {
                Ext.destroy(oldMenu);
            }
        }
        if (menu) {
            menu = me.menu = Ext.menu.Manager.get(menu, {
                ownerCmp: me,
                focusOnToFront: false
            });
            
            
            menu.ownerCmp = me;
        } else {
            menu = me.menu = null;
        }
        if (menu && me.rendered && !me.destroying && arrowEl) {
            arrowEl[menu ? 'addCls' : 'removeCls'](me.arrowCls);
        }
    },
    
    setHandler: function(fn, scope) {
        this.handler = fn || null;
        this.scope = scope;
    },
    
    setIcon: function(icon) {
        var iconEl = this.iconEl,
            oldIcon = this.icon;
        if (iconEl) {
            iconEl.src = icon || Ext.BLANK_IMAGE_URL;
        }
        this.icon = icon;
        this.fireEvent('iconchange', this, oldIcon, icon);
    },
    
    setIconCls: function(iconCls) {
        var me = this,
            iconEl = me.iconEl,
            oldCls = me.iconCls;
        if (iconEl) {
            if (me.iconCls) {
                iconEl.removeCls(me.iconCls);
            }
            if (iconCls) {
                iconEl.addCls(iconCls);
            }
        }
        me.iconCls = iconCls;
        me.fireEvent('iconchange', me, oldCls, iconCls);
    },
    
    setText: function(text) {
        var me = this,
            el = me.textEl || me.el,
            oldText = me.text;
        me.text = text;
        if (me.rendered) {
            el.setHtml(text || '');
            me.updateLayout();
        }
        me.fireEvent('textchange', me, oldText, text);
    },
    getTipAttr: function() {
        return this.tooltipType == 'qtip' ? 'data-qtip' : 'title';
    },
    
    clearTip: function() {
        if (Ext.quickTipsActive && Ext.isObject(this.tooltip)) {
            Ext.tip.QuickTipManager.unregister(this.itemEl);
        }
    },
    
    setTooltip: function(tooltip, initial) {
        var me = this;
        if (me.rendered) {
            if (!initial) {
                me.clearTip();
            }
            if (Ext.quickTipsActive && Ext.isObject(tooltip)) {
                Ext.tip.QuickTipManager.register(Ext.apply({
                    target: me.itemEl.id
                }, tooltip));
                me.tooltip = tooltip;
            } else {
                me.itemEl.dom.setAttribute(me.getTipAttr(), tooltip);
            }
        } else {
            me.tooltip = tooltip;
        }
        return me;
    },
    privates: {
        getFocusEl: function() {
            return this.itemEl;
        }
    }
});


Ext.define('Ext.menu.CheckItem', {
    extend: 'Ext.menu.Item',
    alias: 'widget.menucheckitem',
    
    
    
    
    
    checkedCls: Ext.baseCSSPrefix + 'menu-item-checked',
    
    uncheckedCls: Ext.baseCSSPrefix + 'menu-item-unchecked',
    
    groupCls: Ext.baseCSSPrefix + 'menu-group-icon',
    
    hideOnClick: false,
    
    checkChangeDisabled: false,
    ariaRole: 'menuitemcheckbox',
    childEls: [
        'checkEl'
    ],
    showCheckbox: true,
    isMenuCheckItem: true,
    checkboxCls: Ext.baseCSSPrefix + 'menu-item-checkbox',
    
    
    initComponent: function() {
        var me = this;
        
        me.checked = !!me.checked;
        me.callParent(arguments);
        if (me.group) {
            Ext.menu.Manager.registerCheckable(me);
            if (me.initialConfig.hideOnClick !== false) {
                me.hideOnClick = true;
            }
        }
    },
    beforeRender: function() {
        var me = this;
        me.callParent();
        Ext.apply(me.renderData, {
            checkboxCls: me.checkboxCls,
            showCheckbox: me.showCheckbox
        });
    },
    afterRender: function() {
        var me = this;
        me.callParent();
        me.checked = !me.checked;
        me.setChecked(!me.checked, true);
        if (me.checkChangeDisabled) {
            me.disableCheckChange();
        }
    },
    
    disableCheckChange: function() {
        var me = this,
            checkEl = me.checkEl;
        if (checkEl) {
            checkEl.addCls(me.disabledCls);
        }
        
        if (Ext.isIE8 && me.rendered) {
            me.el.repaint();
        }
        me.checkChangeDisabled = true;
    },
    
    enableCheckChange: function() {
        var me = this,
            checkEl = me.checkEl;
        if (checkEl) {
            checkEl.removeCls(me.disabledCls);
        }
        me.checkChangeDisabled = false;
    },
    onClick: function(e) {
        var me = this;
        if (!me.disabled && !me.checkChangeDisabled && !(me.checked && me.group)) {
            me.setChecked(!me.checked);
            
            
            if (e.type === 'keydown' && me.menu) {
                return false;
            }
        }
        this.callParent([
            e
        ]);
    },
    onDestroy: function() {
        Ext.menu.Manager.unregisterCheckable(this);
        this.callParent(arguments);
    },
    
    setChecked: function(checked, suppressEvents) {
        var me = this,
            checkedCls = me.checkedCls,
            uncheckedCls = me.uncheckedCls,
            el = me.el;
        if (me.checked !== checked && (suppressEvents || me.fireEvent('beforecheckchange', me, checked) !== false)) {
            if (el) {
                if (checked) {
                    el.addCls(checkedCls);
                    el.removeCls(uncheckedCls);
                } else {
                    el.addCls(uncheckedCls);
                    el.removeCls(checkedCls);
                }
            }
            me.checked = checked;
            Ext.menu.Manager.onCheckChange(me, checked);
            if (!suppressEvents) {
                Ext.callback(me.checkHandler, me.scope || me, [
                    me,
                    checked
                ]);
                me.fireEvent('checkchange', me, checked);
            }
        }
    }
});


Ext.define('Ext.menu.Separator', {
    extend: 'Ext.menu.Item',
    alias: 'widget.menuseparator',
    focusable: false,
    
    
    canActivate: false,
    
    
    
    
    
    
    hideOnClick: false,
    
    
    
    
    
    
    
    plain: true,
    
    separatorCls: Ext.baseCSSPrefix + 'menu-item-separator',
    
    text: '&#160;',
    ariaRole: 'separator',
    beforeRender: function(ct, pos) {
        var me = this;
        me.callParent();
        me.addCls(me.separatorCls);
    }
});


Ext.define('Ext.menu.Menu', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.menu',
    requires: [
        'Ext.layout.container.VBox',
        'Ext.menu.CheckItem',
        'Ext.menu.Item',
        'Ext.menu.Manager',
        'Ext.menu.Separator'
    ],
    mixins: [
        'Ext.util.FocusableContainer'
    ],
    
    
    enableKeyNav: true,
    
    allowOtherMenus: false,
    
    ariaRole: 'menu',
    
    
    floating: true,
    
    constrain: true,
    
    hidden: true,
    hideMode: 'visibility',
    
    ignoreParentClicks: false,
    
    isMenu: true,
    
    
    showSeparator: true,
    
    minWidth: undefined,
    defaultMinWidth: 120,
    
    defaultAlign: 'tl-bl?',
    
    focusOnToFront: false,
    bringParentToFront: false,
    defaultFocus: ':focusable',
    
    baseCls: Ext.baseCSSPrefix + 'menu',
    _iconSeparatorCls: Ext.baseCSSPrefix + 'menu-icon-separator',
    _itemCmpCls: Ext.baseCSSPrefix + 'menu-item-cmp',
    
    
    
    
    layout: {
        type: 'vbox',
        align: 'stretchmax',
        overflowHandler: 'Scroller'
    },
    initComponent: function() {
        var me = this,
            cls = [
                Ext.baseCSSPrefix + 'menu'
            ],
            bodyCls = me.bodyCls ? [
                me.bodyCls
            ] : [],
            isFloating = me.floating !== false;
        
        if (me.plain) {
            cls.push(Ext.baseCSSPrefix + 'menu-plain');
        }
        me.cls = cls.join(' ');
        
        bodyCls.push(Ext.baseCSSPrefix + 'menu-body', Ext.dom.Element.unselectableCls);
        me.bodyCls = bodyCls.join(' ');
        if (isFloating) {
            
            if (me.minWidth === undefined) {
                me.minWidth = me.defaultMinWidth;
            }
        } else {
            
            me.hidden = !!me.initialConfig.hidden;
            me.constrain = false;
        }
        me.callParent(arguments);
        
        
        Ext.override(me.getLayout(), {
            configureItem: me.configureItem
        });
    },
    
    
    
    initFloatConstrain: Ext.emptyFn,
    
    
    getInherited: function() {
        var result = this.callParent();
        result.hidden = this.hidden;
        return result;
    },
    beforeRender: function() {
        this.callParent(arguments);
        
        
        if (!this.getSizeModel().width.shrinkWrap) {
            this.layout.align = 'stretch';
        }
    },
    onBoxReady: function() {
        var me = this,
            listeners = {
                click: me.onClick,
                mouseover: me.onMouseOver,
                scope: me
            },
            iconSeparatorCls = me._iconSeparatorCls;
        if (Ext.supports.Touch) {
            listeners.pointerdown = me.onMouseOver;
        }
        
        me.focusableKeyNav.map.addBinding([
            {
                key: 27,
                handler: me.onEscapeKey,
                scope: me
            },
            {
                key: /[\w]/,
                handler: me.onShortcutKey,
                scope: me,
                shift: false,
                ctrl: false,
                alt: false
            }
        ]);
        me.callParent(arguments);
        
        if (me.showSeparator) {
            me.iconSepEl = me.body.insertFirst({
                role: 'presentation',
                cls: iconSeparatorCls + ' ' + iconSeparatorCls + '-' + me.ui,
                html: '&#160;'
            });
        }
        me.mon(me.el, listeners);
        
        
        if (Ext.supports.MSPointerEvents || Ext.supports.PointerEvents) {
            me.mon(me.el, {
                scope: me,
                click: me.preventClick,
                translate: false
            });
        }
        me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);
    },
    onFocusLeave: function(e) {
        var me = this;
        me.callParent([
            e
        ]);
        me.mixins.focusablecontainer.onFocusLeave.call(me, e);
        me.hide();
    },
    
    canActivateItem: function(item) {
        return item && item.isFocusable();
    },
    
    deactivateActiveItem: function() {
        var me = this,
            activeItem = me.lastFocusedChild;
        if (activeItem) {
            activeItem.blur();
        }
    },
    
    getItemFromEvent: function(e) {
        var me = this,
            renderTarget = me.layout.getRenderTarget().dom,
            toEl = e.getTarget();
        
        while (toEl.parentNode !== renderTarget) {
            toEl = toEl.parentNode;
            if (!toEl) {
                return;
            }
        }
        return Ext.getCmp(toEl.id);
    },
    lookupComponent: function(cmp) {
        var me = this;
        if (typeof cmp === 'string') {
            cmp = me.lookupItemFromString(cmp);
        } else if (Ext.isObject(cmp)) {
            cmp = me.lookupItemFromObject(cmp);
        }
        
        
        if (!cmp.dock) {
            cmp.minWidth = cmp.minWidth || me.minWidth;
        }
        return cmp;
    },
    
    lookupItemFromObject: function(cmp) {
        var me = this;
        if (!cmp.isComponent) {
            if (!cmp.xtype) {
                cmp = Ext.create('Ext.menu.' + (Ext.isBoolean(cmp.checked) ? 'Check' : '') + 'Item', cmp);
            } else {
                cmp = Ext.ComponentManager.create(cmp, cmp.xtype);
            }
        }
        if (cmp.isMenuItem) {
            cmp.parentMenu = me;
        }
        return cmp;
    },
    
    lookupItemFromString: function(cmp) {
        return (cmp === 'separator' || cmp === '-') ? new Ext.menu.Separator() : new Ext.menu.Item({
            canActivate: false,
            hideOnClick: false,
            plain: true,
            text: cmp
        });
    },
    
    
    
    
    configureItem: function(cmp) {
        var me = this.owner,
            prefix = Ext.baseCSSPrefix,
            ui = me.ui,
            cls, cmpCls;
        if (cmp.isMenuItem) {
            cmp.setUI(ui);
        } else if (me.items.getCount() > 1 && !cmp.rendered && !cmp.dock) {
            cmpCls = me._itemCmpCls;
            cls = [
                cmpCls + ' ' + cmpCls + '-' + ui
            ];
            
            
            
            if (!me.plain && (cmp.indent !== false || cmp.iconCls === 'no-icon')) {
                cls.push(prefix + 'menu-item-indent-' + ui);
            }
            if (cmp.rendered) {
                cmp.el.addCls(cls);
            } else {
                cmp.cls = (cmp.cls || '') + ' ' + cls.join(' ');
            }
            
            cmp.$extraMenuCls = cls;
        }
        
        this.callParent(arguments);
    },
    onRemove: function(cmp) {
        this.callParent([
            cmp
        ]);
        
        if (!cmp.isDestroyed && cmp.$extraMenuCls) {
            cmp.el.removeCls(cmp.$extraMenuCls);
        }
    },
    onClick: function(e) {
        var me = this,
            type = e.type,
            item, clickResult,
            iskeyEvent = type === 'keydown';
        if (me.disabled) {
            e.stopEvent();
            return;
        }
        item = me.getItemFromEvent(e);
        if (item && item.isMenuItem) {
            if (!item.menu || !me.ignoreParentClicks) {
                clickResult = item.onClick(e);
            } else {
                e.stopEvent();
            }
            
            if (item.menu && clickResult !== false && iskeyEvent) {
                item.expandMenu(e, 0);
            }
        }
        
        if (!item || item.disabled) {
            item = undefined;
        }
        me.fireEvent('click', me, item, e);
    },
    onDestroy: function() {
        var me = this;
        me.parentMenu = me.ownerCmp = null;
        if (me.rendered) {
            me.el.un(me.mouseMonitor);
            Ext.destroy(me.iconSepEl);
        }
        me.callParent(arguments);
    },
    onMouseLeave: function(e) {
        if (this.disabled) {
            return;
        }
        this.fireEvent('mouseleave', this, e);
    },
    onMouseOver: function(e) {
        var me = this,
            fromEl = e.getRelatedTarget(),
            mouseEnter = !me.el.contains(fromEl),
            item = me.getItemFromEvent(e),
            parentMenu = me.parentMenu,
            ownerCmp = me.ownerCmp;
        if (mouseEnter && parentMenu) {
            parentMenu.setActiveItem(ownerCmp);
            ownerCmp.cancelDeferHide();
            parentMenu.mouseMonitor.mouseenter();
        }
        if (me.disabled) {
            return;
        }
        
        if (item) {
            if (!item.containsFocus) {
                item.focus();
            }
            if (item.expandMenu) {
                item.expandMenu(e);
            }
        }
        if (mouseEnter) {
            me.fireEvent('mouseenter', me, e);
        }
        me.fireEvent('mouseover', me, item, e);
    },
    setActiveItem: function(item) {
        var me = this;
        if (item && (item !== me.lastFocusedChild)) {
            me.focusChild(item, 1);
        }
    },
    
    onEscapeKey: function() {
        if (this.floating) {
            this.hide();
        }
    },
    onShortcutKey: function(keyCode, e) {
        var shortcutChar = String.fromCharCode(e.getCharCode()),
            items = this.query('>[text]'),
            len = items.length,
            item = this.lastFocusedChild,
            focusIndex = Ext.Array.indexOf(items, item),
            i = focusIndex;
        
        for (; ; ) {
            if (++i === len) {
                i = 0;
            }
            item = items[i];
            
            if (i === focusIndex) {
                return;
            }
            
            if (item.text && item.text[0].toUpperCase() === shortcutChar) {
                item.focus();
                return;
            }
        }
    },
    
    
    onFocusableContainerTabKey: function(e) {
        if (this.floating) {
            this.hide();
        }
    },
    onFocusableContainerEnterKey: function(e) {
        this.onClick(e);
    },
    onFocusableContainerSpaceKey: function(e) {
        this.onClick(e);
    },
    onFocusableContainerLeftKey: function(e) {
        
        if (this.parentMenu) {
            this.ownerCmp.focus();
            this.hide();
        }
    },
    onFocusableContainerRightKey: function(e) {
        var me = this,
            focusItem = me.lastFocusedChild;
        if (focusItem && focusItem.expandMenu) {
            focusItem.expandMenu(e, 0);
        }
    },
    beforeShow: function() {
        var me = this,
            activeEl, viewHeight;
        
        if (me.floating) {
            
            
            activeEl = Ext.Element.getActiveElement();
            me.focusAnchor = activeEl === document.body ? null : activeEl;
            me.savedMaxHeight = me.maxHeight;
            viewHeight = me.container.getViewSize().height;
            me.maxHeight = Math.min(me.maxHeight || viewHeight, viewHeight);
        }
        me.callParent(arguments);
        
        
        
        if (Ext.supports.Touch) {
            me.tapListener = Ext.getBody().on({
                touchstart: me.onBodyTap,
                scope: me,
                destroyable: true
            });
        }
    },
    onBodyTap: function(e) {
        
        if (!this.owns(e)) {
            this.tapListener.destroy();
            this.hide();
        }
    },
    afterShow: function() {
        var me = this;
        me.callParent(arguments);
        
        if (me.floating && me.autoFocus) {
            me.maxHeight = me.savedMaxHeight;
            me.focus();
        }
    },
    onHide: function(animateTarget, cb, scope) {
        var me = this,
            focusTarget;
        
        if (me.el.contains(Ext.Element.getActiveElement())) {
            
            focusTarget = me.focusAnchor || me.ownerCmp || me.up(':focusable');
            
            if (focusTarget) {
                me.previousFocus = focusTarget;
            }
        }
        this.callParent([
            animateTarget,
            cb,
            scope
        ]);
    },
    preventClick: function(e) {
        if (!this.getItemFromEvent(e).href) {
            e.preventDefault();
        }
    }
});


Ext.define('Ext.grid.filters.filter.Base', {
    mixins: [
        'Ext.mixin.Factoryable'
    ],
    factoryConfig: {
        type: 'grid.filter'
    },
    $configPrefixed: false,
    $configStrict: false,
    config: {
        
        itemDefaults: null,
        menuDefaults: {
            xtype: 'menu'
        },
        
        updateBuffer: 500
    },
    
    
    active: false,
    
    type: 'string',
    
    dataIndex: null,
    
    menu: null,
    isGridFilter: true,
    defaultRoot: 'data',
    
    filterIdPrefix: Ext.baseCSSPrefix + 'gridfilter',
    
    
    
    
    constructor: function(config) {
        var me = this,
            column;
        me.initConfig(config);
        column = me.column;
        column.on('destroy', me.destroy, me);
        me.dataIndex = me.dataIndex || column.dataIndex;
        me.task = new Ext.util.DelayedTask(me.setValue,me);
    },
    
    destroy: function() {
        this.grid = this.menu = Ext.destroy(this.menu);
    },
    addStoreFilter: function(filter) {
        this.getGridStore().getFilters().add(filter);
    },
    createFilter: function(config, key) {
        return new Ext.util.Filter(this.getFilterConfig(config, key));
    },
    
    
    getFilterConfig: function(config, key) {
        config.id = this.getBaseIdPrefix();
        if (!config.property) {
            config.property = this.dataIndex;
        }
        if (!config.root) {
            config.root = this.defaultRoot;
        }
        if (key) {
            config.id += '-' + key;
        }
        return config;
    },
    
    createMenu: function() {
        this.menu = Ext.widget(this.getMenuConfig());
    },
    getBaseIdPrefix: function() {
        return this.filterIdPrefix + '-' + this.dataIndex;
    },
    getMenuConfig: function() {
        return Ext.apply({}, this.getMenuDefaults());
    },
    getGridStore: function() {
        return this.grid.getStore();
    },
    getStoreFilter: function(key) {
        var id = this.getBaseIdPrefix();
        if (key) {
            id += '-' + key;
        }
        return this.getGridStore().getFilters().get(id);
    },
    
    onValueChange: function(field, e) {
        var me = this,
            updateBuffer = me.updateBuffer;
        
        if (field.isFormField && e.getKey() === e.RETURN && field.isValid()) {
            me.menu.hide();
            return;
        }
        if (updateBuffer) {
            me.task.delay(updateBuffer, null, null, [
                me.getValue(field)
            ]);
        } else {
            me.setValue(me.getValue(field));
        }
    },
    
    preprocess: Ext.emptyFn,
    removeStoreFilter: function(filter) {
        this.getGridStore().getFilters().remove(filter);
    },
    
    getValue: Ext.emptyFn,
    
    
    
    setActive: function(active) {
        var me = this,
            menuItem = me.owner.activeFilterMenuItem,
            filterCollection;
        if (me.active !== active) {
            me.active = active;
            
            
            me.preventDefault = true;
            filterCollection = me.getGridStore().getFilters();
            filterCollection.beginUpdate();
            if (active) {
                me.activate();
            } else {
                me.deactivate();
            }
            filterCollection.endUpdate();
            me.preventDefault = false;
            
            if (menuItem && menuItem.activeFilter === me) {
                menuItem.setChecked(active);
            }
            me.setColumnActive(active);
        }
    },
    
    setColumnActive: function(active) {
        this.column[active ? 'addCls' : 'removeCls'](this.owner.filterCls);
    },
    showMenu: function(menuItem) {
        var me = this;
        if (!me.menu) {
            me.createMenu();
        }
        menuItem.activeFilter = me;
        menuItem.setMenu(me.menu, false);
        menuItem.setChecked(me.active);
        
        menuItem.setDisabled(me.disabled === true);
        me.activate(
        true);
    },
    updateStoreFilter: function() {
        this.getGridStore().getFilters().notify('endupdate');
    }
});


Ext.define('Ext.grid.filters.filter.SingleFilter', {
    extend: 'Ext.grid.filters.filter.Base',
    constructor: function(config) {
        var me = this,
            filter, value;
        me.callParent([
            config
        ]);
        value = me.value;
        filter = me.getStoreFilter();
        if (filter) {
            
            me.active = true;
        } else {
            
            
            
            if (me.grid.stateful && me.getGridStore().saveStatefulFilters) {
                value = undefined;
            }
            
            
            me.active = (config.active != undefined) ? config.active : value !== undefined;
            
            filter = me.createFilter({
                operator: me.operator,
                value: value
            });
            if (me.active) {
                me.addStoreFilter(filter);
            }
        }
        if (me.active) {
            me.setColumnActive(true);
        }
        me.filter = filter;
    },
    activate: function(showingMenu) {
        if (showingMenu) {
            this.activateMenu();
        } else {
            this.addStoreFilter(this.filter);
        }
    },
    deactivate: function() {
        this.removeStoreFilter(this.filter);
    },
    getValue: function(field) {
        return field.getValue();
    },
    onFilterRemove: function() {
        
        
        if (!this.menu || this.active) {
            this.active = false;
        }
    }
});


Ext.define('Ext.grid.filters.filter.Boolean', {
    extend: 'Ext.grid.filters.filter.SingleFilter',
    alias: 'grid.filter.boolean',
    type: 'boolean',
    operator: '=',
    
    defaultValue: false,
    
    yesText: 'Yes',
    
    noText: 'No',
    updateBuffer: 0,
    
    createMenu: function(config) {
        var me = this,
            gId = Ext.id(),
            listeners = {
                scope: me,
                click: me.onClick
            },
            itemDefaults = me.getItemDefaults();
        me.callParent(arguments);
        me.menu.add([
            Ext.apply({
                text: me.yesText,
                filterKey: 1,
                group: gId,
                checked: !!me.defaultValue,
                listeners: listeners
            }, itemDefaults),
            Ext.apply({
                text: me.noText,
                filterKey: 0,
                group: gId,
                checked: !me.defaultValue,
                listeners: listeners
            }, itemDefaults)
        ]);
    },
    
    onClick: function(field) {
        this.setValue(!!field.filterKey);
    },
    
    setValue: function(value) {
        var me = this;
        me.filter.setValue(value);
        if (value !== undefined && me.active) {
            me.value = value;
            me.updateStoreFilter();
        } else {
            me.setActive(true);
        }
    },
    
    activateMenu: Ext.emptyFn
});


Ext.define('Ext.grid.filters.filter.TriFilter', {
    extend: 'Ext.grid.filters.filter.Base',
    
    menuItems: [
        'lt',
        'gt',
        '-',
        'eq'
    ],
    constructor: function(config) {
        var me = this,
            stateful = false,
            filter = {},
            filterGt, filterLt, filterEq, value, operator;
        me.callParent([
            config
        ]);
        value = me.value;
        filterLt = me.getStoreFilter('lt');
        filterGt = me.getStoreFilter('gt');
        filterEq = me.getStoreFilter('eq');
        if (filterLt || filterGt || filterEq) {
            
            stateful = me.active = true;
            if (filterLt) {
                me.onStateRestore(filterLt);
            }
            if (filterGt) {
                me.onStateRestore(filterGt);
            }
            if (filterEq) {
                me.onStateRestore(filterEq);
            }
        } else {
            
            
            
            if (me.grid.stateful && me.getGridStore().saveStatefulFilters) {
                value = undefined;
            }
            
            me.active = !!value;
        }
        
        
        
        
        filter.lt = filterLt || me.createFilter({
            operator: 'lt',
            value: (!stateful && value && value.lt) || null
        }, 'lt');
        filter.gt = filterGt || me.createFilter({
            operator: 'gt',
            value: (!stateful && value && value.gt) || null
        }, 'gt');
        filter.eq = filterEq || me.createFilter({
            operator: 'eq',
            value: (!stateful && value && value.eq) || null
        }, 'eq');
        me.filter = filter;
        if (me.active) {
            me.setColumnActive(true);
            if (!stateful) {
                for (operator in value) {
                    me.addStoreFilter(me.filter[operator]);
                }
            }
        }
    },
    
    
    activate: function(showingMenu) {
        var me = this,
            filters = this.filter,
            fields = me.fields,
            filter, field, operator, value;
        if (me.preventFilterRemoval) {
            return;
        }
        for (operator in filters) {
            filter = filters[operator];
            field = fields[operator];
            value = filter.getValue();
            if (value) {
                field.setValue(value);
                field.up('menuitem').setChecked(true, 
                true);
                
                
                if (!showingMenu) {
                    me.addStoreFilter(filter);
                }
            }
        }
    },
    
    deactivate: function() {
        var me = this,
            filters = me.filter,
            f, filter;
        if (!me.hasActiveFilter() || me.preventFilterRemoval) {
            return;
        }
        me.preventFilterRemoval = true;
        for (f in filters) {
            filter = filters[f];
            if (filter.getValue()) {
                me.removeStoreFilter(filter);
            }
        }
        me.preventFilterRemoval = false;
    },
    hasActiveFilter: function() {
        var active = false,
            filters = this.filter,
            filterCollection = this.getGridStore().getFilters(),
            prefix = this.getBaseIdPrefix(),
            filter;
        if (filterCollection.length) {
            for (filter in filters) {
                if (filterCollection.get(prefix + '-' + filter)) {
                    active = true;
                    break;
                }
            }
        }
        return active;
    },
    onFilterRemove: function(operator) {
        var me = this,
            value;
        
        
        
        if (!me.menu && !me.hasActiveFilter()) {
            me.active = false;
        } else if (me.menu) {
            value = {};
            value[operator] = null;
            me.setValue(value);
        }
    },
    onStateRestore: Ext.emptyFn,
    setValue: function(value) {
        var me = this,
            fields = me.fields,
            filters = me.filter,
            add = [],
            remove = [],
            active = false,
            filterCollection = me.getGridStore().getFilters(),
            field, filter, v, i, len;
        if (me.preventFilterRemoval) {
            return;
        }
        me.preventFilterRemoval = true;
        if ('eq' in value) {
            v = filters.lt.getValue();
            if (v || v === 0) {
                remove.push(fields.lt);
            }
            v = filters.gt.getValue();
            if (v || v === 0) {
                remove.push(fields.gt);
            }
            v = value.eq;
            if (v || v === 0) {
                add.push(fields.eq);
                filters.eq.setValue(v);
            } else {
                remove.push(fields.eq);
            }
        } else {
            v = filters.eq.getValue();
            if (v || v === 0) {
                remove.push(fields.eq);
            }
            if ('lt' in value) {
                v = value.lt;
                if (v || v === 0) {
                    add.push(fields.lt);
                    filters.lt.setValue(v);
                } else {
                    remove.push(fields.lt);
                }
            }
            if ('gt' in value) {
                v = value.gt;
                if (v || v === 0) {
                    add.push(fields.gt);
                    filters.gt.setValue(v);
                } else {
                    remove.push(fields.gt);
                }
            }
        }
        if (remove.length || add.length) {
            filterCollection.beginUpdate();
            if (remove.length) {
                for (i = 0 , len = remove.length; i < len; i++) {
                    field = remove[i];
                    filter = field.filter;
                    field.setValue(null);
                    filter.setValue(null);
                    me.removeStoreFilter(filter);
                }
            }
            if (add.length) {
                for (i = 0 , len = add.length; i < len; i++) {
                    me.addStoreFilter(add[i].filter);
                }
                active = true;
            }
            filterCollection.endUpdate();
        }
        if (!active && filterCollection.length) {
            active = me.hasActiveFilter();
        }
        if (!active || !me.active) {
            me.setActive(active);
        }
        me.preventFilterRemoval = false;
    }
});


Ext.define('Ext.grid.filters.filter.Date', {
    extend: 'Ext.grid.filters.filter.TriFilter',
    alias: 'grid.filter.date',
    uses: [
        'Ext.picker.Date',
        'Ext.menu.Menu'
    ],
    type: 'date',
    config: {
        
        fields: {
            lt: {
                text: 'Before'
            },
            gt: {
                text: 'After'
            },
            eq: {
                text: 'On'
            }
        },
        
        pickerDefaults: {
            xtype: 'datepicker',
            border: 0
        },
        updateBuffer: 0,
        
        dateFormat: undefined
    },
    itemDefaults: {
        xtype: 'menucheckitem',
        selectOnFocus: true,
        width: 125,
        menu: {
            layout: 'auto',
            plain: true
        }
    },
    
    
    applyDateFormat: function(dateFormat) {
        return dateFormat || Ext.Date.defaultFormat;
    },
    
    createMenu: function(config) {
        var me = this,
            listeners = {
                scope: me,
                checkchange: me.onCheckChange
            },
            menuItems = me.menuItems,
            fields, itemDefaults, pickerCfg, i, len, key, item, cfg, field;
        me.callParent(arguments);
        itemDefaults = me.getItemDefaults();
        fields = me.getFields();
        pickerCfg = Ext.apply({
            minDate: me.minDate,
            maxDate: me.maxDate,
            format: me.dateFormat,
            listeners: {
                scope: me,
                select: me.onMenuSelect
            }
        }, me.getPickerDefaults());
        me.fields = {};
        for (i = 0 , len = menuItems.length; i < len; i++) {
            key = menuItems[i];
            if (key !== '-') {
                cfg = {
                    menu: {
                        items: [
                            Ext.apply({
                                itemId: key
                            }, pickerCfg)
                        ]
                    }
                };
                if (itemDefaults) {
                    Ext.merge(cfg, itemDefaults);
                }
                if (fields) {
                    Ext.merge(cfg, fields[key]);
                }
                item = me.menu.add(cfg);
                
                field = me.fields[key] = item.down('datepicker');
                field.filter = me.filter[key];
                field.filterKey = key;
                item.on(listeners);
            } else {
                me.menu.add(key);
            }
        }
    },
    
    getPicker: function(item) {
        return this.fields[item];
    },
    
    onCheckChange: function(field, checked) {
        
        
        var filter = field.down('datepicker').filter,
            v;
        
        if (!checked && filter.getValue()) {
            
            
            v = {};
            v[filter.getOperator()] = null;
            this.setValue(v);
        }
    },
    onFilterRemove: function(operator) {
        var v = {};
        v[operator] = null;
        this.setValue(v);
        this.fields[operator].up('menuitem').setChecked(false, 
        true);
    },
    onStateRestore: function(filter) {
        filter.setSerializer(this.getSerializer());
        filter.setConvert(this.convertDateOnly);
    },
    getFilterConfig: function(config, key) {
        config = this.callParent([
            config,
            key
        ]);
        config.serializer = this.getSerializer();
        config.convert = this.convertDateOnly;
        return config;
    },
    convertDateOnly: function(v) {
        var result = null;
        if (v) {
            result = Ext.Date.clearTime(v, true).getTime();
        }
        return result;
    },
    getSerializer: function() {
        var me = this;
        return function(data) {
            var value = data.value;
            if (value) {
                data.value = Ext.Date.format(value, me.getDateFormat());
            }
        };
    },
    
    onMenuSelect: function(picker, date) {
        var fields = this.fields,
            field = fields[picker.itemId],
            gt = fields.gt,
            lt = fields.lt,
            eq = fields.eq,
            v = {};
        field.up('menuitem').setChecked(true, 
        true);
        if (field === eq) {
            lt.up('menuitem').setChecked(false, true);
            gt.up('menuitem').setChecked(false, true);
        } else {
            eq.up('menuitem').setChecked(false, true);
            if (field === gt && (+lt.value < +date)) {
                lt.up('menuitem').setChecked(false, true);
            } else if (field === lt && (+gt.value > +date)) {
                gt.up('menuitem').setChecked(false, true);
            }
        }
        v[field.filterKey] = date;
        this.setValue(v);
        picker.up('menu').hide();
    }
});


Ext.define('Ext.grid.filters.filter.List', {
    extend: 'Ext.grid.filters.filter.SingleFilter',
    alias: 'grid.filter.list',
    type: 'list',
    operator: 'in',
    itemDefaults: {
        checked: false,
        hideOnClick: false
    },
    
    
    
    idField: 'id',
    
    labelField: 'text',
    
    labelIndex: null,
    
    loadingText: 'Loading...',
    
    loadOnShow: true,
    
    single: false,
    plain: true,
    
    constructor: function(config) {
        var me = this,
            options, store;
        me.callParent([
            config
        ]);
        if (me.itemDefaults.checked) {
            Ext.Error.raise('The itemDefaults.checked config is not supported, use the value config instead.');
        }
        options = me.options;
        store = me.store;
        
        
        
        
        
        if (!options && !store) {
            
            
            
            
            
            
            
            me.getGridStore().on({
                scope: me,
                add: me.onDataChanged,
                refresh: me.onDataChanged,
                remove: me.onDataChanged,
                update: me.onDataChanged
            });
        }
        me.labelIndex = me.labelIndex || me.column.dataIndex;
    },
    destroy: function() {
        var me = this,
            store = me.store,
            autoStore = me.autoStore;
        
        
        if (store) {
            if (autoStore || store.autoDestroy) {
                store.destroy();
            } else {
                store.un('load', me.bindMenuStore, me);
            }
            me.store = null;
        }
        if (autoStore) {
            me.getGridStore().un({
                scope: me,
                add: me.onDataChanged,
                refresh: me.onDataChanged,
                remove: me.onDataChanged,
                update: me.onDataChanged
            });
        }
        me.callParent();
    },
    activateMenu: function() {
        var me = this,
            value = me.filter.getValue(),
            items, i, len, checkItem;
        if (!value || !value.length) {
            return;
        }
        items = me.menu.items;
        for (i = 0 , len = items.length; i < len; i++) {
            checkItem = items.getAt(i);
            if (Ext.Array.indexOf(value, checkItem.value) > -1) {
                checkItem.setChecked(true, 
                true);
            }
        }
    },
    bindMenuStore: function(options) {
        var me = this;
        if (me.grid.isDestroyed || me.preventFilterRemoval) {
            return;
        }
        me.createListStore(options);
        me.createMenuItems(me.store);
        me.loaded = true;
    },
    createListStore: function(options) {
        var me = this,
            store = me.store,
            isStore = options.isStore,
            idField = me.idField,
            labelField = me.labelField,
            optionsStore = false,
            storeData, o, record, i, len, value, filter;
        if (isStore) {
            if (options !== me.getGridStore()) {
                optionsStore = true;
                store = me.store = options;
            } else {
                me.autoStore = true;
                storeData = me.getOptionsFromStore(options);
            }
        } else {
            storeData = [];
            for (i = 0 , len = options.length; i < len; i++) {
                value = options[i];
                switch (Ext.typeOf(value)) {
                    case 'array':
                        storeData.push(value);
                        break;
                    case 'object':
                        storeData.push(value);
                        break;
                    default:
                        if (value != null) {
                            o = {};
                            o[idField] = value;
                            o[labelField] = value;
                            storeData.push(o);
                        };
                }
            }
        }
        if (!optionsStore) {
            if (store) {
                store.destroy();
            }
            store = me.store = new Ext.data.Store({
                fields: [
                    idField,
                    labelField
                ],
                data: storeData
            });
            me.loaded = true;
        }
        me.setStoreFilter(store);
    },
    
    createMenu: function(config) {
        var me = this,
            gridStore = me.getGridStore(),
            store = me.store,
            options = me.options,
            menu;
        if (store) {
            me.store = store = Ext.StoreManager.lookup(store);
        }
        me.callParent([
            config
        ]);
        menu = me.menu;
        if (store) {
            if (!store.getCount()) {
                menu.add({
                    text: me.loadingText,
                    iconCls: Ext.baseCSSPrefix + 'mask-msg-text'
                });
                
                
                menu.on('show', me.show, me);
                store.on('load', me.bindMenuStore, me, {
                    single: true
                });
            } else {
                me.createMenuItems(store);
            }
        }
        
        else if (options) {
            me.bindMenuStore(options);
        }
        
        
        
        else if (gridStore.getCount() || gridStore.data.filtered) {
            me.bindMenuStore(gridStore);
        } else 
        {
            gridStore.on('load', me.bindMenuStore, me, {
                single: true
            });
        }
    },
    
    createMenuItems: function(store) {
        var me = this,
            menu = me.menu,
            len = store.getCount(),
            contains = Ext.Array.contains,
            listeners, itemDefaults, record, gid, idValue, idField, labelValue, labelField, i, item, processed;
        
        if (len && menu) {
            listeners = {
                checkchange: me.onCheckChange,
                scope: me
            };
            itemDefaults = me.getItemDefaults();
            menu.suspendLayouts();
            menu.removeAll(true);
            gid = me.single ? Ext.id() : null;
            idField = me.idField;
            labelField = me.labelField;
            processed = [];
            for (i = 0; i < len; i++) {
                record = store.getAt(i);
                idValue = record.get(idField);
                labelValue = record.get(labelField);
                
                if (labelValue == null || contains(processed, idValue)) {
                    
                    continue;
                }
                processed.push(labelValue);
                
                
                item = menu.add(Ext.apply({
                    text: labelValue,
                    group: gid,
                    value: idValue,
                    listeners: listeners
                }, itemDefaults));
            }
            menu.resumeLayouts(true);
        }
    },
    getFilterConfig: function(config, key) {
        
        
        config.value = config.value || [];
        return this.callParent([
            config,
            key
        ]);
    },
    getOptionsFromStore: function(store) {
        var me = this,
            data = store.getData(),
            map = {},
            ret = [],
            dataIndex = me.dataIndex,
            labelIndex = me.labelIndex,
            items, i, length, recData, idValue, labelValue;
        if (store.isFiltered()) {
            data = data.getSource();
        }
        items = data.items;
        length = items.length;
        for (i = 0; i < length; ++i) {
            recData = items[i].data;
            idValue = recData[dataIndex];
            labelValue = recData[labelIndex];
            if (labelValue === undefined) {
                labelValue = idValue;
            }
            
            
            if (!map[idValue]) {
                map[idValue] = 1;
                ret.push([
                    idValue,
                    labelValue
                ]);
            }
        }
        return ret;
    },
    onCheckChange: function() {
        
        
        var me = this,
            updateBuffer = me.updateBuffer;
        if (updateBuffer) {
            me.task.delay(updateBuffer, null, null);
        } else {
            me.setValue();
        }
    },
    onDataChanged: function(store) {
        
        
        if (!this.preventDefault) {
            this.bindMenuStore(store);
        }
    },
    setStoreFilter: function(options) {
        var me = this,
            value = me.value,
            filter = me.filter,
            contains, i, len, val, list;
        
        if (value) {
            if (!Ext.isArray(value)) {
                value = [
                    value
                ];
            }
            filter.setValue(value);
        }
        if (me.active) {
            me.preventFilterRemoval = true;
            me.addStoreFilter(filter);
            me.preventFilterRemoval = false;
        }
    },
    
    setValue: function() {
        var me = this,
            items = me.menu.items,
            value = [],
            i, len, checkItem;
        
        
        me.preventDefault = true;
        for (i = 0 , len = items.length; i < len; i++) {
            checkItem = items.getAt(i);
            if (checkItem.checked) {
                value.push(checkItem.value);
            }
        }
        me.filter.setValue(value);
        len = value.length;
        if (len && me.active) {
            me.updateStoreFilter();
        } else {
            me.setActive(!!len);
        }
        me.preventDefault = false;
    },
    show: function() {
        var store = this.store;
        if (this.loadOnShow && !this.loaded && !store.hasPendingLoad()) {
            store.load();
        }
    }
});


Ext.define('Ext.grid.filters.filter.Number', {
    extend: 'Ext.grid.filters.filter.TriFilter',
    alias: [
        'grid.filter.number',
        'grid.filter.numeric'
    ],
    uses: [
        'Ext.form.field.Number'
    ],
    type: 'number',
    config: {
        
        fields: {
            gt: {
                iconCls: Ext.baseCSSPrefix + 'grid-filters-gt',
                margin: '0 0 3px 0'
            },
            lt: {
                iconCls: Ext.baseCSSPrefix + 'grid-filters-lt',
                margin: '0 0 3px 0'
            },
            eq: {
                iconCls: Ext.baseCSSPrefix + 'grid-filters-eq',
                margin: 0
            }
        }
    },
    
    emptyText: 'Enter Number...',
    itemDefaults: {
        xtype: 'numberfield',
        enableKeyEvents: true,
        hideEmptyLabel: false,
        labelSeparator: '',
        labelWidth: 29,
        selectOnFocus: false
    },
    menuDefaults: {
        
        
        bodyPadding: 3,
        showSeparator: false
    },
    createMenu: function() {
        var me = this,
            listeners = {
                scope: me,
                keyup: me.onValueChange,
                spin: {
                    fn: me.onInputSpin,
                    buffer: 200
                },
                el: {
                    click: me.stopFn
                }
            },
            itemDefaults = me.getItemDefaults(),
            menuItems = me.menuItems,
            fields = me.getFields(),
            field, i, len, key, item, cfg;
        me.callParent();
        me.fields = {};
        for (i = 0 , len = menuItems.length; i < len; i++) {
            key = menuItems[i];
            if (key !== '-') {
                field = fields[key];
                cfg = {
                    labelClsExtra: Ext.baseCSSPrefix + 'grid-filters-icon ' + field.iconCls
                };
                if (itemDefaults) {
                    Ext.merge(cfg, itemDefaults);
                }
                Ext.merge(cfg, field);
                cfg.emptyText = cfg.emptyText || me.emptyText;
                delete cfg.iconCls;
                me.fields[key] = item = me.menu.add(cfg);
                item.filter = me.filter[key];
                item.filterKey = key;
                item.on(listeners);
            } else {
                me.menu.add(key);
            }
        }
    },
    getValue: function(field) {
        var value = {};
        value[field.filterKey] = field.getValue();
        return value;
    },
    
    onInputSpin: function(field, direction) {
        var value = {};
        value[field.filterKey] = field.getValue();
        this.setValue(value);
    },
    stopFn: function(e) {
        e.stopPropagation();
    }
});


Ext.define('Ext.grid.filters.filter.String', {
    extend: 'Ext.grid.filters.filter.SingleFilter',
    alias: 'grid.filter.string',
    type: 'string',
    operator: 'like',
    
    emptyText: 'Enter Filter Text...',
    itemDefaults: {
        xtype: 'textfield',
        enableKeyEvents: true,
        hideEmptyLabel: false,
        iconCls: Ext.baseCSSPrefix + 'grid-filters-find',
        labelSeparator: '',
        labelWidth: 29,
        margin: 0,
        selectOnFocus: true
    },
    menuDefaults: {
        
        
        bodyPadding: 3,
        showSeparator: false
    },
    
    createMenu: function() {
        var me = this,
            config;
        me.callParent();
        config = Ext.apply({}, me.getItemDefaults());
        if (config.iconCls && !('labelClsExtra' in config)) {
            config.labelClsExtra = Ext.baseCSSPrefix + 'grid-filters-icon ' + config.iconCls;
        }
        delete config.iconCls;
        config.emptyText = config.emptyText || me.emptyText;
        me.inputItem = me.menu.add(config);
        me.inputItem.on({
            scope: me,
            keyup: me.onValueChange,
            el: {
                click: function(e) {
                    e.stopPropagation();
                }
            }
        });
    },
    
    setValue: function(value) {
        var me = this;
        if (me.inputItem) {
            me.inputItem.setValue(value);
        }
        me.filter.setValue(value);
        if (value && me.active) {
            me.value = value;
            me.updateStoreFilter();
        } else {
            me.setActive(!!value);
        }
    },
    activateMenu: function() {
        this.inputItem.setValue(this.filter.getValue());
    }
});


Ext.define('Ext.grid.filters.Filters', {
    extend: 'Ext.plugin.Abstract',
    requires: [
        'Ext.grid.filters.filter.*'
    ],
    mixins: [
        'Ext.util.StoreHolder'
    ],
    alias: 'plugin.gridfilters',
    pluginId: 'gridfilters',
    
    defaultFilterTypes: {
        'boolean': 'boolean',
        'int': 'number',
        date: 'date',
        number: 'number'
    },
    
    filterCls: Ext.baseCSSPrefix + 'grid-filters-filtered-column',
    
    menuFilterText: 'Filters',
    
    showMenu: true,
    
    stateId: undefined,
    init: function(grid) {
        var me = this,
            store, headerCt;
        Ext.Assert.falsey(me.grid);
        me.grid = grid;
        grid.filters = me;
        if (me.grid.normalGrid) {
            me.isLocked = true;
        }
        grid.clearFilters = me.clearFilters.bind(me);
        store = grid.store;
        headerCt = grid.headerCt;
        headerCt.on({
            scope: me,
            add: me.onAdd,
            menucreate: me.onMenuCreate
        });
        grid.on({
            scope: me,
            destroy: me.onGridDestroy,
            beforereconfigure: me.onBeforeReconfigure,
            reconfigure: me.onReconfigure
        });
        me.bindStore(store);
        if (grid.stateful) {
            store.statefulFilters = true;
        }
        me.initColumns();
    },
    
    initColumns: function() {
        var grid = this.grid,
            store = grid.getStore(),
            columns = grid.columnManager.getColumns(),
            len = columns.length,
            i, column, filter, filterCollection, block;
        
        for (i = 0; i < len; i++) {
            column = columns[i];
            filter = column.filter;
            if (filter && !filter.isGridFilter) {
                if (!filterCollection) {
                    filterCollection = store.getFilters();
                    filterCollection.beginUpdate();
                }
                this.createColumnFilter(column);
            }
        }
        if (filterCollection) {
            filterCollection.endUpdate();
        }
    },
    createColumnFilter: function(column) {
        var me = this,
            columnFilter = column.filter,
            filter = {
                column: column,
                grid: me.grid,
                owner: me
            },
            field, model, type;
        if (Ext.isString(columnFilter)) {
            filter.type = columnFilter;
        } else {
            Ext.apply(filter, columnFilter);
        }
        if (!filter.type) {
            model = me.store.getModel();
            
            field = model && model.getField(column.dataIndex);
            type = field && field.type;
            filter.type = (type && me.defaultFilterTypes[type]) || column.defaultFilterType || 'string';
        }
        column.filter = Ext.Factory.gridFilter(filter);
    },
    onAdd: function(headerCt, column, index) {
        var filter = column.filter;
        if (filter && !filter.isGridFilter) {
            this.createColumnFilter(column);
        }
    },
    
    onMenuCreate: function(headerCt, menu) {
        menu.on({
            beforeshow: this.onMenuBeforeShow,
            scope: this
        });
    },
    
    onMenuBeforeShow: function(menu) {
        var me = this,
            menuItem, filter, ownerGrid, ownerGridId;
        if (me.showMenu) {
            
            
            if (!me.menuItems) {
                me.menuItems = {};
            }
            
            ownerGrid = menu.up('grid');
            ownerGridId = ownerGrid.id;
            menuItem = me.menuItems[ownerGridId];
            if (!menuItem || menuItem.isDestroyed) {
                menuItem = me.createMenuItem(menu, ownerGridId);
            }
            me.activeFilterMenuItem = menuItem;
            filter = me.getMenuFilter(ownerGrid.headerCt);
            if (filter) {
                filter.showMenu(menuItem);
            }
            menuItem.setVisible(!!filter);
            me.sep.setVisible(!!filter);
        }
    },
    createMenuItem: function(menu, ownerGridId) {
        var me = this,
            item;
        me.sep = menu.add('-');
        item = menu.add({
            checked: false,
            itemId: 'filters',
            text: me.menuFilterText,
            listeners: {
                scope: me,
                checkchange: me.onCheckChange
            }
        });
        return (me.menuItems[ownerGridId] = item);
    },
    
    onGridDestroy: function() {
        var me = this,
            menuItems = me.menuItems,
            item;
        me.bindStore(null);
        me.sep = Ext.destroy(me.sep);
        for (item in menuItems) {
            menuItems[item].destroy();
        }
        me.grid = null;
    },
    onUnbindStore: function(store) {
        store.getFilters().un('remove', this.onFilterRemove, this);
    },
    onBindStore: function(store, initial, propName) {
        this.local = !store.getRemoteFilter();
        store.getFilters().on('remove', this.onFilterRemove, this);
    },
    onFilterRemove: function(filterCollection, list) {
        
        
        
        
        var len = list.items.length,
            columnManager = this.grid.columnManager,
            i, item, header, filter;
        for (i = 0; i < len; i++) {
            item = list.items[i];
            header = columnManager.getHeaderByDataIndex(item.getProperty());
            if (header) {
                
                
                
                
                
                
                
                
                
                
                
                
                filter = header.filter;
                if (!filter || !filter.menu || item.getId().indexOf(filter.getBaseIdPrefix()) === -1) {
                    
                    continue;
                }
                if (!filter.preventFilterRemoval) {
                    
                    filter.onFilterRemove(item.getOperator());
                }
            }
        }
    },
    
    getMenuFilter: function(headerCt) {
        return headerCt.getMenu().activeHeader.filter;
    },
    
    onCheckChange: function(item, value) {
        
        var grid = this.isLocked ? item.up('grid') : this.grid,
            filter = this.getMenuFilter(grid.headerCt);
        filter.setActive(value);
    },
    getHeaders: function() {
        return this.grid.view.headerCt.columnManager.getColumns();
    },
    
    isStateful: function() {
        return this.grid.stateful;
    },
    
    addFilter: function(filters) {
        var me = this,
            grid = me.grid,
            store = me.store,
            hasNewColumns = false,
            suppressNextFilter = true,
            dataIndex, column, i, len, filter, columnFilter;
        if (!Ext.isArray(filters)) {
            filters = [
                filters
            ];
        }
        for (i = 0 , len = filters.length; i < len; i++) {
            filter = filters[i];
            dataIndex = filter.dataIndex;
            column = grid.columnManager.getHeaderByDataIndex(dataIndex);
            
            if (column) {
                hasNewColumns = true;
                
                if (filter.value) {
                    suppressNextFilter = false;
                }
                columnFilter = column.filter;
                
                if (columnFilter && columnFilter.isGridFilter) {
                    columnFilter.deactivate();
                    columnFilter.destroy();
                    if (me.activeFilterMenuItem) {
                        me.activeFilterMenuItem.menu = null;
                    }
                }
                column.filter = filter;
            }
        }
        
        if (hasNewColumns) {
            store.suppressNextFilter = suppressNextFilter;
            me.initColumns();
            store.suppressNextFilter = false;
        }
    },
    
    addFilters: function(filters) {
        if (filters) {
            this.addFilter(filters);
        }
    },
    
    clearFilters: function(autoFilter) {
        var grid = this.grid,
            columns = grid.columnManager.getColumns(),
            store = grid.store,
            oldAutoFilter = store.getAutoFilter(),
            column, filter, i, len, filterCollection;
        if (autoFilter !== undefined) {
            store.setAutoFilter(autoFilter);
        }
        
        for (i = 0 , len = columns.length; i < len; i++) {
            column = columns[i];
            filter = column.filter;
            if (filter && filter.isGridFilter) {
                if (!filterCollection) {
                    filterCollection = store.getFilters();
                    filterCollection.beginUpdate();
                }
                filter.setActive(false);
            }
        }
        if (filterCollection) {
            filterCollection.endUpdate();
        }
        if (autoFilter !== undefined) {
            store.setAutoFilter(oldAutoFilter);
        }
    },
    onBeforeReconfigure: function(grid, store, columns) {
        if (store) {
            store.getFilters().beginUpdate();
        }
        this.reconfiguring = true;
    },
    onReconfigure: function(grid, store, columns, oldStore) {
        var me = this;
        if (store && oldStore !== store) {
            me.bindStore(store);
        }
        if (columns) {
            me.initColumns();
        }
        if (store) {
            store.getFilters().endUpdate();
        }
        me.reconfiguring = false;
    }
});


Ext.define('Ext.grid.locking.HeaderContainer', {
    extend: 'Ext.grid.header.Container',
    requires: [
        'Ext.grid.ColumnManager'
    ],
    headerCtRelayEvents: [
        "blur",
        "focus",
        "move",
        "resize",
        "destroy",
        "beforedestroy",
        "boxready",
        "afterrender",
        "render",
        "beforerender",
        "removed",
        "hide",
        "beforehide",
        "show",
        "beforeshow",
        "enable",
        "disable",
        "added",
        "deactivate",
        "beforedeactivate",
        "activate",
        "beforeactivate",
        "remove",
        "add",
        "beforeremove",
        "beforeadd",
        "afterlayout",
        "menucreate",
        "sortchange",
        "columnschanged",
        "columnshow",
        "columnhide",
        "columnmove",
        "headertriggerclick",
        "headercontextmenu",
        "headerclick",
        "columnresize",
        "statesave",
        "beforestatesave",
        "staterestore",
        "beforestaterestore"
    ],
    constructor: function(lockable) {
        var me = this,
            lockedGrid = lockable.lockedGrid,
            normalGrid = lockable.normalGrid;
        me.lockable = lockable;
        me.callParent();
        
        lockedGrid.visibleColumnManager.rootColumns = normalGrid.visibleColumnManager.rootColumns = lockable.visibleColumnManager = me.visibleColumnManager = new Ext.grid.ColumnManager(true,lockedGrid.headerCt,normalGrid.headerCt);
        lockedGrid.columnManager.rootColumns = normalGrid.columnManager.rootColumns = lockable.columnManager = me.columnManager = new Ext.grid.ColumnManager(false,lockedGrid.headerCt,normalGrid.headerCt);
        
        me.relayEvents(lockedGrid.headerCt, me.headerCtRelayEvents);
        me.relayEvents(normalGrid.headerCt, me.headerCtRelayEvents);
    },
    getRefItems: function() {
        return this.lockable.lockedGrid.headerCt.getRefItems().concat(this.lockable.normalGrid.headerCt.getRefItems());
    },
    
    
    getGridColumns: function() {
        return this.lockable.lockedGrid.headerCt.getGridColumns().concat(this.lockable.normalGrid.headerCt.getGridColumns());
    },
    
    getColumnsState: function() {
        var me = this,
            locked = me.lockable.lockedGrid.headerCt.getColumnsState(),
            normal = me.lockable.normalGrid.headerCt.getColumnsState();
        return locked.concat(normal);
    },
    
    applyColumnsState: function(columns) {
        var me = this,
            lockedGrid = me.lockable.lockedGrid,
            lockedHeaderCt = lockedGrid.headerCt,
            normalHeaderCt = me.lockable.normalGrid.headerCt,
            lockedCols = Ext.Array.toValueMap(lockedHeaderCt.items.items, 'headerId'),
            normalCols = Ext.Array.toValueMap(normalHeaderCt.items.items, 'headerId'),
            locked = [],
            normal = [],
            lockedWidth = 1,
            length = columns.length,
            i, existing, lockedDefault, col;
        for (i = 0; i < length; i++) {
            col = columns[i];
            lockedDefault = lockedCols[col.id];
            existing = lockedDefault || normalCols[col.id];
            if (existing) {
                if (existing.applyColumnState) {
                    existing.applyColumnState(col);
                }
                if (existing.locked === undefined) {
                    existing.locked = !!lockedDefault;
                }
                if (existing.locked) {
                    locked.push(existing);
                    if (!existing.hidden && typeof existing.width === 'number') {
                        lockedWidth += existing.width;
                    }
                } else {
                    normal.push(existing);
                }
            }
        }
        
        if (locked.length + normal.length === lockedHeaderCt.items.getCount() + normalHeaderCt.items.getCount()) {
            lockedHeaderCt.removeAll(false);
            normalHeaderCt.removeAll(false);
            lockedHeaderCt.add(locked);
            normalHeaderCt.add(normal);
            lockedGrid.setWidth(lockedWidth);
        }
    },
    disable: function() {
        var topGrid = this.lockable;
        topGrid.lockedGrid.headerCt.disable();
        topGrid.normalGrid.headerCt.disable();
    },
    enable: function() {
        var topGrid = this.lockable;
        topGrid.lockedGrid.headerCt.enable();
        topGrid.normalGrid.headerCt.enable();
    }
});


Ext.define('Ext.grid.locking.View', {
    alternateClassName: 'Ext.grid.LockingView',
    requires: [
        'Ext.view.AbstractView',
        'Ext.view.Table'
    ],
    mixins: [
        'Ext.util.Observable',
        'Ext.util.StoreHolder',
        'Ext.util.Focusable'
    ],
    
    isLockingView: true,
    loadMask: true,
    eventRelayRe: /^(beforeitem|beforecontainer|item|container|cell|refresh)/,
    constructor: function(config) {
        var me = this,
            lockedView, normalView;
        me.ownerGrid = config.ownerGrid;
        me.ownerGrid.view = me;
        
        me.navigationModel = config.locked.xtype === 'treepanel' ? new Ext.tree.NavigationModel(me) : new Ext.grid.NavigationModel(me);
        
        
        
        
        config.locked.viewConfig.bindStore = Ext.emptyFn;
        config.normal.viewConfig.bindStore = me.subViewBindStore;
        config.normal.viewConfig.isNormalView = config.locked.viewConfig.isLockedView = true;
        
        
        
        config.locked.viewConfig.beforeLayout = config.normal.viewConfig.beforeLayout = me.beforeLayout;
        
        config.locked.viewConfig.navigationModel = config.normal.viewConfig.navigationModel = me.navigationModel;
        me.lockedGrid = me.ownerGrid.lockedGrid = Ext.ComponentManager.create(config.locked);
        me.lockedView = lockedView = me.lockedGrid.getView();
        
        
        
        if (me.ownerGrid.shrinkWrapLocked) {
            me.lockedGrid.width += (Ext.num(lockedView.getSelectionModel().headerWidth, 0) + (me.lockedGrid.getVisibleColumnManager().getColumns().length ? 1 : 0));
        }
        
        me.selModel = config.normal.viewConfig.selModel = lockedView.getSelectionModel();
        if (me.lockedGrid.isTree) {
            
            me.lockedView.animate = false;
            
            config.normal.store = lockedView.store;
            
            config.normal.viewConfig.stripeRows = me.lockedView.stripeRows;
            config.normal.rowLines = me.lockedGrid.rowLines;
        }
        
        
        
        
        me.normalGrid = me.ownerGrid.normalGrid = Ext.ComponentManager.create(config.normal);
        lockedView.lockingPartner = normalView = me.normalView = me.normalGrid.getView();
        normalView.lockingPartner = lockedView;
        me.loadMask = (config.loadMask !== undefined) ? config.loadMask : me.loadMask;
        me.mixins.observable.constructor.call(me);
        
        me.relayEvents(lockedView, Ext.view.Table.events);
        me.relayEvents(normalView, Ext.view.Table.events);
        normalView.on({
            scope: me,
            itemmouseleave: me.onItemMouseLeave,
            itemmouseenter: me.onItemMouseEnter
        });
        lockedView.on({
            scope: me,
            itemmouseleave: me.onItemMouseLeave,
            itemmouseenter: me.onItemMouseEnter
        });
        me.ownerGrid.on({
            render: me.onPanelRender,
            scope: me
        });
        me.loadingText = normalView.loadingText;
        me.loadingCls = normalView.loadingCls;
        me.loadingUseMsg = normalView.loadingUseMsg;
        
        
        me.all = normalView.all;
        
        
        
        me.bindStore(normalView.dataSource, true, 'dataSource');
    },
    
    
    subViewBindStore: function(dataSource) {
        var selModel = this.getSelectionModel();
        
        
        if (dataSource !== null) {
            dataSource = this.store;
        }
        selModel.bindStore(dataSource);
        selModel.bindComponent(this);
    },
    
    beforeLayout: function() {
        
        var me = this.ownerCt.ownerLockable.view,
            lockedView = me.lockedGrid.view,
            normalView = me.normalGrid.view;
        if (!me.relayingOperation) {
            
            
            if (me.lockedGrid.isVisible()) {
                if (lockedView.refreshNeeded) {
                    lockedView.doFirstRefresh(lockedView.dataSource);
                }
            }
            if (normalView.refreshNeeded) {
                normalView.doFirstRefresh(normalView.dataSource);
            }
        }
    },
    onPanelRender: function() {
        var me = this,
            mask = me.loadMask,
            cfg = {
                target: me.ownerGrid,
                msg: me.loadingText,
                msgCls: me.loadingCls,
                useMsg: me.loadingUseMsg,
                store: me.ownerGrid.store
            };
        
        
        me.el = me.ownerGrid.getTargetEl();
        me.rendered = true;
        me.initFocusableEvents();
        me.fireEvent('render', me);
        if (mask) {
            
            if (Ext.isObject(mask)) {
                cfg = Ext.apply(cfg, mask);
            }
            
            
            
            
            me.loadMask = new Ext.LoadMask(cfg);
        }
    },
    getRefOwner: function() {
        return this.ownerGrid;
    },
    
    
    getVisibleColumnManager: function() {
        
        
        return this.ownerGrid.getVisibleColumnManager();
    },
    getTopLevelVisibleColumnManager: function() {
        
        
        return this.ownerGrid.getVisibleColumnManager();
    },
    getGridColumns: function() {
        return this.getVisibleColumnManager().getColumns();
    },
    getEl: function(column) {
        return this.getViewForColumn(column).getEl();
    },
    getCellSelector: function() {
        return this.normalView.getCellSelector();
    },
    getViewForColumn: function(column) {
        var view = this.lockedView,
            inLocked;
        view.headerCt.cascade(function(col) {
            if (col === column) {
                inLocked = true;
                return false;
            }
        });
        return inLocked ? view : this.normalView;
    },
    onItemMouseEnter: function(view, record) {
        var me = this,
            locked = me.lockedView,
            other = me.normalView,
            item;
        if (view.trackOver) {
            if (view !== locked) {
                other = locked;
            }
            item = other.getNode(record);
            other.highlightItem(item);
        }
    },
    onItemMouseLeave: function(view, record) {
        var me = this,
            locked = me.lockedView,
            other = me.normalView;
        if (view.trackOver) {
            if (view !== locked) {
                other = locked;
            }
            other.clearHighlight();
        }
    },
    relayFn: function(name, args) {
        args = args || [];
        var me = this,
            view = me.lockedView;
        
        
        me.relayingOperation = true;
        view[name].apply(view, args);
        view = me.normalView;
        view[name].apply(view, args);
        me.relayingOperation = false;
    },
    getSelectionModel: function() {
        return this.normalView.getSelectionModel();
    },
    getNavigationModel: function() {
        return this.navigationModel;
    },
    getStore: function() {
        return this.ownerGrid.store;
    },
    
    onBindStore: function(store, initial, propName) {
        var me = this;
        
        
        
        if (me.normalView.componentLayoutCounter) {
            Ext.suspendLayouts();
            me.normalView.doFirstRefresh(store);
            me.lockedView.doFirstRefresh(store);
            Ext.resumeLayouts(true);
        }
    },
    getStoreListeners: function() {
        var me = this;
        return {
            refresh: me.onDataRefresh,
            replace: me.onReplace,
            add: me.onAdd,
            remove: me.onRemove,
            update: me.onUpdate,
            clear: me.refresh,
            beginupdate: me.onBeginUpdate,
            endupdate: me.onEndUpdate
        };
    },
    onBeginUpdate: function() {
        Ext.suspendLayouts();
        this.relayFn('onBeginUpdate', arguments);
        Ext.resumeLayouts(true);
    },
    onEndUpdate: function() {
        Ext.suspendLayouts();
        this.relayFn('onEndUpdate', arguments);
        Ext.resumeLayouts(true);
    },
    onDataRefresh: function() {
        Ext.suspendLayouts();
        this.relayFn('onDataRefresh', arguments);
        Ext.resumeLayouts(true);
    },
    onReplace: function() {
        Ext.suspendLayouts();
        this.relayFn('onReplace', arguments);
        Ext.resumeLayouts(true);
    },
    onAdd: function() {
        Ext.suspendLayouts();
        this.relayFn('onAdd', arguments);
        Ext.resumeLayouts(true);
    },
    onRemove: function() {
        Ext.suspendLayouts();
        this.relayFn('onRemove', arguments);
        Ext.resumeLayouts(true);
    },
    onUpdate: function() {
        var normalView = this.normalGrid.view;
        Ext.suspendLayouts();
        this.relayFn('onUpdate', arguments);
        
        
        
        if (normalView.hasVariableRowHeight() && normalView.bufferedRenderer) {
            Ext.on({
                afterlayout: normalView.bufferedRenderer.refreshSize,
                scope: normalView.bufferedRenderer,
                single: true
            });
        }
        Ext.resumeLayouts(true);
    },
    refresh: function() {
        Ext.suspendLayouts();
        this.relayFn('refresh', arguments);
        Ext.resumeLayouts(true);
    },
    getNode: function(nodeInfo) {
        
        return this.normalView.getNode(nodeInfo);
    },
    getRow: function(nodeInfo) {
        
        return this.normalView.getRow(nodeInfo);
    },
    getCell: function(record, column) {
        var view = this.getViewForColumn(column),
            row = view.getRow(record);
        return Ext.fly(row).down(column.getCellSelector());
    },
    indexOf: function(record) {
        var result = this.lockedView.indexOf(record);
        if (!result) {
            result = this.normalView.indexOf(record);
        }
        return result;
    },
    focus: function() {
        
        var target = this.ownerGrid.down('>tablepanel:not(hidden)>tableview');
        if (target) {
            target.focus();
        }
    },
    focusRow: function(row) {
        var view,
            
            
            lastFocused = this.getNavigationModel().lastFocused;
        view = lastFocused ? lastFocused.view : this.normalView;
        view.focusRow(row);
    },
    focusCell: function(position) {
        position.view.focusCell(position);
    },
    onRowFocus: function() {
        this.relayFn('onRowFocus', arguments);
    },
    isVisible: function(deep) {
        return this.ownerGrid.isVisible(deep);
    },
    getFocusEl: function() {
        var view,
            
            
            lastFocused = this.getNavigationModel().lastFocused;
        view = lastFocused ? lastFocused.view : this.normalView;
        return view.getFocusEl();
    },
    
    
    getCellInclusive: function(pos, returnDom) {
        var col = pos.column,
            lockedSize = this.lockedGrid.getColumnManager().getColumns().length;
        
        if (col >= lockedSize) {
            
            pos = Ext.apply({}, pos);
            pos.column -= lockedSize;
            return this.normalView.getCellInclusive(pos, returnDom);
        } else {
            return this.lockedView.getCellInclusive(pos, returnDom);
        }
    },
    getHeaderByCell: function(cell) {
        if (cell) {
            return this.getVisibleColumnManager().getHeaderById(cell.getAttribute('data-columnId'));
        }
        return false;
    },
    onRowSelect: function() {
        this.relayFn('onRowSelect', arguments);
    },
    onRowDeselect: function() {
        this.relayFn('onRowDeselect', arguments);
    },
    onCellSelect: function(cellContext) {
        
        cellContext.column.getView().onCellSelect({
            record: cellContext.record,
            column: cellContext.column
        });
    },
    onCellDeselect: function(cellContext) {
        
        cellContext.column.getView().onCellDeselect({
            record: cellContext.record,
            column: cellContext.column
        });
    },
    getCellByPosition: function(pos, returnDom) {
        var me = this,
            view = pos.view,
            col = pos.column;
        
        if (view === me) {
            view = col.getView();
        }
        return view.getCellByPosition(pos, returnDom);
    },
    getRecord: function(node) {
        var result = this.lockedView.getRecord(node);
        if (!result) {
            result = this.normalView.getRecord(node);
        }
        return result;
    },
    scrollBy: function() {
        var normal = this.normalView;
        normal.scrollBy.apply(normal, arguments);
    },
    ensureVisible: function() {
        var normal = this.normalView;
        normal.ensureVisible.apply(normal, arguments);
    },
    disable: function() {
        this.relayFn('disable', arguments);
    },
    enable: function() {
        this.relayFn('enable', arguments);
    },
    addElListener: function() {
        this.relayFn('addElListener', arguments);
    },
    refreshNode: function() {
        this.relayFn('refreshNode', arguments);
    },
    addRowCls: function() {
        this.relayFn('addRowCls', arguments);
    },
    removeRowCls: function() {
        this.relayFn('removeRowCls', arguments);
    },
    destroy: function() {
        var me = this;
        
        me.bindStore(null, false, 'dataSource');
        
        me.isDestroyed = true;
        
        
        me.clearListeners();
        Ext.destroy(me.loadMask, me.navigationModel, me.selModel);
    }
}, function() {
    this.borrow(Ext.Component, [
        'up'
    ]);
    this.borrow(Ext.view.AbstractView, [
        'doFirstRefresh',
        'applyFirstRefresh'
    ]);
    this.borrow(Ext.view.Table, [
        'cellSelector',
        'selectedCellCls',
        'selectedItemCls'
    ]);
});


Ext.define('Ext.grid.locking.Lockable', {
    alternateClassName: 'Ext.grid.Lockable',
    requires: [
        'Ext.grid.locking.View',
        'Ext.grid.header.Container',
        'Ext.grid.locking.HeaderContainer',
        'Ext.view.Table'
    ],
    
    syncRowHeight: true,
    
    
    
    headerCounter: 0,
    
    scrollDelta: 40,
    
    
    
    
    lockedGridCls: Ext.baseCSSPrefix + 'grid-inner-locked',
    normalGridCls: Ext.baseCSSPrefix + 'grid-inner-normal',
    
    
    unlockText: 'Unlock',
    
    
    lockText: 'Lock',
    
    
    
    bothCfgCopy: [
        'invalidateScrollerOnRefresh',
        'hideHeaders',
        'enableColumnHide',
        'enableColumnMove',
        'enableColumnResize',
        'sortableColumns',
        'multiColumnSort',
        'columnLines',
        'rowLines',
        'variableRowHeight',
        'numFromEdge',
        'trailingBufferZone',
        'leadingBufferZone',
        'scrollToLoadBuffer'
    ],
    normalCfgCopy: [
        'verticalScroller',
        'verticalScrollDock',
        'verticalScrollerType',
        'scroll'
    ],
    lockedCfgCopy: [],
    
    
    
    determineXTypeToCreate: function(lockedSide) {
        var me = this,
            typeToCreate, xtypes, xtypesLn, xtype, superxtype;
        if (me.subGridXType) {
            typeToCreate = me.subGridXType;
        } else {
            
            
            if (!lockedSide) {
                return 'gridpanel';
            }
            xtypes = this.getXTypes().split('/');
            xtypesLn = xtypes.length;
            xtype = xtypes[xtypesLn - 1];
            superxtype = xtypes[xtypesLn - 2];
            if (superxtype !== 'tablepanel') {
                typeToCreate = superxtype;
            } else {
                typeToCreate = xtype;
            }
        }
        return typeToCreate;
    },
    
    
    injectLockable: function() {
        
        this.focusable = false;
        
        this.lockable = true;
        
        
        this.hasView = true;
        var me = this,
            scrollbarSize = Ext.getScrollbarSize(),
            scrollbarWidth = scrollbarSize.width,
            store = me.store = Ext.StoreManager.lookup(me.store),
            lockedViewConfig = me.lockedViewConfig,
            normalViewConfig = me.normalViewConfig,
            Obj = Ext.Object,
            
            allFeatures, 
            allPlugins, lockedGrid, normalGrid, i, columns, lockedHeaderCt, normalHeaderCt,
            viewConfig = me.viewConfig,
            
            loadMaskCfg = viewConfig && viewConfig.loadMask,
            loadMask = (loadMaskCfg !== undefined) ? loadMaskCfg : me.loadMask,
            bufferedRenderer = me.bufferedRenderer,
            clipVertLockedScrollbar = scrollbarWidth > 0 && Ext.supports.touchScroll !== 2;
        allFeatures = me.constructLockableFeatures();
        
        me.features = null;
        
        allPlugins = me.constructLockablePlugins();
        me.plugins = allPlugins.topPlugins;
        lockedGrid = {
            id: me.id + '-locked',
            isLocked: true,
            bufferedRenderer: bufferedRenderer,
            ownerGrid: me,
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(true),
            store: store,
            
            
            
            reserveScrollbar: clipVertLockedScrollbar,
            scrollable: {
                indicators: {
                    x: true,
                    y: false
                }
            },
            scrollerOwner: false,
            
            
            animate: false,
            border: false,
            cls: me.lockedGridCls,
            
            
            
            
            isLayoutRoot: function() {
                return this.floatedFromCollapse || me.normalGrid.floatedFromCollapse;
            },
            features: allFeatures.lockedFeatures,
            plugins: allPlugins.lockedPlugins
        };
        normalGrid = {
            id: me.id + '-normal',
            isLocked: false,
            bufferedRenderer: bufferedRenderer,
            ownerGrid: me,
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(),
            store: store,
            
            reserveScrollbar: me.reserveScrollbar,
            scrollerOwner: false,
            border: false,
            cls: me.normalGridCls,
            
            isLayoutRoot: function() {
                return this.floatedFromCollapse || me.lockedGrid.floatedFromCollapse;
            },
            features: allFeatures.normalFeatures,
            plugins: allPlugins.normalPlugins
        };
        me.addCls(Ext.baseCSSPrefix + 'grid-locked');
        
        
        
        Ext.copyTo(normalGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(normalGrid, me, me.normalCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.lockedCfgCopy, true);
        Ext.apply(normalGrid, me.normalGridConfig);
        Ext.apply(lockedGrid, me.lockedGridConfig);
        for (i = 0; i < me.normalCfgCopy.length; i++) {
            delete me[me.normalCfgCopy[i]];
        }
        for (i = 0; i < me.lockedCfgCopy.length; i++) {
            delete me[me.lockedCfgCopy[i]];
        }
        me.addStateEvents([
            'lockcolumn',
            'unlockcolumn'
        ]);
        columns = me.processColumns(me.columns || [], lockedGrid);
        lockedGrid.columns = columns.locked;
        
        if (!lockedGrid.columns.items.length) {
            lockedGrid.hidden = true;
        }
        normalGrid.columns = columns.normal;
        if (!normalGrid.columns.items.length) {
            normalGrid.hidden = true;
        }
        
        normalGrid.flex = 1;
        
        lockedGrid.viewConfig = lockedViewConfig = (lockedViewConfig ? Obj.chain(lockedViewConfig) : {});
        normalGrid.viewConfig = normalViewConfig = (normalViewConfig ? Obj.chain(normalViewConfig) : {});
        lockedViewConfig.loadingUseMsg = false;
        lockedViewConfig.loadMask = false;
        if (clipVertLockedScrollbar) {
            lockedViewConfig.margin = '0 -' + scrollbarWidth + ' 0 0';
        }
        normalViewConfig.loadMask = false;
        if (viewConfig && viewConfig.id) {
            Ext.log.warn('id specified on Lockable viewConfig, it will be shared between both views: "' + viewConfig.id + '"');
        }
        Ext.applyIf(lockedViewConfig, viewConfig);
        Ext.applyIf(normalViewConfig, viewConfig);
        
        
        if (!me.initialConfig.layout) {
            me.layout = {
                type: 'hbox',
                align: 'stretch'
            };
        }
        me.getLayout();
        
        
        if (me.layout.type === 'border') {
            if (me.split) {
                lockedGrid.split = true;
            }
            if (!lockedGrid.region) {
                lockedGrid.region = 'west';
            }
            if (!normalGrid.region) {
                normalGrid.region = 'center';
            }
            me.addCls(Ext.baseCSSPrefix + 'grid-locked-split');
        }
        if (!(me.layout instanceof Ext.layout.container.Box)) {
            me.split = false;
        }
        
        
        me.view = new Ext.grid.locking.View({
            loadMask: loadMask,
            locked: lockedGrid,
            normal: normalGrid,
            ownerGrid: me
        });
        
        
        lockedGrid = me.lockedGrid;
        normalGrid = me.normalGrid;
        
        normalGrid.getView().getScrollable().addPartner(lockedGrid.getView().getScrollable(), 'y');
        
        
        
        
        
        
        
        
        if (scrollbarSize.height && Ext.supports.touchScroll !== 2) {
            lockedGrid.on({
                afterlayout: me.afterLockedViewLayout,
                scope: me
            });
            
            lockedGrid.getView().getOverflowStyle();
        }
        lockedHeaderCt = lockedGrid.headerCt;
        normalHeaderCt = normalGrid.headerCt;
        if (clipVertLockedScrollbar) {
            
            
            lockedHeaderCt.reserveScrollbar = false;
        }
        
        
        me.headerCt = me.view.headerCt = new Ext.grid.locking.HeaderContainer(me);
        lockedHeaderCt.lockedCt = true;
        lockedHeaderCt.lockableInjected = true;
        normalHeaderCt.lockableInjected = true;
        lockedHeaderCt.on({
            add: me.delaySyncLockedWidth,
            remove: me.delaySyncLockedWidth,
            columnshow: me.delaySyncLockedWidth,
            columnhide: me.delaySyncLockedWidth,
            sortchange: me.onLockedHeaderSortChange,
            columnresize: me.delaySyncLockedWidth,
            scope: me
        });
        normalHeaderCt.on({
            add: me.delaySyncLockedWidth,
            remove: me.delaySyncLockedWidth,
            columnshow: me.delaySyncLockedWidth,
            columnhide: me.delaySyncLockedWidth,
            sortchange: me.onNormalHeaderSortChange,
            scope: me
        });
        me.modifyHeaderCt();
        me.items = [
            lockedGrid
        ];
        if (me.split) {
            me.addCls(Ext.baseCSSPrefix + 'grid-locked-split');
            me.items[1] = {
                xtype: 'splitter'
            };
        }
        me.items.push(normalGrid);
        me.relayHeaderCtEvents(lockedHeaderCt);
        me.relayHeaderCtEvents(normalHeaderCt);
        
        
        me.storeRelayers = me.relayEvents(store, [
            
            'filterchange',
            
            'groupchange'
        ]);
        
        
        me.gridRelayers = me.relayEvents(normalGrid, [
            
            'viewready'
        ]);
    },
    getLockingViewConfig: function() {
        return {
            xclass: 'Ext.grid.locking.View',
            locked: this.lockedGrid,
            normal: this.normalGrid,
            panel: this
        };
    },
    processColumns: function(columns, lockedGrid) {
        
        var me = this,
            i, len, column,
            cp = new Ext.grid.header.Container(),
            lockedHeaders = [],
            normalHeaders = [],
            lockedHeaderCt = {
                itemId: 'lockedHeaderCt',
                stretchMaxPartner: '^^>>#normalHeaderCt',
                items: lockedHeaders
            },
            normalHeaderCt = {
                itemId: 'normalHeaderCt',
                stretchMaxPartner: '^^>>#lockedHeaderCt',
                items: normalHeaders
            },
            result = {
                lockedWidth: lockedGrid.width || 0,
                locked: lockedHeaderCt,
                normal: normalHeaderCt
            },
            shrinkWrapLocked = !(lockedGrid.width || lockedGrid.flex),
            copy;
        
        
        if (!me.hasOwnProperty('shrinkWrapLocked')) {
            me.shrinkWrapLocked = shrinkWrapLocked;
        }
        
        if (Ext.isObject(columns)) {
            Ext.applyIf(lockedHeaderCt, columns);
            Ext.applyIf(normalHeaderCt, columns);
            copy = Ext.apply({}, columns);
            delete copy.items;
            Ext.apply(cp, copy);
            columns = columns.items;
        }
        
        
        cp.constructing = true;
        for (i = 0 , len = columns.length; i < len; ++i) {
            column = columns[i];
            
            
            
            if (!column.isComponent) {
                column = cp.applyDefaults(column);
                column.initOwnerCt = cp;
                column = cp.lookupComponent(column);
                delete column.initOwnerCt;
            }
            
            
            column.processed = true;
            if (column.locked || column.autoLock) {
                
                
                if (shrinkWrapLocked && !column.hidden) {
                    result.lockedWidth += me.getColumnWidth(column) || cp.defaultWidth;
                }
                lockedHeaders.push(column);
            } else {
                normalHeaders.push(column);
            }
            if (!column.headerId) {
                column.headerId = (column.initialConfig || column).id || ('h' + (++me.headerCounter));
            }
        }
        me.fireEvent('processcolumns', me, lockedHeaders, normalHeaders);
        cp.destroy();
        
        
        if (shrinkWrapLocked) {
            lockedGrid.width = result.lockedWidth;
        }
        return result;
    },
    
    
    getColumnWidth: function(column) {
        var result = column.width || 0,
            subcols, len, i;
        if (column.flex) {
            Ext.Error.raise("Locked columns in an unsized locked side do NOT support a flex width. You must set a width on the " + column.text + "column.");
        }
        if (!result && column.isGroupHeader) {
            subcols = column.items.items;
            len = subcols.length;
            for (i = 0; i < len; i++) {
                result += this.getColumnWidth(subcols[i]);
            }
        }
        return result;
    },
    
    
    
    
    afterLockedViewLayout: function() {
        var me = this,
            lockedGrid = me.lockedGrid,
            normalGrid = me.normalGrid,
            lockedView = lockedGrid.getView(),
            normalView = normalGrid.getView(),
            spacerHeight = Ext.getScrollbarSize().height,
            lockedViewHorizScrollBar = (lockedView.scrollFlags.x && lockedGrid.headerCt.tooNarrow ? spacerHeight : 0),
            normalViewHorizScrollBar = (normalView.scrollFlags.x && normalGrid.headerCt.tooNarrow ? spacerHeight : 0),
            normalScroller = normalView.getScrollable(),
            lockedScroller = lockedView.getScrollable();
        if (lockedViewHorizScrollBar !== normalViewHorizScrollBar) {
            if (lockedViewHorizScrollBar) {
                normalScroller.setX('scroll');
                lockedScroller.setX(true);
            } else {
                lockedScroller.setX('scroll');
                normalScroller.setX(true);
            }
        } else {
            lockedScroller.setX(normalViewHorizScrollBar ? 'scroll' : true);
            normalScroller.setX(true);
        }
    },
    ensureLockedVisible: function() {
        this.lockedGrid.ensureVisible.apply(this.lockedGrid, arguments);
        this.normalGrid.ensureVisible.apply(this.normalGrid, arguments);
    },
    
    syncRowHeights: function() {
        
        if (!this.isDestroyed) {
            var me = this,
                normalView = me.normalGrid.getView(),
                lockedView = me.lockedGrid.getView(),
                
                normalSync = normalView.syncRowHeightBegin(),
                lockedSync = lockedView.syncRowHeightBegin(),
                scrollTop;
            
            normalView.syncRowHeightMeasure(normalSync);
            lockedView.syncRowHeightMeasure(lockedSync);
            
            normalView.syncRowHeightFinish(normalSync, lockedSync);
            lockedView.syncRowHeightFinish(lockedSync, normalSync);
            
            scrollTop = normalView.getScrollY();
            lockedView.setScrollY(scrollTop);
        }
    },
    
    
    modifyHeaderCt: function() {
        var me = this;
        me.lockedGrid.headerCt.getMenuItems = me.getMenuItems(me.lockedGrid.headerCt.getMenuItems, true);
        me.normalGrid.headerCt.getMenuItems = me.getMenuItems(me.normalGrid.headerCt.getMenuItems, false);
        me.lockedGrid.headerCt.showMenuBy = Ext.Function.createInterceptor(me.lockedGrid.headerCt.showMenuBy, me.showMenuBy);
        me.normalGrid.headerCt.showMenuBy = Ext.Function.createInterceptor(me.normalGrid.headerCt.showMenuBy, me.showMenuBy);
    },
    onUnlockMenuClick: function() {
        this.unlock();
    },
    onLockMenuClick: function() {
        this.lock();
    },
    showMenuBy: function(clickEvent, t, header) {
        var menu = this.getMenu(),
            unlockItem = menu.down('#unlockItem'),
            lockItem = menu.down('#lockItem'),
            sep = unlockItem.prev();
        if (header.lockable === false) {
            sep.hide();
            unlockItem.hide();
            lockItem.hide();
        } else {
            sep.show();
            unlockItem.show();
            lockItem.show();
            if (!unlockItem.initialConfig.disabled) {
                unlockItem.setDisabled(header.lockable === false);
            }
            if (!lockItem.initialConfig.disabled) {
                lockItem.setDisabled(!header.isLockable());
            }
        }
    },
    getMenuItems: function(getMenuItems, locked) {
        var me = this,
            unlockText = me.unlockText,
            lockText = me.lockText,
            unlockCls = Ext.baseCSSPrefix + 'hmenu-unlock',
            lockCls = Ext.baseCSSPrefix + 'hmenu-lock',
            unlockHandler = me.onUnlockMenuClick.bind(me),
            lockHandler = me.onLockMenuClick.bind(me);
        
        return function() {
            
            
            var o = getMenuItems.call(this);
            o.push('-', {
                itemId: 'unlockItem',
                iconCls: unlockCls,
                text: unlockText,
                handler: unlockHandler,
                disabled: !locked
            });
            o.push({
                itemId: 'lockItem',
                iconCls: lockCls,
                text: lockText,
                handler: lockHandler,
                disabled: locked
            });
            return o;
        };
    },
    syncTaskDelay: 1,
    delaySyncLockedWidth: function() {
        var me = this,
            task = me.syncLockedWidthTask;
        if (!task) {
            task = me.syncLockedWidthTask = new Ext.util.DelayedTask(me.syncLockedWidth,me);
        }
        
        if (me.syncTaskDelay === 0) {
            me.syncLockedWidth();
        } else {
            task.delay(1);
        }
    },
    
    syncLockedWidth: function() {
        var me = this,
            locked = me.lockedGrid,
            lockedView = locked.view,
            lockedViewEl = lockedView.el.dom,
            normal = me.normalGrid,
            lockedColCount = locked.getVisibleColumnManager().getColumns().length,
            normalColCount = normal.getVisibleColumnManager().getColumns().length,
            task = me.syncLockedWidthTask;
        
        if (task) {
            task.cancel();
        }
        Ext.suspendLayouts();
        
        
        if (normalColCount) {
            normal.show();
            if (lockedColCount) {
                
                
                if (me.shrinkWrapLocked && !locked.headerCt.forceFit) {
                    delete locked.flex;
                    
                    locked.setWidth(locked.headerCt.getTableWidth() + locked.el.getBorderWidth('lr'));
                }
                locked.addCls(me.lockedGridCls);
                locked.show();
                if (me.split) {
                    me.child('splitter').show();
                }
            } else {
                
                
                
                locked.getView().clearViewEl(true);
                locked.hide();
                if (me.split) {
                    me.child('splitter').hide();
                }
            }
            
            if (Ext.supports.touchScroll !== 2 && Ext.Component.pendingLayouts) {
                
                
                
                
                lockedView.getScrollable().setX(true);
            }
            
            me.ignoreMousewheel = lockedView.scrollFlags.y;
        } else 
        
        {
            normal.hide();
            
            lockedViewEl.style.borderBottomWidth = '0';
            
            locked.flex = 1;
            delete locked.width;
            locked.removeCls(me.lockedGridCls);
            locked.show();
            me.ignoreMousewheel = true;
        }
        Ext.resumeLayouts(true);
        return [
            lockedColCount,
            normalColCount
        ];
    },
    onLockedHeaderSortChange: Ext.emptyFn,
    onNormalHeaderSortChange: Ext.emptyFn,
    
    
    lock: function(activeHd, toIdx, toCt) {
        var me = this,
            normalGrid = me.normalGrid,
            lockedGrid = me.lockedGrid,
            normalView = normalGrid.view,
            lockedView = lockedGrid.view,
            normalHCt = normalGrid.headerCt,
            refreshFlags, ownerCt, hadFocus;
        activeHd = activeHd || normalHCt.getMenu().activeHeader;
        hadFocus = activeHd.hasFocus;
        toCt = toCt || lockedGrid.headerCt;
        ownerCt = activeHd.ownerCt;
        
        if (!activeHd.isLockable()) {
            return;
        }
        
        
        if (activeHd.flex) {
            activeHd.width = activeHd.getWidth();
            activeHd.flex = null;
        }
        Ext.suspendLayouts();
        
        
        if (lockedGrid.hidden) {
            lockedGrid.show();
        }
        
        normalView.blockRefresh = lockedView.blockRefresh = true;
        ownerCt.remove(activeHd, false);
        activeHd.locked = true;
        
        if (Ext.isDefined(toIdx)) {
            toCt.insert(toIdx, activeHd);
        } else {
            toCt.add(activeHd);
        }
        normalView.blockRefresh = lockedView.blockRefresh = false;
        refreshFlags = me.syncLockedWidth();
        if (refreshFlags[0]) {
            lockedGrid.getView().refreshView();
        }
        if (refreshFlags[1]) {
            normalGrid.getView().refreshView();
        }
        Ext.resumeLayouts(true);
        if (hadFocus) {
            activeHd.focus();
        }
        me.fireEvent('lockcolumn', me, activeHd);
    },
    
    
    unlock: function(activeHd, toIdx, toCt) {
        var me = this,
            normalGrid = me.normalGrid,
            lockedGrid = me.lockedGrid,
            normalView = normalGrid.view,
            lockedView = lockedGrid.view,
            lockedHCt = lockedGrid.headerCt,
            refreshFlags, hadFocus;
        
        if (!Ext.isDefined(toIdx)) {
            toIdx = 0;
        }
        activeHd = activeHd || lockedHCt.getMenu().activeHeader;
        hadFocus = activeHd.hasFocus;
        toCt = toCt || normalGrid.headerCt;
        Ext.suspendLayouts();
        
        normalView.blockRefresh = lockedView.blockRefresh = true;
        activeHd.ownerCt.remove(activeHd, false);
        activeHd.locked = false;
        toCt.insert(toIdx, activeHd);
        normalView.blockRefresh = lockedView.blockRefresh = false;
        
        
        refreshFlags = me.syncLockedWidth();
        if (refreshFlags[0]) {
            lockedGrid.getView().refreshView();
        }
        if (refreshFlags[1]) {
            normalGrid.getView().refreshView();
        }
        Ext.resumeLayouts(true);
        if (hadFocus) {
            activeHd.focus();
        }
        me.fireEvent('unlockcolumn', me, activeHd);
    },
    
    reconfigureLockable: function(store, columns) {
        var me = this,
            oldStore = me.store,
            lockedGrid = me.lockedGrid,
            normalGrid = me.normalGrid,
            view;
        if (columns) {
            
            lockedGrid.reconfiguring = normalGrid.reconfiguring = true;
            lockedGrid.headerCt.removeAll();
            normalGrid.headerCt.removeAll();
            columns = me.processColumns(columns, lockedGrid);
            
            lockedGrid.headerCt.add(columns.locked.items);
            normalGrid.headerCt.add(columns.normal.items);
            lockedGrid.reconfiguring = normalGrid.reconfiguring = false;
            
            
            me.syncLockedWidth();
        }
        if (store && store !== oldStore) {
            store = Ext.data.StoreManager.lookup(store);
            me.store = store;
            lockedGrid.view.blockRefresh = normalGrid.view.blockrefresh = true;
            lockedGrid.bindStore(store);
            
            
            
            view = lockedGrid.view;
            view.store = store;
            
            
            
            
            
            if (!view.dataSource.isFeatureStore) {
                view.dataSource = store;
            }
            if (view.bufferedRenderer) {
                view.bufferedRenderer.bindStore(store);
            }
            normalGrid.bindStore(store);
            view = normalGrid.view;
            view.store = store;
            
            
            
            
            
            if (!view.dataSource.isFeatureStore) {
                view.dataSource = store;
            }
            if (view.bufferedRenderer) {
                view.bufferedRenderer.bindStore(store);
            }
            me.view.store = store;
            me.view.bindStore(normalGrid.view.dataSource, false, 'dataSource');
            lockedGrid.view.blockRefresh = normalGrid.view.blockrefresh = false;
        }
        lockedGrid.getView().refreshView();
        normalGrid.getView().refreshView();
    },
    constructLockableFeatures: function() {
        var features = this.features,
            feature, featureClone, lockedFeatures, normalFeatures,
            i = 0,
            len;
        if (features) {
            if (!Ext.isArray(features)) {
                features = [
                    features
                ];
            }
            lockedFeatures = [];
            normalFeatures = [];
            len = features.length;
            for (; i < len; i++) {
                feature = features[i];
                if (!feature.isFeature) {
                    feature = Ext.create('feature.' + feature.ftype, feature);
                }
                switch (feature.lockableScope) {
                    case 'locked':
                        lockedFeatures.push(feature);
                        break;
                    case 'normal':
                        normalFeatures.push(feature);
                        break;
                    default:
                        feature.lockableScope = 'both';
                        lockedFeatures.push(feature);
                        normalFeatures.push(featureClone = feature.clone());
                        
                        featureClone.lockingPartner = feature;
                        feature.lockingPartner = featureClone;
                }
            }
        }
        return {
            normalFeatures: normalFeatures,
            lockedFeatures: lockedFeatures
        };
    },
    constructLockablePlugins: function() {
        var plugins = this.plugins,
            plugin, normalPlugin, lockedPlugin, topPlugins, lockedPlugins, normalPlugins,
            i = 0,
            len, lockableScope, pluginCls;
        if (plugins) {
            if (!Ext.isArray(plugins)) {
                plugins = [
                    plugins
                ];
            }
            topPlugins = [];
            lockedPlugins = [];
            normalPlugins = [];
            len = plugins.length;
            for (; i < len; i++) {
                plugin = plugins[i];
                
                if (plugin.init) {
                    lockableScope = plugin.lockableScope;
                } else 
                
                {
                    pluginCls = plugin.ptype ? Ext.ClassManager.getByAlias(('plugin.' + plugin.ptype)) : Ext.ClassManager.get(plugin.xclass);
                    lockableScope = pluginCls.prototype.lockableScope;
                }
                switch (lockableScope) {
                    case 'both':
                        lockedPlugins.push(lockedPlugin = plugin.clonePlugin());
                        normalPlugins.push(normalPlugin = plugin.clonePlugin());
                        
                        lockedPlugin.lockingPartner = normalPlugin;
                        normalPlugin.lockingPartner = lockedPlugin;
                        
                        
                        Ext.destroy(plugin);
                        break;
                    case 'locked':
                        lockedPlugins.push(plugin);
                        break;
                    case 'normal':
                        normalPlugins.push(plugin);
                        break;
                    default:
                        topPlugins.push(plugin);
                }
            }
        }
        return {
            topPlugins: topPlugins,
            normalPlugins: normalPlugins,
            lockedPlugins: lockedPlugins
        };
    },
    destroyLockable: function() {
        
        var me = this,
            task = me.syncLockedWidthTask;
        if (task) {
            task.cancel();
            me.syncLockedWidthTask = null;
        }
        Ext.destroy(me.view, me.headerCt);
    }
}, function() {
    this.borrow(Ext.Component, [
        'constructPlugin'
    ]);
});


Ext.define('Ext.grid.plugin.BufferedRenderer', {
    extend: 'Ext.AbstractPlugin',
    alias: 'plugin.bufferedrenderer',
    
    isBufferedRenderer: true,
    lockableScope: 'both',
    
    numFromEdge: 2,
    
    trailingBufferZone: 10,
    
    leadingBufferZone: 20,
    
    synchronousRender: true,
    
    scrollToLoadBuffer: 200,
    
    viewSize: 100,
    
    rowHeight: 21,
    
    position: 0,
    lastScrollDirection: 1,
    bodyTop: 0,
    scrollHeight: 0,
    loadId: 0,
    
    init: function(grid) {
        var me = this,
            view = grid.view,
            viewListeners = {
                scroll: me.onViewScroll,
                resize: me.onViewResize,
                refresh: me.onViewRefresh,
                columnschanged: me.checkVariableRowHeight,
                boxready: me.onViewBoxReady,
                scope: me,
                destroyable: true
            },
            initialConfig = view.initialConfig;
        
        
        if (grid.isTree || (grid.ownerLockable && grid.ownerLockable.isTree)) {
            view.blockRefresh = false;
            
            if (initialConfig && initialConfig.loadMask === undefined) {
                view.loadMask = true;
            }
        }
        if (view.positionBody) {
            viewListeners.refresh = me.onViewRefresh;
        }
        me.grid = grid;
        me.view = view;
        me.isRTL = view.getInherited().rtl;
        view.bufferedRenderer = me;
        view.preserveScrollOnRefresh = true;
        view.animate = false;
        me.bindStore(view.dataSource);
        
        if (view.hasOwnProperty('rowHeight')) {
            me.rowHeight = view.rowHeight;
        }
        me.position = 0;
        me.gridListeners = grid.on({
            reconfigure: 'onReconfigure',
            scope: me,
            destroyable: true
        });
        me.viewListeners = view.on(viewListeners);
    },
    
    checkVariableRowHeight: function() {
        var me = this,
            grid = me.grid;
        me.variableRowHeight = me.view.hasVariableRowHeight();
        if (grid.ownerLockable) {
            grid.ownerLockable.syncRowHeight = me.variableRowHeight;
        }
    },
    bindStore: function(store) {
        var me = this,
            view = me.view,
            dataSource = view.dataSource,
            hasFeatureStore = dataSource && dataSource.isFeatureStore;
        
        
        
        
        
        
        
        if (hasFeatureStore === store.isFeatureStore) {
            if (me.store) {
                me.unbindStore();
            }
            me.storeListeners = store.on({
                scope: me,
                groupchange: me.onStoreGroupChange,
                clear: me.onStoreClear,
                beforeload: me.onBeforeStoreLoad,
                load: me.onStoreLoad,
                destroyable: true
            });
            me.store = store;
        }
        
        if (me.view.componentLayout.layoutCount) {
            
            delete me.viewSize;
            if (store.isBufferedStore) {
                store.setViewSize(me.viewSize);
            }
            me.onViewResize(me.view, 0, me.view.getHeight());
        }
    },
    onReconfigure: function(grid, store) {
        if (store && store !== this.store) {
            this.bindStore(store);
        }
    },
    unbindStore: function() {
        this.storeListeners.destroy();
        this.store = null;
    },
    
    onBeforeStoreLoad: function() {
        var view = this.view;
        if (view && view.rendered) {
            view.el.dom.style.pointerEvents = 'none';
        }
        this.disable();
    },
    
    onStoreLoad: function() {
        var view = this.view;
        if (view && view.rendered) {
            view.el.dom.style.pointerEvents = '';
        }
        this.enable();
    },
    onStoreClear: function() {
        var me = this,
            view = me.view;
        
        if (view.rendered && !me.store.isDestroyed) {
            if (me.scrollTop !== 0) {
                
                me.bodyTop = me.scrollTop = me.position = me.scrollHeight = 0;
                me.view.setScrollY(0);
            }
            me.lastScrollDirection = me.scrollOffset = null;
            
            
            if (!view.hasOwnProperty('rowHeight')) {
                delete me.rowHeight;
            }
        }
    },
    
    onStoreGroupChange: function(store) {
        this.refreshSize();
    },
    onViewBoxReady: function(view) {
        this.refreshScroller(view, this.scrollHeight);
    },
    onViewRefresh: function(view, records) {
        var me = this,
            rows = view.all,
            height;
        
        me.checkVariableRowHeight();
        
        
        
        
        if (!view.componentLayoutCounter && (view.headerCt.down('{flex}') || me.variableRowHeight)) {
            view.on({
                boxready: Ext.Function.pass(me.onViewRefresh, [
                    view,
                    records
                ], me),
                single: true
            });
            return;
        }
        
        
        if (!view.hasOwnProperty('rowHeight') && rows.getCount()) {
            
            
            delete me.rowHeight;
        }
        me.refreshSize();
        
        if (me.refreshing) {
            return;
        }
        if (me.scrollTop !== view.getScrollY()) {
            
            
            
            me.onViewScroll();
        } else {
            if (!me.hasOwnProperty('bodyTop')) {
                me.bodyTop = rows.startIndex * me.rowHeight;
                view.setScrollY(me.bodyTop);
            }
            me.setBodyTop(me.bodyTop);
            
            
            height = view.getHeight();
            if (rows.getCount() && height > 0) {
                me.onViewResize(view, null, height);
                
                
                
                if (records && (rows.getCount() !== records.length)) {
                    records.length = 0;
                    records.push.apply(records, me.store.getRange(rows.startIndex, rows.endIndex));
                }
            }
        }
    },
    refreshSize: function() {
        var me = this,
            view = me.view,
            oldScrollHeight = me.scrollHeight,
            scrollHeight;
        
        
        
        scrollHeight = me.getScrollHeight();
        
        if (view.all.endIndex === (me.store.getCount()) - 1) {
            me.stretchView(view, me.scrollHeight = me.bodyTop + view.body.dom.offsetHeight);
        } else 
        {
            if (scrollHeight !== oldScrollHeight) {
                me.stretchView(view, scrollHeight);
            }
        }
    },
    onViewResize: function(view, width, height, oldWidth, oldHeight) {
        var me = this,
            newViewSize;
        
        if (!oldHeight || height !== oldHeight) {
            
            newViewSize = Math.ceil(height / me.rowHeight) + me.trailingBufferZone + me.leadingBufferZone;
            me.viewSize = me.setViewSize(newViewSize);
            me.viewClientHeight = view.el.dom.clientHeight;
        }
    },
    onWrappedColumnWidthChange: function(oldWidth, newWidth) {
        var me = this,
            view = me.view;
        
        if (me.store.getCount() && me.bodyTop) {
            
            me.refreshSize();
            
            me.setViewSize(Math.ceil(view.getHeight() / me.rowHeight) + me.trailingBufferZone + me.leadingBufferZone);
            
            if (me.viewSize >= me.store.getCount()) {
                me.setBodyTop(0);
            }
            
            else if (newWidth > oldWidth && me.bodyTop + view.body.dom.offsetHeight - 1 > me.scrollHeight) {
                me.setBodyTop(Math.max(0, me.scrollHeight - view.body.dom.offsetHeight));
            }
            
            else if (me.bodyTop > me.scrollTop || me.bodyTop + view.body.dom.offsetHeight < me.scrollTop + view.getHeight(true)) {
                me.setBodyTop(me.scrollTop - me.trailingBufferZone * me.rowHeight);
            }
        }
    },
    stretchView: function(view, scrollRange) {
        var me = this,
            recordCount = me.store.getCount(),
            el, stretcherSpec;
        
        if (me.scrollTop > scrollRange) {
            me.position = me.scrollTop = scrollRange - view.body.dom.offsetHeight;
            view.setScrollY(me.scrollTop);
        }
        if (me.bodyTop > scrollRange) {
            view.body.translate(null, me.bodyTop = me.position);
        }
        
        
        
        if (view.touchScroll) {
            if (view.getScrollable()) {
                me.refreshScroller(view, scrollRange);
            } else if (!me.pendingScrollerRefresh) {
                view.on({
                    boxready: function() {
                        me.refreshScroller(view, scrollRange);
                        me.pendingScrollerRefresh = false;
                    },
                    single: true
                });
                me.pendingScrollerRefresh = true;
            }
        }
        
        if (!Ext.supports.touchScroll || Ext.supports.touchScroll === 1) {
            if (!me.stretcher) {
                el = view.getTargetEl();
                
                
                if (view.refreshCounter) {
                    view.fixedNodes++;
                }
                stretcherSpec = {
                    style: {
                        width: '1px',
                        height: '1px',
                        'marginTop': (scrollRange - 1) + 'px',
                        position: 'absolute'
                    }
                };
                stretcherSpec.style[me.isRTL ? 'right' : 'left'] = 0;
                me.stretcher = el.createChild(stretcherSpec, el.dom.firstChild);
            }
            
            if (me.hasOwnProperty('viewSize') && recordCount <= me.viewSize) {
                me.stretcher.dom.style.display = 'none';
            } else {
                me.stretcher.dom.style.marginTop = (scrollRange - 1) + 'px';
                me.stretcher.dom.style.display = '';
            }
        }
    },
    refreshScroller: function(view, scrollRange) {
        var scroller = view.getScrollable();
        scroller.setSize({
            x: view.headerCt.getTableWidth(),
            y: scrollRange
        });
    },
    setViewSize: function(viewSize, fromLockingPartner) {
        var me = this,
            store = me.store,
            view = me.view,
            rows = view.all,
            elCount = rows.getCount(),
            start, end,
            lockingPartner = me.view.lockingPartner && me.view.lockingPartner.bufferedRenderer,
            diff = elCount - viewSize,
            i, j, records, newRows, storeCount;
        
        if (lockingPartner && !fromLockingPartner && lockingPartner.view.componentLayoutCounter) {
            if (lockingPartner.viewSize > viewSize) {
                viewSize = lockingPartner.viewSize;
            } else {
                lockingPartner.setViewSize(viewSize, true);
            }
        }
        diff = elCount - viewSize;
        if (diff) {
            
            me.scrollTop = view.getScrollY();
            me.viewSize = viewSize;
            if (store.isBufferedStore) {
                store.setViewSize(viewSize);
            }
            
            
            if (elCount) {
                storeCount = store.getCount();
                start = rows.startIndex;
                end = Math.min(start + viewSize - 1, storeCount - 1);
                
                if (!(start === rows.startIndex && end === rows.endIndex)) {
                    
                    if (lockingPartner) {
                        lockingPartner.disable();
                    }
                    
                    if (diff < 0) {
                        
                        if (storeCount > elCount) {
                            
                            store.getRange(rows.endIndex + 1, end, {
                                callback: function(records, start) {
                                    newRows = view.doAdd(records, start);
                                    view.fireEvent('itemadd', records, start, newRows);
                                }
                            });
                        }
                    } else 
                    {
                        
                        start = rows.endIndex - (diff - 1);
                        end = rows.endIndex;
                        rows.removeRange(start, end, true);
                        if (view.hasListeners.itemremove) {
                            records = store.getRange(start, end);
                            for (i = end , j = records.length - 1; j >= 0; --i , --j) {
                                view.fireEvent('itemremove', records[j], i, me);
                            }
                        }
                    }
                    if (lockingPartner) {
                        lockingPartner.enable();
                    }
                }
            }
        }
        return viewSize;
    },
    
    
    getViewRange: function() {
        var me = this,
            rows = me.view.all,
            store = me.store,
            startIndex = 0;
        
        if (rows.getCount()) {
            startIndex = rows.startIndex;
        }
        
        
        
        else if (store.isBufferedStore) {
            if (!store.currentPage) {
                store.currentPage = 1;
            }
            startIndex = rows.startIndex = (store.currentPage - 1) * (store.pageSize || 1);
            
            store.currentPage = 1;
        }
        if (store.data.getCount()) {
            return store.getRange(startIndex, startIndex + (me.viewSize || store.defaultViewSize) - 1);
        } else {
            return [];
        }
    },
    
    onReplace: function(store, startIndex, oldRecords, newRecords) {
        var me = this,
            view = me.view,
            rows = view.all,
            oldStartIndex,
            renderedSize = rows.getCount(),
            lastAffectedIndex = startIndex + oldRecords.length - 1,
            recordIncrement = newRecords.length - oldRecords.length,
            scrollIncrement = recordIncrement * me.rowHeight;
        
        
        if (renderedSize && lastAffectedIndex < rows.startIndex) {
            
            rows.moveBlock(recordIncrement);
            me.refreshSize();
            
            
            oldStartIndex = rows.startIndex;
            if (recordIncrement > 0) {
                me.handleViewScroll(-1);
            }
            
            
            if (rows.startIndex === oldStartIndex) {
                
                
                if (rows.startIndex) {
                    me.setBodyTop(me.bodyTop += scrollIncrement);
                    view.suspendEvent('scroll');
                    view.scrollBy(0, scrollIncrement);
                    view.resumeEvent('scroll');
                    me.position = me.scrollTop = view.getScrollY();
                }
            } else 
            {
                view.suspendEvent('scroll');
                view.scrollBy(0, (oldStartIndex - rows.startIndex) * me.rowHeight);
                view.resumeEvent('scroll');
            }
            view.refreshSize(rows.getCount() !== renderedSize);
            return;
        }
        
        
        if (renderedSize && startIndex > rows.endIndex) {
            me.refreshSize();
            
            
            if (recordIncrement > 0) {
                me.handleViewScroll(1);
            }
            view.refreshSize(rows.getCount() !== renderedSize);
            return;
        }
        
        if (startIndex < rows.startIndex && lastAffectedIndex < rows.endIndex) {
            me.refreshView(rows.startIndex - oldRecords.length + newRecords.length);
            return;
        }
        if (startIndex < rows.startIndex && lastAffectedIndex <= rows.endIndex && scrollIncrement) {
            view.suspendEvent('scroll');
            view.setScrollY(me.position = me.scrollTop += scrollIncrement);
            view.resumeEvent('scroll');
        }
        
        
        me.refreshView();
    },
    
    scrollTo: function(recordIdx, options) {
        var args = arguments,
            me = this,
            view = me.view,
            viewDom = view.el.dom,
            store = me.store,
            total = store.getCount(),
            startIdx, endIdx, targetRow, tableTop, groupingFeature, group, record, direction,
            scrollDecrement = 0,
            doSelect, doFocus, animate, highlight, callback, scope;
        
        if (options && typeof options === 'object') {
            doSelect = options.select;
            doFocus = options.focus;
            highlight = options.highlight;
            animate = options.animate;
            callback = options.callback;
            scope = options.scope;
        } else 
        {
            doSelect = args[1];
            callback = args[2];
            scope = args[3];
        }
        
        
        
        if ((groupingFeature = view.dataSource.groupingFeature) && (groupingFeature.collapsible)) {
            if (recordIdx.isEntity) {
                record = recordIdx;
            } else {
                record = view.store.getAt(Math.min(Math.max(recordIdx, 0), view.store.getCount() - 1));
            }
            group = groupingFeature.getGroup(record);
            if (group.isCollapsed) {
                groupingFeature.expand(group.getGroupKey());
                total = store.getCount();
            }
            
            recordIdx = groupingFeature.indexOf(record);
        } else {
            if (recordIdx.isEntity) {
                record = recordIdx;
                recordIdx = store.indexOf(record);
                
                if (recordIdx === -1) {
                    Ext.Error.raise('Unknown record passed to BufferedRenderer#scrollTo');
                    return;
                }
            } else {
                
                recordIdx = Math.min(Math.max(recordIdx, 0), total - 1);
                record = store.getAt(recordIdx);
            }
        }
        
        if (record && (targetRow = view.getNode(record))) {
            view.getScrollable().scrollIntoView(targetRow, null, animate, highlight);
            
            
            me.onViewScroll();
            if (doSelect) {
                view.selModel.select(record);
            }
            if (doFocus) {
                view.getNavigationModel().setPosition(record, 0);
            }
            if (callback) {
                callback.call(scope || me, recordIdx, record, targetRow);
            }
            return;
        }
        
        
        if (recordIdx < view.all.startIndex) {
            
            direction = -1;
            startIdx = Math.max(Math.min(recordIdx - (Math.floor((me.leadingBufferZone + me.trailingBufferZone) / 2)), total - me.viewSize + 1), 0);
            endIdx = Math.min(startIdx + me.viewSize - 1, total - 1);
        } else 
        {
            
            direction = 1;
            endIdx = Math.min(recordIdx + (Math.floor((me.leadingBufferZone + me.trailingBufferZone) / 2)), total - 1);
            startIdx = Math.max(endIdx - (me.viewSize - 1), 0);
        }
        tableTop = Math.max(startIdx * me.rowHeight, 0);
        store.getRange(startIdx, endIdx, {
            callback: function(range, start, end) {
                var scroller = view.getScrollable();
                
                
                
                me.renderRange(start, end, true, true);
                record = store.data.getRange(recordIdx, recordIdx + 1)[0];
                targetRow = view.getNode(record);
                
                
                if (!targetRow) {
                    return;
                }
                
                view.body.translate(null, me.bodyTop = tableTop);
                view.suspendEvent('scroll');
                if (direction === 1) {
                    scrollDecrement = viewDom.clientHeight - targetRow.offsetHeight;
                }
                me.position = me.scrollTop = Math.min(Math.max(0, tableTop - view.body.getOffsetsTo(targetRow)[1]) - scrollDecrement, scroller.getSize().y - viewDom.clientHeight);
                scroller.scrollIntoView(targetRow, null, animate, highlight);
                view.resumeEvent('scroll');
                if (doSelect) {
                    view.selModel.select(record);
                }
                if (doFocus) {
                    view.getNavigationModel().setPosition(record, 0);
                }
                if (callback) {
                    callback.call(scope || me, recordIdx, record, targetRow);
                }
            }
        });
    },
    onViewScroll: function() {
        var me = this,
            store = me.store,
            totalCount = (store.getCount()),
            vscrollDistance, scrollDirection,
            scrollTop = me.scrollTop = me.view.getScrollY();
        
        
        if (!(me.disabled || totalCount < me.viewSize)) {
            vscrollDistance = scrollTop - me.position;
            scrollDirection = vscrollDistance > 0 ? 1 : -1;
            
            if (Math.abs(vscrollDistance) >= 20 || (scrollDirection !== me.lastScrollDirection)) {
                me.lastScrollDirection = scrollDirection;
                me.handleViewScroll(me.lastScrollDirection);
            }
        }
    },
    handleViewScroll: function(direction) {
        var me = this,
            rows = me.view.all,
            store = me.store,
            viewSize = me.viewSize,
            lastItemIndex = (store.getCount()) - 1,
            requestStart, requestEnd;
        
        if (direction === -1) {
            
            if (rows.startIndex) {
                if (me.topOfViewCloseToEdge()) {
                    requestStart = Math.max(0, me.getLastVisibleRowIndex() + me.trailingBufferZone - viewSize);
                }
            }
        } else 
        {
            
            if (rows.endIndex < lastItemIndex) {
                if (me.bottomOfViewCloseToEdge()) {
                    requestStart = Math.max(0, me.getFirstVisibleRowIndex() - me.trailingBufferZone);
                }
            }
        }
        
        
        if (requestStart == null) {
            me.loadId++;
        } else 
        {
            requestEnd = Math.min(requestStart + viewSize - 1, lastItemIndex);
            
            
            if (me.variableRowHeight && requestEnd === rows.endIndex && requestEnd < lastItemIndex) {
                requestEnd++;
                
                
                me.viewSize = viewSize++;
                if (store.isBufferedStore) {
                    store.setViewSize(me.viewSize);
                }
            }
            
            if (requestStart !== rows.startIndex || requestEnd !== rows.endIndex) {
                me.renderRange(requestStart, requestEnd);
                return true;
            }
        }
    },
    bottomOfViewCloseToEdge: function() {
        var me = this;
        if (me.variableRowHeight) {
            return me.bodyTop + me.view.body.dom.offsetHeight < me.scrollTop + me.view.lastBox.height + (me.numFromEdge * me.rowHeight);
        } else {
            return (me.view.all.endIndex - me.getLastVisibleRowIndex()) < me.numFromEdge;
        }
    },
    topOfViewCloseToEdge: function() {
        var me = this;
        if (me.variableRowHeight) {
            
            return me.bodyTop > me.scrollTop - (me.numFromEdge * me.rowHeight);
        } else {
            return (me.getFirstVisibleRowIndex() - me.view.all.startIndex) < me.numFromEdge;
        }
    },
    
    refreshView: function(startIndex) {
        var me = this,
            viewSize = me.viewSize,
            rows = me.view.all,
            store = me.store,
            storeCount = store.getCount(),
            maxIndex = storeCount - 1,
            endIndex;
        
        if (storeCount < viewSize) {
            startIndex = 0;
            endIndex = maxIndex;
        } else {
            
            
            
            
            startIndex = Math.max(0, Math.min(startIndex == null ? rows.startIndex : startIndex, maxIndex - (viewSize - me.leadingBufferZone) + 1));
            
            endIndex = Math.min(startIndex + viewSize - 1, maxIndex);
            if (endIndex - startIndex + 1 > viewSize) {
                startIndex = endIndex - viewSize + 1;
            }
        }
        store.getRange(startIndex, endIndex, {
            callback: me.doRefreshView,
            scope: me
        });
    },
    doRefreshView: function(range, startIndex, endIndex, options) {
        var me = this,
            view = me.view,
            navModel = view.getNavigationModel(),
            focusPosition = navModel.getPosition(),
            rows = view.all,
            previousStartIndex = rows.startIndex,
            previousEndIndex = rows.endIndex,
            previousFirstItem, previousLastItem,
            prevRowCount = rows.getCount(),
            newNodes,
            viewMoved = startIndex !== rows.startIndex,
            calculatedTop, scrollIncrement;
        if (view.refreshCounter) {
            
            
            if (focusPosition && focusPosition.view === view) {
                focusPosition = focusPosition.clone();
                navModel.setPosition();
            } else {
                focusPosition = null;
            }
            
            
            view.refreshing = me.refreshing = true;
            view.clearViewEl(true);
            view.refreshCounter++;
            if (range.length) {
                newNodes = view.doAdd(range, startIndex);
                if (viewMoved) {
                    
                    previousFirstItem = rows.item(previousStartIndex, true);
                    previousLastItem = rows.item(previousEndIndex, true);
                    
                    if (previousFirstItem) {
                        scrollIncrement = -previousFirstItem.offsetTop;
                    } else if (previousLastItem) {
                        scrollIncrement = previousLastItem.offsetTop + previousLastItem.offsetHeight;
                    }
                    
                    if (scrollIncrement) {
                        me.setBodyTop(me.bodyTop += scrollIncrement);
                        view.suspendEvent('scroll');
                        view.setScrollY(me.position = me.scrollTop = me.bodyTop ? me.scrollTop + scrollIncrement : 0);
                        view.resumeEvent('scroll');
                    } else 
                    {
                        
                        me.setBodyTop(me.bodyTop = calculatedTop = startIndex * me.rowHeight);
                        view.suspendEvent('scroll');
                        view.setScrollY(me.position = me.scrollTop = Math.max(calculatedTop - me.rowHeight * (calculatedTop < me.bodyTop ? me.leadingBufferZone : me.trailingBufferZone , 0)));
                        view.resumeEvent('scroll');
                    }
                }
            }
            
            me.refreshSize();
            view.refreshSize(rows.getCount() !== prevRowCount);
            view.fireEvent('refresh', view, range);
            
            
            
            if (focusPosition) {
                view.cellFocused = true;
                navModel.setPosition(focusPosition, null, null, null, true);
            }
            view.headerCt.setSortState();
            view.refreshNeeded = view.refreshing = me.refreshing = false;
        } else {
            view.refresh();
        }
    },
    renderRange: function(start, end, forceSynchronous, fromLockingPartner) {
        var me = this,
            rows = me.view.all,
            store = me.store;
        
        
        
        if (!(start === rows.startIndex && end === rows.endIndex)) {
            
            if (store.rangeCached(start, end)) {
                me.cancelLoad();
                if (me.synchronousRender || forceSynchronous) {
                    me.onRangeFetched(null, start, end, null, fromLockingPartner);
                } else {
                    if (!me.renderTask) {
                        me.renderTask = new Ext.util.DelayedTask(me.onRangeFetched,me,null,false);
                    }
                    
                    
                    
                    
                    me.renderTask.delay(1, null, null, [
                        null,
                        start,
                        end,
                        null,
                        fromLockingPartner
                    ]);
                }
            } else 
            {
                me.attemptLoad(start, end);
            }
        }
    },
    onRangeFetched: function(range, start, end, options, fromLockingPartner) {
        var me = this,
            view = me.view,
            oldStart,
            rows = view.all,
            removeCount,
            increment = 0,
            calculatedTop, newTop,
            lockingPartner = me.view.lockingPartner && me.view.lockingPartner.bufferedRenderer,
            newRows, partnerNewRows, topAdditionSize, topBufferZone, i,
            variableRowHeight = me.variableRowHeight;
        
        if (view.isDestroyed) {
            return;
        }
        
        if (range) {
            
            me.scrollTop = me.view.getScrollY();
        } else {
            range = me.store.getRange(start, end);
            
            if (!range) {
                return;
            }
        }
        
        calculatedTop = start * me.rowHeight;
        
        if (start < rows.startIndex && end > rows.endIndex) {
            
            topAdditionSize = rows.startIndex - start;
            
            view.clearViewEl(true);
            newRows = view.doAdd(range, start);
            view.fireEvent('itemadd', range, start, newRows);
            for (i = 0; i < topAdditionSize; i++) {
                increment -= newRows[i].offsetHeight;
            }
            
            newTop = me.bodyTop + increment;
        } else {
            
            
            
            if (me.teleported || start > rows.endIndex || end < rows.startIndex) {
                newTop = calculatedTop;
                
                
                
                if (variableRowHeight) {
                    topBufferZone = me.scrollTop < me.position ? me.leadingBufferZone : me.trailingBufferZone;
                    if (start > topBufferZone) {
                        newTop = me.scrollTop - me.rowHeight * topBufferZone;
                    }
                }
                
                view.clearViewEl(true);
                me.teleported = false;
            }
            if (!rows.getCount()) {
                newRows = view.doAdd(range, start);
                view.fireEvent('itemadd', range, start, newRows);
            }
            
            else if (end > rows.endIndex) {
                removeCount = Math.max(start - rows.startIndex, 0);
                
                if (variableRowHeight) {
                    increment = rows.item(rows.startIndex + removeCount, true).offsetTop;
                }
                newRows = rows.scroll(Ext.Array.slice(range, rows.endIndex + 1 - start), 1, removeCount, start, end);
                
                if (variableRowHeight) {
                    
                    newTop = me.bodyTop + increment;
                } else {
                    newTop = calculatedTop;
                }
            } else 
            {
                removeCount = Math.max(rows.endIndex - end, 0);
                oldStart = rows.startIndex;
                newRows = rows.scroll(Ext.Array.slice(range, 0, rows.startIndex - start), -1, removeCount, start, end);
                
                if (variableRowHeight) {
                    
                    newTop = me.bodyTop - rows.item(oldStart, true).offsetTop;
                    
                    if (!rows.startIndex) {
                        
                        
                        if (newTop) {
                            view.setScrollY(me.position = (me.scrollTop -= newTop));
                            newTop = 0;
                        }
                    }
                    
                    else if (newTop < 0) {
                        increment = rows.startIndex * me.rowHeight;
                        view.setScrollY(me.position = (me.scrollTop += increment));
                        newTop = me.bodyTop + increment;
                    }
                } else {
                    newTop = calculatedTop;
                }
            }
            
            
            me.position = me.scrollTop;
        }
        
        newTop = Math.max(Math.floor(newTop), 0);
        if (view.positionBody) {
            me.setBodyTop(newTop);
        }
        
        
        if (lockingPartner && !lockingPartner.disabled && !fromLockingPartner) {
            
            lockingPartner.scrollTop = lockingPartner.position = me.scrollTop;
            partnerNewRows = lockingPartner.onRangeFetched(null, start, end, options, true);
            if (lockingPartner.bodyTop !== newTop) {
                lockingPartner.setBodyTop(newTop);
            }
            
            lockingPartner.view.setScrollY(me.scrollTop);
            if (variableRowHeight) {
                me.syncRowHeights(newRows, partnerNewRows);
            }
        }
        return newRows;
    },
    syncRowHeights: function(itemEls, partnerItemEls) {
        var me = this,
            ln = itemEls.length,
            otherLn = partnerItemEls.length,
            mySynchronizer = [],
            otherSynchronizer = [],
            RowSynchronizer = Ext.grid.locking.RowSynchronizer,
            i, rowSync;
        
        
        
        
        if (ln !== otherLn) {
            itemEls = me.view.all.slice();
            partnerItemEls = me.view.lockingPartner.all.slice();
            ln = otherLn = itemEls.length;
        }
        for (i = 0; i < ln; i++) {
            mySynchronizer[i] = rowSync = new RowSynchronizer(me.view,itemEls[i]);
            rowSync.measure();
        }
        for (i = 0; i < otherLn; i++) {
            otherSynchronizer[i] = rowSync = new RowSynchronizer(me.view.lockingPartner,partnerItemEls[i]);
            rowSync.measure();
        }
        for (i = 0; i < ln; i++) {
            mySynchronizer[i].finish(otherSynchronizer[i]);
            otherSynchronizer[i].finish(mySynchronizer[i]);
        }
    },
    setBodyTop: function(bodyTop) {
        var me = this,
            view = me.view,
            store = me.store,
            body = view.body;
        body.translate((me.isRTL && Ext.supports.xOriginBug && view.scrollFlags.y) ? Ext.getScrollbarSize().width : null, me.bodyTop = bodyTop);
        
        if (me.variableRowHeight) {
            
            if (view.all.endIndex === (store.getCount()) - 1) {
                me.stretchView(view, me.scrollHeight = me.bodyTop + body.dom.offsetHeight - 1);
            }
            
            else if (me.bodyTop + body.dom.offsetHeight - 1 > me.scrollHeight) {
                me.stretchView(view, me.scrollHeight += ((store.getCount()) - view.all.endIndex) * me.rowHeight);
            }
        }
    },
    getFirstVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) {
        var me = this,
            view = me.view,
            rows = view.all,
            elements = rows.elements,
            clientHeight = me.viewClientHeight,
            target, targetTop,
            bodyTop = me.bodyTop;
        
        if (rows.getCount() && me.variableRowHeight) {
            if (!arguments.length) {
                startRow = rows.startIndex;
                endRow = rows.endIndex;
                viewportTop = me.scrollTop;
                viewportBottom = viewportTop + clientHeight;
                
                if (bodyTop > viewportBottom || bodyTop + view.body.dom.offsetHeight < viewportTop) {
                    me.teleported = true;
                    return Math.floor(me.scrollTop / me.rowHeight);
                }
                
                target = startRow + Math.min(me.numFromEdge + ((me.lastScrollDirection === -1) ? me.leadingBufferZone : me.trailingBufferZone), Math.floor((endRow - startRow) / 2));
            } else {
                target = startRow + Math.floor((endRow - startRow) / 2);
            }
            targetTop = bodyTop + elements[target].offsetTop;
            
            if (targetTop + elements[target].offsetHeight < viewportTop) {
                return me.getFirstVisibleRowIndex(target + 1, endRow, viewportTop, viewportBottom);
            }
            
            if (targetTop <= viewportTop) {
                return target;
            }
            
            else if (target !== startRow) {
                return me.getFirstVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom);
            }
        }
        return Math.floor(me.scrollTop / me.rowHeight);
    },
    getLastVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) {
        var me = this,
            view = me.view,
            rows = view.all,
            elements = rows.elements,
            clientHeight = me.viewClientHeight,
            target, targetTop, targetBottom,
            bodyTop = me.bodyTop;
        
        if (rows.getCount() && me.variableRowHeight) {
            if (!arguments.length) {
                startRow = rows.startIndex;
                endRow = rows.endIndex;
                viewportTop = me.scrollTop;
                viewportBottom = viewportTop + clientHeight;
                
                if (bodyTop > viewportBottom || bodyTop + view.body.dom.offsetHeight < viewportTop) {
                    me.teleported = true;
                    return Math.floor(me.scrollTop / me.rowHeight) + Math.ceil(clientHeight / me.rowHeight);
                }
                
                target = endRow - Math.min(me.numFromEdge + ((me.lastScrollDirection === 1) ? me.leadingBufferZone : me.trailingBufferZone), Math.floor((endRow - startRow) / 2));
            } else {
                target = startRow + Math.floor((endRow - startRow) / 2);
            }
            targetTop = bodyTop + elements[target].offsetTop;
            
            if (targetTop > viewportBottom) {
                return me.getLastVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom);
            }
            targetBottom = targetTop + elements[target].offsetHeight;
            
            if (targetBottom >= viewportBottom) {
                return target;
            }
            
            else if (target !== endRow) {
                return me.getLastVisibleRowIndex(target + 1, endRow, viewportTop, viewportBottom);
            }
        }
        return me.getFirstVisibleRowIndex() + Math.ceil(clientHeight / me.rowHeight);
    },
    getScrollHeight: function(calculatedOnly) {
        var me = this,
            view = me.view,
            rows = view.all,
            store = me.store,
            recCount = store.getCount(),
            rowCount, scrollHeight;
        if (!recCount) {
            return 0;
        }
        if (!me.hasOwnProperty('rowHeight')) {
            if (rowCount = rows.getCount()) {
                me.rowHeight = me.variableRowHeight ? Math.floor(view.body.dom.clientHeight / rowCount) : rows.first(true).offsetHeight;
            }
        }
        scrollHeight = Math.floor(recCount * me.rowHeight);
        
        if (!calculatedOnly) {
            
            if (scrollHeight && (rows.endIndex === recCount - 1)) {
                scrollHeight = Math.max(scrollHeight, me.bodyTop + view.body.dom.offsetHeight - 1);
            }
        }
        return me.scrollHeight = scrollHeight;
    },
    attemptLoad: function(start, end) {
        var me = this;
        if (me.scrollToLoadBuffer) {
            if (!me.loadTask) {
                me.loadTask = new Ext.util.DelayedTask(me.doAttemptLoad,me,[]);
            }
            me.loadTask.delay(me.scrollToLoadBuffer, me.doAttemptLoad, me, [
                start,
                end
            ]);
        } else {
            me.doAttemptLoad(start, end);
        }
    },
    cancelLoad: function() {
        if (this.loadTask) {
            this.loadTask.cancel();
        }
    },
    doAttemptLoad: function(start, end) {
        var me = this;
        this.store.getRange(start, end, {
            loadId: ++me.loadId,
            callback: function(range, start, end, options) {
                
                if (options.loadId === me.loadId) {
                    this.onRangeFetched(range, start, end, options);
                }
            },
            scope: this,
            fireEvent: false
        });
    },
    destroy: function() {
        var me = this,
            view = me.view;
        if (view && view.el) {
            view.un('scroll', me.onViewScroll, me);
        }
        
        Ext.destroy(me.viewListeners, me.storeListeners, me.gridListeners, me.stretcher);
    }
}, function(cls) {
    
    
    if (Ext.supports.Touch) {
        cls.prototype.leadingBufferZone = cls.prototype.trailingBufferZone = 2;
        cls.prototype.numFromEdge = 1;
    }
});


Ext.define('Ext.grid.plugin.Editing', {
    extend: 'Ext.plugin.Abstract',
    alias: 'editing.editing',
    requires: [
        'Ext.grid.column.Column',
        'Ext.util.KeyNav',
        
        
        'Ext.form.field.Base',
        'Ext.view.Table'
    ],
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    clicksToEdit: 2,
    
    triggerEvent: undefined,
    
    relayedEvents: [
        'beforeedit',
        'edit',
        'validateedit',
        'canceledit'
    ],
    
    defaultFieldUI: 'default',
    
    defaultFieldXType: 'textfield',
    
    editStyle: '',
    
    
    
    
    constructor: function(config) {
        var me = this;
        me.callParent(arguments);
        me.mixins.observable.constructor.call(me);
        
        me.on("edit", function(editor, e) {
            me.fireEvent("afteredit", editor, e);
        });
    },
    
    init: function(grid) {
        var me = this;
        me.grid = grid;
        me.view = grid.view;
        me.initEvents();
        
        me.mon(grid, {
            beforereconfigure: me.onBeforeReconfigure,
            reconfigure: me.onReconfigure,
            scope: me,
            beforerender: {
                fn: me.onReconfigure,
                single: true,
                scope: me
            }
        });
        grid.relayEvents(me, me.relayedEvents);
        
        if (me.grid.ownerLockable) {
            me.grid.ownerLockable.relayEvents(me, me.relayedEvents);
        }
        
        
        grid.isEditable = true;
        grid.editingPlugin = grid.view.editingPlugin = me;
    },
    resolveListenerScope: function(defaultScope) {
        return this.grid.resolveListenerScope(defaultScope);
    },
    onBeforeReconfigure: function() {
        this.reconfiguring = true;
    },
    
    onReconfigure: function() {
        
        
        this.initFieldAccessors(this.grid.getTopLevelColumnManager().getColumns());
        delete this.reconfiguring;
    },
    
    destroy: function() {
        var me = this,
            grid = me.grid;
        Ext.destroy(me.keyNav);
        
        me.clearListeners();
        if (grid) {
            grid.editingPlugin = grid.view.editingPlugin = me.grid = me.view = me.editor = me.keyNav = null;
        }
    },
    
    getEditStyle: function() {
        return this.editStyle;
    },
    
    initFieldAccessors: function(columns) {
        
        if (columns.isGroupHeader) {
            columns = columns.getGridColumns();
        }
        
        else if (!Ext.isArray(columns)) {
            columns = [
                columns
            ];
        }
        var me = this,
            c,
            cLen = columns.length,
            column;
        for (c = 0; c < cLen; c++) {
            column = columns[c];
            if (!column.getEditor) {
                column.getEditor = function(record, defaultField) {
                    return me.getColumnField(this, defaultField);
                };
            }
            if (!column.hasEditor) {
                column.hasEditor = function() {
                    return me.hasColumnField(this);
                };
            }
            if (!column.setEditor) {
                column.setEditor = function(field) {
                    me.setColumnField(this, field);
                };
            }
        }
    },
    
    removeFieldAccessors: function(columns) {
        
        if (columns.isGroupHeader) {
            columns = columns.getGridColumns();
        }
        
        else if (!Ext.isArray(columns)) {
            columns = [
                columns
            ];
        }
        var c,
            cLen = columns.length,
            column;
        for (c = 0; c < cLen; c++) {
            column = columns[c];
            column.getEditor = column.hasEditor = column.setEditor = column.field = column.editor = null;
        }
    },
    
    
    getColumnField: function(columnHeader, defaultField) {
        var me = this,
            field = columnHeader.field;
        if (!(field && field.isFormField)) {
            field = columnHeader.field = me.createColumnField(columnHeader, defaultField);
        }
        if (field && field.ui === 'default' && !field.hasOwnProperty('ui')) {
            field.ui = me.defaultFieldUI;
        }
        return field;
    },
    
    
    hasColumnField: function(columnHeader) {
        return !!(columnHeader.field && columnHeader.field.isComponent);
    },
    
    
    setColumnField: function(columnHeader, field) {
        columnHeader.field = field;
        columnHeader.field = this.createColumnField(columnHeader);
    },
    createColumnField: function(column, defaultField) {
        var field = column.field,
            dataIndex;
        if (!field && column.editor) {
            field = column.editor;
            column.editor = null;
        }
        if (!field && defaultField) {
            field = defaultField;
        }
        if (field) {
            dataIndex = column.dataIndex;
            if (field.isComponent) {
                field.column = column;
            } else {
                if (Ext.isString(field)) {
                    field = {
                        name: dataIndex,
                        xtype: field,
                        column: column
                    };
                } else {
                    field = Ext.apply({
                        name: dataIndex,
                        column: column
                    }, field);
                }
                field = Ext.ComponentManager.create(field, this.defaultFieldXType);
            }
            
            
            
            field.dataIndex = dataIndex;
            field.isEditorComponent = true;
            column.field = field;
        }
        return field;
    },
    
    initEvents: function() {
        var me = this;
        me.initEditTriggers();
        me.initCancelTriggers();
    },
    
    initCancelTriggers: Ext.emptyFn,
    
    initEditTriggers: function() {
        var me = this,
            view = me.view;
        
        if (me.triggerEvent === 'cellfocus') {
            me.mon(view, 'cellfocus', me.onCellFocus, me);
        } else if (me.triggerEvent === 'rowfocus') {
            me.mon(view, 'rowfocus', me.onRowFocus, me);
        } else {
            
            
            
            
            
            
            if (view.getSelectionModel().isCellModel) {
                view.onCellFocus = me.beforeViewCellFocus.bind(me);
            }
            
            me.mon(view, me.triggerEvent || ('cell' + (me.clicksToEdit === 1 ? 'click' : 'dblclick')), me.onCellClick, me);
        }
        
        
        me.initAddRemoveHeaderEvents();
        
        me.view.getNavigationModel().addKeyBindings({
            enter: me.onEnterKey,
            esc: me.onEscKey,
            scope: me
        });
    },
    
    beforeViewCellFocus: function(position) {
        
        if (this.view.selModel.keyNavigation || !this.editing || !this.isCellEditable || !this.isCellEditable(position.row, position.columnHeader)) {
            this.view.focusCell.apply(this.view, arguments);
        }
    },
    
    onRowFocus: function(record, row, rowIdx) {
        this.startEdit(row, 0);
    },
    
    onCellFocus: function(record, cell, position) {
        this.startEdit(position.row, position.column);
    },
    
    
    onCellClick: function(view, cell, colIdx, record, row, rowIdx, e) {
        
        
        
        var expanderSelector = view.expanderSelector,
            
            columnHeader = view.ownerCt.getColumnManager().getHeaderAtIndex(colIdx),
            editor = columnHeader.getEditor(record);
        if (this.shouldStartEdit(editor) && (!expanderSelector || !e.getTarget(expanderSelector))) {
            this.startEdit(record, columnHeader);
        }
    },
    initAddRemoveHeaderEvents: function() {
        var me = this,
            headerCt = me.grid.headerCt;
        me.mon(headerCt, {
            scope: me,
            add: me.onColumnAdd,
            columnmove: me.onColumnMove,
            beforedestroy: me.beforeGridHeaderDestroy
        });
    },
    
    onColumnAdd: function(ct, column) {
        this.initFieldAccessors(column);
    },
    
    onColumnMove: Ext.emptyFn,
    
    onEnterKey: function(e) {
        var me = this,
            grid = me.grid,
            navModel, record, pos, column, targetComponent;
        if (me.editing) {
            targetComponent = Ext.getCmp(e.getTarget().getAttribute('componentId'));
            
            if (!(targetComponent && targetComponent.isPickerField && targetComponent.isExpanded)) {
                me.completeEdit();
            }
        } else if (e.view === me.view) {
            navModel = grid.getView().getNavigationModel();
            
            pos = navModel.getPosition();
            if (pos) {
                record = pos.record;
                column = pos.column;
            }
            
            if (record && column) {
                me.startEdit(record, column);
            }
        }
    },
    
    onEscKey: function(e) {
        if (this.editing) {
            var targetComponent = Ext.getCmp(e.getTarget().getAttribute('componentId'));
            
            if (!(targetComponent && targetComponent.isPickerField && targetComponent.isExpanded)) {
                return this.cancelEdit();
            }
        }
    },
    
    beforeEdit: Ext.emptyFn,
    shouldStartEdit: function(editor) {
        return !!editor;
    },
    
    startEdit: function(record, columnHeader) {
        var me = this,
            context,
            layoutView = me.grid.lockable ? me.grid : me.view;
        
        
        if (!layoutView.componentLayoutCounter) {
            layoutView.on({
                boxready: Ext.Function.bind(me.startEdit, me, [
                    record,
                    columnHeader
                ]),
                single: true
            });
            return false;
        }
        
        if (me.disabled || me.grid.collapsed || !me.grid.view.isVisible(true)) {
            return false;
        }
        context = me.getEditingContext(record, columnHeader);
        if (context == null) {
            return false;
        }
        if (!me.preventBeforeCheck) {
            if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel) {
                return false;
            }
        }
        return context;
    },
    
    getEditingContext: function(record, columnHeader) {
        var me = this,
            grid = me.grid,
            colMgr = grid.visibleColumnManager,
            view, gridRow, rowIdx, colIdx, result;
        
        
        if (Ext.isNumber(columnHeader)) {
            columnHeader = colMgr.getHeaderAtIndex(columnHeader);
        }
        
        if (!columnHeader) {
            return;
        }
        
        if (columnHeader.hidden) {
            columnHeader = columnHeader.next(':not([hidden])') || columnHeader.prev(':not([hidden])');
        }
        
        view = columnHeader.getRootHeaderCt().view;
        gridRow = view.getRow(record);
        
        if (!gridRow) {
            return;
        }
        colIdx = colMgr.getHeaderIndex(columnHeader);
        if (Ext.isNumber(record)) {
            
            rowIdx = record;
            record = view.getRecord(gridRow);
        } else {
            rowIdx = view.indexOf(gridRow);
        }
        
        
        if (!record) {
            return;
        }
        
        result = new Ext.grid.CellContext(view).setAll(view, rowIdx, colIdx, record, columnHeader);
        
        result.grid = grid;
        result.store = view.dataSource;
        result.field = columnHeader.dataIndex;
        result.value = result.originalValue = record.get(columnHeader.dataIndex);
        result.row = gridRow;
        result.node = view.getNode(record);
        return result;
    },
    
    cancelEdit: function() {
        var me = this;
        me.editing = false;
        me.fireEvent('canceledit', me, me.context);
    },
    
    completeEdit: function() {
        var me = this;
        if (me.editing && me.validateEdit()) {
            me.fireEvent('edit', me, me.context);
        }
        me.context = null;
        me.editing = false;
    },
    
    validateEdit: function() {
        var me = this,
            context = me.context;
        return me.fireEvent('validateedit', me, context) !== false && !context.cancel;
    }
});


Ext.define('Ext.grid.plugin.CellEditing', {
    alias: 'plugin.cellediting',
    extend: 'Ext.grid.plugin.Editing',
    requires: [
        'Ext.grid.CellEditor',
        'Ext.util.DelayedTask'
    ],
    lockableScope: 'both',
    
    
    
    
    init: function(grid) {
        var me = this,
            lockingPartner = me.lockingPartner;
        me.callParent(arguments);
        
        if (lockingPartner) {
            if (lockingPartner.editors) {
                me.editors = lockingPartner.editors;
            } else {
                me.editors = lockingPartner.editors = new Ext.util.MixedCollection(false,function(editor) {
                    return editor.editorId;
                });
            }
        } else {
            me.editors = new Ext.util.MixedCollection(false,function(editor) {
                return editor.editorId;
            });
        }
    },
    
    beforeGridHeaderDestroy: function(headerCt) {
        var me = this,
            columns = me.grid.getColumnManager().getColumns(),
            len = columns.length,
            i, column, editor;
        for (i = 0; i < len; i++) {
            column = columns[i];
            
            editor = me.editors.getByKey(column.getItemId());
            
            if (!editor) {
                
                editor = column.editor || column.field;
            }
            
            Ext.destroy(editor);
            me.removeFieldAccessors(column);
        }
    },
    onReconfigure: function(grid, store, columns) {
        
        if (columns) {
            this.editors.clear();
        }
        this.callParent();
    },
    
    destroy: function() {
        var me = this;
        if (me.editors) {
            me.editors.each(Ext.destroy, Ext);
            me.editors.clear();
        }
        me.callParent(arguments);
    },
    
    
    initCancelTriggers: function() {
        var me = this,
            grid = me.grid;
        me.mon(grid, {
            columnresize: me.cancelEdit,
            columnmove: me.cancelEdit,
            scope: me
        });
    },
    isCellEditable: function(record, columnHeader) {
        var me = this,
            context = me.getEditingContext(record, columnHeader);
        if (me.grid.view.isVisible(true) && context) {
            columnHeader = context.column;
            record = context.record;
            if (columnHeader && me.getEditor(record, columnHeader)) {
                return true;
            }
        }
    },
    
    startEdit: function(record, columnHeader, 
    context) {
        var me = this,
            useCurrentActiveEditor, isFieldEditable, currentActiveEditor, newEditor;
        if (!context) {
            me.preventBeforeCheck = true;
            context = me.callParent(arguments);
            delete me.preventBeforeCheck;
            if (context === false) {
                return false;
            }
        }
        
        
        if (context && context.view.isVisible(true)) {
            record = context.record;
            columnHeader = context.column;
            
            
            isFieldEditable = (columnHeader && columnHeader.getEditor(record)) && !(me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel);
            if (isFieldEditable) {
                currentActiveEditor = me.getActiveEditor();
                newEditor = me.getEditor(record, columnHeader);
                
                
                if (currentActiveEditor) {
                    useCurrentActiveEditor = newEditor === currentActiveEditor;
                    me.completeEdit(!!newEditor);
                    
                    
                    
                    if (me.editing) {
                        return false;
                    }
                }
            } else {
                return false;
            }
            
            me.context = context;
            
            context.view.scrollCellIntoView(context.view.getCell(record, columnHeader));
            if (newEditor) {
                if (useCurrentActiveEditor) {
                    
                    
                    
                    newEditor.editing = false;
                    if (Ext.isIE) {
                        
                        
                        
                        
                        
                        
                        newEditor.selectSameEditor = true;
                    }
                }
                me.showEditor(newEditor, context, context.value);
                
                if (currentActiveEditor && !useCurrentActiveEditor) {
                    currentActiveEditor.hide();
                }
                return true;
            }
            return false;
        }
    },
    showEditor: function(ed, context, value) {
        var me = this,
            record = context.record,
            view = context.view,
            columnHeader = context.column,
            sm = view.getSelectionModel(),
            selectMode;
        
        
        if (!columnHeader.up(me.view.ownerCt)) {
            return me.lockingPartner.showEditor(ed, me.lockingPartner.getEditingContext(record, columnHeader), value);
        }
        me.setEditingContext(context);
        me.setActiveEditor(ed);
        me.setActiveRecord(record);
        me.setActiveColumn(columnHeader);
        selectMode = sm.getSelectionMode();
        
        
        
        
        
        
        
        if (!sm.isCellSelected(view, record, columnHeader) && (selectMode !== 'MULTI' || !sm.getSelection().length || (sm.getSelection().length === 1 && sm.isSelected(record)))) {
            sm.selectByPosition({
                row: record,
                column: columnHeader,
                view: view
            }, selectMode === 'MULTI');
        }
        
        if (!view.cellFocused) {
            view.getNavigationModel().setPosition(context, null, null, null, true);
        }
        
        
        
        view.getNavigationModel().setPosition();
        ed.startEdit(view.getCell(record, columnHeader), value, context);
        me.editing = true;
        me.scroll = view.el.getScroll();
    },
    completeEdit: function(remainVisible) {
        var activeEd = this.getActiveEditor();
        if (activeEd) {
            activeEd.completeEdit(remainVisible);
        }
    },
    
    setEditingContext: function(context) {
        this.context = context;
        if (this.lockingPartner) {
            this.lockingPartner.context = context;
        }
    },
    setActiveEditor: function(ed) {
        this.activeEditor = ed;
        if (this.lockingPartner) {
            this.lockingPartner.activeEditor = ed;
        }
    },
    getActiveEditor: function() {
        return this.activeEditor;
    },
    setActiveColumn: function(column) {
        this.activeColumn = column;
        if (this.lockingPartner) {
            this.lockingPartner.activeColumn = column;
        }
    },
    getActiveColumn: function() {
        return this.activeColumn;
    },
    setActiveRecord: function(record) {
        this.activeRecord = record;
        if (this.lockingPartner) {
            this.lockingPartner.activeRecord = record;
        }
    },
    getActiveRecord: function() {
        return this.activeRecord;
    },
    getEditor: function(record, column) {
        var me = this,
            editors = me.editors,
            editorId = column.getItemId(),
            editor = editors.getByKey(editorId),
            
            editorOwner = me.grid.ownerLockable || me.grid;
        if (!editor) {
            editor = column.getEditor(record);
            if (!editor) {
                return false;
            }
            
            if (editor instanceof Ext.grid.CellEditor) {
                editor.floating = true;
            } else 
            {
                editor = new Ext.grid.CellEditor({
                    floating: true,
                    editorId: editorId,
                    field: editor
                });
            }
            
            
            
            editor.field.excludeForm = true;
            
            if (editor.ownerCt !== editorOwner) {
                editorOwner.add(editor);
                editor.on({
                    scope: me,
                    specialkey: me.onSpecialKey,
                    complete: me.onEditComplete,
                    canceledit: me.cancelEdit
                });
                column.on('removed', me.onColumnRemoved, me);
            }
            editors.add(editor);
        }
        if (column.isTreeColumn) {
            editor.isForTree = column.isTreeColumn;
            editor.addCls(Ext.baseCSSPrefix + 'tree-cell-editor');
        }
        
        
        
        editor.setGrid(me.grid);
        
        editor.editingPlugin = me;
        return editor;
    },
    onColumnRemoved: function(column) {
        var me = this,
            context = me.context,
            editor,
            
            editorOwner = me.grid.ownerLockable || me.grid;
        
        if (context && context.column === column) {
            me.cancelEdit();
        }
        
        column.un('removed', me.onColumnRemoved, me);
        if (column.getEditor && (editor = column.getEditor()) && editor.ownerCt === editorOwner) {
            editorOwner.remove(editor);
            editor.un({
                scope: me,
                specialkey: me.onSpecialKey,
                complete: me.onEditComplete,
                canceledit: me.cancelEdit
            });
        }
    },
    
    setColumnField: function(column, field) {
        var ed = this.editors.getByKey(column.getItemId());
        Ext.destroy(ed, column.field);
        this.editors.removeAtKey(column.getItemId());
        this.callParent(arguments);
    },
    
    getCell: function(record, column) {
        return this.grid.getView().getCell(record, column);
    },
    
    onSpecialKey: function(ed, field, e) {
        var sm;
        if (e.getKey() === e.TAB) {
            e.stopEvent();
            if (ed) {
                
                
                ed.onEditorTab(e);
            }
            sm = ed.getRefOwner().getSelectionModel();
            return sm.onEditorTab(ed.editingPlugin, e);
        }
    },
    onEditComplete: function(ed, value, startValue) {
        var me = this,
            activeColumn = me.getActiveColumn(),
            context = me.context,
            view, record;
        if (activeColumn) {
            view = context.view;
            record = context.record;
            me.setActiveEditor(null);
            me.setActiveColumn(null);
            me.setActiveRecord(null);
            context.value = value;
            if (!me.validateEdit()) {
                me.editing = false;
                return;
            }
            
            
            if (!record.isEqual(value, startValue)) {
                record.set(activeColumn.dataIndex, value);
                
                context.rowIdx = view.indexOf(record);
            }
            me.fireEvent('edit', me, context);
            me.editing = false;
        }
    },
    
    cancelEdit: function() {
        var me = this,
            context = me.context,
            view, sm, record, preserveCurrentSelection,
            activeEd = me.getActiveEditor();
        if (activeEd) {
            me.setActiveEditor(null);
            me.setActiveColumn(null);
            me.setActiveRecord(null);
            view = context.view;
            sm = view.getSelectionModel();
            preserveCurrentSelection = sm.getSelectionMode() === 'MULTI' && (sm.getSelection().length > 1 || !sm.isSelected(record));
            if (activeEd.field) {
                me.context.value = ('editedValue' in activeEd) ? activeEd.editedValue : activeEd.getValue();
                activeEd.cancelEdit();
            }
            
            
            
            
            context.view.getNavigationModel().deferSetPosition(100, context, null, null, null, preserveCurrentSelection);
            
            me.callParent(arguments);
            return;
        }
        
        return true;
    },
    
    startEditByPosition: function(position) {
        var me = this,
            cm = me.grid.getColumnManager(),
            index;
        
        
        if (!position.isCellContext) {
            position.column = me.grid.getColumnManager().getColumns()[position.column];
            position.record = me.view.dataSource.getAt(position.row);
        }
        
        
        
        index = cm.getHeaderIndex(position.column);
        position.column = cm.getVisibleHeaderClosestToIndex(index);
        return me.startEdit(position.record, position.column);
    }
});


Ext.define('Ext.plugin.AbstractClipboard', {
    extend: 'Ext.plugin.Abstract',
    requires: [
        'Ext.util.KeyMap'
    ],
    cachedConfig: {
        
        formats: {
            text: {
                get: 'getTextData',
                put: 'putTextData'
            }
        }
    },
    config: {
        
        memory: null,
        
        source: 'system',
        
        system: 'text'
    },
    destroy: function() {
        var me = this,
            keyMap = me.keyMap,
            shared = me.shared;
        if (keyMap) {
            
            
            me.keyMap = Ext.destroy(keyMap);
            if (!--shared.counter) {
                shared.textArea = Ext.destroy(shared.textArea);
            }
        } else {
            
            
            me.renderListener = Ext.destroy(me.renderListener);
        }
        me.callParent();
    },
    init: function(comp) {
        var me = this;
        if (comp.rendered) {
            this.finishInit(comp);
        } else {
            me.renderListener = comp.on({
                render: function() {
                    me.renderListener = null;
                    me.finishInit(comp);
                },
                destroyable: true,
                single: true
            });
        }
    },
    
    
    privates: {
        
        shared: {
            counter: 0,
            data: null,
            textArea: null
        },
        applyMemory: function(value) {
            
            value = this.applySource(value);
            if (value) {
                for (var i = value.length; i-- > 0; ) {
                    if (value[i] === 'system') {
                        Ext.Error.raise('Invalid clipboard format "' + value[i] + '"');
                    }
                }
            }
            return value;
        },
        applySource: function(value) {
            
            if (value) {
                if (Ext.isString(value)) {
                    value = [
                        value
                    ];
                } else if (value.length === 0) {
                    value = null;
                }
            }
            if (value) {
                var formats = this.getFormats();
                for (var i = value.length; i-- > 0; ) {
                    if (value[i] !== 'system' && !formats[value[i]]) {
                        Ext.Error.raise('Invalid clipboard format "' + value[i] + '"');
                    }
                }
            }
            return value || null;
        },
        applySystem: function(value) {
            var formats = this.getFormats();
            if (!formats[value]) {
                Ext.Error.raise('Invalid clipboard format "' + value + '"');
            }
            return value;
        },
        doCutCopy: function(event, erase) {
            var me = this,
                formats = me.allFormats || me.syncFormats(),
                data = me.getData(erase, formats),
                memory = me.getMemory(),
                system = me.getSystem(),
                sys;
            me.shared.data = memory && data;
            if (system) {
                sys = data[system];
                if (formats[system] < 3) {
                    delete data[system];
                }
                me.setClipboardData(sys);
            }
        },
        doPaste: function(format, data) {
            var formats = this.getFormats();
            this[formats[format].put](data, format);
        },
        finishInit: function(comp) {
            var me = this;
            me.keyMap = new Ext.util.KeyMap({
                target: comp.el,
                binding: [
                    {
                        ctrl: true,
                        key: 'x',
                        fn: me.onCut,
                        scope: me
                    },
                    {
                        ctrl: true,
                        key: 'c',
                        fn: me.onCopy,
                        scope: me
                    },
                    {
                        ctrl: true,
                        key: 'v',
                        fn: me.onPaste,
                        scope: me
                    }
                ]
            });
            ++me.shared.counter;
            comp.on({
                destroy: 'destroy',
                scope: me
            });
        },
        getData: function(erase, format) {
            var me = this,
                formats = me.getFormats(),
                data, i, name, names;
            if (Ext.isString(format)) {
                if (!formats[format]) {
                    Ext.Error.raise('Invalid clipboard format "' + format + '"');
                }
                data = me[formats[format].get](format, erase);
            } else {
                data = {};
                names = [];
                if (format) {
                    for (name in format) {
                        if (!formats[name]) {
                            Ext.Error.raise('Invalid clipboard format "' + name + '"');
                        }
                        names.push(name);
                    }
                } else {
                    names = Ext.Object.getAllKeys(formats);
                }
                for (i = names.length; i-- > 0; ) {
                    data[name] = me[formats[name].get](name, erase && !i);
                }
            }
            return data;
        },
        
        getHiddenTextArea: function() {
            var shared = this.shared,
                ret = shared.textArea;
            if (!ret) {
                shared.textArea = ret = Ext.getBody().createChild({
                    tag: 'textarea',
                    tabIndex: -1,
                    
                    style: {
                        position: 'absolute',
                        top: '-1000px',
                        width: '1px'
                    }
                });
            }
            return ret;
        },
        onCopy: function(event) {
            this.doCutCopy(event, false);
        },
        onCut: function(event) {
            this.doCutCopy(event, true);
        },
        onPaste: function() {
            var me = this,
                sharedData = me.shared.data,
                source = me.getSource(),
                i, n, s;
            if (source) {
                for (i = 0 , n = source.length; i < n; ++i) {
                    s = source[i];
                    if (s === 'system') {
                        
                        s = me.getSystem();
                        me.pasteClipboardData(s);
                        break;
                    } else if (sharedData && (s in sharedData)) {
                        me.doPaste(s, sharedData[s]);
                        break;
                    }
                }
            }
        },
        pasteClipboardData: function(format) {
            var me = this,
                area, focusEl;
            if (window.clipboardData && clipboardData.getData) {
                me.doPaste(format, clipboardData.getData("text"));
            } else {
                focusEl = Ext.Element.getActiveElement();
                area = me.getHiddenTextArea().dom;
                area.value = '';
                area.focus();
                
                
                
                Ext.defer(function() {
                    
                    if (focusEl) {
                        focusEl.focus();
                    }
                    me.doPaste(format, area.value);
                    area.value = '';
                }, 100, me);
            }
        },
        setClipboardData: function(data) {
            if (window.clipboardData && clipboardData.setData) {
                clipboardData.setData("text", data);
            } else {
                var me = this,
                    area = me.getHiddenTextArea().dom,
                    focusEl = Ext.Element.getActiveElement();
                area.value = data;
                area.focus();
                area.select();
                
                
                
                Ext.defer(function() {
                    area.value = '';
                    if (focusEl) {
                        focusEl.focus();
                    }
                }, 50);
            }
        },
        syncFormats: function() {
            var me = this,
                map = {},
                memory = me.getMemory(),
                system = me.getSystem(),
                i, s;
            if (system) {
                map[system] = 1;
            }
            if (memory) {
                for (i = memory.length; i-- > 0; ) {
                    s = memory[i];
                    map[s] = map[s] ? 3 : 2;
                }
            }
            
            
            
            return me.allFormats = map;
        },
        updateMemory: function() {
            this.allFormats = null;
        },
        updateSystem: function() {
            this.allFormats = null;
        }
    }
});


Ext.define('Ext.grid.plugin.Clipboard', {
    extend: 'Ext.plugin.AbstractClipboard',
    alias: 'plugin.clipboard',
    requires: [
        'Ext.util.Format',
        'Ext.util.TSV'
    ],
    formats: {
        cell: {
            get: 'getCells'
        },
        html: {
            get: 'getCellData'
        },
        raw: {
            get: 'getCellData'
        }
    },
    getCellData: function(format, erase) {
        var cmp = this.getCmp(),
            selModel = cmp.getSelectionModel(),
            ret = [],
            isRaw = format === 'raw',
            isText = format === 'text',
            viewNode, cell, data, dataIndex, lastRecord, record, row, view;
        selModel.getSelected().eachCell(function(cellContext) {
            view = cellContext.column.getView();
            record = cellContext.record;
            if (lastRecord !== record) {
                lastRecord = record;
                ret.push(row = []);
            }
            dataIndex = cellContext.column.dataIndex;
            if (isRaw) {
                data = record.data[dataIndex];
            } else {
                
                viewNode = view.all.item(cellContext.rowIdx);
                
                if (!viewNode) {
                    viewNode = Ext.fly(view.createRowElement(record, cellContext.rowIdx));
                }
                cell = viewNode.down(cellContext.column.getCellInnerSelector());
                data = cell.dom.innerHTML;
                if (isText) {
                    data = Ext.util.Format.stripTags(data);
                }
            }
            row.push(data);
            if (erase && dataIndex) {
                record.set(dataIndex, null);
            }
        });
        return Ext.util.TSV.encode(ret);
    },
    getCells: function(format, erase) {
        var cmp = this.getCmp(),
            selModel = cmp.getSelectionModel(),
            ret = [],
            dataIndex, lastRecord, record, row;
        selModel.getSelected().eachCell(function(cellContext) {
            record = cellContext.record;
            if (lastRecord !== record) {
                lastRecord = record;
                ret.push(row = {
                    model: record.self,
                    fields: []
                });
            }
            dataIndex = cellContext.column.dataIndex;
            row.fields.push({
                name: dataIndex,
                value: record.data[dataIndex]
            });
            if (erase && dataIndex) {
                record.set(dataIndex, null);
            }
        });
        return ret;
    },
    getTextData: function(format, erase) {
        return this.getCellData(format, erase);
    },
    putCellData: function(data, format) {
        var values = Ext.util.TSV.decode(data),
            row,
            recCount = values.length,
            colCount = recCount ? values[0].length : 0,
            sourceRowIdx, sourceColIdx,
            view = this.getCmp().getView(),
            maxRowIdx = view.dataSource.getCount() - 1,
            maxColIdx = view.getVisibleColumnManager().getColumns().length - 1,
            navModel = view.getNavigationModel(),
            destination = navModel.getPosition(),
            dataIndex, destinationStartColumn,
            dataObject = {};
        if (destination) {
            
            
            
            destination = new Ext.grid.CellContext(view).setPosition(destination.record, destination.column);
        } else {
            destination = new Ext.grid.CellContext(view).setPosition(0, 0);
        }
        destinationStartColumn = destination.colIdx;
        for (sourceRowIdx = 0; sourceRowIdx < recCount; sourceRowIdx++) {
            row = values[sourceRowIdx];
            
            for (sourceColIdx = 0; sourceColIdx < colCount; sourceColIdx++) {
                dataIndex = destination.column.dataIndex;
                if (dataIndex) {
                    switch (format) {
                        
                        case 'raw':
                            dataObject[dataIndex] = row[sourceColIdx];
                            break;
                        
                        case 'text':
                            dataObject[dataIndex] = row[sourceColIdx];
                            break;
                        
                        case 'html':
                            break;
                    }
                }
                
                if (destination.colIdx === maxColIdx) {
                    break;
                }
                destination.setColumn(destination.colIdx + 1);
            }
            
            destination.record.set(dataObject);
            
            if (destination.rowIdx === maxRowIdx) {
                break;
            }
            
            destination.setPosition(destination.rowIdx + 1, destinationStartColumn);
        }
    },
    putTextData: function(data, format) {
        this.putCellData(data, format);
    }
});


Ext.define('Ext.grid.plugin.DragDrop', {
    extend: 'Ext.plugin.Abstract',
    alias: 'plugin.gridviewdragdrop',
    uses: [
        'Ext.view.DragZone',
        'Ext.grid.ViewDropZone'
    ],
    
    
    
    
    dragText: '{0} selected row{1}',
    
    
    ddGroup: "GridDD",
    
    
    
    enableDrop: true,
    
    enableDrag: true,
    
    containerScroll: false,
    
    
    
    
    init: function(view) {
        view.on('render', this.onViewRender, this, {
            single: true
        });
    },
    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },
    enable: function() {
        var me = this;
        if (me.dragZone) {
            me.dragZone.unlock();
        }
        if (me.dropZone) {
            me.dropZone.unlock();
        }
        me.callParent();
    },
    disable: function() {
        var me = this;
        if (me.dragZone) {
            me.dragZone.lock();
        }
        if (me.dropZone) {
            me.dropZone.lock();
        }
        me.callParent();
    },
    onViewRender: function(view) {
        var me = this,
            scrollEl;
        if (me.enableDrag) {
            if (me.containerScroll) {
                scrollEl = view.getEl();
            }
            me.dragZone = new Ext.view.DragZone(Ext.apply({
                view: view,
                ddGroup: me.dragGroup || me.ddGroup,
                dragText: me.dragText,
                containerScroll: me.containerScroll,
                scrollEl: scrollEl
            }, me.dragZone));
        }
        if (me.enableDrop) {
            me.dropZone = new Ext.grid.ViewDropZone(Ext.apply({
                view: view,
                ddGroup: me.dropGroup || me.ddGroup
            }, me.dropZone));
        }
    }
});


Ext.define('Ext.grid.plugin.RowEditing', {
    extend: 'Ext.grid.plugin.Editing',
    alias: 'plugin.rowediting',
    requires: [
        'Ext.grid.RowEditor'
    ],
    lockableScope: 'top',
    editStyle: 'row',
    
    autoCancel: true,
    
    
    errorSummary: true,
    constructor: function() {
        var me = this;
        me.callParent(arguments);
        if (!me.clicksToMoveEditor) {
            me.clicksToMoveEditor = me.clicksToEdit;
        }
        me.autoCancel = !!me.autoCancel;
    },
    
    destroy: function() {
        Ext.destroy(this.editor);
        this.callParent(arguments);
    },
    onBeforeReconfigure: function() {
        this.callParent(arguments);
        this.cancelEdit();
    },
    onReconfigure: function(grid, store, columns) {
        var ed = this.editor;
        this.callParent(arguments);
        
        if (columns && ed && ed.rendered) {
            ed.needsSyncFieldWidths = true;
        }
    },
    shouldStartEdit: function(editor) {
        return true;
    },
    
    startEdit: function(record, columnHeader) {
        var me = this,
            editor = me.getEditor(),
            context;
        if (Ext.isEmpty(columnHeader)) {
            columnHeader = me.grid.getTopLevelVisibleColumnManager().getHeaderAtIndex(0);
        }
        if (editor.beforeEdit() !== false) {
            context = me.callParent([
                record,
                columnHeader
            ]);
            if (context) {
                me.context = context;
                
                if (me.lockingPartner) {
                    me.lockingPartner.cancelEdit();
                }
                editor.startEdit(context.record, context.column, context);
                me.editing = true;
                return true;
            }
        }
        return false;
    },
    cancelEdit: function() {
        var me = this;
        if (me.editing) {
            me.getContextFieldValues();
            me.getEditor().cancelEdit();
            me.callParent(arguments);
            return;
        }
        
        return true;
    },
    completeEdit: function() {
        var me = this;
        if (me.editing && me.validateEdit()) {
            me.editing = false;
            me.fireEvent('edit', me, me.context);
        }
    },
    validateEdit: function() {
        this.getContextFieldValues();
        return this.callParent(arguments) && this.getEditor().completeEdit();
    },
    getEditor: function() {
        var me = this;
        if (!me.editor) {
            me.editor = me.initEditor();
        }
        return me.editor;
    },
    getContextFieldValues: function() {
        var editor = this.editor,
            context = this.context,
            record = context.record,
            newValues = {},
            originalValues = {},
            editors = editor.query('>[isFormField]'),
            len = editors.length,
            i, name, item;
        for (i = 0; i < len; i++) {
            item = editors[i];
            name = item.dataIndex;
            newValues[name] = item.getValue();
            originalValues[name] = record.get(name);
        }
        Ext.apply(context, {
            newValues: newValues,
            originalValues: originalValues
        });
    },
    
    initEditor: function() {
        return new Ext.grid.RowEditor(this.initEditorConfig());
    },
    initEditorConfig: function() {
        var me = this,
            grid = me.grid,
            view = me.view,
            headerCt = grid.headerCt,
            btns = [
                'saveBtnText',
                'cancelBtnText',
                'errorsText',
                'dirtyText'
            ],
            b,
            bLen = btns.length,
            cfg = {
                autoCancel: me.autoCancel,
                errorSummary: me.errorSummary,
                fields: headerCt.getGridColumns(),
                hidden: true,
                view: view,
                
                editingPlugin: me
            },
            item;
        for (b = 0; b < bLen; b++) {
            item = btns[b];
            if (Ext.isDefined(me[item])) {
                cfg[item] = me[item];
            }
        }
        return cfg;
    },
    
    initEditTriggers: function() {
        var me = this,
            view = me.view,
            moveEditorEvent = me.clicksToMoveEditor === 1 ? 'click' : 'dblclick';
        me.callParent(arguments);
        if (me.clicksToMoveEditor !== me.clicksToEdit) {
            me.mon(view, 'cell' + moveEditorEvent, me.moveEditorByClick, me);
        }
        view.on({
            render: function() {
                me.mon(me.grid.headerCt, {
                    scope: me,
                    columnresize: me.onColumnResize,
                    columnhide: me.onColumnHide,
                    columnshow: me.onColumnShow
                });
            },
            single: true
        });
    },
    moveEditorByClick: function() {
        var me = this;
        if (me.editing) {
            me.superclass.onCellClick.apply(me, arguments);
        }
    },
    
    onColumnAdd: function(ct, column) {
        if (column.isHeader) {
            var me = this,
                editor;
            me.initFieldAccessors(column);
            
            
            editor = me.editor;
            if (editor) {
                editor.onColumnAdd(column);
            }
        }
    },
    
    beforeGridHeaderDestroy: function(headerCt) {
        var columns = this.grid.getColumnManager().getColumns(),
            len = columns.length,
            i, column, field;
        for (i = 0; i < len; i++) {
            column = columns[i];
            
            if (column.hasEditor) {
                if (column.hasEditor() && (field = column.getEditor())) {
                    field.destroy();
                }
                this.removeFieldAccessors(column);
            }
        }
    },
    
    onColumnResize: function(ct, column, width) {
        if (column.isHeader) {
            var me = this,
                editor = me.getEditor();
            if (editor) {
                editor.onColumnResize(column, width);
            }
        }
    },
    
    onColumnHide: function(ct, column) {
        
        var me = this,
            editor = me.getEditor();
        if (editor) {
            editor.onColumnHide(column);
        }
    },
    
    onColumnShow: function(ct, column) {
        
        var me = this,
            editor = me.getEditor();
        if (editor) {
            editor.onColumnShow(column);
        }
    },
    
    onColumnMove: function(ct, column, fromIdx, toIdx) {
        
        var me = this,
            editor = me.getEditor();
        
        
        me.initFieldAccessors(column);
        if (editor) {
            
            
            editor.onColumnMove(column, fromIdx, toIdx);
        }
    },
    
    setColumnField: function(column, field) {
        var me = this,
            editor = me.getEditor();
        if (editor) {
            
            editor.destroyColumnEditor(column);
        }
        me.callParent(arguments);
        if (editor) {
            editor.insertColumnEditor(column);
        }
    },
    createColumnField: function(column, defaultField) {
        var editor = this.editor,
            def;
        if (editor) {
            def = editor.getDefaultFieldCfg();
        }
        return this.callParent([
            column,
            defaultField || def
        ]);
    }
});





Ext.define('Ext.grid.plugin.RowExpander', {
    extend: 'Ext.plugin.Abstract',
    lockableScope: 'normal',
    requires: [
        'Ext.grid.feature.RowBody'
    ],
    alias: 'plugin.rowexpander',
    
    columnWidth: 24,
    
    rowBodyTpl: null,
    
    lockedTpl: null,
    
    expandOnEnter: true,
    
    expandOnDblClick: true,
    
    selectRowOnExpand: false,
    
    headerWidth: 24,
    
    bodyBefore: false,
    rowBodyTrSelector: '.' + Ext.baseCSSPrefix + 'grid-rowbody-tr',
    rowBodyHiddenCls: Ext.baseCSSPrefix + 'grid-row-body-hidden',
    rowCollapsedCls: Ext.baseCSSPrefix + 'grid-row-collapsed',
    addCollapsedCls: {
        fn: function(out, values, parent) {
            var me = this.rowExpander;
            if (!me.recordsExpanded[values.record.internalId]) {
                values.itemClasses.push(me.rowCollapsedCls);
            }
            this.nextTpl.applyOut(values, out, parent);
        },
        syncRowHeights: function(lockedItem, normalItem) {
            this.rowExpander.syncRowHeights(lockedItem, normalItem);
        },
        
        
        priority: 20000
    },
    
    
    setCmp: function(grid) {
        var me = this,
            features;
        me.callParent(arguments);
        me.recordsExpanded = {};
        if (!me.rowBodyTpl) {
            Ext.Error.raise("The 'rowBodyTpl' config is required and is not defined.");
        }
        me.rowBodyTpl = Ext.XTemplate.getTpl(me, 'rowBodyTpl');
        features = me.getFeatureConfig(grid);
        if (grid.features) {
            grid.features = Ext.Array.push(features, grid.features);
        } else {
            grid.features = features;
        }
    },
    
    
    getFeatureConfig: function(grid) {
        var me = this,
            features = [],
            featuresCfg = {
                ftype: 'rowbody',
                rowExpander: me,
                bodyBefore: me.bodyBefore,
                recordsExpanded: me.recordsExpanded,
                rowBodyHiddenCls: me.rowBodyHiddenCls,
                rowCollapsedCls: me.rowCollapsedCls,
                setupRowData: me.getRowBodyFeatureData,
                setup: me.setup
            };
        features.push(Ext.apply({
            lockableScope: 'normal',
            getRowBodyContents: me.getRowBodyContentsFn(me.rowBodyTpl)
        }, featuresCfg));
        
        
        
        if (grid.enableLocking) {
            features.push(Ext.apply({
                lockableScope: 'locked',
                getRowBodyContents: me.lockedTpl ? me.getRowBodyContentsFn(me.lockedTpl) : function() {
                    return '';
                }
            }, featuresCfg));
        }
        return features;
    },
    getRowBodyContentsFn: function(rowBodyTpl) {
        var me = this;
        return function(rowValues) {
            rowBodyTpl.owner = me;
            return rowBodyTpl.applyTemplate(rowValues.record.getData());
        };
    },
    init: function(grid) {
        if (grid.lockable) {
            grid = grid.normalGrid;
        }
        var me = this,
            ownerLockable = grid.ownerLockable,
            view, lockedView;
        me.callParent(arguments);
        me.grid = grid;
        view = me.view = grid.getView();
        
        
        me.bindView(view);
        view.addRowTpl(me.addCollapsedCls).rowExpander = me;
        
        if (ownerLockable) {
            me.addExpander(ownerLockable.lockedGrid.headerCt.items.getCount() ? ownerLockable.lockedGrid : grid);
            
            lockedView = ownerLockable.lockedGrid.getView();
            
            
            me.bindView(lockedView);
            lockedView.addRowTpl(me.addCollapsedCls).rowExpander = me;
            ownerLockable.syncRowHeight = true;
            ownerLockable.mon(ownerLockable, {
                processcolumns: me.onLockableProcessColumns,
                lockcolumn: me.onColumnLock,
                unlockcolumn: me.onColumnUnlock,
                scope: me
            });
            
            me.viewListeners = view.on({
                itemadd: Ext.Function.createAnimationFrame(ownerLockable.syncRowHeights, ownerLockable)
            });
        } else {
            me.addExpander(grid);
            grid.on('beforereconfigure', me.beforeReconfigure, me);
        }
    },
    beforeReconfigure: function(grid, store, columns, oldStore, oldColumns) {
        var me = this;
        if (me.viewListeners) {
            me.viewListeners.destroy();
        }
        if (columns) {
            me.expanderColumn = new Ext.grid.Column(me.getHeaderConfig());
            columns.unshift(me.expanderColumn);
        }
    },
    onLockableProcessColumns: function(lockable, lockedHeaders, normalHeaders) {
        this.addExpander(lockedHeaders.length ? lockable.lockedGrid : lockable.normalGrid);
    },
    
    addExpander: function(expanderGrid) {
        var me = this,
            expanderHeader = me.getHeaderConfig();
        
        if (expanderGrid.isLocked && expanderGrid.ownerLockable.shrinkWrapLocked) {
            expanderGrid.width += expanderHeader.width;
            me.grid = expanderGrid;
        }
        me.expanderColumn = expanderGrid.headerCt.insert(0, expanderHeader);
        
        
        expanderGrid.getSelectionModel().injectCheckbox = 1;
    },
    getRowBodyFeatureData: function(record, idx, rowValues) {
        var me = this;
        me.self.prototype.setupRowData.apply(me, arguments);
        rowValues.rowBody = me.getRowBodyContents(rowValues);
        rowValues.rowBodyCls = me.recordsExpanded[record.internalId] ? '' : me.rowBodyHiddenCls;
    },
    setup: function(rows, rowValues) {
        var me = this,
            lockable = me.grid.ownerLockable;
        me.self.prototype.setup.apply(me, arguments);
        
        if (lockable && Ext.Array.indexOf(me.grid.columnManager.getColumns(), me.rowExpander.expanderColumn) !== -1) {
            rowValues.rowBodyColspan -= 1;
        }
    },
    bindView: function(view) {
        if (this.expandOnEnter) {
            view.on('itemkeydown', this.onKeyDown, this);
        }
        if (this.expandOnDblClick) {
            view.on('itemdblclick', this.onDblClick, this);
        }
    },
    onKeyDown: function(view, record, row, rowIdx, e) {
        if (e.getKey() === e.ENTER) {
            var ds = view.store,
                sels = view.getSelectionModel().getSelection(),
                ln = sels.length,
                i = 0;
            for (; i < ln; i++) {
                rowIdx = ds.indexOf(sels[i]);
                this.toggleRow(rowIdx, sels[i]);
            }
        }
    },
    onDblClick: function(view, record, row, rowIdx, e) {
        this.toggleRow(rowIdx, record);
    },
    toggleRow: function(rowIdx, record) {
        var me = this,
            view = me.view,
            bufferedRenderer = view.bufferedRenderer,
            scroller = view.getScrollable(),
            fireView = view,
            rowNode = view.getNode(rowIdx),
            normalRow = Ext.fly(rowNode),
            lockedRow,
            nextBd = normalRow.down(me.rowBodyTrSelector, true),
            wasCollapsed = normalRow.hasCls(me.rowCollapsedCls),
            addOrRemoveCls = wasCollapsed ? 'removeCls' : 'addCls',
            
            rowSpan = wasCollapsed ? 2 : 1,
            ownerLockable = me.grid.ownerLockable,
            expanderCell;
        normalRow[addOrRemoveCls](me.rowCollapsedCls);
        Ext.fly(nextBd)[addOrRemoveCls](me.rowBodyHiddenCls);
        me.recordsExpanded[record.internalId] = wasCollapsed;
        
        if (me.grid.ownerLockable) {
            
            fireView = ownerLockable.getView();
            
            if (ownerLockable.lockedGrid.isVisible()) {
                view = ownerLockable.view.lockedGrid.view;
                
                lockedRow = Ext.fly(view.getNode(rowIdx));
                
                if (lockedRow) {
                    lockedRow[addOrRemoveCls](me.rowCollapsedCls);
                    
                    nextBd = lockedRow.down(me.rowBodyTrSelector, true);
                    Ext.fly(nextBd)[addOrRemoveCls](me.rowBodyHiddenCls);
                }
            }
        }
        
        
        if (me.expanderColumn) {
            expanderCell = Ext.fly(view.getRow(rowIdx)).down(me.expanderColumn.getCellSelector(), true);
            if (expanderCell) {
                expanderCell.rowSpan = rowSpan;
            }
        }
        fireView.fireEvent(wasCollapsed ? 'expandbody' : 'collapsebody', rowNode, record, nextBd);
        
        
        if (view.getSizeModel().height.shrinkWrap || ownerLockable) {
            view.refreshSize(true);
        }
        
        
        if (scroller) {
            if (bufferedRenderer) {
                bufferedRenderer.refreshSize();
            } else {
                scroller.refresh(true);
            }
        }
    },
    
    syncRowHeights: function(lockedItem, normalItem) {
        var me = this,
            lockedBd = Ext.fly(lockedItem).down(me.rowBodyTrSelector),
            normalBd = Ext.fly(normalItem).down(me.rowBodyTrSelector),
            lockedHeight, normalHeight;
        
        if (normalBd.isVisible()) {
            
            if ((lockedHeight = lockedBd.getHeight()) !== (normalHeight = normalBd.getHeight())) {
                if (lockedHeight > normalHeight) {
                    normalBd.setHeight(lockedHeight);
                } else {
                    lockedBd.setHeight(normalHeight);
                }
            }
        } else 
        {
            lockedBd.dom.style.height = normalBd.dom.style.height = '';
        }
    },
    onColumnUnlock: function(lockable, column) {
        var me = this,
            lockedColumns;
        lockable = me.grid.ownerLockable;
        lockedColumns = lockable.lockedGrid.visibleColumnManager.getColumns();
        
        if (lockedColumns.length === 1) {
            if (lockedColumns[0] === me.expanderColumn) {
                lockable.unlock(me.expanderColumn);
                me.grid = lockable.normalGrid;
            } else {
                lockable.lock(me.expanderColumn, 0);
            }
        }
    },
    onColumnLock: function(lockable, column) {
        var me = this,
            lockedColumns, lockedGrid;
        lockable = me.grid.ownerLockable;
        lockedColumns = lockable.lockedGrid.visibleColumnManager.getColumns();
        
        if (lockedColumns.length === 1) {
            me.grid = lockedGrid = lockable.lockedGrid;
            lockedGrid.headerCt.insert(0, me.expanderColumn);
        }
    },
    getHeaderConfig: function() {
        var me = this,
            lockable = me.grid.ownerLockable;
        return {
            width: me.headerWidth,
            lockable: false,
            autoLock: true,
            sortable: false,
            resizable: false,
            draggable: false,
            hideable: false,
            menuDisabled: true,
            tdCls: Ext.baseCSSPrefix + 'grid-cell-special',
            innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-row-expander',
            renderer: function() {
                return '<div class="' + Ext.baseCSSPrefix + 'grid-row-expander" role="presentation"></div>';
            },
            processEvent: function(type, view, cell, rowIndex, cellIndex, e, record) {
                if (e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-expander')) {
                    if (type === "click") {
                        me.toggleRow(rowIndex, record);
                        return me.selectRowOnExpand;
                    }
                }
            },
            
            
            isLocked: function() {
                return lockable && (lockable.lockedGrid.isVisible() || this.locked);
            },
            
            editRenderer: function() {
                return '&#160;';
            }
        };
    }
});


Ext.define('Ext.grid.property.Grid', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.propertygrid',
    alternateClassName: 'Ext.grid.PropertyGrid',
    uses: [
        'Ext.grid.plugin.CellEditing',
        'Ext.grid.property.Store',
        'Ext.grid.property.HeaderContainer',
        'Ext.XTemplate',
        'Ext.grid.CellEditor',
        'Ext.form.field.Date',
        'Ext.form.field.Text',
        'Ext.form.field.Number',
        'Ext.form.field.ComboBox'
    ],
    
    
    
    
    
    
    valueField: 'value',
    
    nameField: 'name',
    
    inferTypes: true,
    
    
    enableColumnMove: false,
    columnLines: true,
    stripeRows: false,
    trackMouseOver: false,
    clicksToEdit: 1,
    enableHdMenu: false,
    gridCls: Ext.baseCSSPrefix + 'property-grid',
    
    
    
    initComponent: function() {
        var me = this;
        me.source = me.source || {};
        me.addCls(me.gridCls);
        me.plugins = me.plugins || [];
        
        me.plugins.push(new Ext.grid.plugin.CellEditing({
            clicksToEdit: me.clicksToEdit,
            
            startEdit: function(record, column) {
                
                return this.self.prototype.startEdit.call(this, record, me.valueColumn);
            }
        }));
        me.selModel = {
            type: 'cellmodel',
            onCellSelect: function(position) {
                
                position.column = me.valueColumn;
                position.colIdx = me.valueColumn.getVisibleIndex();
                return this.self.prototype.onCellSelect.call(this, position);
            }
        };
        me.sourceConfig = Ext.apply({}, me.sourceConfig);
        
        if (!me.store) {
            me.propStore = me.store = new Ext.grid.property.Store(me,me.source);
        }
        me.configure(me.sourceConfig);
        if (me.sortableColumns) {
            me.store.sort('name', 'ASC');
        }
        me.columns = new Ext.grid.property.HeaderContainer(me,me.store);
        me.callParent();
        
        me.getView().walkCells = this.walkCells;
        
        me.editors = {
            'date': new Ext.grid.CellEditor({
                field: new Ext.form.field.Date({
                    selectOnFocus: true
                })
            }),
            'string': new Ext.grid.CellEditor({
                field: new Ext.form.field.Text({
                    selectOnFocus: true
                })
            }),
            'number': new Ext.grid.CellEditor({
                field: new Ext.form.field.Number({
                    selectOnFocus: true
                })
            }),
            'boolean': new Ext.grid.CellEditor({
                field: new Ext.form.field.ComboBox({
                    editable: false,
                    store: [
                        [
                            true,
                            me.headerCt.trueText
                        ],
                        [
                            false,
                            me.headerCt.falseText
                        ]
                    ]
                })
            })
        };
        
        me.store.on('update', me.onUpdate, me);
    },
    configure: function(config) {
        var me = this,
            store = me.store,
            i = 0,
            len = me.store.getCount(),
            nameField = me.nameField,
            valueField = me.valueField,
            name, value, rec, type;
        me.configureLegacy(config);
        if (me.inferTypes) {
            for (; i < len; ++i) {
                rec = store.getAt(i);
                name = rec.get(nameField);
                if (!me.getConfigProp(name, 'type')) {
                    value = rec.get(valueField);
                    if (Ext.isDate(value)) {
                        type = 'date';
                    } else if (Ext.isNumber(value)) {
                        type = 'number';
                    } else if (Ext.isBoolean(value)) {
                        type = 'boolean';
                    } else {
                        type = 'string';
                    }
                    me.setConfigProp(name, 'type', type);
                }
            }
        }
    },
    getConfigProp: function(fieldName, key, defaultValue) {
        var config = this.sourceConfig[fieldName],
            out;
        if (config) {
            out = config[key];
        }
        return out || defaultValue;
    },
    setConfigProp: function(fieldName, key, value) {
        var sourceCfg = this.sourceConfig,
            o = sourceCfg[fieldName];
        if (!o) {
            o = sourceCfg[fieldName] = {
                __copied: true
            };
        } else if (!o.__copied) {
            o = Ext.apply({
                __copied: true
            }, o);
            sourceCfg[fieldName] = o;
        }
        o[key] = value;
        return value;
    },
    
    configureLegacy: function(config) {
        var me = this;
        me.copyLegacyObject(config, me.customRenderers, 'renderer');
        me.copyLegacyObject(config, me.customEditors, 'editor');
        me.copyLegacyObject(config, me.propertyNames, 'displayName');
        
        if (me.customRenderers || me.customEditors || me.propertyNames) {
            if (Ext.global.console && Ext.global.console.warn) {
                Ext.global.console.warn(this.$className, 'customRenderers, customEditors & propertyNames have been consolidated into a new config, see "sourceConfig". These configurations will be deprecated.');
            }
        }
    },
    copyLegacyObject: function(config, o, keyName) {
        var key;
        for (key in o) {
            if (o.hasOwnProperty(key)) {
                if (!config[key]) {
                    config[key] = {};
                }
                config[key][keyName] = o[key];
            }
        }
    },
    
    onUpdate: function(store, record, operation) {
        var me = this,
            v, oldValue;
        if (me.rendered && operation == Ext.data.Model.EDIT) {
            v = record.get(me.valueField);
            oldValue = record.modified.value;
            if (me.fireEvent('beforepropertychange', me.source, record.getId(), v, oldValue) !== false) {
                if (me.source) {
                    me.source[record.getId()] = v;
                }
                record.commit();
                me.fireEvent('propertychange', me.source, record.getId(), v, oldValue);
            } else {
                record.reject();
            }
        }
    },
    
    
    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {
        var me = this,
            valueColumn = me.ownerCt.valueColumn;
        if (direction == 'left') {
            direction = 'up';
        } else if (direction == 'right') {
            direction = 'down';
        }
        pos = Ext.view.Table.prototype.walkCells.call(me, pos, direction, e, preventWrap, verifierFn, scope);
        
        pos.column = valueColumn;
        pos.colIdx = valueColumn.getVisibleIndex();
        return pos;
    },
    
    
    getCellEditor: function(record, column) {
        var me = this,
            propName = record.get(me.nameField),
            val = record.get(me.valueField),
            editor = me.getConfigProp(propName, 'editor'),
            type = me.getConfigProp(propName, 'type'),
            editors = me.editors;
        
        
        if (editor) {
            if (!(editor instanceof Ext.grid.CellEditor)) {
                if (!(editor instanceof Ext.form.field.Base)) {
                    editor = Ext.ComponentManager.create(editor, 'textfield');
                }
                editor = me.setConfigProp(propName, 'editor', new Ext.grid.CellEditor({
                    field: editor
                }));
            }
        } else if (type) {
            switch (type) {
                case 'date':
                    editor = editors.date;
                    break;
                case 'number':
                    editor = editors.number;
                    break;
                case 'boolean':
                    editor = me.editors['boolean'];
                    break;
                default:
                    editor = editors.string;
            }
        } else if (Ext.isDate(val)) {
            editor = editors.date;
        } else if (Ext.isNumber(val)) {
            editor = editors.number;
        } else if (Ext.isBoolean(val)) {
            editor = editors['boolean'];
        } else {
            editor = editors.string;
        }
        
        editor.editorId = propName;
        editor.field.column = me.valueColumn;
        return editor;
    },
    beforeDestroy: function() {
        var me = this;
        me.callParent();
        me.destroyEditors(me.editors);
        me.destroyEditors(me.customEditors);
        delete me.source;
    },
    destroyEditors: function(editors) {
        for (var ed in editors) {
            if (editors.hasOwnProperty(ed)) {
                Ext.destroy(editors[ed]);
            }
        }
    },
    
    setSource: function(source, sourceConfig) {
        var me = this;
        me.source = source;
        if (sourceConfig !== undefined) {
            me.sourceConfig = Ext.apply({}, sourceConfig);
            me.configure(me.sourceConfig);
        }
        me.propStore.setSource(source);
    },
    
    getSource: function() {
        return this.propStore.getSource();
    },
    
    setProperty: function(prop, value, create) {
        this.propStore.setValue(prop, value, create);
    },
    
    removeProperty: function(prop) {
        this.propStore.remove(prop);
    }
});


Ext.define('Ext.grid.property.HeaderContainer', {
    extend: 'Ext.grid.header.Container',
    alternateClassName: 'Ext.grid.PropertyColumnModel',
    nameWidth: 115,
    
    
    nameText: 'Name',
    
    
    valueText: 'Value',
    
    
    dateFormat: 'm/j/Y',
    
    
    trueText: 'true',
    
    
    falseText: 'false',
    
    
    nameColumnCls: Ext.baseCSSPrefix + 'grid-property-name',
    nameColumnInnerCls: Ext.baseCSSPrefix + 'grid-cell-inner-property-name',
    
    constructor: function(grid, store) {
        var me = this;
        me.grid = grid;
        me.store = store;
        me.callParent([
            {
                isRootHeader: true,
                enableColumnResize: Ext.isDefined(grid.enableColumnResize) ? grid.enableColumnResize : me.enableColumnResize,
                enableColumnMove: Ext.isDefined(grid.enableColumnMove) ? grid.enableColumnMove : me.enableColumnMove,
                items: [
                    {
                        header: me.nameText,
                        width: grid.nameColumnWidth || me.nameWidth,
                        sortable: grid.sortableColumns,
                        dataIndex: grid.nameField,
                        scope: me,
                        renderer: me.renderProp,
                        itemId: grid.nameField,
                        menuDisabled: true,
                        tdCls: me.nameColumnCls,
                        innerCls: me.nameColumnInnerCls
                    },
                    {
                        header: me.valueText,
                        scope: me,
                        renderer: me.renderCell,
                        getEditor: me.getCellEditor.bind(me),
                        sortable: grid.sortableColumns,
                        flex: 1,
                        fixed: true,
                        dataIndex: grid.valueField,
                        itemId: grid.valueField,
                        menuDisabled: true
                    }
                ]
            }
        ]);
        
        me.grid.valueColumn = me.items.getAt(1);
    },
    getCellEditor: function(record) {
        return this.grid.getCellEditor(record, this);
    },
    
    
    renderProp: function(v) {
        return this.getPropertyName(v);
    },
    
    
    renderCell: function(val, meta, rec) {
        var me = this,
            grid = me.grid,
            renderer = grid.getConfigProp(rec.get(grid.nameField), 'renderer'),
            result = val;
        if (renderer) {
            return renderer.apply(me, arguments);
        }
        if (Ext.isDate(val)) {
            result = me.renderDate(val);
        } else if (Ext.isBoolean(val)) {
            result = me.renderBool(val);
        }
        return Ext.util.Format.htmlEncode(result);
    },
    
    renderDate: Ext.util.Format.date,
    
    renderBool: function(bVal) {
        return this[bVal ? 'trueText' : 'falseText'];
    },
    
    
    getPropertyName: function(name) {
        return this.grid.getConfigProp(name, 'displayName', name);
    }
});


Ext.define('Ext.grid.property.Property', {
    extend: 'Ext.data.Model',
    alternateClassName: 'Ext.PropGridProperty',
    fields: [
        {
            name: 'name',
            type: 'string'
        },
        {
            name: 'value'
        }
    ],
    idProperty: 'name'
});


Ext.define('Ext.grid.property.Reader', {
    extend: 'Ext.data.reader.Reader',
    successProperty: null,
    totalProperty: null,
    messageProperty: null,
    read: function(dataObject) {
        return this.readRecords(dataObject);
    },
    readRecords: function(dataObject) {
        var Model = this.getModel(),
            result = {
                records: [],
                success: true
            },
            val, propName;
        for (propName in dataObject) {
            if (dataObject.hasOwnProperty(propName)) {
                val = dataObject[propName];
                if (this.isEditableValue(val)) {
                    result.records.push(new Model({
                        name: propName,
                        value: val
                    }));
                }
            }
        }
        result.total = result.count = result.records.length;
        return new Ext.data.ResultSet(result);
    },
    
    isEditableValue: function(val) {
        return Ext.isPrimitive(val) || Ext.isDate(val) || val === null;
    }
});


Ext.define('Ext.grid.property.Store', {
    extend: 'Ext.data.Store',
    alternateClassName: 'Ext.grid.PropertyStore',
    remoteSort: true,
    requires: [
        'Ext.grid.property.Reader',
        'Ext.data.proxy.Memory',
        'Ext.grid.property.Property'
    ],
    
    constructor: function(grid, source) {
        var me = this;
        me.grid = grid;
        me.source = source;
        me.callParent([
            {
                data: source,
                model: Ext.grid.property.Property,
                proxy: me.getProxy()
            }
        ]);
    },
    
    getProxy: function() {
        var proxy = this.proxy;
        if (!proxy) {
            proxy = this.proxy = new Ext.data.proxy.Memory({
                model: Ext.grid.property.Property,
                reader: this.getReader()
            });
        }
        return proxy;
    },
    
    getReader: function() {
        var reader = this.reader;
        if (!reader) {
            reader = this.reader = new Ext.grid.property.Reader({
                model: Ext.grid.property.Property
            });
        }
        return reader;
    },
    
    
    setSource: function(dataObject) {
        var me = this;
        me.source = dataObject;
        me.suspendEvents();
        me.removeAll();
        me.getProxy().setData(dataObject);
        me.load();
        me.resumeEvents();
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },
    
    getProperty: function(row) {
        return Ext.isNumber(row) ? this.getAt(row) : this.getById(row);
    },
    
    setValue: function(prop, value, create) {
        var me = this,
            rec = me.getRec(prop);
        if (rec) {
            rec.set('value', value);
            me.source[prop] = value;
        } else if (create) {
            
            me.source[prop] = value;
            rec = new Ext.grid.property.Property({
                name: prop,
                value: value
            },prop);
            me.add(rec);
        }
    },
    
    remove: function(prop) {
        var rec = this.getRec(prop);
        if (rec) {
            this.callParent([
                rec
            ]);
            delete this.source[prop];
        }
    },
    
    getRec: function(prop) {
        return this.getById(prop);
    },
    
    
    getSource: function() {
        return this.source;
    },
    onDestroy: function() {
        Ext.destroy(this.reader, this.proxy);
        this.callParent();
    }
});


Ext.define('Ext.grid.selection.Selection', {
    constructor: function(view) {
        if (!view || !(view.isTableView || view.isLockingView)) {
            Ext.Error.raise('Selection must be created for a given TableView or LockingView');
        }
        
        this.view = view.ownerGrid.view;
    }
});


Ext.define('Ext.grid.selection.Cells', {
    extend: 'Ext.grid.selection.Selection',
    type: 'cells',
    
    isCells: true,
    
    
    clone: function() {
        var me = this,
            result = new me.self(me.view);
        if (me.startCell) {
            result.startCell = me.startCell.clone();
            result.endCell = me.endCell.clone();
        }
        return result;
    },
    
    contains: function(cellContext) {
        var range;
        if (!cellContext || !cellContext.isCellContext) {
            return false;
        }
        if (this.startCell) {
            
            range = this.getRowRange();
            if (cellContext.rowIdx >= range[0] && cellContext.rowIdx <= range[1]) {
                
                range = this.getColumnRange();
                return (cellContext.colIdx >= range[0] && cellContext.colIdx <= range[1]);
            }
        }
        return false;
    },
    eachRow: function(fn, scope) {
        var me = this,
            rowRange = me.getRowRange(),
            context = new Ext.grid.CellContext(me.view),
            rowIdx;
        for (rowIdx = rowRange[0]; rowIdx <= rowRange[1]; rowIdx++) {
            context.setRow(rowIdx);
            if (fn.call(scope || me, context.record) === false) {
                return;
            }
        }
    },
    eachColumn: function(fn, scope) {
        var me = this,
            colRange = me.getColumnRange(),
            context = new Ext.grid.CellContext(me.view),
            colIdx;
        for (colIdx = colRange[0]; colIdx <= colRange[1]; colIdx++) {
            context.setColumn(colIdx);
            if (fn.call(scope || me, context.column, colIdx) === false) {
                return;
            }
        }
    },
    eachCell: function(fn, scope) {
        var me = this,
            rowRange = me.getRowRange(),
            colRange = me.getColumnRange(),
            context = new Ext.grid.CellContext(me.view),
            rowIdx, colIdx;
        for (rowIdx = rowRange[0]; rowIdx <= rowRange[1]; rowIdx++) {
            context.setRow(rowIdx);
            for (colIdx = colRange[0]; colIdx <= colRange[1]; colIdx++) {
                context.setColumn(colIdx);
                if (fn.call(scope || me, context, colIdx, rowIdx) === false) {
                    return;
                }
            }
        }
    },
    
    getFirstRowIndex: function() {
        return this.startCell ? Math.min(this.startCell.rowIdx, this.endCell.rowIdx) : 0;
    },
    
    getLastRowIndex: function() {
        return this.startCell ? Math.max(this.startCell.rowIdx, this.endCell.rowIdx) : -1;
    },
    
    getFirstColumnIndex: function() {
        return this.startCell ? Math.min(this.startCell.colIdx, this.endCell.colIdx) : 0;
    },
    
    getLastColumnIndex: function() {
        return this.startCell ? Math.max(this.startCell.colIdx, this.endCell.colIdx) : -1;
    },
    
    privates: {
        
        clear: function() {
            var me = this,
                view = me.view;
            me.eachCell(function(cellContext) {
                view.onCellDeselect(cellContext);
            });
            me.startCell = me.endCell = null;
        },
        
        setRangeStart: function(startCell) {
            
            this.startCell = (this.endCell = startCell.clone()).clone();
            this.view.onCellSelect(startCell);
        },
        
        setRangeEnd: function(endCell) {
            var me = this,
                range, lastRange, rowStart, rowEnd, colStart, colEnd, rowIdx, colIdx,
                view = me.view,
                rows = view.all,
                cell = new Ext.grid.CellContext(view),
                maxColIdx = view.getVisibleColumnManager().getColumns().length - 1;
            me.endCell = endCell.clone();
            range = me.getRange();
            lastRange = me.lastRange || range;
            rowStart = Math.max(Math.min(range[0][1], lastRange[0][1]), rows.startIndex);
            rowEnd = Math.min(Math.max(range[1][1], lastRange[1][1]), rows.endIndex);
            colStart = Math.min(range[0][0], lastRange[0][0]);
            colEnd = Math.min(Math.max(range[1][0], lastRange[1][0]), maxColIdx);
            
            for (rowIdx = rowStart; rowIdx <= rowEnd; rowIdx++) {
                for (colIdx = colStart; colIdx <= colEnd; colIdx++) {
                    cell.setPosition(rowIdx, colIdx);
                    
                    if (rowIdx < range[0][1] || rowIdx > range[1][1] || colIdx < range[0][0] || colIdx > range[1][0]) {
                        view.onCellDeselect(cell);
                    } else {
                        view.onCellSelect(cell);
                    }
                }
            }
            me.lastRange = range;
        },
        
        getRange: function() {
            return [
                [
                    this.getFirstColumnIndex(),
                    this.getFirstRowIndex()
                ],
                [
                    this.getLastColumnIndex(),
                    this.getLastRowIndex()
                ]
            ];
        },
        
        getRangeSize: function() {
            return this.getCount();
        },
        
        getCount: function() {
            var range = this.getRange();
            return (range[1][0] - range[0][0] + 1) * (range[1][1] - range[0][1] + 1);
        },
        
        selectAll: function() {
            var me = this,
                view = me.view;
            me.clear();
            me.setRangeStart(new Ext.grid.CellContext(view).setPosition(0, 0));
            me.setRangeEnd(new Ext.grid.CellContext(view).setPosition(view.dataSource.getCount() - 1, view.getVisibleColumnManager().getColumns().length - 1));
        },
        
        isAllSelected: function() {
            var start = this.rangeStart,
                end = this.rangeEnd;
            
            if (start) {
                if (!start.colIdx && !start.rowIdx) {
                    return end.colIdx === end.view.getVisibleColumnManager().getColumns().length - 1 && end.rowIdx === end.view.dataSource.getCount - 1;
                }
            }
            return false;
        },
        
        getColumnRange: function() {
            return [
                this.getFirstColumnIndex(),
                this.getLastColumnIndex()
            ];
        },
        
        getRowRange: function() {
            return [
                this.getFirstRowIndex(),
                this.getLastRowIndex()
            ];
        }
    }
});


Ext.define('Ext.grid.selection.Columns', {
    extend: 'Ext.grid.selection.Selection',
    type: 'columns',
    
    isColumns: true,
    
    
    clone: function() {
        var me = this,
            result = new me.self(me.view),
            columns = me.selectedColumns;
        if (columns) {
            result.selectedColumns = Ext.Array.slice(columns);
        }
        return result;
    },
    eachRow: function(fn, scope) {
        var columns = this.selectedColumns;
        if (columns && columns.length) {
            this.view.dataSource.each(fn, scope || this);
        }
    },
    eachColumn: function(fn, scope) {
        var me = this,
            view = me.view,
            columns = me.selectedColumns,
            len, i,
            context = new Ext.grid.CellContext(view);
        if (columns) {
            len = columns.length;
            for (i = 0; i < len; i++) {
                context.setColumn(columns[i]);
                if (fn.call(scope || me, context.column, context.colIdx) === false) {
                    return false;
                }
            }
        }
    },
    eachCell: function(fn, scope) {
        var me = this,
            view = me.view,
            columns = me.selectedColumns,
            len, i,
            context = new Ext.grid.CellContext(view);
        if (columns) {
            len = columns.length;
            
            view.dataSource.each(function(record) {
                context.setRow(record);
                for (i = 0; i < len; i++) {
                    context.setColumn(columns[i]);
                    if (fn.call(scope || me, context, context.colIdx, context.rowIdx) === false) {
                        return false;
                    }
                }
            });
        }
    },
    
    
    
    contains: function(column) {
        var selectedColumns = this.selectedColumns;
        if (column && column.isColumn && selectedColumns && selectedColumns.length) {
            return Ext.Array.contains(selectedColumns, column);
        }
        return false;
    },
    
    getCount: function() {
        var selectedColumns = this.selectedColumns;
        return selectedColumns ? selectedColumns.length : 0;
    },
    
    getColumns: function() {
        return this.selectedColumns || [];
    },
    
    privates: {
        
        add: function(column) {
            if (!column.isColumn) {
                Ext.Error.raise('Column selection must be passed a grid Column header object');
            }
            Ext.Array.include((this.selectedColumns || (this.selectedColumns = [])), column);
            this.refreshColumns(column);
        },
        
        clear: function() {
            var me = this,
                prevSelection = me.selectedColumns;
            if (prevSelection && prevSelection.length) {
                me.selectedColumns = [];
                me.refreshColumns.apply(me, prevSelection);
            }
        },
        
        isAllSelected: function() {
            var selectedColumns = this.selectedColumns;
            
            return selectedColumns && selectedColumns.length === this.view.ownerGrid.getVisibleColumnManager().getColumns().length;
        },
        
        refreshColumns: function(column) {
            var me = this,
                view = me.view,
                rows = view.all,
                rowIdx,
                columns = arguments,
                len = columns.length,
                colIdx,
                cellContext = new Ext.grid.CellContext(view),
                selected = [];
            if (view.rendered) {
                for (colIdx = 0; colIdx < len; colIdx++) {
                    selected[colIdx] = me.contains(columns[colIdx]);
                }
                for (rowIdx = rows.startIndex; rowIdx <= rows.endIndex; rowIdx++) {
                    cellContext.setRow(rowIdx);
                    for (colIdx = 0; colIdx < len; colIdx++) {
                        
                        cellContext.setColumn(columns[colIdx]);
                        if (selected[colIdx]) {
                            view.onCellSelect(cellContext);
                        } else {
                            view.onCellDeselect(cellContext);
                        }
                    }
                }
            }
        },
        
        remove: function(column) {
            if (!column.isColumn) {
                Ext.Error.raise('Column selection must be passed a grid Column header object');
            }
            if (this.selectedColumns) {
                Ext.Array.remove(this.selectedColumns, column);
                this.refreshColumns(column);
            }
        },
        
        selectAll: function() {
            var me = this;
            me.clear();
            me.selectedColumns = me.view.getVisibleColumnManager().getColumns();
            me.refreshColumns.apply(me, me.selectedColumns);
        }
    }
});


Ext.define('Ext.grid.selection.Rows', {
    extend: 'Ext.grid.selection.Selection',
    requires: [
        'Ext.util.Collection'
    ],
    type: 'rows',
    
    isRows: true,
    
    
    clone: function() {
        var me = this,
            result = new me.self(me.view);
        
        if (me.selectedRecords) {
            result.selectedRecords = me.selectedRecords.clone();
        }
        
        if (me.rangeStart) {
            result.setRangeStart(me.rangeStart);
            result.setRangeEnd(me.rangeEnd);
        }
        return result;
    },
    
    
    add: function(record) {
        if (!(record.isModel)) {
            Ext.Error.raise('Row selection must be passed a record');
        }
        var selection = this.selectedRecords || (this.selectedRecords = this.createRecordCollection());
        if (!selection.byInternalId.get(record.internalId)) {
            selection.add(record);
            this.view.onRowSelect(record);
        }
    },
    remove: function(record) {
        if (!(record.isModel)) {
            Ext.Error.raise('Row selection must be passed a record');
        }
        var me = this;
        if (me.selectedRecords && me.selectedRecords.byInternalId.get(record.internalId)) {
            me.selectedRecords.remove(record);
            me.view.onRowDeselect(record);
            
            
            me.allSelected = false;
            return true;
        }
    },
    
    contains: function(record) {
        if (!record || !record.isModel) {
            return false;
        }
        var me = this,
            result = false,
            selectedRecords = me.selectedRecords,
            recIndex, dragRange;
        
        
        
        
        if (me.allSelected) {
            me.add(record);
            return true;
        }
        
        if (selectedRecords) {
            result = !!selectedRecords.byInternalId.get(record.internalId);
        }
        
        if (!result && me.rangeStart != null) {
            dragRange = me.getRange();
            recIndex = me.view.dataSource.indexOf(record);
            result = recIndex >= dragRange[0] && recIndex <= dragRange[1];
        }
        return result;
    },
    
    getCount: function() {
        var me = this,
            selectedRecords = me.selectedRecords,
            result = selectedRecords ? selectedRecords.getCount() : 0,
            range = me.getRange(),
            i,
            store = me.view.dataSource;
        
        for (i = range[0]; i <= range[1]; i++) {
            if (!selectedRecords || !selectedRecords.byInternalId.get(store.getAt(i).internalId)) {
                result++;
            }
        }
        return result;
    },
    
    getRecords: function() {
        var selectedRecords = this.selectedRecords;
        return selectedRecords ? selectedRecords.getRange() : [];
    },
    selectAll: function() {
        var me = this;
        me.clear();
        me.setRangeStart(0);
        me.setRangeEnd(me.view.dataSource.getCount() - 1);
        
        me.addRange();
        
        
        me.allSelected = true;
    },
    eachRow: function(fn, scope) {
        var selectedRecords = this.selectedRecords;
        if (selectedRecords) {
            selectedRecords.each(fn, scope || this);
        }
    },
    eachColumn: function(fn, scope) {
        var columns = this.view.getVisibleColumnManager().getColumns(),
            len = columns.length,
            i;
        
        if (this.selectedRecords) {
            for (i = 0; i < len; i++) {
                if (fn.call(this || scope, columns[i], i) === false) {
                    return;
                }
            }
        }
    },
    eachCell: function(fn, scope) {
        var me = this,
            selection = me.selectedRecords,
            view = me.view,
            columns = view.ownerGrid.getVisibleColumnManager().getColumns(),
            colCount, i, j, context, range, recCount,
            abort = false;
        if (columns) {
            colCount = columns.length;
            context = new Ext.grid.CellContext(view);
            
            if (selection) {
                selection.each(function(record) {
                    context.setRow(record);
                    for (i = 0; i < colCount; i++) {
                        context.setColumn(columns[i]);
                        if (fn.call(scope || me, context, context.colIdx, context.rowIdx) === false) {
                            abort = true;
                            return false;
                        }
                    }
                });
            }
            
            if (!abort && me.rangeStart != null) {
                range = me.getRange();
                me.view.dataSource.getRange(range[0], range[1], {
                    callback: function(records) {
                        recCount = records.length;
                        for (i = 0; !abort && i < recCount; i++) {
                            context.setRow(records[i]);
                            for (j = 0; !abort && j < colCount; j++) {
                                context.setColumn(columns[j]);
                                if (fn.call(scope || me, context, context.colIdx, context.rowIdx) === false) {
                                    abort = true;
                                }
                            }
                        }
                    }
                });
            }
        }
    },
    destroy: function() {
        this.selectedRecords = Ext.destroy(this.selectedRecords);
    },
    
    privates: {
        
        clear: function() {
            var me = this,
                view = me.view;
            
            
            me.allSelected = false;
            if (me.selectedRecords) {
                me.eachRow(function(record) {
                    view.onRowDeselect(record);
                });
                me.selectedRecords.clear();
            }
        },
        
        isAllSelected: function() {
            
            
            
            return !!this.allSelected;
        },
        
        setRangeStart: function(start) {
            
            
            this.allSelected = false;
            this.rangeStart = this.rangeEnd = start;
            this.view.onRowSelect(start);
        },
        
        setRangeEnd: function(end) {
            var me = this,
                range, lastRange, rowIdx, row,
                view = me.view,
                store = view.dataSource,
                rows = view.all,
                selected = me.selectedRecords,
                rec;
            
            
            me.rangeEnd = end;
            range = me.getRange();
            lastRange = me.lastRange || range;
            
            for (rowIdx = Math.max(Math.min(range[0], lastRange[0]), rows.startIndex) , end = Math.min(Math.max(range[1], lastRange[1]), rows.endIndex); rowIdx <= end; rowIdx++) {
                row = rows.item(rowIdx);
                
                if (rowIdx < range[0] || rowIdx > range[1]) {
                    
                    if (selected && (rec = selected.byInternalId.get(store.getAt(rowIdx).internalId))) {
                        selected.remove(rec);
                    }
                    view.onRowDeselect(rowIdx);
                } else {
                    view.onRowSelect(rowIdx);
                }
            }
            me.lastRange = range;
        },
        
        getRange: function() {
            var start = this.rangeStart,
                end = this.rangeEnd;
            if (start == null) {
                return [
                    0,
                    -1
                ];
            } else if (start <= end) {
                return [
                    start,
                    end
                ];
            }
            return [
                end,
                start
            ];
        },
        
        getRangeSize: function() {
            var range = this.getRange();
            return range[1] - range[0] + 1;
        },
        
        createRecordCollection: function() {
            var result = new Ext.util.Collection({
                    rootProperty: 'data',
                    extraKeys: {
                        byInternalId: {
                            rootProperty: false,
                            property: 'internalId'
                        }
                    }
                });
            return result;
        },
        
        addRange: function() {
            var me = this,
                range, selection;
            if (me.rangeStart != null) {
                range = me.getRange();
                selection = me.selectedRecords || (me.selectedRecords = me.createRecordCollection());
                me.view.dataSource.getRange(range[0], range[1], {
                    callback: function(range) {
                        selection.add.apply(selection, range);
                    }
                });
                
                me.setRangeStart(me.lastRange = null);
            }
        }
    }
});


Ext.define('Ext.grid.selection.SpreadsheetModel', {
    extend: 'Ext.selection.Model',
    requires: [
        'Ext.grid.selection.Selection',
        'Ext.grid.selection.Cells',
        'Ext.grid.selection.Rows',
        'Ext.grid.selection.Columns'
    ],
    alias: 'selection.spreadsheet',
    isSpreadsheetModel: true,
    config: {
        
        columnSelect: {
            $value: false,
            lazy: true
        },
        
        cellSelect: {
            $value: true,
            lazy: true
        },
        
        rowSelect: {
            $value: true,
            lazy: true
        },
        
        dragSelect: {
            $value: true,
            lazy: true
        },
        
        selected: null
    },
    
    
    checkboxSelect: false,
    
    checkboxColumnIndex: 0,
    
    showHeaderCheckbox: true,
    
    checkboxHeaderWidth: 24,
    
    rowNumbererHeaderWidth: 46,
    columnSelectCls: Ext.baseCSSPrefix + 'ssm-column-select',
    rowNumbererHeaderCls: Ext.baseCSSPrefix + 'ssm-row-numberer-hd',
    rowNumbererTdCls: Ext.grid.column.RowNumberer.prototype.tdCls + ' ' + Ext.baseCSSPrefix + 'ssm-row-numberer-cell',
    
    checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on',
    tdCls: Ext.baseCSSPrefix + 'grid-cell-special ' + Ext.baseCSSPrefix + 'grid-cell-row-checker',
    
    
    
    
    
    
    
    bindComponent: function(view) {
        var me = this,
            viewListeners, lockedGrid;
        if (me.view !== view) {
            if (me.view) {
                me.navigationModel = null;
                Ext.destroy(me.viewListeners, me.navigationListeners);
            }
            me.view = view;
            if (view) {
                
                me.getCellSelect();
                lockedGrid = view.ownerGrid.lockedGrid;
                
                if (lockedGrid) {
                    me.hasLockedHeader = true;
                    me.onViewCreated(lockedGrid, lockedGrid.getView());
                } else 
                {
                    view.grid.on({
                        viewcreated: me.onViewCreated,
                        scope: me,
                        single: true
                    });
                }
                me.gridListeners = view.ownerGrid.on({
                    columnschanged: me.onColumnsChanged,
                    scope: me,
                    destroyable: true
                });
                viewListeners = me.getViewListeners();
                viewListeners.scope = me;
                viewListeners.destroyable = true;
                me.viewListeners = view.on(viewListeners);
                me.navigationModel = view.getNavigationModel();
                me.navigationListeners = me.navigationModel.on({
                    navigate: me.onNavigate,
                    scope: me,
                    destroyable: true
                });
                
                if (me.getColumnSelect()) {
                    view.ownerGrid.addCls(me.columnSelectCls);
                }
            }
        }
    },
    
    getCheckboxHeaderConfig: function() {
        var me = this,
            showCheck = me.showHeaderCheckbox !== false;
        return {
            isCheckerHd: showCheck,
            text: '&#160;',
            clickTargetName: 'el',
            width: me.checkboxHeaderWidth,
            sortable: false,
            draggable: false,
            resizable: false,
            hideable: false,
            menuDisabled: true,
            dataIndex: '',
            tdCls: me.tdCls,
            cls: showCheck ? Ext.baseCSSPrefix + 'column-header-checkbox ' : '',
            defaultRenderer: me.checkboxRenderer.bind(me),
            editRenderer: '&#160;',
            locked: me.hasLockedHeader
        };
    },
    
    checkboxRenderer: function() {
        return '<div class="' + Ext.baseCSSPrefix + 'grid-row-checker" role="presentation">&#160;</div>';
    },
    
    onHeaderClick: function(headerCt, header, e) {
        
        var me = this,
            sel = me.selected;
        if (header === me.numbererColumn || header === me.checkColumn) {
            e.stopEvent();
            
            if (!sel || !sel.isAllSelected()) {
                me.selectAll(headerCt.view);
            } else {
                me.deselectAll();
            }
            me.updateHeaderState();
        } else if (me.columnSelect) {
            if (me.isColumnSelected(header)) {
                me.deselectColumn(header);
            } else {
                me.selectColumn(header, e.ctrlKey);
            }
        }
    },
    
    updateHeaderState: function() {
        
        var me = this,
            store = me.view.dataSource,
            storeCount = store.getCount(),
            views = me.views,
            sel = me.selected,
            isChecked = sel && sel.isRows && !store.isBufferedStore && storeCount > 0 && (storeCount === sel.getCount()),
            checkHd = me.checkColumn,
            cls = me.checkerOnCls;
        if (views && views.length) {
            if (checkHd) {
                if (isChecked) {
                    checkHd.addCls(cls);
                } else {
                    checkHd.removeCls(cls);
                }
            }
        }
    },
    
    onReconfigure: function(grid, store, columns) {
        if (columns) {
            this.addCheckbox(this.views[0]);
        }
    },
    
    getCellContext: function(record, column) {
        return new Ext.grid.CellContext(this.view.ownerGrid.getView()).setPosition(record, column);
    },
    select: function(records, keepExisting, suppressEvent) {
        
        var me = this,
            sel = me.selected,
            view = me.view,
            store = view.dataSource,
            len, i, record,
            changed = false;
        
        if (!sel || !sel.isRows || sel.view !== view) {
            me.resetSelection(true);
            sel = me.selected = new Ext.grid.selection.Rows(view);
        } else if (!keepExisting) {
            sel.clear();
        }
        if (!Ext.isArray(records)) {
            records = [
                records
            ];
        }
        len = records.length;
        for (i = 0; i < len; i++) {
            record = records[i];
            if (typeof record === 'number') {
                record = store.getAt(record);
            }
            if (!sel.contains(record)) {
                sel.add(record);
                changed = true;
            }
        }
        if (changed) {
            me.updateHeaderState();
            if (suppressEvent) {
                me.fireSelectionChange();
            }
        }
    },
    deselect: function(records, suppressEvent) {
        
        var me = this,
            sel = me.selected,
            store = me.view.dataSource,
            len, i, record,
            changed = false;
        if (sel && sel.isRows) {
            if (!Ext.isArray(records)) {
                records = [
                    records
                ];
            }
            len = records.length;
            for (i = 0; i < len; i++) {
                record = records[i];
                if (typeof record === 'number') {
                    record = store.getAt(record);
                }
                changed = changed || sel.remove(record);
            }
        }
        if (changed) {
            me.updateHeaderState();
            if (!suppressEvent) {
                me.fireSelectionChange();
            }
        }
    },
    
    selectCells: function(rangeStart, rangeEnd, suppressEvent) {
        var me = this,
            view = me.view.ownerGrid.view,
            sel;
        rangeStart = rangeStart.isCellContext ? rangeStart.clone() : new Ext.grid.CellContext(view).setPosition(rangeStart);
        rangeEnd = rangeEnd.isCellContext ? rangeEnd.clone() : new Ext.grid.CellContext(view).setPosition(rangeEnd);
        me.resetSelection(true);
        me.selected = sel = new Ext.grid.selection.Cells(rangeStart.view);
        sel.setRangeStart(rangeStart);
        sel.setRangeEnd(rangeEnd);
        if (!suppressEvent) {
            me.fireSelectionChange();
        }
    },
    
    selectAll: function(suppressEvent) {
        var me = this,
            sel = me.selected,
            doSelect,
            view = me.view;
        if (me.rowSelect) {
            if (!sel || !sel.isRows) {
                me.resetSelection(true);
                me.selected = sel = new Ext.grid.selection.Rows(view);
            }
            doSelect = true;
        } else if (me.cellSelect) {
            if (!sel || !sel.isCells) {
                me.resetSelection(true);
                me.selected = sel = new Ext.grid.selection.Cells(view);
            }
            doSelect = true;
        } else if (me.columnSelect) {
            if (!sel || !sel.isColumns) {
                me.resetSelection(true);
                me.selected = sel = new Ext.grid.selection.Columns(view);
            }
            doSelect = true;
        }
        if (doSelect) {
            sel.selectAll();
            me.updateHeaderState();
            if (!suppressEvent) {
                me.fireSelectionChange();
            }
        }
    },
    
    deselectAll: function(suppressEvent) {
        var sel = this.selected;
        if (sel && sel.getCount()) {
            sel.clear();
            if (!suppressEvent) {
                this.fireSelectionChange();
            }
        }
    },
    
    selectRows: function(rows, keepSelection, suppressEvent) {
        var me = this,
            sel = me.selected,
            isSelectingRows = sel && !sel.isRows,
            len = rows.length,
            i;
        if (!keepSelection || isSelectingRows) {
            me.resetSelection(true);
        }
        if (!isSelectingRows) {
            me.selected = sel = new Ext.grid.selection.Rows(me.view);
        }
        for (i = 0; i < len; i++) {
            sel.add(rows[i]);
        }
        if (!suppressEvent) {
            me.fireSelectionChange();
        }
    },
    isSelected: function(record) {
        
        return this.isRowSelected(record);
    },
    
    selectColumn: function(column, keepSelection, suppressEvent) {
        var me = this,
            selData = me.selected,
            view = column.getView();
        
        if (!selData || !selData.isColumns || selData.view !== view.ownerGrid.view) {
            me.resetSelection(true);
            me.selected = selData = new Ext.grid.selection.Columns(view);
        }
        if (!selData.contains(column)) {
            if (!keepSelection) {
                selData.clear();
            }
            selData.add(column);
            me.updateHeaderState();
            if (!suppressEvent) {
                me.fireSelectionChange();
            }
        }
    },
    
    deselectColumn: function(column, suppressEvent) {
        var me = this,
            selData = me.getSelected();
        if (selData && selData.isColumns && selData.contains(column)) {
            selData.remove(column);
            me.updateHeaderState();
            if (!suppressEvent) {
                me.fireSelectionChange();
            }
        }
    },
    getSelection: function() {
        
        
        var selData = this.selected;
        if (selData && selData.isRows) {
            return selData.getRecords();
        }
        return [];
    },
    destroy: function() {
        var me = this,
            scrollEls = me.scrollEls;
        Ext.destroy(me.gridListeners, me.viewListeners, me.selected, me.navigationListeners);
        if (scrollEls) {
            Ext.dd.ScrollManager.unregister(scrollEls);
        }
        me.selected = me.gridListeners = me.viewListeners = me.selectionData = me.navigationListeners = me.scrollEls = null;
        me.callParent();
    },
    
    privates: {
        
        getViewListeners: function() {
            return {
                beforerefresh: this.onBeforeViewRefresh,
                keyup: {
                    element: 'el',
                    fn: this.onViewKeyUp,
                    scope: this
                }
            };
        },
        
        onViewKeyUp: function(e) {
            var sel = this.selected;
            
            if (e.keyCode === e.SHIFT && sel && sel.isRows && sel.getRangeSize()) {
                
                sel.addRange();
            }
        },
        
        onColumnsChanged: function() {
            var selData = this.selected,
                rowRange, colCount, colIdx, rowIdx, view, context;
            
            
            if (selData && selData.isCells) {
                view = selData.view;
                context = new Ext.grid.CellContext(view);
                rowRange = selData.getRowRange();
                colCount = view.getVisibleColumnManager().getColumns().length;
                for (rowIdx = rowRange[0]; rowIdx <= rowRange[1]; rowIdx++) {
                    context.setRow(rowIdx);
                    for (colIdx = 0; colIdx < colCount; colIdx++) {
                        context.setColumn(colIdx);
                        view.onCellDeselect(context);
                    }
                }
            }
        },
        
        onBeforeViewRefresh: function(view) {
            var selData = this.selected;
            
            if (view.refreshCounter) {
                if (selData && selData.isCells) {
                    this.resetSelection();
                }
            }
        },
        
        resetSelection: function(suppressEvent) {
            var sel = this.selected;
            if (sel) {
                sel.clear();
                if (!suppressEvent) {
                    this.fireSelectionChange();
                }
            }
        },
        onViewCreated: function(grid, view) {
            var me = this,
                ownerGrid = view.ownerGrid,
                headerCt = view.headerCt,
                shrinkwrapLocked = ownerGrid.shrinkWrapLocked;
            
            if (!ownerGrid.lockable || view.isLockedView) {
                
                if (me.getRowSelect() && !headerCt.down('rownumberer')) {
                    
                    me.numbererColumn = headerCt.add(0, {
                        xtype: 'rownumberer',
                        width: me.rowNumbererHeaderWidth,
                        editRenderer: '&#160;',
                        tdCls: me.rowNumbererTdCls,
                        cls: me.rowNumbererHeaderCls,
                        locked: me.hasLockedHeader
                    });
                    if (shrinkwrapLocked) {
                        grid.width += me.numbererColumn.width;
                    }
                }
                if (me.checkboxSelect) {
                    me.addCheckbox(view, true);
                    me.mon(view.ownerGrid, 'reconfigure', me.onReconfigure, me);
                    if (shrinkwrapLocked) {
                        grid.width += me.checkColumn.width;
                    }
                }
            }
            
            headerCt.sortOnClick = !me.getColumnSelect();
            if (me.getDragSelect()) {
                view.on('render', me.onViewRender, me, {
                    single: true
                });
            }
        },
        
        onViewRender: function(view) {
            var me = this,
                el = view.getEl();
            el.ddScrollConfig = {
                vthresh: 50,
                hthresh: 50,
                frequency: 300,
                increment: 100
            };
            Ext.dd.ScrollManager.register(el);
            
            (me.scrollEls || (me.scrollEls = [])).push(el);
            view.on('cellmousedown', me.handleMouseDown, me);
            
            if (view.lockingPartner) {
                view.lockingPartner.on('cellmousedown', me.handleMouseDown, me);
            }
        },
        
        handleMouseDown: function(view, td, cellIndex, record, tr, rowIdx, e) {
            var me = this,
                sel = me.selected,
                header = e.position.column,
                isCheckClick, startDragSelect;
            
            
            if (e.button || e.shiftKey || e.altKey || e.pointerType === 'touch') {
                return;
            }
            if (header) {
                isCheckClick = header === me.checkColumn;
                
                if (header === me.numbererColumn || isCheckClick || !me.cellSelect) {
                    
                    if (me.rowSelect) {
                        if (sel && sel.isRows) {
                            if (!e.ctrlKey && !isCheckClick) {
                                sel.clear();
                            }
                        } else {
                            if (sel) {
                                sel.clear();
                            }
                            sel = me.selected = new Ext.grid.selection.Rows(view);
                        }
                        startDragSelect = true;
                    }
                } else {
                    if (sel) {
                        sel.clear();
                    }
                    if (!sel || !sel.isCells) {
                        sel = me.selected = new Ext.grid.selection.Cells(view);
                    }
                    startDragSelect = true;
                }
                me.lastOverRecord = me.lastOverColumn = null;
                
                Ext.getBody().on('mouseup', me.onMouseUp, me, {
                    single: true,
                    view: sel.view
                });
                
                if (startDragSelect) {
                    sel.view.el.on('mousemove', me.onMouseMove, me, {
                        view: sel.view
                    });
                }
            }
        },
        
        onMouseMove: function(e, target, opts) {
            var me = this,
                view = opts.view,
                record, rowIdx,
                cell = e.getTarget(view.cellSelector),
                header = opts.view.getHeaderByCell(cell),
                selData = me.selected,
                pos, recChange, colChange;
            if (header) {
                record = view.getRecord(cell.parentNode);
                rowIdx = me.store.indexOf(record);
                recChange = record !== me.lastOverRecord;
                colChange = header !== me.lastOverColumn;
                if (recChange || colChange) {
                    pos = me.getCellContext(record, header);
                }
                
                if (selData.isRows) {
                    
                    if (recChange) {
                        if (me.lastOverRecord) {
                            selData.setRangeEnd(rowIdx);
                        } else {
                            selData.setRangeStart(rowIdx);
                        }
                    }
                } else 
                {
                    
                    if (recChange || colChange) {
                        if (me.lastOverRecord) {
                            selData.setRangeEnd(pos);
                        } else {
                            selData.setRangeStart(pos);
                        }
                    }
                }
                
                
                if (recChange || colChange) {
                    
                    
                    view.getNavigationModel().setPosition(new Ext.grid.CellContext(header.getView()).setPosition(record, header));
                }
                me.lastOverColumn = header;
                me.lastOverRecord = record;
            }
        },
        
        onMouseUp: function(e, target, opts) {
            var me = this,
                view = opts.view;
            if (view && !view.isDestroyed) {
                view.el.un('mousemove', me.onMouseMove, me);
                
                if (me.selected.isRows) {
                    me.selected.addRange();
                }
                me.fireSelectionChange();
            }
        },
        
        addCheckbox: function(view, initial) {
            var me = this,
                checkbox = me.checkboxColumnIndex,
                headerCt = view.headerCt;
            
            if (checkbox !== false) {
                if (checkbox === 'first') {
                    checkbox = 0;
                } else if (checkbox === 'last') {
                    checkbox = headerCt.getColumnCount();
                }
                me.checkColumn = headerCt.add(checkbox, me.getCheckboxHeaderConfig());
            }
            if (initial !== true) {
                view.refresh();
            }
        },
        
        onNavigate: function(navigateEvent) {
            var me = this,
                
                view = navigateEvent.view.ownerGrid.view,
                record = navigateEvent.record,
                sel = me.selected,
                
                
                
                pos = new Ext.grid.CellContext(view).setPosition(record, navigateEvent.column),
                keyEvent = navigateEvent.keyEvent,
                keyCode = keyEvent.getKey(),
                selectionChanged;
            
            if (keyEvent.ctrlKey && (keyCode === keyEvent.UP || keyCode === keyEvent.LEFT || keyCode === keyEvent.RIGHT || keyCode === keyEvent.DOWN)) {
                return;
            }
            
            if (!(me.cellSelect || me.columnSelect || me.rowSelect) || !navigateEvent.record || keyEvent.type === 'mousedown') {
                return;
            }
            
            if (keyEvent.ctrlKey && keyEvent.keyCode === keyEvent.A) {
                
                if (!sel || sel.getCount() < 2) {
                    me.selectAll();
                } else {
                    me.deselectAll();
                }
                me.updateHeaderState();
                return;
            }
            if (keyEvent.shiftKey) {
                
                if (pos.column === me.numbererColumn || pos.column === me.checkColumn || !me.cellSelect || (sel && sel.isRows)) {
                    if (me.rowSelect) {
                        
                        if (!sel || !sel.isRows || sel.view !== view) {
                            me.resetSelection(true);
                            sel = me.selected = new Ext.grid.selection.Rows(view);
                        }
                        
                        if (!sel.getRangeSize()) {
                            sel.setRangeStart(navigateEvent.previousRecordIndex || 0);
                        }
                        sel.setRangeEnd(navigateEvent.recordIndex);
                        selectionChanged = true;
                    }
                } else 
                {
                    if (me.cellSelect) {
                        
                        if (!sel || !sel.isCells || sel.view !== view) {
                            me.resetSelection(true);
                            sel = me.selected = new Ext.grid.selection.Cells(view);
                        }
                        
                        if (!sel.getRangeSize()) {
                            sel.setRangeStart(navigateEvent.previousPosition || me.getCellContext(0, 0));
                        }
                        sel.setRangeEnd(pos);
                        selectionChanged = true;
                    }
                }
            } else {
                
                if (pos.column === me.numbererColumn || pos.column === me.checkColumn || !me.cellSelect) {
                    if (me.rowSelect) {
                        
                        if (!sel || !sel.isRows || sel.view !== view) {
                            me.resetSelection(true);
                            sel = me.selected = new Ext.grid.selection.Rows(view);
                        }
                        if (keyEvent.ctrlKey || pos.column === me.checkColumn) {
                            if (sel.contains(record)) {
                                sel.remove(record);
                            } else {
                                sel.add(record);
                            }
                        } else {
                            sel.clear();
                            sel.add(record);
                        }
                        selectionChanged = true;
                    }
                } else 
                {
                    if (me.cellSelect) {
                        
                        if (!sel || !sel.isCells || sel.view !== view) {
                            me.resetSelection(true);
                            me.selected = sel = new Ext.grid.selection.Cells(view);
                        } else {
                            sel.clear();
                        }
                        sel.setRangeStart(pos);
                        selectionChanged = true;
                    }
                }
            }
            
            if (selectionChanged) {
                if (sel.isRows) {
                    me.updateHeaderState();
                }
                me.fireSelectionChange();
            }
        },
        
        isRowSelected: function(record) {
            var me = this,
                sel = me.selected;
            if (sel && sel.isRows) {
                record = Ext.isNumber(record) ? me.store.getAt(record) : record;
                return sel.contains(record);
            } else {
                return false;
            }
        },
        
        isColumnSelected: function(column) {
            var me = this,
                sel = me.selected;
            if (sel && sel.isColumns) {
                return sel.contains(column);
            } else {
                return false;
            }
        },
        
        isCellSelected: function(view, row, column) {
            var me = this,
                testPos,
                sel = me.selected;
            
            view = view.ownerGrid.view;
            if (sel) {
                if (sel.isColumns) {
                    if (typeof column === 'number') {
                        column = view.getVisibleColumnManager().getColumns()[column];
                    }
                    return sel.contains(column);
                }
                if (sel.isCells) {
                    testPos = new Ext.grid.CellContext(view).setPosition({
                        row: row,
                        
                        
                        column: column
                    });
                    return sel.contains(testPos);
                }
            }
            return false;
        },
        
        applySelected: function(selected) {
            
            if (selected && !(selected.isRows || selected.isCells || selected.isColumns)) {
                Ext.error.raise('SpreadsheelModel#setSelected must be passed an instance of Ext.grid.selection.Selection');
            }
            return selected;
        },
        
        updateSelected: function(selected, oldSelected) {
            var view, columns, len, i, cell;
            
            if (oldSelected) {
                oldSelected.clear();
            }
            
            if (selected && selected.getCount()) {
                view = selected.view;
                
                if (selected.isRows) {
                    selected.eachRow(view.onRowSelect, view);
                }
                
                else if (selected.isColumns) {
                    columns = selected.getColumns();
                    len = columns.length;
                    if (len) {
                        cell = new Ext.grid.CelContext(view);
                        view.store.each(function(rec) {
                            cell.setRow(rec);
                            for (i = 0; i < len; i++) {
                                cell.setColumn(columns[i]);
                                view.onCellSelect(cell);
                            }
                        });
                    }
                }
                
                else if (selected.isCells) {
                    selected.eachCell(view.onCellSelect, view);
                }
            }
        },
        
        updateRowSelect: function(rowSelect) {
            var me = this,
                sel = me.selected,
                view = me.view;
            if (view && view.rendered) {
                
                if (view.isNormalView) {
                    view = view.lockingPartner;
                }
                if (rowSelect) {
                    if (me.checkColumn) {
                        me.checkColumn.show();
                    }
                    if (me.numbererColumn) {
                        me.numbererColumn.show();
                    } else {
                        me.numbererColumn = view.headerCt.add(0, {
                            xtype: 'rownumberer',
                            width: me.rowNumbererHeaderWidth,
                            editRenderer: '&#160;',
                            tdCls: me.rowNumbererTdCls,
                            cls: me.rowNumbererHeaderCls,
                            locked: me.hasLockedHeader
                        });
                    }
                } else {
                    if (me.checkColumn) {
                        me.checkColumn.hide();
                    }
                    if (me.numbererColumn) {
                        me.numbererColumn.hide();
                    }
                }
                if (!rowSelect && sel && sel.isRows) {
                    sel.clear();
                }
            }
        },
        
        updateColumnSelect: function(columnSelect) {
            var me = this,
                sel = me.selected,
                views = me.views,
                len = views ? views.length : 0,
                i;
            for (i = 0; i < len; i++) {
                views[i].headerCt.sortOnClick = !columnSelect;
            }
            if (!columnSelect && sel && sel.isColumns) {
                sel.clear();
            }
            if (columnSelect) {
                me.view.ownerGrid.addCls(me.columnSelectCls);
            } else {
                me.view.ownerGrid.removeCls(me.columnSelectCls);
            }
        },
        
        updateCellSelect: function(cellSelect) {
            var me = this,
                sel = me.selected;
            if (!cellSelect && sel && sel.isCells) {
                sel.clear();
            }
        },
        
        fireSelectionChange: function() {
            var grid = this.view.ownerGrid;
            grid.fireEvent('selectionchange', grid, this.selected);
        },
        
        onIdChanged: function(store, rec, oldId, newId) {
            var sel = this.selected;
            if (sel && sel.isRows && sel.selectedRecords) {
                sel.selectedRecords.updateKey(rec, oldId);
            }
        },
        
        onPageAdd: function(pageMap, pageNumber, records) {
            var sel = this.selected,
                len = records.length,
                i, record,
                selected = sel.selectedRecords;
            
            if (sel && sel.isRows && selected) {
                for (i = 0; i < len; i++) {
                    record = records[i];
                    if (selected.get(record.id)) {
                        selected.replace(record);
                    }
                }
            }
        },
        
        refresh: function() {
            var sel = this.getSelected();
            
            
            if (sel && sel.isRows) {
                this.callParent();
            }
        },
        
        onStoreAdd: function() {
            var sel = this.getSelected();
            
            if (sel && sel.isRows) {
                this.callParent(arguments);
                this.updateHeaderState();
            }
        },
        
        onStoreClear: function() {
            this.resetSelection();
        },
        
        onStoreLoad: function() {
            var sel = this.getSelected();
            
            if (sel && sel.isRows) {
                this.callParent(arguments);
                this.updateHeaderState();
            }
        },
        
        onStoreRefresh: function() {
            var sel = this.selected;
            
            
            if (sel && sel.isRows && sel.selectedRecords) {
                this.updateSelectedInstances(sel.selectedRecords);
            }
            this.updateHeaderState();
        },
        
        onStoreRemove: function() {
            var sel = this.getSelected();
            
            if (sel && sel.isRows) {
                this.callParent(arguments);
            }
        }
    }
});


Ext.define('Ext.util.Queue', {
    constructor: function() {
        this.clear();
    },
    add: function(obj) {
        var me = this,
            key = me.getKey(obj);
        if (!me.map[key]) {
            ++me.length;
            me.items.push(obj);
            me.map[key] = obj;
        }
        return obj;
    },
    
    clear: function() {
        var me = this,
            items = me.items;
        me.items = [];
        me.map = {};
        me.length = 0;
        return items;
    },
    contains: function(obj) {
        var key = this.getKey(obj);
        return this.map.hasOwnProperty(key);
    },
    
    getCount: function() {
        return this.length;
    },
    getKey: function(obj) {
        return obj.id;
    },
    
    remove: function(obj) {
        var me = this,
            key = me.getKey(obj),
            items = me.items,
            index;
        if (me.map[key]) {
            index = Ext.Array.indexOf(items, obj);
            Ext.Array.erase(items, index, 1);
            delete me.map[key];
            --me.length;
        }
        return obj;
    }
});


Ext.define('Ext.layout.ContextItem', {
    heightModel: null,
    widthModel: null,
    sizeModel: null,
    
    optOut: false,
    ownerSizePolicy: null,
    
    boxChildren: null,
    boxParent: null,
    children: [],
    dirty: null,
    
    dirtyCount: 0,
    hasRawContent: true,
    isContextItem: true,
    isTopLevel: false,
    consumersContentHeight: 0,
    consumersContentWidth: 0,
    consumersContainerHeight: 0,
    consumersContainerWidth: 0,
    consumersHeight: 0,
    consumersWidth: 0,
    ownerCtContext: null,
    remainingChildDimensions: 0,
    
    props: null,
    
    state: null,
    
    wrapsComponent: false,
    constructor: function(config) {
        var me = this,
            sizeModels = Ext.layout.SizeModel.sizeModels,
            configured = sizeModels.configured,
            shrinkWrap = sizeModels.shrinkWrap,
            el, lastBox, ownerCt, ownerCtContext, props, sizeModel, target, lastWidth, lastHeight, sameWidth, sameHeight, widthModel, heightModel, optOut;
        Ext.apply(me, config);
        target = me.target;
        el = me.el;
        me.id = target.id;
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        me.flushedProps = {};
        me.props = props = {};
        
        me.styles = {};
        if (!target.isComponent) {
            lastBox = el.lastBox;
        } else {
            me.wrapsComponent = true;
            me.framing = target.frameSize || null;
            me.isComponentChild = target.ownerLayout && target.ownerLayout.isComponentLayout;
            lastBox = target.lastBox;
            
            
            ownerCt = target.ownerCt;
            if (ownerCt && (ownerCtContext = ownerCt.el && me.context.items[ownerCt.el.id])) {
                me.ownerCtContext = ownerCtContext;
            }
            
            
            me.sizeModel = sizeModel = target.getSizeModel(ownerCtContext && ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]);
            
            
            
            
            
            me.widthModel = widthModel = sizeModel.width;
            me.heightModel = heightModel = sizeModel.height;
            
            
            
            if (lastBox && lastBox.invalid === false) {
                sameWidth = (target.width === (lastWidth = lastBox.width));
                sameHeight = (target.height === (lastHeight = lastBox.height));
                if (widthModel === shrinkWrap && heightModel === shrinkWrap) {
                    optOut = true;
                } else if (widthModel === configured && sameWidth) {
                    optOut = heightModel === shrinkWrap || (heightModel === configured && sameHeight);
                }
                if (optOut) {
                    
                    me.optOut = true;
                    props.width = lastWidth;
                    props.height = lastHeight;
                }
            }
        }
        me.lastBox = lastBox;
    },
    
    init: function(full, options) {
        var me = this,
            oldProps = me.props,
            oldDirty = me.dirty,
            ownerCtContext = me.ownerCtContext,
            ownerLayout = me.target.ownerLayout,
            firstTime = !me.state,
            ret = full || firstTime,
            children, i, n, ownerCt, sizeModel, target,
            oldHeightModel = me.heightModel,
            oldWidthModel = me.widthModel,
            newHeightModel, newWidthModel,
            remainingCount = 0;
        me.dirty = me.invalid = false;
        me.props = {};
        
        me.remainingChildDimensions = 0;
        if (me.boxChildren) {
            me.boxChildren.length = 0;
        }
        
        if (!firstTime) {
            me.clearAllBlocks('blocks');
            me.clearAllBlocks('domBlocks');
        }
        
        if (!me.wrapsComponent) {
            return ret;
        }
        
        target = me.target;
        me.state = {};
        
        if (firstTime) {
            
            
            if (target.beforeLayout && target.beforeLayout !== Ext.emptyFn) {
                target.beforeLayout();
            }
            
            
            
            
            
            
            if (!ownerCtContext && (ownerCt = target.ownerCt)) {
                ownerCtContext = me.context.items[ownerCt.el.id];
            }
            if (ownerCtContext) {
                me.ownerCtContext = ownerCtContext;
                me.isBoxParent = ownerLayout && ownerLayout.isItemBoxParent(me);
            } else {
                me.isTopLevel = true;
            }
            
            me.frameBodyContext = me.getEl('frameBody');
        } else {
            ownerCtContext = me.ownerCtContext;
            
            me.isTopLevel = !ownerCtContext;
            
            
            children = me.children;
            for (i = 0 , n = children.length; i < n; ++i) {
                children[i].init(true);
            }
        }
        
        
        
        me.hasRawContent = !(target.isContainer && target.items.items.length > 0);
        if (full) {
            
            
            me.widthModel = me.heightModel = null;
            sizeModel = target.getSizeModel(ownerCtContext && ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]);
            if (firstTime) {
                me.sizeModel = sizeModel;
            }
            me.widthModel = sizeModel.width;
            me.heightModel = sizeModel.height;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (ownerCtContext && !me.isComponentChild) {
                if (ownerLayout.needsItemSize || !target.liquidLayout) {
                    ownerCtContext.remainingChildDimensions += 2;
                } else {
                    if (me.widthModel.calculated) {
                        ++ownerCtContext.remainingChildDimensions;
                    }
                    if (me.heightModel.calculated) {
                        ++ownerCtContext.remainingChildDimensions;
                    }
                }
            }
        } else if (oldProps) {
            
            
            me.recoverProp('x', oldProps, oldDirty);
            me.recoverProp('y', oldProps, oldDirty);
            
            if (me.widthModel.calculated) {
                me.recoverProp('width', oldProps, oldDirty);
            } else if ('width' in oldProps) {
                ++remainingCount;
            }
            if (me.heightModel.calculated) {
                me.recoverProp('height', oldProps, oldDirty);
            } else if ('height' in oldProps) {
                ++remainingCount;
            }
            
            
            
            
            
            if (ownerCtContext && !me.isComponentChild) {
                ownerCtContext.remainingChildDimensions += remainingCount;
            }
        }
        if (oldProps && ownerLayout && ownerLayout.manageMargins) {
            me.recoverProp('margin-top', oldProps, oldDirty);
            me.recoverProp('margin-right', oldProps, oldDirty);
            me.recoverProp('margin-bottom', oldProps, oldDirty);
            me.recoverProp('margin-left', oldProps, oldDirty);
        }
        
        
        if (options) {
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            newHeightModel = options.heightModel;
            newWidthModel = options.widthModel;
            if (newWidthModel && newHeightModel && oldWidthModel && oldHeightModel) {
                if (oldWidthModel.shrinkWrap && oldHeightModel.shrinkWrap) {
                    if (newWidthModel.constrainedMax && newHeightModel.constrainedMin) {
                        newHeightModel = null;
                    }
                }
            }
            
            if (newWidthModel) {
                me.widthModel = newWidthModel;
            }
            if (newHeightModel) {
                me.heightModel = newHeightModel;
            }
            if (options.state) {
                Ext.apply(me.state, options.state);
            }
        }
        return ret;
    },
    
    initContinue: function(full) {
        var me = this,
            ownerCtContext = me.ownerCtContext,
            comp = me.target,
            widthModel = me.widthModel,
            inheritedState = comp.getInherited(),
            boxParent;
        if (widthModel.fixed) {
            
            inheritedState.inShrinkWrapTable = false;
        } else {
            delete inheritedState.inShrinkWrapTable;
        }
        if (full) {
            if (ownerCtContext && widthModel.shrinkWrap) {
                boxParent = ownerCtContext.isBoxParent ? ownerCtContext : ownerCtContext.boxParent;
                if (boxParent) {
                    boxParent.addBoxChild(me);
                }
            } else if (widthModel.natural) {
                me.boxParent = ownerCtContext;
            }
        }
        return full;
    },
    
    initDone: function(containerLayoutDone) {
        var me = this,
            props = me.props,
            state = me.state;
        
        if (me.remainingChildDimensions === 0) {
            props.containerChildrenSizeDone = true;
        }
        if (containerLayoutDone) {
            props.containerLayoutDone = true;
        }
        if (me.boxChildren && me.boxChildren.length && me.widthModel.shrinkWrap) {
            
            
            me.el.setWidth(10000);
            
            state.blocks = (state.blocks || 0) + 1;
        }
    },
    
    initAnimation: function() {
        var me = this,
            target = me.target,
            ownerCtContext = me.ownerCtContext;
        if (ownerCtContext && ownerCtContext.isTopLevel) {
            
            
            me.animatePolicy = target.ownerLayout.getAnimatePolicy(me);
        } else if (!ownerCtContext && target.isCollapsingOrExpanding && target.animCollapse) {
            
            
            
            me.animatePolicy = target.componentLayout.getAnimatePolicy(me);
        }
        if (me.animatePolicy) {
            me.context.queueAnimation(me);
        }
    },
    
    addBlock: function(name, layout, propName) {
        var me = this,
            collection = me[name] || (me[name] = {}),
            blockedLayouts = collection[propName] || (collection[propName] = {});
        if (!blockedLayouts[layout.id]) {
            blockedLayouts[layout.id] = layout;
            ++layout.blockCount;
            ++me.context.blockCount;
        }
    },
    addBoxChild: function(boxChildItem) {
        var me = this,
            children,
            widthModel = boxChildItem.widthModel;
        boxChildItem.boxParent = this;
        
        
        
        
        
        
        boxChildItem.measuresBox = widthModel.shrinkWrap ? boxChildItem.hasRawContent : widthModel.natural;
        if (boxChildItem.measuresBox) {
            children = me.boxChildren;
            if (children) {
                children.push(boxChildItem);
            } else {
                me.boxChildren = [
                    boxChildItem
                ];
            }
        }
    },
    
    addPositionStyles: function(styles, props) {
        var x = props.x,
            y = props.y,
            count = 0;
        if (x !== undefined) {
            styles.left = x + 'px';
            ++count;
        }
        if (y !== undefined) {
            styles.top = y + 'px';
            ++count;
        }
        return count;
    },
    
    addTrigger: function(propName, inDom) {
        var me = this,
            name = inDom ? 'domTriggers' : 'triggers',
            collection = me[name] || (me[name] = {}),
            context = me.context,
            layout = context.currentLayout,
            triggers = collection[propName] || (collection[propName] = {});
        if (!triggers[layout.id]) {
            triggers[layout.id] = layout;
            ++layout.triggerCount;
            triggers = context.triggers[inDom ? 'dom' : 'data'];
            (triggers[layout.id] || (triggers[layout.id] = [])).push({
                item: this,
                prop: propName
            });
            if (me.props[propName] !== undefined) {
                if (!inDom || !(me.dirty && (propName in me.dirty))) {
                    ++layout.firedTriggers;
                }
            }
        }
    },
    boxChildMeasured: function() {
        var me = this,
            state = me.state,
            count = (state.boxesMeasured = (state.boxesMeasured || 0) + 1);
        if (count === me.boxChildren.length) {
            
            
            state.clearBoxWidth = 1;
            ++me.context.progressCount;
            me.markDirty();
        }
    },
    borderNames: [
        'border-top-width',
        'border-right-width',
        'border-bottom-width',
        'border-left-width'
    ],
    marginNames: [
        'margin-top',
        'margin-right',
        'margin-bottom',
        'margin-left'
    ],
    paddingNames: [
        'padding-top',
        'padding-right',
        'padding-bottom',
        'padding-left'
    ],
    trblNames: [
        'top',
        'right',
        'bottom',
        'left'
    ],
    cacheMissHandlers: {
        borderInfo: function(me) {
            var info = me.getStyles(me.borderNames, me.trblNames);
            info.width = info.left + info.right;
            info.height = info.top + info.bottom;
            return info;
        },
        marginInfo: function(me) {
            var info = me.getStyles(me.marginNames, me.trblNames);
            info.width = info.left + info.right;
            info.height = info.top + info.bottom;
            return info;
        },
        paddingInfo: function(me) {
            
            var item = me.frameBodyContext || me,
                info = item.getStyles(me.paddingNames, me.trblNames);
            info.width = info.left + info.right;
            info.height = info.top + info.bottom;
            return info;
        }
    },
    checkCache: function(entry) {
        return this.cacheMissHandlers[entry](this);
    },
    clearAllBlocks: function(name) {
        var collection = this[name],
            propName;
        if (collection) {
            for (propName in collection) {
                this.clearBlocks(name, propName);
            }
        }
    },
    
    clearBlocks: function(name, propName) {
        var collection = this[name],
            blockedLayouts = collection && collection[propName],
            context, layout, layoutId;
        if (blockedLayouts) {
            delete collection[propName];
            context = this.context;
            for (layoutId in blockedLayouts) {
                layout = blockedLayouts[layoutId];
                --context.blockCount;
                if (!--layout.blockCount && !layout.pending && !layout.done) {
                    context.queueLayout(layout);
                }
            }
        }
    },
    
    block: function(layout, propName) {
        this.addBlock('blocks', layout, propName);
    },
    
    domBlock: function(layout, propName) {
        this.addBlock('domBlocks', layout, propName);
    },
    
    fireTriggers: function(name, propName) {
        var collection = this[name],
            triggers = collection && collection[propName],
            context = this.context,
            layout, layoutId;
        if (triggers) {
            for (layoutId in triggers) {
                layout = triggers[layoutId];
                ++layout.firedTriggers;
                if (!layout.done && !layout.blockCount && !layout.pending) {
                    context.queueLayout(layout);
                }
            }
        }
    },
    
    flush: function() {
        var me = this,
            dirty = me.dirty,
            state = me.state,
            targetEl = me.el;
        me.dirtyCount = 0;
        
        if ('attributes' in me) {
            targetEl.set(me.attributes);
            delete me.attributes;
        }
        
        if ('innerHTML' in me) {
            targetEl.innerHTML = me.innerHTML;
            delete me.innerHTML;
        }
        if (state && state.clearBoxWidth) {
            state.clearBoxWidth = 0;
            me.el.setStyle('width', null);
            if (!--state.blocks) {
                me.context.queueItemLayouts(me);
            }
        }
        if (dirty) {
            delete me.dirty;
            me.writeProps(dirty, true);
        }
    },
    
    flushAnimations: function() {
        var me = this,
            animateFrom = me.previousSize,
            target, targetAnim, duration, animateProps, anim, changeCount, j, propsLen, propName, oldValue, newValue;
        
        if (animateFrom) {
            target = me.target;
            targetAnim = target.getAnimationProps();
            duration = targetAnim.duration;
            animateProps = Ext.Object.getKeys(me.animatePolicy);
            
            
            anim = Ext.apply({}, {
                from: {},
                to: {},
                duration: duration || Ext.fx.Anim.prototype.duration
            }, targetAnim);
            for (changeCount = 0 , j = 0 , propsLen = animateProps.length; j < propsLen; j++) {
                propName = animateProps[j];
                oldValue = animateFrom[propName];
                newValue = me.peek(propName);
                if (oldValue != newValue) {
                    propName = me.translateProps[propName] || propName;
                    anim.from[propName] = oldValue;
                    anim.to[propName] = newValue;
                    ++changeCount;
                }
            }
            
            if (changeCount) {
                
                if (me.isCollapsingOrExpanding === 1) {
                    target.componentLayout.undoLayout(me);
                } else 
                {
                    me.writeProps(anim.from);
                }
                me.el.animate(anim);
                anim = Ext.fx.Manager.getFxQueue(me.el.id)[0];
                target.$layoutAnim = anim;
                anim.on({
                    afteranimate: function() {
                        delete target.$layoutAnim;
                        if (me.isCollapsingOrExpanding === 1) {
                            target.componentLayout.redoLayout(me);
                            target.afterCollapse(true);
                        } else if (me.isCollapsingOrExpanding === 2) {
                            target.afterExpand(true);
                        }
                    }
                });
            }
        }
    },
    
    getBorderInfo: function() {
        var me = this,
            info = me.borderInfo;
        if (!info) {
            me.borderInfo = info = me.checkCache('borderInfo');
        }
        return info;
    },
    
    getEl: function(nameOrEl, owner) {
        var me = this,
            src, el, elContext;
        if (nameOrEl) {
            if (nameOrEl.dom) {
                el = nameOrEl;
            } else {
                src = me.target;
                if (owner) {
                    src = owner;
                }
                el = src[nameOrEl];
                if (typeof el == 'function') {
                    
                    el = el.call(src);
                    if (el === me.el) {
                        return this;
                    }
                }
            }
            
            if (el) {
                elContext = me.context.getEl(me, el);
            }
        }
        return elContext || null;
    },
    
    getFrameInfo: function() {
        var me = this,
            info = me.frameInfo,
            framing, border;
        if (!info) {
            framing = me.framing;
            border = me.getBorderInfo();
            me.frameInfo = info = framing ? {
                top: framing.top + border.top,
                right: framing.right + border.right,
                bottom: framing.bottom + border.bottom,
                left: framing.left + border.left,
                width: framing.width + border.width,
                height: framing.height + border.height
            } : border;
        }
        return info;
    },
    
    getMarginInfo: function() {
        var me = this,
            info = me.marginInfo,
            comp, manageMargins, margins, ownerLayout, ownerLayoutId;
        if (!info) {
            if (!me.wrapsComponent) {
                info = me.checkCache('marginInfo');
            } else {
                comp = me.target;
                ownerLayout = comp.ownerLayout;
                ownerLayoutId = ownerLayout ? ownerLayout.id : null;
                manageMargins = ownerLayout && ownerLayout.manageMargins;
                
                info = comp.margin$;
                if (info && info.ownerId !== ownerLayoutId) {
                    
                    info = null;
                }
                if (!info) {
                    
                    
                    info = me.parseMargins(comp, comp.margin) || me.checkCache('marginInfo');
                    if (manageMargins) {
                        
                        me.setProp('margin-top', 0);
                        me.setProp('margin-right', 0);
                        me.setProp('margin-bottom', 0);
                        me.setProp('margin-left', 0);
                    }
                    
                    info.ownerId = ownerLayoutId;
                    comp.margin$ = info;
                }
                info.width = info.left + info.right;
                info.height = info.top + info.bottom;
            }
            me.marginInfo = info;
        }
        return info;
    },
    
    clearMarginCache: function() {
        delete this.marginInfo;
        delete this.target.margin$;
    },
    
    getPaddingInfo: function() {
        var me = this,
            info = me.paddingInfo;
        if (!info) {
            me.paddingInfo = info = me.checkCache('paddingInfo');
        }
        return info;
    },
    
    getProp: function(propName) {
        var me = this,
            result = me.props[propName];
        me.addTrigger(propName);
        return result;
    },
    
    getDomProp: function(propName) {
        var me = this,
            result = (me.dirty && (propName in me.dirty)) ? undefined : me.props[propName];
        me.addTrigger(propName, true);
        return result;
    },
    
    getStyle: function(styleName) {
        var me = this,
            styles = me.styles,
            info, value;
        if (styleName in styles) {
            value = styles[styleName];
        } else {
            info = me.styleInfo[styleName];
            value = me.el.getStyle(styleName);
            if (info && info.parseInt) {
                value = parseInt(value, 10) || 0;
            }
            styles[styleName] = value;
        }
        return value;
    },
    
    getStyles: function(styleNames, altNames) {
        var me = this,
            styleCache = me.styles,
            values = {},
            hits = 0,
            n = styleNames.length,
            i, missing, missingAltNames, name, info, styleInfo, styles, value;
        altNames = altNames || styleNames;
        
        
        for (i = 0; i < n; ++i) {
            name = styleNames[i];
            if (name in styleCache) {
                values[altNames[i]] = styleCache[name];
                ++hits;
                if (i && hits == 1) {
                    
                    missing = styleNames.slice(0, i);
                    missingAltNames = altNames.slice(0, i);
                }
            } else if (hits) {
                (missing || (missing = [])).push(name);
                (missingAltNames || (missingAltNames = [])).push(altNames[i]);
            }
        }
        if (hits < n) {
            missing = missing || styleNames;
            missingAltNames = missingAltNames || altNames;
            styleInfo = me.styleInfo;
            styles = me.el.getStyle(missing);
            for (i = missing.length; i--; ) {
                name = missing[i];
                info = styleInfo[name];
                value = styles[name];
                if (info && info.parseInt) {
                    value = parseInt(value, 10) || 0;
                }
                values[missingAltNames[i]] = value;
                styleCache[name] = value;
            }
        }
        return values;
    },
    
    hasProp: function(propName) {
        return this.getProp(propName) != null;
    },
    
    hasDomProp: function(propName) {
        return this.getDomProp(propName) != null;
    },
    
    invalidate: function(options) {
        this.context.queueInvalidate(this, options);
    },
    markDirty: function() {
        if (++this.dirtyCount == 1) {
            
            this.context.queueFlush(this);
        }
    },
    onBoxMeasured: function() {
        var boxParent = this.boxParent,
            state = this.state;
        if (boxParent && boxParent.widthModel.shrinkWrap && !state.boxMeasured && this.measuresBox) {
            
            
            state.boxMeasured = 1;
            
            boxParent.boxChildMeasured();
        }
    },
    parseMargins: function(comp, margins) {
        if (margins === true) {
            margins = 5;
        }
        var type = typeof margins,
            ret;
        if (type == 'string' || type == 'number') {
            ret = comp.parseBox(margins);
        } else if (margins) {
            ret = {
                top: 0,
                right: 0,
                bottom: 0,
                left: 0
            };
            
            if (margins) {
                margins = Ext.apply(ret, comp.parseBox(margins));
            }
        }
        
        return ret;
    },
    peek: function(propName) {
        return this.props[propName];
    },
    
    recoverProp: function(propName, oldProps, oldDirty) {
        var me = this,
            props = me.props,
            dirty;
        if (propName in oldProps) {
            props[propName] = oldProps[propName];
            if (oldDirty && propName in oldDirty) {
                dirty = me.dirty || (me.dirty = {});
                dirty[propName] = oldDirty[propName];
            }
        }
    },
    redo: function(deep) {
        var me = this,
            items, len, i;
        me.revertProps(me.props);
        if (deep && me.wrapsComponent) {
            
            if (me.childItems) {
                for (i = 0 , items = me.childItems , len = items.length; i < len; i++) {
                    items[i].redo(deep);
                }
            }
            
            for (i = 0 , items = me.children , len = items.length; i < len; i++) {
                items[i].redo();
            }
        }
    },
    
    removeEl: function(nameOrEl, owner) {
        var me = this,
            src, el;
        if (nameOrEl) {
            if (nameOrEl.dom) {
                el = nameOrEl;
            } else {
                src = me.target;
                if (owner) {
                    src = owner;
                }
                el = src[nameOrEl];
                if (typeof el == 'function') {
                    
                    el = el.call(src);
                    if (el === me.el) {
                        return this;
                    }
                }
            }
            
            if (el) {
                me.context.removeEl(me, el);
            }
        }
    },
    revertProps: function(props) {
        var name,
            flushed = this.flushedProps,
            reverted = {};
        for (name in props) {
            if (flushed.hasOwnProperty(name)) {
                reverted[name] = props[name];
            }
        }
        this.writeProps(reverted);
    },
    
    setAttribute: function(name, value) {
        var me = this;
        if (!me.attributes) {
            me.attributes = {};
        }
        me.attributes[name] = value;
        me.markDirty();
    },
    setBox: function(box) {
        var me = this;
        if ('left' in box) {
            me.setProp('x', box.left);
        }
        if ('top' in box) {
            me.setProp('y', box.top);
        }
        
        
        
        me.setSize(box.width, box.height);
    },
    
    setContentHeight: function(height, measured) {
        if (!measured && this.hasRawContent) {
            return 1;
        }
        return this.setProp('contentHeight', height);
    },
    
    setContentWidth: function(width, measured) {
        if (!measured && this.hasRawContent) {
            return 1;
        }
        return this.setProp('contentWidth', width);
    },
    
    setContentSize: function(width, height, measured) {
        return this.setContentWidth(width, measured) + this.setContentHeight(height, measured) == 2;
    },
    
    setProp: function(propName, value, dirty) {
        var me = this,
            valueType = typeof value,
            info;
        if (valueType == 'undefined' || (valueType === 'number' && isNaN(value))) {
            return 0;
        }
        if (me.props[propName] === value) {
            return 1;
        }
        me.props[propName] = value;
        ++me.context.progressCount;
        if (dirty === false) {
            
            
            me.fireTriggers('domTriggers', propName);
            me.clearBlocks('domBlocks', propName);
        } else {
            info = me.styleInfo[propName];
            if (info) {
                if (!me.dirty) {
                    me.dirty = {};
                }
                me.dirty[propName] = value;
                me.markDirty();
            }
        }
        
        me.fireTriggers('triggers', propName);
        me.clearBlocks('blocks', propName);
        return 1;
    },
    
    setHeight: function(height, dirty) 
    {
        var me = this,
            comp = me.target,
            ownerCtContext = me.ownerCtContext,
            frameBody, frameInfo, min, oldHeight, rem;
        if (height < 0) {
            height = 0;
        }
        if (!me.wrapsComponent) {
            if (!me.setProp('height', height, dirty)) {
                return NaN;
            }
        } else {
            min = me.collapsedVert ? 0 : (comp.minHeight || 0);
            height = Ext.Number.constrain(height, min, comp.maxHeight);
            oldHeight = me.props.height;
            if (!me.setProp('height', height, dirty)) {
                return NaN;
            }
            
            
            if (ownerCtContext && !me.isComponentChild && isNaN(oldHeight)) {
                rem = --ownerCtContext.remainingChildDimensions;
                if (!rem) {
                    
                    
                    
                    ownerCtContext.setProp('containerChildrenSizeDone', true);
                }
            }
            frameBody = me.frameBodyContext;
            if (frameBody) {
                frameInfo = me.getFrameInfo();
                frameBody[me.el.vertical ? 'setWidth' : 'setHeight'](height - frameInfo.height, dirty);
            }
        }
        return height;
    },
    
    setWidth: function(width, dirty) 
    {
        var me = this,
            comp = me.target,
            ownerCtContext = me.ownerCtContext,
            frameBody, frameInfo, min, oldWidth, rem;
        if (width < 0) {
            width = 0;
        }
        if (!me.wrapsComponent) {
            if (!me.setProp('width', width, dirty)) {
                return NaN;
            }
        } else {
            min = me.collapsedHorz ? 0 : (comp.minWidth || 0);
            width = Ext.Number.constrain(width, min, comp.maxWidth);
            oldWidth = me.props.width;
            if (!me.setProp('width', width, dirty)) {
                return NaN;
            }
            
            
            if (ownerCtContext && !me.isComponentChild && isNaN(oldWidth)) {
                rem = --ownerCtContext.remainingChildDimensions;
                if (!rem) {
                    
                    
                    
                    ownerCtContext.setProp('containerChildrenSizeDone', true);
                }
            }
            
            frameBody = me.frameBodyContext;
            if (frameBody) {
                frameInfo = me.getFrameInfo();
                frameBody.setWidth(width - frameInfo.width, dirty);
            }
        }
        
        return width;
    },
    setSize: function(width, height, dirty) {
        this.setWidth(width, dirty);
        this.setHeight(height, dirty);
    },
    translateProps: {
        x: 'left',
        y: 'top'
    },
    undo: function(deep) {
        var me = this,
            items, len, i;
        me.revertProps(me.lastBox);
        if (deep && me.wrapsComponent) {
            
            if (me.childItems) {
                for (i = 0 , items = me.childItems , len = items.length; i < len; i++) {
                    items[i].undo(deep);
                }
            }
            
            for (i = 0 , items = me.children , len = items.length; i < len; i++) {
                items[i].undo();
            }
        }
    },
    unsetProp: function(propName) {
        var dirty = this.dirty;
        delete this.props[propName];
        if (dirty) {
            delete dirty[propName];
        }
    },
    writeProps: function(dirtyProps, flushing) {
        if (!(dirtyProps && typeof dirtyProps == 'object')) {
            Ext.Logger.warn('writeProps expected dirtyProps to be an object');
            return;
        }
        var me = this,
            el = me.el,
            styles = {},
            styleCount = 0,
            
            styleInfo = me.styleInfo,
            info, propName, numericValue,
            width = dirtyProps.width,
            height = dirtyProps.height,
            target = me.target,
            hasWidth, hasHeight, isAbsolute, scrollbarSize, style, targetEl;
        
        if ('displayed' in dirtyProps) {
            el.setDisplayed(dirtyProps.displayed);
        }
        
        for (propName in dirtyProps) {
            if (flushing) {
                me.fireTriggers('domTriggers', propName);
                me.clearBlocks('domBlocks', propName);
                me.flushedProps[propName] = 1;
            }
            info = styleInfo[propName];
            if (info && info.dom) {
                
                if (info.suffix && (numericValue = parseInt(dirtyProps[propName], 10))) {
                    styles[propName] = numericValue + info.suffix;
                } else 
                {
                    styles[propName] = dirtyProps[propName];
                }
                ++styleCount;
            }
        }
        
        if ('x' in dirtyProps || 'y' in dirtyProps) {
            if (target.isComponent) {
                target.setPosition(dirtyProps.x, dirtyProps.y);
            } else {
                
                styleCount += me.addPositionStyles(styles, dirtyProps);
            }
        }
        
        
        
        
        
        
        if (me.wrapsComponent && Ext.isIE9) {
            
            
            if ((hasWidth = width !== undefined && me.hasOverflowY) || (hasHeight = height !== undefined && me.hasOverflowX)) {
                
                isAbsolute = me.isAbsolute;
                if (isAbsolute === undefined) {
                    isAbsolute = false;
                    targetEl = me.target.getTargetEl();
                    style = targetEl.getStyle('position');
                    me.isAbsolute = isAbsolute = (style === 'absolute');
                }
                
                if (isAbsolute) {
                    scrollbarSize = Ext.getScrollbarSize();
                    if (hasWidth) {
                        width = parseInt(width, 10) + scrollbarSize.width;
                        styles.width = width + 'px';
                        ++styleCount;
                    }
                    if (hasHeight) {
                        height = parseInt(height, 10) + scrollbarSize.height;
                        styles.height = height + 'px';
                        ++styleCount;
                    }
                }
            }
        }
        
        if (styleCount) {
            el.setStyle(styles);
        }
    },
    
    
    debugHooks: {
        $enabled: false,
        
        addBlock: function(name, layout, propName) {
            
            (layout.blockedBy || (layout.blockedBy = {}))[this.id + '.' + propName + (name.substring(0, 3) == 'dom' ? ':dom' : '')] = 1;
            return this.callParent(arguments);
        },
        addBoxChild: function(boxChildItem) {
            var ret = this.callParent(arguments),
                boxChildren = this.boxChildren,
                boxParents;
            if (boxChildren && boxChildren.length == 1) {
                
                
                
                boxParents = this.context.boxParents || (this.context.boxParents = new Ext.util.MixedCollection());
                boxParents.add(this);
            }
            return ret;
        },
        addTrigger: function(propName, inDom) {
            var layout = this.context.currentLayout,
                triggers;
            
            this.callParent(arguments);
            triggers = this.context.triggersByLayoutId;
            (triggers[layout.id] || (triggers[layout.id] = {}))[this.id + '.' + propName + (inDom ? ':dom' : '')] = {
                item: this,
                name: propName
            };
        },
        checkAuthority: function(prop) {
            var me = this,
                model = me[prop + 'Model'],
                
                layout = me.context.currentLayout,
                ok, setBy;
            if (layout == me.target.ownerLayout) {
                
                ok = model.calculated;
            } else if (layout.isComponentLayout) {
                
                
                
                
                
                ok = me.isTopLevel || model.auto || model.configured;
            }
            if (!ok) {
                setBy = me.context.getLayoutName(layout);
                Ext.log(setBy + ' cannot set ' + prop);
            }
        },
        clearBlocks: function(name, propName) {
            var collection = this[name],
                blockedLayouts = collection && collection[propName],
                key = this.id + '.' + propName + (name.substring(0, 3) == 'dom' ? ':dom' : ''),
                layout, layoutId;
            if (blockedLayouts) {
                for (layoutId in blockedLayouts) {
                    layout = blockedLayouts[layoutId];
                    delete layout.blockedBy[key];
                }
            }
            return this.callParent(arguments);
        },
        getEl: function(el) {
            var child = this.callParent(arguments);
            if (child && child !== this && child.parent !== this) {
                Ext.Error.raise({
                    msg: 'Got element from wrong component'
                });
            }
            return child;
        },
        init: function() {
            var me = this,
                ret;
            ret = me.callParent(arguments);
            if (me.context.logOn.initItem) {
                Ext.log(me.id, ' consumers: content=', me.consumersContentWidth, '/', me.consumersContentHeight, ', container=', me.consumersContainerWidth, '/', me.consumersContainerHeight, ', size=', me.consumersWidth, '/', me.consumersHeight);
            }
            return ret;
        },
        invalidate: function() {
            if (this.wrapsComponent) {
                if (this.context.logOn.invalidate) {
                    Ext.log('invalidate: ', this.id);
                }
            } else {
                Ext.Error.raise({
                    msg: 'Cannot invalidate an element contextItem'
                });
            }
            return this.callParent(arguments);
        },
        setProp: function(propName, value, dirty) {
            var me = this,
                layout = me.context.currentLayout,
                setBy = me.context.getLayoutName(layout),
                fullName = me.id + '.' + propName,
                setByProps;
            if (value !== null) {
                setByProps = me.setBy || (me.setBy = {});
                if (!setByProps[propName]) {
                    setByProps[propName] = setBy;
                } else if (setByProps[propName] != setBy) {
                    Ext.log({
                        level: 'warn'
                    }, 'BAD! ', fullName, ' set by ', setByProps[propName], ' and ', setBy);
                }
            }
            if (me.context.logOn.setProp) {
                if (typeof value != 'undefined' && !isNaN(value) && me.props[propName] !== value) {
                    Ext.log('set ', fullName, ' = ', value, ' (', dirty, ')');
                }
            }
            return this.callParent(arguments);
        },
        setHeight: function(height, dirty, 
        force) {
            if (!force && this.wrapsComponent) {
                this.checkAuthority('height');
            }
            return this.callParent(arguments);
        },
        setWidth: function(width, dirty, 
        force) {
            if (!force && this.wrapsComponent) {
                this.checkAuthority('width');
            }
            return this.callParent(arguments);
        }
    }
}, 

function() {
    var px = {
            dom: true,
            parseInt: true,
            suffix: 'px'
        },
        isDom = {
            dom: true
        },
        faux = {
            dom: false
        };
    
    
    
    
    
    
    
    
    this.prototype.styleInfo = {
        containerChildrenSizeDone: faux,
        containerLayoutDone: faux,
        displayed: faux,
        done: faux,
        x: faux,
        y: faux,
        
        columnsChanged: faux,
        rowHeights: faux,
        viewOverflowY: faux,
        left: px,
        top: px,
        right: px,
        bottom: px,
        width: px,
        height: px,
        'border-top-width': px,
        'border-right-width': px,
        'border-bottom-width': px,
        'border-left-width': px,
        'margin-top': px,
        'margin-right': px,
        'margin-bottom': px,
        'margin-left': px,
        'padding-top': px,
        'padding-right': px,
        'padding-bottom': px,
        'padding-left': px,
        'line-height': isDom,
        display: isDom
    };
});


Ext.define('Ext.layout.Context', {
    requires: [
        'Ext.perf.Monitor',
        'Ext.util.Queue',
        'Ext.layout.ContextItem',
        'Ext.layout.Layout',
        'Ext.fx.Anim',
        'Ext.fx.Manager'
    ],
    remainingLayouts: 0,
    
    state: 0,
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);
        
        me.items = {};
        
        me.layouts = {};
        
        me.blockCount = 0;
        
        me.cycleCount = 0;
        
        me.flushCount = 0;
        
        me.calcCount = 0;
        me.animateQueue = me.newQueue();
        me.completionQueue = me.newQueue();
        me.finalizeQueue = me.newQueue();
        me.finishQueue = me.newQueue();
        me.flushQueue = me.newQueue();
        me.invalidateData = {};
        
        me.layoutQueue = me.newQueue();
        
        
        me.invalidQueue = [];
        me.triggers = {
            data: {},
            
            dom: {}
        };
    },
    callLayout: function(layout, methodName) {
        this.currentLayout = layout;
        layout[methodName](this.getCmp(layout.owner));
    },
    cancelComponent: function(comp, isChild, isDestroying) {
        var me = this,
            components = comp,
            isArray = !comp.isComponent,
            length = isArray ? components.length : 1,
            i, k, klen, items, layout, newQueue, oldQueue, entry, temp, ownerCtContext;
        for (i = 0; i < length; ++i) {
            if (isArray) {
                comp = components[i];
            }
            
            if (isDestroying && comp.ownerCt) {
                ownerCtContext = this.items[comp.ownerCt.el.id];
                if (ownerCtContext) {
                    Ext.Array.remove(ownerCtContext.childItems, me.getCmp(comp));
                }
            }
            if (!isChild) {
                oldQueue = me.invalidQueue;
                klen = oldQueue.length;
                if (klen) {
                    me.invalidQueue = newQueue = [];
                    for (k = 0; k < klen; ++k) {
                        entry = oldQueue[k];
                        temp = entry.item.target;
                        if (temp != comp && !temp.up(comp)) {
                            newQueue.push(entry);
                        }
                    }
                }
            }
            layout = comp.componentLayout;
            me.cancelLayout(layout);
            if (layout.getLayoutItems) {
                items = layout.getLayoutItems();
                if (items.length) {
                    me.cancelComponent(items, true);
                }
            }
            if (comp.isContainer && !comp.collapsed) {
                layout = comp.layout;
                me.cancelLayout(layout);
                items = layout.getVisibleItems();
                if (items.length) {
                    me.cancelComponent(items, true);
                }
            }
        }
    },
    cancelLayout: function(layout) {
        var me = this;
        me.completionQueue.remove(layout);
        me.finalizeQueue.remove(layout);
        me.finishQueue.remove(layout);
        me.layoutQueue.remove(layout);
        if (layout.running) {
            me.layoutDone(layout);
        }
        layout.ownerContext = null;
    },
    clearTriggers: function(layout, inDom) {
        var id = layout.id,
            collection = this.triggers[inDom ? 'dom' : 'data'],
            triggers = collection && collection[id],
            length = (triggers && triggers.length) || 0,
            i, item, trigger;
        for (i = 0; i < length; ++i) {
            trigger = triggers[i];
            item = trigger.item;
            collection = inDom ? item.domTriggers : item.triggers;
            delete collection[trigger.prop][id];
        }
    },
    
    flush: function() {
        var me = this,
            items = me.flushQueue.clear(),
            length = items.length,
            i;
        if (length) {
            ++me.flushCount;
            for (i = 0; i < length; ++i) {
                items[i].flush();
            }
        }
    },
    flushAnimations: function() {
        var me = this,
            items = me.animateQueue.clear(),
            len = items.length,
            i;
        if (len) {
            for (i = 0; i < len; i++) {
                
                
                
                if (items[i].target.animate !== false) {
                    items[i].flushAnimations();
                }
            }
            
            
            Ext.fx.Manager.runner();
        }
    },
    flushInvalidates: function() {
        var me = this,
            queue = me.invalidQueue,
            length = queue && queue.length,
            comp, components, entry, i;
        me.invalidQueue = [];
        if (length) {
            components = [];
            for (i = 0; i < length; ++i) {
                comp = (entry = queue[i]).item.target;
                
                
                
                
                if (!comp.container.isDetachedBody) {
                    components.push(comp);
                    if (entry.options) {
                        me.invalidateData[comp.id] = entry.options;
                    }
                }
            }
            me.invalidate(components, null);
        }
    },
    flushLayouts: function(queueName, methodName, dontClear) {
        var me = this,
            layouts = dontClear ? me[queueName].items : me[queueName].clear(),
            length = layouts.length,
            i, layout;
        if (length) {
            for (i = 0; i < length; ++i) {
                layout = layouts[i];
                if (!layout.running) {
                    me.callLayout(layout, methodName);
                }
            }
            me.currentLayout = null;
        }
    },
    
    getCmp: function(cmp) {
        return this.getItem(cmp, cmp.el);
    },
    
    getEl: function(parent, el) {
        var item = this.getItem(el, el);
        if (!item.parent) {
            item.parent = parent;
            
            
            
            if (parent.children.length) {
                parent.children.push(item);
            } else {
                parent.children = [
                    item
                ];
            }
        }
        
        return item;
    },
    getItem: function(target, el) {
        var id = el.id,
            items = this.items,
            item = items[id] || (items[id] = new Ext.layout.ContextItem({
                context: this,
                target: target,
                el: el
            }));
        return item;
    },
    handleFailure: function() {
        
        
        
        var layouts = this.layouts,
            layout, key;
        Ext.failedLayouts = (Ext.failedLayouts || 0) + 1;
        for (key in layouts) {
            layout = layouts[key];
            if (layouts.hasOwnProperty(key)) {
                layout.running = false;
                layout.ownerContext = null;
            }
        }
        if (Ext.repoDevMode && !this.pageAnalyzerMode) {
            Ext.Error.raise('Layout run failed');
        } else {
            Ext.log.error('Layout run failed');
        }
    },
    
    invalidate: function(components, full) {
        var me = this,
            isArray = !components.isComponent,
            containerLayoutDone, ownerLayout, firstTime, i, comp, item, items, length, componentLayout, layout, invalidateOptions, token, skipLayout;
        for (i = 0 , length = isArray ? components.length : 1; i < length; ++i) {
            comp = isArray ? components[i] : components;
            if (comp.rendered && !comp.hidden) {
                ownerLayout = comp.ownerLayout;
                componentLayout = comp.componentLayout;
                firstTime = !componentLayout.ownerContext;
                skipLayout = false;
                if ((!ownerLayout || !ownerLayout.needsItemSize) && comp.liquidLayout) {
                    
                    
                    
                    skipLayout = true;
                }
                
                
                if (!skipLayout || (ownerLayout && ownerLayout.setsItemSize)) {
                    item = me.getCmp(comp);
                    
                    
                    
                    
                    
                    layout = (comp.isContainer && !comp.collapsed) ? comp.layout : null;
                    
                    invalidateOptions = me.invalidateData[item.id];
                    delete me.invalidateData[item.id];
                    
                    
                    
                    
                    
                    
                    token = item.init(full, invalidateOptions);
                }
                if (skipLayout) {
                    
                    continue;
                }
                if (invalidateOptions) {
                    me.processInvalidate(invalidateOptions, item, 'before');
                }
                
                
                
                if (componentLayout.beforeLayoutCycle) {
                    componentLayout.beforeLayoutCycle(item);
                }
                if (layout && layout.beforeLayoutCycle) {
                    
                    
                    
                    
                    layout.beforeLayoutCycle(item);
                }
                
                
                token = item.initContinue(token);
                
                
                containerLayoutDone = true;
                
                
                
                
                if (componentLayout.getLayoutItems) {
                    componentLayout.renderChildren();
                    items = componentLayout.getLayoutItems();
                    if (items.length) {
                        me.invalidate(items, true);
                    }
                }
                if (layout) {
                    containerLayoutDone = false;
                    layout.renderChildren();
                    if (layout.needsItemSize || layout.activeItemCount) {
                        
                        
                        
                        
                        items = layout.getVisibleItems();
                        if (items.length) {
                            me.invalidate(items, true);
                        }
                    }
                }
                
                
                item.initDone(containerLayoutDone);
                
                
                me.resetLayout(componentLayout, item, firstTime);
                if (layout) {
                    me.resetLayout(layout, item, firstTime);
                }
                
                
                
                item.initAnimation();
                if (invalidateOptions) {
                    me.processInvalidate(invalidateOptions, item, 'after');
                }
            }
        }
        me.currentLayout = null;
    },
    
    isDescendant: function(ancestor, descendant) {
        if (ancestor.isContainer) {
            for (var c = descendant.ownerCt; c; c = c.ownerCt) {
                if (c === ancestor) {
                    return true;
                }
            }
        }
        return false;
    },
    layoutDone: function(layout) {
        var ownerContext = layout.ownerContext;
        layout.running = false;
        
        if (layout.isComponentLayout) {
            if (ownerContext.measuresBox) {
                ownerContext.onBoxMeasured();
            }
            
            ownerContext.setProp('done', true);
        } else {
            ownerContext.setProp('containerLayoutDone', true);
        }
        --this.remainingLayouts;
        ++this.progressCount;
    },
    
    newQueue: function() {
        return new Ext.util.Queue();
    },
    processInvalidate: function(options, item, name) {
        
        
        if (options[name]) {
            var me = this,
                currentLayout = me.currentLayout;
            me.currentLayout = options.layout || null;
            options[name](item, options);
            me.currentLayout = currentLayout;
        }
    },
    
    queueAnimation: function(item) {
        this.animateQueue.add(item);
    },
    
    queueCompletion: function(layout) {
        this.completionQueue.add(layout);
    },
    
    queueFinalize: function(layout) {
        this.finalizeQueue.add(layout);
    },
    
    queueFlush: function(item) {
        this.flushQueue.add(item);
    },
    chainFns: function(oldOptions, newOptions, funcName) {
        var me = this,
            oldLayout = oldOptions.layout,
            newLayout = newOptions.layout,
            oldFn = oldOptions[funcName],
            newFn = newOptions[funcName];
        
        
        return function(contextItem) {
            var prev = me.currentLayout;
            if (oldFn) {
                me.currentLayout = oldLayout;
                oldFn.call(oldOptions.scope || oldOptions, contextItem, oldOptions);
            }
            me.currentLayout = newLayout;
            newFn.call(newOptions.scope || newOptions, contextItem, newOptions);
            me.currentLayout = prev;
        };
    },
    purgeInvalidates: function() {
        var me = this,
            newQueue = [],
            oldQueue = me.invalidQueue,
            oldLength = oldQueue.length,
            oldIndex, newIndex, newEntry, newComp, oldEntry, oldComp, keep;
        for (oldIndex = 0; oldIndex < oldLength; ++oldIndex) {
            oldEntry = oldQueue[oldIndex];
            oldComp = oldEntry.item.target;
            keep = true;
            for (newIndex = newQueue.length; newIndex--; ) {
                newEntry = newQueue[newIndex];
                newComp = newEntry.item.target;
                if (oldComp.isLayoutChild(newComp)) {
                    keep = false;
                    break;
                }
                if (newComp.isLayoutChild(oldComp)) {
                    Ext.Array.erase(newQueue, newIndex, 1);
                }
            }
            if (keep) {
                newQueue.push(oldEntry);
            }
        }
        me.invalidQueue = newQueue;
    },
    
    queueInvalidate: function(item, options) {
        var me = this,
            newQueue = [],
            oldQueue = me.invalidQueue,
            index = oldQueue.length,
            comp, old, oldComp, oldOptions, oldState;
        if (item.isComponent) {
            item = me.getCmp(comp = item);
        } else {
            comp = item.target;
        }
        item.invalid = true;
        
        
        
        while (index--) {
            old = oldQueue[index];
            oldComp = old.item.target;
            if (!comp.isFloating && comp.up(oldComp)) {
                return;
            }
            
            if (oldComp == comp) {
                
                if (!(oldOptions = old.options)) {
                    old.options = options;
                } else if (options) {
                    if (options.widthModel) {
                        oldOptions.widthModel = options.widthModel;
                    }
                    if (options.heightModel) {
                        oldOptions.heightModel = options.heightModel;
                    }
                    if (!(oldState = oldOptions.state)) {
                        oldOptions.state = options.state;
                    } else if (options.state) {
                        Ext.apply(oldState, options.state);
                    }
                    if (options.before) {
                        oldOptions.before = me.chainFns(oldOptions, options, 'before');
                    }
                    if (options.after) {
                        oldOptions.after = me.chainFns(oldOptions, options, 'after');
                    }
                }
                
                return;
            }
            if (!oldComp.isLayoutChild(comp)) {
                newQueue.push(old);
            }
        }
        
        
        
        
        
        newQueue.push({
            item: item,
            options: options
        });
        me.invalidQueue = newQueue;
    },
    queueItemLayouts: function(item) {
        var comp = item.isComponent ? item : item.target,
            layout = comp.componentLayout;
        if (!layout.pending && !layout.invalid && !layout.done) {
            this.queueLayout(layout);
        }
        layout = comp.layout;
        if (layout && !layout.pending && !layout.invalid && !layout.done && !comp.collapsed) {
            this.queueLayout(layout);
        }
    },
    
    queueLayout: function(layout) {
        this.layoutQueue.add(layout);
        layout.pending = true;
    },
    
    removeEl: function(parent, el) {
        var id = el.id,
            children = parent.children,
            items = this.items;
        if (children) {
            Ext.Array.remove(children, items[id]);
        }
        delete items[id];
    },
    
    resetLayout: function(layout, ownerContext, firstTime) {
        var me = this;
        me.currentLayout = layout;
        layout.done = false;
        layout.pending = true;
        layout.firedTriggers = 0;
        me.layoutQueue.add(layout);
        if (firstTime) {
            me.layouts[layout.id] = layout;
            
            layout.running = true;
            if (layout.finishedLayout) {
                me.finishQueue.add(layout);
            }
            
            ++me.remainingLayouts;
            ++layout.layoutCount;
            
            layout.ownerContext = ownerContext;
            layout.beginCount = 0;
            
            layout.blockCount = 0;
            
            layout.calcCount = 0;
            
            layout.triggerCount = 0;
            
            if (!layout.initialized) {
                layout.initLayout();
            }
            layout.beginLayout(ownerContext);
        } else {
            ++layout.beginCount;
            if (!layout.running) {
                
                ++me.remainingLayouts;
                layout.running = true;
                if (layout.isComponentLayout) {
                    
                    
                    
                    ownerContext.unsetProp('done');
                }
                
                me.completionQueue.remove(layout);
                me.finalizeQueue.remove(layout);
            }
        }
        layout.beginLayoutCycle(ownerContext, firstTime);
    },
    
    run: function() {
        var me = this,
            flushed = false,
            watchDog = 100;
        me.purgeInvalidates();
        me.flushInvalidates();
        me.state = 1;
        me.totalCount = me.layoutQueue.getCount();
        
        
        
        
        
        
        me.flush();
        
        while ((me.remainingLayouts || me.invalidQueue.length) && watchDog--) {
            if (me.invalidQueue.length) {
                me.flushInvalidates();
            }
            
            if (me.runCycle()) {
                flushed = false;
            }
            
            
            else if (!flushed) {
                
                
                me.flush();
                flushed = true;
                
                me.flushLayouts('completionQueue', 'completeLayout');
            } else if (!me.invalidQueue.length) {
                
                me.state = 2;
                break;
            }
            if (!(me.remainingLayouts || me.invalidQueue.length)) {
                me.flush();
                me.flushLayouts('completionQueue', 'completeLayout');
                me.flushLayouts('finalizeQueue', 'finalizeLayout');
            }
        }
        return me.runComplete();
    },
    runComplete: function() {
        var me = this;
        me.state = 2;
        if (me.remainingLayouts) {
            me.handleFailure();
            return false;
        }
        me.flush();
        
        me.flushLayouts('finishQueue', 'finishedLayout', true);
        
        me.flushLayouts('finishQueue', 'notifyOwner');
        me.flush();
        
        me.flushAnimations();
        return true;
    },
    
    runCycle: function() {
        var me = this,
            layouts = me.layoutQueue.clear(),
            length = layouts.length,
            i;
        ++me.cycleCount;
        
        
        me.progressCount = 0;
        for (i = 0; i < length; ++i) {
            me.runLayout(me.currentLayout = layouts[i]);
        }
        me.currentLayout = null;
        return me.progressCount > 0;
    },
    
    runLayout: function(layout) {
        var me = this,
            ownerContext = me.getCmp(layout.owner);
        layout.pending = false;
        if (ownerContext.state.blocks) {
            return;
        }
        
        
        
        
        layout.done = true;
        ++layout.calcCount;
        ++me.calcCount;
        layout.calculate(ownerContext);
        if (layout.done) {
            me.layoutDone(layout);
            if (layout.completeLayout) {
                me.queueCompletion(layout);
            }
            if (layout.finalizeLayout) {
                me.queueFinalize(layout);
            }
        } else if (!layout.pending && !layout.invalid && !(layout.blockCount + layout.triggerCount - layout.firedTriggers)) {
            
            
            me.queueLayout(layout);
        }
    },
    
    setItemSize: function(item, width, height) {
        var items = item,
            len = 1,
            contextItem, i;
        
        
        
        
        
        if (item.isComposite) {
            items = item.elements;
            len = items.length;
            item = items[0];
        } else if (!item.dom && !item.el) {
            
            len = items.length;
            item = items[0];
        }
        
        for (i = 0; i < len; ) {
            contextItem = this.get(item);
            contextItem.setSize(width, height);
            item = items[++i];
        }
    },
    
    
    
    debugHooks: {
        $enabled: false,
        
        pageAnalyzerMode: true,
        logOn: {
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            0: 0
        },
        
        
        cancelComponent: function(comp) {
            if (this.logOn.cancelComponent) {
                Ext.log('cancelCmp: ', comp.id);
            }
            this.callParent(arguments);
        },
        cancelLayout: function(layout) {
            if (this.logOn.cancelLayout) {
                Ext.log('cancelLayout: ', this.getLayoutName(layout));
            }
            this.callParent(arguments);
        },
        callLayout: function(layout, methodName) {
            var accum = this.accumByType[layout.type],
                frame = accum && accum.enter();
            this.callParent(arguments);
            if (accum) {
                frame.leave();
            }
        },
        checkRemainingLayouts: function() {
            var me = this,
                expected = 0,
                key, layout;
            for (key in me.layouts) {
                layout = me.layouts[key];
                if (me.layouts.hasOwnProperty(key) && layout.running) {
                    ++expected;
                }
            }
            if (me.remainingLayouts != expected) {
                Ext.Error.raise({
                    msg: 'Bookkeeping error me.remainingLayouts'
                });
            }
        },
        flush: function() {
            if (this.logOn.flush) {
                var items = this.flushQueue;
                Ext.log('--- Flush ', items && items.getCount());
            }
            return this.callParent(arguments);
        },
        flushInvalidates: function() {
            if (this.logOn.flushInvalidate) {
                Ext.log('>> flushInvalidates');
            }
            var ret = this.callParent(arguments);
            if (this.logOn.flushInvalidate) {
                Ext.log('<< flushInvalidates');
            }
            return ret;
        },
        getCmp: function(target) {
            var ret = this.callParent(arguments);
            if (!ret.wrapsComponent) {
                Ext.Error.raise({
                    msg: target.id + ' is not a component'
                });
            }
            return ret;
        },
        getEl: function(parent, target) {
            var ret = this.callParent(arguments);
            if (ret && ret.wrapsComponent) {
                Ext.Error.raise({
                    msg: parent.id + '/' + target.id + ' is a component (expected element)'
                });
            }
            return ret;
        },
        getLayoutName: function(layout) {
            return layout.owner.id + '<' + layout.type + '>';
        },
        layoutDone: function(layout) {
            var me = this,
                name = me.getLayoutName(layout);
            if (me.logOn.layoutDone) {
                Ext.log('layoutDone: ', name, ' ( ', me.remainingLayouts, ' running)');
            }
            if (!layout.running) {
                Ext.Error.raise({
                    msg: name + ' is already done'
                });
            }
            if (!me.remainingLayouts) {
                Ext.Error.raise({
                    msg: name + ' finished but no layouts are running'
                });
            }
            me.callParent(arguments);
        },
        layoutTreeHasFailures: function(layout, reported) {
            var me = this;
            function hasFailure(lo) {
                var failure = !lo.done,
                    key, childLayout;
                if (lo.done) {
                    for (key in me.layouts) {
                        if (me.layouts.hasOwnProperty(key)) {
                            childLayout = me.layouts[key];
                            if (childLayout.owner.ownerLayout === lo) {
                                if (hasFailure(childLayout)) {
                                    failure = true;
                                }
                            }
                        }
                    }
                }
                return failure;
            }
            if (hasFailure(layout)) {
                return true;
            }
            function markReported(lo) {
                var key, childLayout;
                reported[lo.id] = 1;
                for (key in me.layouts) {
                    if (me.layouts.hasOwnProperty(key)) {
                        childLayout = me.layouts[key];
                        if (childLayout.owner.ownerLayout === lo) {
                            markReported(childLayout);
                        }
                    }
                }
            }
            markReported(layout);
            return false;
        },
        queueLayout: function(layout) {
            if (layout.done || layout.blockCount || layout.pending) {
                Ext.Error.raise({
                    msg: this.getLayoutName(layout) + ' should not be queued for layout'
                });
            }
            if (this.logOn.queueLayout) {
                Ext.log('Queue ', this.getLayoutName(layout));
            }
            return this.callParent(arguments);
        },
        reportLayoutResult: function(layout, reported) {
            var me = this,
                owner = layout.owner,
                ownerContext = me.getCmp(owner),
                blockedBy = [],
                triggeredBy = [],
                key, value, i, length, childLayout, item, setBy, info;
            reported[layout.id] = 1;
            for (key in layout.blockedBy) {
                if (layout.blockedBy.hasOwnProperty(key)) {
                    blockedBy.push(layout.blockedBy[key]);
                }
            }
            blockedBy.sort();
            for (key in me.triggersByLayoutId[layout.id]) {
                if (me.triggersByLayoutId[layout.id].hasOwnProperty(key)) {
                    value = me.triggersByLayoutId[layout.id][key];
                    triggeredBy.push({
                        name: key,
                        info: value
                    });
                }
            }
            triggeredBy.sort(function(a, b) {
                return a.name < b.name ? -1 : (b.name < a.name ? 1 : 0);
            });
            Ext.log({
                indent: 1
            }, (layout.done ? '++' : '--'), me.getLayoutName(layout), (ownerContext.isBoxParent ? ' [isBoxParent]' : ''), (ownerContext.boxChildren ? ' - boxChildren: ' + ownerContext.state.boxesMeasured + '/' + ownerContext.boxChildren.length : ''), ownerContext.boxParent ? (' - boxParent: ' + ownerContext.boxParent.id) : '', ' - size: ', ownerContext.widthModel.name, '/', ownerContext.heightModel.name);
            if (!layout.done || me.reportOnSuccess) {
                if (blockedBy.length) {
                    ++Ext.log.indent;
                    Ext.log({
                        indent: 1
                    }, 'blockedBy:  count=', layout.blockCount);
                    length = blockedBy.length;
                    for (i = 0; i < length; i++) {
                        Ext.log(blockedBy[i]);
                    }
                    Ext.log.indent -= 2;
                }
                if (triggeredBy.length) {
                    ++Ext.log.indent;
                    Ext.log({
                        indent: 1
                    }, 'triggeredBy: count=' + layout.triggerCount);
                    length = triggeredBy.length;
                    for (i = 0; i < length; i++) {
                        info = value.info || value;
                        item = info.item;
                        setBy = (item.setBy && item.setBy[info.name]) || '?';
                        value = triggeredBy[i];
                        Ext.log(value.name, ' (', item.props[info.name], ') dirty: ', (item.dirty ? !!item.dirty[info.name] : false), ', setBy: ', setBy);
                    }
                    Ext.log.indent -= 2;
                }
            }
            for (key in me.layouts) {
                if (me.layouts.hasOwnProperty(key)) {
                    childLayout = me.layouts[key];
                    if (!childLayout.done && childLayout.owner.ownerLayout === layout) {
                        me.reportLayoutResult(childLayout, reported);
                    }
                }
            }
            for (key in me.layouts) {
                if (me.layouts.hasOwnProperty(key)) {
                    childLayout = me.layouts[key];
                    if (childLayout.done && childLayout.owner.ownerLayout === layout) {
                        me.reportLayoutResult(childLayout, reported);
                    }
                }
            }
            --Ext.log.indent;
        },
        resetLayout: function(layout) {
            var me = this,
                type = layout.type,
                name = me.getLayoutName(layout),
                accum = me.accumByType[type],
                frame;
            if (me.logOn.resetLayout) {
                Ext.log('resetLayout: ', name, ' ( ', me.remainingLayouts, ' running)');
            }
            if (!me.state) {
                
                if (!accum && me.profileLayoutsByType) {
                    me.accumByType[type] = accum = Ext.Perf.get('layout_' + layout.type);
                }
                me.numByType[type] = (me.numByType[type] || 0) + 1;
            }
            frame = accum && accum.enter();
            me.callParent(arguments);
            if (accum) {
                frame.leave();
            }
            me.checkRemainingLayouts();
        },
        round: function(t) {
            return Math.round(t * 1000) / 1000;
        },
        run: function() {
            var me = this,
                ret, time, key, value, i, layout, boxParent, children, n, reported, unreported, calcs, total, calcsLength, calc;
            me.accumByType = {};
            me.calcsByType = {};
            me.numByType = {};
            me.timesByType = {};
            me.triggersByLayoutId = {};
            Ext.log.indentSize = 3;
            Ext.log('==================== LAYOUT ====================');
            time = Ext.perf.getTimestamp();
            ret = me.callParent(arguments);
            time = Ext.perf.getTimestamp() - time;
            if (me.logOn.boxParent && me.boxParents) {
                for (key in me.boxParents) {
                    if (me.boxParents.hasOwnProperty(key)) {
                        boxParent = me.boxParents[key];
                        children = boxParent.boxChildren;
                        n = children.length;
                        Ext.log('boxParent: ', boxParent.id);
                        for (i = 0; i < n; ++i) {
                            Ext.log(' --> ', children[i].id);
                        }
                    }
                }
            }
            if (ret) {
                Ext.log('----------------- SUCCESS -----------------');
            } else {
                Ext.log({
                    level: 'error'
                }, '----------------- FAILURE -----------------');
            }
            for (key in me.layouts) {
                if (me.layouts.hasOwnProperty(key)) {
                    layout = me.layouts[key];
                    if (layout.running) {
                        Ext.log.error('Layout left running: ', me.getLayoutName(layout));
                    }
                    if (layout.ownerContext) {
                        Ext.log.error('Layout left connected: ', me.getLayoutName(layout));
                    }
                }
            }
            if (!ret || me.reportOnSuccess) {
                reported = {};
                unreported = 0;
                for (key in me.layouts) {
                    if (me.layouts.hasOwnProperty(key)) {
                        layout = me.layouts[key];
                        if (me.items[layout.owner.el.id].isTopLevel) {
                            if (me.reportOnSuccess || me.layoutTreeHasFailures(layout, reported)) {
                                me.reportLayoutResult(layout, reported);
                            }
                        }
                    }
                }
                
                for (key in me.layouts) {
                    if (me.layouts.hasOwnProperty(key)) {
                        layout = me.layouts[key];
                        if (!reported[layout.id]) {
                            if (!unreported) {
                                Ext.log('----- Unreported!! -----');
                            }
                            ++unreported;
                            me.reportLayoutResult(layout, reported);
                        }
                    }
                }
            }
            Ext.log('Cycles: ', me.cycleCount, ', Flushes: ', me.flushCount, ', Calculates: ', me.calcCount, ' in ', me.round(time), ' msec');
            Ext.log('Calculates by type:');
            
            calcs = [];
            for (key in me.numByType) {
                if (me.numByType.hasOwnProperty(key)) {
                    total = me.numByType[key];
                    calcs.push({
                        type: key,
                        total: total,
                        calcs: me.calcsByType[key],
                        multiple: Math.round(me.calcsByType[key] / total * 10) / 10,
                        calcTime: me.round(me.timesByType[key]),
                        avgCalcTime: me.round(me.timesByType[key] / me.calcsByType[key])
                    });
                }
            }
            calcs.sort(function(a, b) {
                return b.calcTime - a.calcTime;
            });
            calcsLength = calcs.length;
            for (i = 0; i < calcsLength; i++) {
                calc = calcs[i];
                Ext.log(calc.type, ': ', calc.total, ' in ', calc.calcs, ' tries (', calc.multiple, 'x) at ', calc.calcTime, ' msec (avg ', calc.avgCalcTime, ' msec)');
            }
            return ret;
        },
        runCycle: function() {
            if (this.logOn.runCycle) {
                Ext.log('>>> Cycle ', this.cycleCount, ' (queue length: ', this.layoutQueue.length, ')');
            }
            return this.callParent(arguments);
        },
        runLayout: function(layout) {
            var me = this,
                type = layout.type,
                accum = me.accumByType[type],
                frame, ret, time;
            if (me.logOn.calculate) {
                Ext.log('-- calculate ', this.getLayoutName(layout));
            }
            frame = accum && accum.enter();
            time = Ext.perf.getTimestamp();
            ret = me.callParent(arguments);
            time = Ext.perf.getTimestamp() - time;
            if (accum) {
                frame.leave();
            }
            me.calcsByType[type] = (me.calcsByType[type] || 0) + 1;
            me.timesByType[type] = (me.timesByType[type] || 0) + time;
            
            return ret;
        }
    }
});



Ext.define('Ext.layout.component.FieldSet', {
    extend: 'Ext.layout.component.Body',
    alias: [
        'layout.fieldset'
    ],
    type: 'fieldset',
    defaultCollapsedWidth: 100,
    beforeLayoutCycle: function(ownerContext) {
        if (ownerContext.target.collapsed) {
            ownerContext.heightModel = this.sizeModels.shrinkWrap;
        }
    },
    beginLayout: function(ownerContext) {
        var legend = this.owner.legend;
        this.callParent([
            ownerContext
        ]);
        if (legend) {
            ownerContext.legendContext = ownerContext.context.getCmp(legend);
        }
    },
    beginLayoutCycle: function(ownerContext) {
        var target = ownerContext.target,
            lastSize;
        this.callParent(arguments);
        
        
        
        if (target.collapsed) {
            ownerContext.setContentHeight(0);
            
            
            ownerContext.restoreMinHeight = target.minHeight;
            delete target.minHeight;
            
            
            
            if (ownerContext.widthModel.shrinkWrap) {
                lastSize = this.lastComponentSize;
                ownerContext.setContentWidth((lastSize && lastSize.contentWidth) || this.defaultCollapsedWidth);
            }
        }
    },
    finishedLayout: function(ownerContext) {
        var owner = this.owner,
            restore = ownerContext.restoreMinHeight;
        this.callParent(arguments);
        if (restore) {
            owner.minHeight = restore;
        }
    },
    calculateOwnerWidthFromContentWidth: function(ownerContext, contentWidth) {
        var legendContext = ownerContext.legendContext;
        if (legendContext) {
            contentWidth = Math.max(contentWidth, legendContext.getProp('width'));
        }
        return this.callParent([
            ownerContext,
            contentWidth
        ]);
    },
    calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) {
        var border = ownerContext.getBorderInfo(),
            legendContext = ownerContext.legendContext;
        
        
        return ownerContext.getProp('contentHeight') + ownerContext.getPaddingInfo().height + 
        (Ext.isIE8 ? ownerContext.bodyContext.getPaddingInfo().top : 0) + (legendContext ? legendContext.getProp('height') : border.top) + border.bottom;
    },
    publishInnerHeight: function(ownerContext, height) {
        
        
        
        var legendContext = ownerContext.legendContext,
            legendHeight = 0;
        if (legendContext) {
            legendHeight = legendContext.getProp('height');
        }
        if (legendHeight === undefined) {
            this.done = false;
        } else {
            this.callParent([
                ownerContext,
                height - legendHeight
            ]);
        }
    },
    getLayoutItems: function() {
        var legend = this.owner.legend;
        return legend ? [
            legend
        ] : [];
    }
});


Ext.define('Ext.layout.container.Absolute', {
    
    alias: 'layout.absolute',
    extend: 'Ext.layout.container.Anchor',
    alternateClassName: 'Ext.layout.AbsoluteLayout',
    
    targetCls: Ext.baseCSSPrefix + 'abs-layout-ct',
    itemCls: Ext.baseCSSPrefix + 'abs-layout-item',
    
    ignoreOnContentChange: true,
    type: 'absolute',
    
    adjustWidthAnchor: function(value, childContext) {
        var padding = this.targetPadding,
            x = childContext.getStyle('left');
        return value - x + padding.left;
    },
    
    adjustHeightAnchor: function(value, childContext) {
        var padding = this.targetPadding,
            y = childContext.getStyle('top');
        return value - y + padding.top;
    },
    isItemLayoutRoot: function(item) {
        return this.ignoreOnContentChange || this.callParent(arguments);
    },
    isItemShrinkWrap: function(item) {
        return true;
    },
    beginLayout: function(ownerContext) {
        var me = this,
            target = me.getTarget();
        me.callParent(arguments);
        
        if (target.dom !== document.body) {
            target.position();
        }
        me.targetPadding = ownerContext.targetContext.getPaddingInfo();
    },
    isItemBoxParent: function(itemContext) {
        return true;
    },
    onContentChange: function() {
        if (this.ignoreOnContentChange) {
            return false;
        }
        return this.callParent(arguments);
    },
    calculateContentSize: function(ownerContext, dimensions) {
        var me = this,
            containerDimensions = (dimensions || 0) | ((ownerContext.widthModel.shrinkWrap ? 1 : 0) | (ownerContext.heightModel.shrinkWrap ? 2 : 0)),
            calcWidth = (containerDimensions & 1) || undefined,
            calcHeight = (containerDimensions & 2) || undefined,
            childItems = ownerContext.childItems,
            length = childItems.length,
            contentHeight = 0,
            contentWidth = 0,
            needed = 0,
            props = ownerContext.props,
            targetPadding, child, childContext, height, i, margins, width;
        if (calcWidth) {
            if (isNaN(props.contentWidth)) {
                ++needed;
            } else {
                calcWidth = undefined;
            }
        }
        if (calcHeight) {
            if (isNaN(props.contentHeight)) {
                ++needed;
            } else {
                calcHeight = undefined;
            }
        }
        if (needed) {
            for (i = 0; i < length; ++i) {
                childContext = childItems[i];
                child = childContext.target;
                height = calcHeight && childContext.getProp('height');
                width = calcWidth && childContext.getProp('width');
                margins = childContext.getMarginInfo();
                height += margins.bottom;
                width += margins.right;
                contentHeight = Math.max(contentHeight, (child.y || 0) + height);
                contentWidth = Math.max(contentWidth, (child.x || 0) + width);
                if (isNaN(contentHeight) && isNaN(contentWidth)) {
                    me.done = false;
                    return;
                }
            }
            if (calcWidth || calcHeight) {
                targetPadding = ownerContext.targetContext.getPaddingInfo();
            }
            if (calcWidth && !ownerContext.setContentWidth(contentWidth + targetPadding.width)) {
                me.done = false;
            }
            if (calcHeight && !ownerContext.setContentHeight(contentHeight + targetPadding.height)) {
                me.done = false;
            }
        }
    }
});


Ext.define('Ext.layout.container.Accordion', {
    extend: 'Ext.layout.container.VBox',
    alias: 'layout.accordion',
    type: 'accordion',
    alternateClassName: 'Ext.layout.AccordionLayout',
    targetCls: Ext.baseCSSPrefix + 'accordion-layout-ct',
    itemCls: [
        Ext.baseCSSPrefix + 'box-item',
        Ext.baseCSSPrefix + 'accordion-item'
    ],
    align: 'stretch',
    enableSplitters: false,
    
    fill: true,
    
    
    titleCollapse: true,
    
    hideCollapseTool: false,
    
    collapseFirst: undefined,
    
    animate: true,
    
    activeOnTop: false,
    
    multi: false,
    panelCollapseMode: 'header',
    defaultAnimatePolicy: {
        y: true,
        height: true
    },
    constructor: function() {
        var me = this;
        me.callParent(arguments);
        if (me.animate) {
            me.animatePolicy = {};
            
            me.animatePolicy[me.names.x] = true;
            me.animatePolicy[me.names.width] = true;
        } else {
            me.animatePolicy = null;
        }
    },
    beforeRenderItems: function(items) {
        var me = this,
            ln = items.length,
            owner = me.owner,
            collapseFirst = me.collapseFirst,
            hasCollapseFirst = Ext.isDefined(collapseFirst),
            expandedItem = me.getExpanded(true)[0],
            multi = me.multi,
            comp, i;
        for (i = 0; i < ln; i++) {
            comp = items[i];
            if (!comp.rendered) {
                
                if (!multi || comp.collapsible !== false) {
                    comp.collapsible = true;
                }
                if (comp.collapsible) {
                    if (hasCollapseFirst) {
                        comp.collapseFirst = collapseFirst;
                    }
                    if (me.hideCollapseTool) {
                        comp.hideCollapseTool = me.hideCollapseTool;
                        comp.titleCollapse = true;
                    } else if (me.titleCollapse && comp.titleCollapse === undefined) {
                        
                        
                        comp.titleCollapse = me.titleCollapse;
                    }
                }
                comp.hideHeader = comp.width = null;
                comp.title = comp.title || '&#160;';
                comp.addBodyCls(Ext.baseCSSPrefix + 'accordion-body');
                
                
                
                if (!multi) {
                    if (expandedItem) {
                        comp.collapsed = expandedItem !== comp;
                    } else if (comp.hasOwnProperty('collapsed') && comp.collapsed === false) {
                        expandedItem = comp;
                    } else {
                        comp.collapsed = true;
                    }
                    
                    owner.mon(comp, 'show', me.onComponentShow, me);
                }
                
                
                comp.headerOverCls = Ext.baseCSSPrefix + 'accordion-hd-over';
            }
        }
        
        
        if (!me.processing && !multi) {
            if (!expandedItem) {
                if (ln) {
                    items[0].collapsed = false;
                }
            } else if (me.activeOnTop) {
                expandedItem.collapsed = false;
                me.configureItem(expandedItem);
                if (owner.items.indexOf(expandedItem) > 0) {
                    owner.insert(0, expandedItem);
                }
            }
        }
    },
    getItemsRenderTree: function(items) {
        this.beforeRenderItems(items);
        return this.callParent(arguments);
    },
    renderItems: function(items, target) {
        this.beforeRenderItems(items);
        this.callParent(arguments);
    },
    configureItem: function(item) {
        this.callParent(arguments);
        
        item.ignoreHeaderBorderManagement = true;
        
        
        item.animCollapse = false;
        
        if (this.fill) {
            item.flex = 1;
        }
    },
    beginLayout: function(ownerContext) {
        this.callParent(arguments);
        this.updatePanelClasses(ownerContext);
    },
    updatePanelClasses: function(ownerContext) {
        var children = ownerContext.visibleItems,
            ln = children.length,
            siblingCollapsed = true,
            i, child, header;
        for (i = 0; i < ln; i++) {
            child = children[i];
            header = child.header;
            header.addCls(Ext.baseCSSPrefix + 'accordion-hd');
            if (siblingCollapsed) {
                header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
            } else {
                header.addCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
            }
            if (i + 1 == ln && child.collapsed) {
                header.addCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
            } else {
                header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
            }
            siblingCollapsed = child.collapsed;
        }
    },
    
    
    
    onBeforeComponentExpand: function(toExpand) {
        var me = this,
            owner = me.owner,
            multi = me.multi,
            moveToTop = !multi && !me.animate && me.activeOnTop,
            expanded, previousValue, anim;
        if (!me.processing) {
            me.processing = true;
            previousValue = owner.deferLayouts;
            owner.deferLayouts = true;
            if (!multi) {
                expanded = me.getExpanded()[0];
                if (expanded && expanded !== toExpand) {
                    anim = expanded.$layoutAnim;
                    
                    if (anim) {
                        anim.jumpToEnd();
                    }
                    expanded.collapse();
                }
            }
            if (moveToTop) {
                
                Ext.suspendLayouts();
                owner.insert(0, toExpand);
                Ext.resumeLayouts();
            }
            owner.deferLayouts = previousValue;
            me.processing = false;
        }
    },
    onBeforeComponentCollapse: function(comp) {
        var me = this,
            owner = me.owner,
            toExpand, expanded, previousValue;
        if (me.owner.items.getCount() === 1) {
            
            return false;
        }
        if (!me.processing) {
            me.processing = true;
            previousValue = owner.deferLayouts;
            owner.deferLayouts = true;
            toExpand = comp.next() || comp.prev();
            
            
            if (me.multi) {
                expanded = me.getExpanded();
                
                
                if (expanded.length === 1) {
                    toExpand.expand();
                }
            } else if (toExpand) {
                toExpand.expand();
            }
            owner.deferLayouts = previousValue;
            me.processing = false;
        }
    },
    onComponentShow: function(comp) {
        this.onBeforeComponentExpand(comp);
    },
    onAdd: function(item) {
        var me = this;
        me.callParent(arguments);
        if (item.collapseMode === 'placeholder') {
            item.collapseMode = me.panelCollapseMode;
        }
        item.collapseDirection = item.headerPosition;
        
        
        
        if (me.layoutCount && !me.multi && me.owner.items.getCount() > 1) {
            
            
            me.processing = true;
            item.collapse();
            me.processing = false;
        }
    },
    onRemove: function(panel, destroying) {
        var me = this,
            item;
        me.callParent(arguments);
        if (!me.owner.destroying && !me.multi && !panel.collapsed) {
            item = me.owner.items.first();
            if (item) {
                item.expand();
            }
        }
    },
    getExpanded: function(explicitCheck) {
        var items = this.owner.items.items,
            len = items.length,
            i = 0,
            out = [],
            add, item;
        for (; i < len; ++i) {
            item = items[i];
            if (!item.hidden) {
                if (explicitCheck) {
                    add = item.hasOwnProperty('collapsed') && item.collapsed === false;
                } else {
                    add = !item.collapsed;
                }
                if (add) {
                    out.push(item);
                }
            }
        }
        return out;
    },
    
    
    afterCollapse: Ext.emptyFn,
    afterExpand: Ext.emptyFn
});


Ext.define('Ext.layout.container.Center', {
    extend: 'Ext.layout.container.Fit',
    alias: [
        'layout.center',
        'layout.ux.center'
    ],
    alternateClassName: 'Ext.ux.layout.Center',
    percentRe: /^\d+(?:\.\d+)?\%$/,
    itemCls: Ext.baseCSSPrefix + 'center-layout-item',
    targetCls: Ext.baseCSSPrefix + 'center-layout',
    getItemSizePolicy: function(item, ownerSizeModel) {
        var me = this,
            sizeModel = ownerSizeModel || me.owner.getSizeModel(),
            percentRe = me.percentRe,
            mode = ((sizeModel.width.shrinkWrap || !percentRe.test(item.width)) ? 0 : 1) | ((sizeModel.height.shrinkWrap || !percentRe.test(item.height)) ? 0 : 2);
        return me.sizePolicies[mode];
    },
    getPos: function(itemContext, info, dimension) {
        var size = itemContext.props[dimension] + info.margins[dimension],
            pos = Math.round((info.targetSize[dimension] - size) / 2);
        if (isNaN(pos)) {
            this.done = false;
        }
        return Math.max(pos, 0);
    },
    positionItemX: function(itemContext, info) {
        var left = this.getPos(itemContext, info, 'width');
        itemContext.setProp('x', left);
    },
    positionItemY: function(itemContext, info) {
        var top = this.getPos(itemContext, info, 'height');
        itemContext.setProp('y', top);
    },
    setItemHeight: function(itemContext, info) {
        var ratio = parseFloat(itemContext.target.height) / 100;
        itemContext.setHeight(Math.round((info.targetSize.height - info.margins.height) * ratio));
    },
    setItemWidth: function(itemContext, info) {
        var ratio = parseFloat(itemContext.target.width) / 100;
        itemContext.setWidth(Math.round((info.targetSize.width - info.margins.width) * ratio));
    }
});


Ext.define('Ext.layout.container.Form', {
    extend: 'Ext.layout.container.Auto',
    alternateClassName: 'Ext.layout.FormLayout',
    alias: 'layout.form',
    type: 'form',
    formWrapCls: Ext.baseCSSPrefix + 'form-layout-wrap',
    formWrapAutoLabelCls: Ext.baseCSSPrefix + 'form-layout-auto-label',
    formWrapSizedLabelCls: Ext.baseCSSPrefix + 'form-layout-sized-label',
    formColGroupCls: Ext.baseCSSPrefix + 'form-layout-colgroup',
    formColumnCls: Ext.baseCSSPrefix + 'form-layout-column',
    formLabelColumnCls: Ext.baseCSSPrefix + 'form-layout-label-column',
    
    
    childEls: [
        'formWrap',
        'labelColumn'
    ],
    beforeBodyTpl: '<div id="{ownerId}-formWrap" data-ref="formWrap" class="{formWrapCls}"' + '<tpl if="itemSpacing"> style="border-spacing:{itemSpacing}px"</tpl>>' + '<div class="{formColGroupCls}">' + '<div id="{ownerId}-labelColumn" data-ref="labelColumn" class="{formColumnCls} {formLabelColumnCls}"' + '<tpl if="labelWidth"> style="width:{labelWidth}"</tpl>>' + '</div>' + '<div class="{formColumnCls}"></div>' + '</div>',
    afterBodyTpl: '</div>',
    getRenderData: function() {
        var me = this,
            labelWidth = me.labelWidth,
            formWrapCls = me.formWrapCls,
            data = me.callParent();
        if (labelWidth) {
            if (typeof labelWidth === 'number') {
                labelWidth += 'px';
            }
            data.labelWidth = labelWidth;
            formWrapCls += ' ' + me.formWrapSizedLabelCls;
        } else {
            formWrapCls += ' ' + me.formWrapAutoLabelCls;
        }
        data.formWrapCls = formWrapCls;
        data.formColGroupCls = me.formColGroupCls;
        data.formColumnCls = me.formColumnCls;
        data.formLabelColumnCls = me.formLabelColumnCls;
        return data;
    },
    getRenderTarget: function() {
        return this.formWrap;
    }
});

Ext.define('Ext.layout.container.SegmentedButton', {
    extend: 'Ext.layout.container.Container',
    alias: 'layout.segmentedbutton',
    needsItemSize: false,
    setsItemSize: false,
    _btnRowCls: Ext.baseCSSPrefix + 'segmented-button-row',
    getRenderTree: function() {
        var me = this,
            result = me.callParent(),
            i, ln;
        if (me.owner.getVertical()) {
            for (i = 0 , ln = result.length; i < ln; i++) {
                result[i] = {
                    cls: me._btnRowCls,
                    cn: result[i]
                };
            }
        }
        return result;
    },
    getItemLayoutEl: function(item) {
        var dom = item.el.dom;
        return this.owner.getVertical() ? dom.parentNode : dom;
    },
    onDestroy: function() {
        
        
        
        
        
        
        if (this.rendered) {
            var targetEl = this.getRenderTarget(),
                row;
            while ((row = targetEl.last())) {
                row.destroy();
            }
        }
    }
});


Ext.define('Ext.menu.ColorPicker', {
    extend: 'Ext.menu.Menu',
    alias: 'widget.colormenu',
    requires: [
        'Ext.picker.Color'
    ],
    
    hideOnClick: true,
    
    pickerId: null,
    
    
    
    initComponent: function() {
        var me = this,
            cfg = Ext.apply({}, me.initialConfig);
        
        delete cfg.listeners;
        Ext.apply(me, {
            plain: true,
            showSeparator: false,
            bodyPadding: 0,
            items: Ext.applyIf({
                cls: Ext.baseCSSPrefix + 'menu-color-item',
                margin: 0,
                id: me.pickerId,
                xtype: 'colorpicker'
            }, cfg)
        });
        me.callParent(arguments);
        me.picker = me.down('colorpicker');
        
        me.relayEvents(me.picker, [
            'select'
        ]);
        if (me.hideOnClick) {
            me.on('select', me.hidePickerOnSelect, me);
        }
    },
    
    hidePickerOnSelect: function() {
        Ext.menu.Manager.hideAll();
    }
});


Ext.define('Ext.menu.DatePicker', {
    extend: 'Ext.menu.Menu',
    alias: 'widget.datemenu',
    requires: [
        'Ext.picker.Date'
    ],
    ariaRole: 'dialog',
    
    
    ariaLabel: 'Date picker',
    
    
    hideOnClick: true,
    
    pickerId: null,
    
    
    initComponent: function() {
        var me = this,
            cfg = Ext.apply({}, me.initialConfig);
        
        delete cfg.listeners;
        Ext.apply(me, {
            showSeparator: false,
            plain: true,
            bodyPadding: 0,
            
            items: Ext.applyIf({
                cls: Ext.baseCSSPrefix + 'menu-date-item',
                margin: 0,
                border: false,
                id: me.pickerId,
                xtype: 'datepicker'
            }, cfg)
        });
        me.callParent(arguments);
        me.picker = me.down('datepicker');
        
        me.relayEvents(me.picker, [
            'select'
        ]);
        if (me.hideOnClick) {
            me.on('select', me.hidePickerOnSelect, me);
        }
    },
    hidePickerOnSelect: function() {
        Ext.menu.Manager.hideAll();
    }
});


Ext.define('Ext.panel.Pinnable', {
    extend: 'Ext.Mixin',
    mixinId: 'pinnable',
    pinnable: true,
    pinnedTip: 'Unpin this item',
    unpinnedTip: 'Pin this item',
    initPinnable: function() {
        var me = this,
            pinned = me.isPinned();
        me.addTool(me.pinTool = Ext.widget({
            xtype: 'tool',
            type: pinned ? 'unpin' : 'pin',
            callback: 'togglePin',
            scope: me,
            tooltip: pinned ? me.pinnedTip : me.unpinnedTip
        }));
    },
    isPinned: function() {
        return !this.floating;
    },
    setPinned: function(pinned) {
        var me = this,
            args;
        if (pinned !== me.isPinned()) {
            args = [
                me,
                pinned
            ];
            if (me.fireEventArgs('beforepinchange', args) !== false) {
                me.updatePinned(pinned);
                me.fireEventArgs('pinchange', args);
            }
        }
    },
    togglePin: function() {
        this.setPinned(!this.isPinned());
    },
    updatePinned: function(pinned) {
        var me = this,
            tool = me.pinTool;
        tool.setTooltip(pinned ? me.pinnedTip : me.unpinnedTip);
        tool.setType(pinned ? 'unpin' : 'pin');
    }
});


Ext.define('Ext.plugin.Manager', {
    alternateClassName: [
        'Ext.PluginManager',
        'Ext.PluginMgr'
    ],
    singleton: true,
    typeName: 'ptype',
    
    create: function(config, defaultType, host) {
        var result, type;
        if (config.init) {
            result = config;
        } else {
            
            if (host) {
                config = Ext.apply({}, config);
                
                config.cmp = host;
            } else 
            {
                host = config.cmp;
            }
            if (config.xclass) {
                result = Ext.create(config);
            } else {
                
                type = 'plugin.' + (config.ptype || defaultType);
                result = Ext.ClassManager.instantiateByAlias(type, config);
            }
        }
        
        if (result && host && result.setCmp && !result.setCmpCalled) {
            result.setCmp(host);
            result.setCmpCalled = true;
        }
        return result;
    }
});


Ext.define('Ext.resizer.BorderSplitterTracker', {
    extend: 'Ext.resizer.SplitterTracker',
    requires: [
        'Ext.util.Region'
    ],
    getPrevCmp: null,
    getNextCmp: null,
    
    calculateConstrainRegion: function() {
        var me = this,
            splitter = me.splitter,
            collapseTarget = splitter.collapseTarget,
            defaultSplitMin = splitter.defaultSplitMin,
            sizePropCap = splitter.vertical ? 'Width' : 'Height',
            minSizeProp = 'min' + sizePropCap,
            maxSizeProp = 'max' + sizePropCap,
            getSizeMethod = 'get' + sizePropCap,
            neighbors = splitter.neighbors,
            length = neighbors.length,
            box = collapseTarget.el.getBox(),
            left = box.x,
            top = box.y,
            right = box.right,
            bottom = box.bottom,
            size = splitter.vertical ? (right - left) : (bottom - top),
            
            i, neighbor, neighborMaxSize, minRange, maxRange, maxGrowth, maxShrink, targetSize;
        
        minRange = (collapseTarget[minSizeProp] || Math.min(size, defaultSplitMin)) - size;
        
        maxRange = collapseTarget[maxSizeProp];
        if (!maxRange) {
            maxRange = 1000000000;
        } else {
            maxRange -= size;
        }
        targetSize = size;
        for (i = 0; i < length; ++i) {
            neighbor = neighbors[i];
            size = neighbor[getSizeMethod]();
            neighborMaxSize = neighbor[maxSizeProp];
            if (neighborMaxSize === null) {
                
                
                
                
                neighborMaxSize = undefined;
            }
            maxGrowth = size - neighborMaxSize;
            
            maxShrink = size - (neighbor[minSizeProp] || Math.min(size, defaultSplitMin));
            if (!isNaN(maxGrowth)) {
                
                
                if (minRange < maxGrowth) {
                    minRange = maxGrowth;
                }
            }
            
            
            if (maxRange > maxShrink) {
                maxRange = maxShrink;
            }
        }
        if (maxRange - minRange < 2) {
            return null;
        }
        box = new Ext.util.Region(top,right,bottom,left);
        me.constraintAdjusters[me.getCollapseDirection()](box, minRange, maxRange, splitter);
        me.dragInfo = {
            minRange: minRange,
            maxRange: maxRange,
            
            targetSize: targetSize
        };
        return box;
    },
    constraintAdjusters: {
        
        left: function(box, minRange, maxRange, splitter) {
            box[0] = box.x = box.left = box.right + minRange;
            box.right += maxRange + splitter.getWidth();
        },
        
        top: function(box, minRange, maxRange, splitter) {
            box[1] = box.y = box.top = box.bottom + minRange;
            box.bottom += maxRange + splitter.getHeight();
        },
        
        bottom: function(box, minRange, maxRange, splitter) {
            box.bottom = box.top - minRange;
            box.top -= maxRange + splitter.getHeight();
        },
        
        right: function(box, minRange, maxRange, splitter) {
            box.right = box.left - minRange;
            box[0] = box.x = box.left = box.x - maxRange + splitter.getWidth();
        }
    },
    onBeforeStart: function(e) {
        var me = this,
            splitter = me.splitter,
            collapseTarget = splitter.collapseTarget,
            neighbors = splitter.neighbors,
            length = neighbors.length,
            i, neighbor;
        if (collapseTarget.collapsed) {
            return false;
        }
        
        for (i = 0; i < length; ++i) {
            neighbor = neighbors[i];
            if (neighbor.collapsed && neighbor.isHorz === collapseTarget.isHorz) {
                return false;
            }
        }
        if (!(me.constrainTo = me.calculateConstrainRegion())) {
            return false;
        }
        return true;
    },
    performResize: function(e, offset) {
        var me = this,
            splitter = me.splitter,
            collapseDirection = splitter.getCollapseDirection(),
            collapseTarget = splitter.collapseTarget,
            
            adjusters = me.splitAdjusters[splitter.vertical ? 'horz' : 'vert'],
            delta = offset[adjusters.index],
            dragInfo = me.dragInfo,
            
            
            
            
            
            owner;
        if (collapseDirection == 'right' || collapseDirection == 'bottom') {
            
            delta = -delta;
        }
        
        delta = Math.min(Math.max(dragInfo.minRange, delta), dragInfo.maxRange);
        if (delta) {
            (owner = splitter.ownerCt).suspendLayouts();
            adjusters.adjustTarget(collapseTarget, dragInfo.targetSize, delta);
            
            
            
            
            
            
            
            owner.resumeLayouts(true);
        }
    },
    splitAdjusters: {
        horz: {
            index: 0,
            
            
            
            adjustTarget: function(target, size, delta) {
                target.flex = null;
                target.setSize(size + delta);
            }
        },
        vert: {
            index: 1,
            
            
            
            adjustTarget: function(target, targetSize, delta) {
                target.flex = null;
                target.setSize(undefined, targetSize + delta);
            }
        }
    },
    getCollapseDirection: function() {
        return this.splitter.getCollapseDirection();
    }
});


Ext.define('Ext.resizer.Handle', {
    extend: 'Ext.Component',
    handleCls: '',
    baseHandleCls: Ext.baseCSSPrefix + 'resizable-handle',
    
    
    region: '',
    ariaRole: 'presentation',
    beforeRender: function() {
        var me = this;
        me.callParent();
        me.protoEl.unselectable();
        me.addCls(me.baseHandleCls, me.baseHandleCls + '-' + me.region, me.handleCls);
    }
});


Ext.define('Ext.resizer.ResizeTracker', {
    extend: 'Ext.dd.DragTracker',
    dynamic: true,
    preserveRatio: false,
    
    constrainTo: null,
    proxyCls: Ext.baseCSSPrefix + 'resizable-proxy',
    constructor: function(config) {
        var me = this,
            widthRatio, heightRatio, throttledResizeFn;
        if (!config.el) {
            if (config.target.isComponent) {
                me.el = config.target.getEl();
            } else {
                me.el = config.target;
            }
        }
        this.callParent(arguments);
        
        if (me.preserveRatio && me.minWidth && me.minHeight) {
            widthRatio = me.minWidth / me.el.getWidth();
            heightRatio = me.minHeight / me.el.getHeight();
            
            
            
            if (heightRatio > widthRatio) {
                me.minWidth = me.el.getWidth() * heightRatio;
            } else {
                me.minHeight = me.el.getHeight() * widthRatio;
            }
        }
        
        
        if (me.throttle) {
            throttledResizeFn = Ext.Function.createThrottled(function() {
                Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments);
            }, me.throttle);
            me.resize = function(box, direction, atEnd) {
                if (atEnd) {
                    Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments);
                } else {
                    throttledResizeFn.apply(null, arguments);
                }
            };
        }
    },
    onBeforeStart: function(e) {
        
        this.startBox = this.target.getBox();
    },
    
    getProxy: function() {
        var me = this;
        if (!me.dynamic && !me.proxy) {
            me.proxy = me.createProxy(me.target || me.el);
            
            
            me.hideProxy = true;
        }
        if (me.proxy) {
            me.proxy.show();
            return me.proxy;
        }
    },
    
    createProxy: function(target) {
        var proxy,
            cls = this.proxyCls;
        if (target.isComponent) {
            proxy = target.getProxy().addCls(cls);
        } else {
            proxy = target.createProxy({
                tag: 'div',
                role: 'presentation',
                cls: cls,
                id: target.id + '-rzproxy'
            }, Ext.getBody());
        }
        proxy.removeCls(Ext.baseCSSPrefix + 'proxy-el');
        return proxy;
    },
    onStart: function(e) {
        
        this.activeResizeHandle = Ext.get(this.getDragTarget().id);
        
        if (!this.dynamic) {
            this.resize(this.startBox);
        }
    },
    onDrag: function(e) {
        
        if (this.dynamic || this.proxy) {
            this.updateDimensions(e);
        }
    },
    updateDimensions: function(e, atEnd) {
        var me = this,
            region = me.activeResizeHandle.region,
            offset = me.getOffset(me.constrainTo ? 'dragTarget' : null),
            box = me.startBox,
            ratio,
            widthAdjust = 0,
            heightAdjust = 0,
            snappedWidth, snappedHeight,
            adjustX = 0,
            adjustY = 0,
            dragRatio, oppositeCorner, axis, 
            newBox, newHeight, newWidth;
        region = me.convertRegionName(region);
        switch (region) {
            case 'south':
                heightAdjust = offset[1];
                axis = 2;
                break;
            case 'north':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                axis = 2;
                break;
            case 'east':
                widthAdjust = offset[0];
                axis = 1;
                break;
            case 'west':
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                axis = 1;
                break;
            case 'northeast':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                widthAdjust = offset[0];
                oppositeCorner = [
                    box.x,
                    box.y + box.height
                ];
                axis = 3;
                break;
            case 'southeast':
                heightAdjust = offset[1];
                widthAdjust = offset[0];
                oppositeCorner = [
                    box.x,
                    box.y
                ];
                axis = 3;
                break;
            case 'southwest':
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                heightAdjust = offset[1];
                oppositeCorner = [
                    box.x + box.width,
                    box.y
                ];
                axis = 3;
                break;
            case 'northwest':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                oppositeCorner = [
                    box.x + box.width,
                    box.y + box.height
                ];
                axis = 3;
                break;
        }
        newBox = {
            width: box.width + widthAdjust,
            height: box.height + heightAdjust,
            x: box.x + adjustX,
            y: box.y + adjustY
        };
        
        snappedWidth = Ext.Number.snap(newBox.width, me.widthIncrement);
        snappedHeight = Ext.Number.snap(newBox.height, me.heightIncrement);
        if (snappedWidth !== newBox.width || snappedHeight !== newBox.height) {
            switch (region) {
                case 'northeast':
                    newBox.y -= snappedHeight - newBox.height;
                    break;
                case 'north':
                    newBox.y -= snappedHeight - newBox.height;
                    break;
                case 'southwest':
                    newBox.x -= snappedWidth - newBox.width;
                    break;
                case 'west':
                    newBox.x -= snappedWidth - newBox.width;
                    break;
                case 'northwest':
                    newBox.x -= snappedWidth - newBox.width;
                    newBox.y -= snappedHeight - newBox.height;
            }
            newBox.width = snappedWidth;
            newBox.height = snappedHeight;
        }
        
        if (newBox.width < me.minWidth || newBox.width > me.maxWidth) {
            newBox.width = Ext.Number.constrain(newBox.width, me.minWidth, me.maxWidth);
            
            if (adjustX) {
                newBox.x = box.x + (box.width - newBox.width);
            }
        } else {
            me.lastX = newBox.x;
        }
        if (newBox.height < me.minHeight || newBox.height > me.maxHeight) {
            newBox.height = Ext.Number.constrain(newBox.height, me.minHeight, me.maxHeight);
            
            if (adjustY) {
                newBox.y = box.y + (box.height - newBox.height);
            }
        } else {
            me.lastY = newBox.y;
        }
        
        if (me.preserveRatio || e.shiftKey) {
            ratio = me.startBox.width / me.startBox.height;
            
            newHeight = Math.min(Math.max(me.minHeight, newBox.width / ratio), me.maxHeight);
            newWidth = Math.min(Math.max(me.minWidth, newBox.height * ratio), me.maxWidth);
            
            if (axis === 1) {
                newBox.height = newHeight;
            }
            
            else if (axis === 2) {
                newBox.width = newWidth;
            } else 
            {
                
                
                dragRatio = Math.abs(oppositeCorner[0] - this.lastXY[0]) / Math.abs(oppositeCorner[1] - this.lastXY[1]);
                
                if (dragRatio > ratio) {
                    newBox.height = newHeight;
                } else {
                    newBox.width = newWidth;
                }
                
                if (region === 'northeast') {
                    newBox.y = box.y - (newBox.height - box.height);
                } else if (region === 'northwest') {
                    newBox.y = box.y - (newBox.height - box.height);
                    newBox.x = box.x - (newBox.width - box.width);
                } else if (region === 'southwest') {
                    newBox.x = box.x - (newBox.width - box.width);
                }
            }
        }
        
        me.setPosition = newBox.x !== me.startBox.x || newBox.y !== me.startBox.y;
        me.resize(newBox, atEnd);
    },
    resize: function(box, atEnd) {
        var me = this,
            target,
            setPosition = me.setPosition;
        
        if (me.dynamic || (!me.dynamic && atEnd)) {
            
            if (setPosition) {
                me.target.setBox(box);
            } else {
                me.target.setSize(box.width, box.height);
            }
        }
        
        if (!atEnd) {
            target = me.getProxy();
            if (target && target !== me.target) {
                if (setPosition || me.hideProxy) {
                    target.setBox(box);
                } else {
                    target.setSize(box.width, box.height);
                }
            }
        }
    },
    onEnd: function(e) {
        this.updateDimensions(e, true);
        if (this.proxy && this.hideProxy) {
            this.proxy.hide();
        }
    },
    convertRegionName: function(name) {
        return name;
    }
});


Ext.define('Ext.resizer.Resizer', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    uses: [
        'Ext.resizer.ResizeTracker',
        'Ext.Component'
    ],
    alternateClassName: 'Ext.Resizable',
    handleCls: Ext.baseCSSPrefix + 'resizable-handle',
    overCls: Ext.baseCSSPrefix + 'resizable-handle-over',
    pinnedCls: Ext.baseCSSPrefix + 'resizable-pinned',
    wrapCls: Ext.baseCSSPrefix + 'resizable-wrap',
    wrappedCls: Ext.baseCSSPrefix + 'resizable-wrapped',
    delimiterRe: /(?:\s*[,;]\s*)|\s+/,
    
    dynamic: true,
    
    handles: 's e se',
    
    height: null,
    
    width: null,
    
    heightIncrement: 0,
    
    widthIncrement: 0,
    
    minHeight: 20,
    
    minWidth: 20,
    
    maxHeight: 10000,
    
    maxWidth: 10000,
    
    pinned: false,
    
    preserveRatio: false,
    
    transparent: false,
    
    possiblePositions: {
        n: 'north',
        s: 'south',
        e: 'east',
        w: 'west',
        se: 'southeast',
        sw: 'southwest',
        nw: 'northwest',
        ne: 'northeast'
    },
    
    
    ariaRole: 'presentation',
    
    
    
    constructor: function(config) {
        var me = this,
            handles = me.handles,
            unselectableCls = Ext.dom.Element.unselectableCls,
            handleEls = [],
            resizeTarget, handleCls, possibles, tag, len, i, pos, el, box, wrapTarget, positioning, targetBaseCls;
        if (Ext.isString(config) || Ext.isElement(config) || config.dom) {
            resizeTarget = config;
            config = arguments[1] || {};
            config.target = resizeTarget;
        }
        
        me.mixins.observable.constructor.call(me, config);
        
        
        resizeTarget = me.target;
        if (resizeTarget) {
            if (resizeTarget.isComponent) {
                
                
                
                resizeTarget.addClsWithUI('resizable');
                if (resizeTarget.minWidth) {
                    me.minWidth = resizeTarget.minWidth;
                }
                if (resizeTarget.minHeight) {
                    me.minHeight = resizeTarget.minHeight;
                }
                if (resizeTarget.maxWidth) {
                    me.maxWidth = resizeTarget.maxWidth;
                }
                if (resizeTarget.maxHeight) {
                    me.maxHeight = resizeTarget.maxHeight;
                }
                if (resizeTarget.floating) {
                    if (!me.hasOwnProperty('handles')) {
                        me.handles = 'n ne e se s sw w nw';
                    }
                }
                me.el = resizeTarget.getEl();
            } else {
                resizeTarget = me.el = me.target = Ext.get(resizeTarget);
            }
        } else 
        {
            resizeTarget = me.target = me.el = Ext.get(me.el);
        }
        
        
        me.el.addCls(Ext.Component.prototype.borderBoxCls);
        
        if (Ext.isNumber(me.width)) {
            me.width = Ext.Number.constrain(me.width, me.minWidth, me.maxWidth);
        }
        if (Ext.isNumber(me.height)) {
            me.height = Ext.Number.constrain(me.height, me.minHeight, me.maxHeight);
        }
        
        if (me.width !== null || me.height !== null) {
            me.target.setSize(me.width, me.height);
        }
        
        
        
        tag = me.el.dom.tagName.toUpperCase();
        if (tag === 'TEXTAREA' || tag === 'IMG' || tag === 'TABLE') {
            
            me.originalTarget = me.target;
            wrapTarget = resizeTarget.isComponent ? resizeTarget.getEl() : resizeTarget;
            
            me.el.addCls(me.wrappedCls);
            me.target = me.el = me.el.wrap({
                role: 'presentation',
                cls: me.wrapCls,
                id: me.el.id + '-rzwrap',
                style: wrapTarget.getStyle([
                    'margin-top',
                    'margin-bottom'
                ])
            });
            positioning = wrapTarget.getPositioning();
            
            me.el.setPositioning(positioning);
            wrapTarget.clearPositioning();
            box = wrapTarget.getBox();
            if (positioning.position !== 'absolute') {
                
                box.x = 0;
                box.y = 0;
            }
            me.el.setBox(box);
            
            wrapTarget.setStyle('position', 'absolute');
            me.isTargetWrapped = true;
        }
        
        
        me.el.position();
        if (me.pinned) {
            me.el.addCls(me.pinnedCls);
        }
        
        me.resizeTracker = new Ext.resizer.ResizeTracker({
            disabled: me.disabled,
            target: resizeTarget,
            el: me.el,
            constrainTo: me.constrainTo,
            handleCls: me.handleCls,
            overCls: me.overCls,
            throttle: me.throttle,
            
            
            proxy: me.originalTarget ? me.el : null,
            dynamic: me.originalTarget ? true : me.dynamic,
            originalTarget: me.originalTarget,
            delegate: '.' + me.handleCls,
            preserveRatio: me.preserveRatio,
            heightIncrement: me.heightIncrement,
            widthIncrement: me.widthIncrement,
            minHeight: me.minHeight,
            maxHeight: me.maxHeight,
            minWidth: me.minWidth,
            maxWidth: me.maxWidth
        });
        
        me.resizeTracker.on({
            mousedown: me.onBeforeResize,
            drag: me.onResize,
            dragend: me.onResizeEnd,
            scope: me
        });
        if (me.handles === 'all') {
            me.handles = 'n s e w ne nw se sw';
        }
        handles = me.handles = me.handles.split(me.delimiterRe);
        possibles = me.possiblePositions;
        len = handles.length;
        handleCls = me.handleCls + ' ' + me.handleCls + '-{0}';
        if (me.target.isComponent) {
            targetBaseCls = me.target.baseCls;
            handleCls += ' ' + targetBaseCls + '-handle ' + targetBaseCls + '-handle-{0}';
            if (Ext.supports.CSS3BorderRadius) {
                handleCls += ' ' + targetBaseCls + '-handle-{0}-br';
            }
        }
        for (i = 0; i < len; i++) {
            
            if (handles[i] && possibles[handles[i]]) {
                pos = possibles[handles[i]];
                handleEls.push('<div id="', me.el.id, '-', pos, '-handle" class="', Ext.String.format(handleCls, pos), ' ', unselectableCls, '" unselectable="on" role="presentation"', '></div>');
            }
        }
        Ext.DomHelper.append(me.el, handleEls.join(''));
        
        handleEls.length = 0;
        
        for (i = 0; i < len; i++) {
            
            if (handles[i] && possibles[handles[i]]) {
                pos = possibles[handles[i]];
                el = me[pos] = me.el.getById(me.el.id + '-' + pos + '-handle');
                handleEls.push(el);
                el.region = pos;
                if (me.transparent) {
                    el.setOpacity(0);
                }
            }
        }
        me.resizeTracker.handleEls = handleEls;
    },
    disable: function() {
        this.resizeTracker.disable();
    },
    enable: function() {
        this.resizeTracker.enable();
    },
    
    onBeforeResize: function(tracker, e) {
        return this.fireResizeEvent('beforeresize', tracker, e);
    },
    
    onResize: function(tracker, e) {
        return this.fireResizeEvent('resizedrag', tracker, e);
    },
    
    onResizeEnd: function(tracker, e) {
        return this.fireResizeEvent('resize', tracker, e);
    },
    
    fireResizeEvent: function(name, tracker, e) {
        var me = this,
            box;
        if (me.hasListeners[name]) {
            box = me.el.getBox();
            return me.fireEvent(name, me, box.width, box.height, e);
        }
    },
    
    resizeTo: function(width, height) {
        var me = this;
        me.target.setSize(width, height);
        me.fireEvent('resize', me, width, height, null);
    },
    
    getEl: function() {
        return this.el;
    },
    
    getTarget: function() {
        return this.target;
    },
    destroy: function() {
        var me = this,
            i,
            handles = me.handles,
            len = handles.length,
            positions = me.possiblePositions,
            handle;
        me.resizeTracker.destroy();
        
        if (me.isTargetWrapped) {
            me.target.destroy();
        }
        for (i = 0; i < len; i++) {
            if ((handle = me[positions[handles[i]]])) {
                handle.destroy();
            }
        }
    }
});


Ext.define('Ext.selection.CellModel', {
    extend: 'Ext.selection.DataViewModel',
    alias: 'selection.cellmodel',
    requires: [
        'Ext.grid.CellContext'
    ],
    
    isCellModel: true,
    
    deselectOnContainerClick: false,
    
    enableKeyNav: true,
    
    preventWrap: false,
    
    
    bindComponent: function(view) {
        var me = this,
            grid;
        
        if (me.view && me.gridListeners) {
            me.gridListeners.destroy();
        }
        
        me.callParent([
            view
        ]);
        if (view) {
            
            
            grid = view.grid || view.ownerCt;
            if (grid.optimizedColumnMove !== false) {
                me.gridListeners = grid.on({
                    columnmove: me.onColumnMove,
                    scope: me,
                    destroyable: true
                });
            }
        }
    },
    getViewListeners: function() {
        var result = this.callParent();
        result.refresh = this.onViewRefresh;
        return result;
    },
    getHeaderCt: function() {
        var selection = this.navigationModel.getPosition(),
            view = selection ? selection.view : this.primaryView;
        return view.headerCt;
    },
    
    onNavigate: function(e) {
        
        if (!e.record) {
            return;
        }
        this.setPosition(e.position);
    },
    selectWithEvent: function(record, e) {
        this.select(record);
    },
    select: function(pos, keepExisting, suppressEvent) {
        var me = this,
            row,
            oldPos = me.getPosition(),
            store = me.view.store;
        if (pos || pos === 0) {
            if (pos.isModel) {
                row = store.indexOf(pos);
                if (row !== -1) {
                    pos = {
                        row: row,
                        column: oldPos ? oldPos.column : 0
                    };
                } else {
                    pos = null;
                }
            } else if (typeof pos === 'number') {
                pos = {
                    row: pos,
                    column: 0
                };
            }
        }
        if (pos) {
            me.selectByPosition(pos, suppressEvent);
        } else {
            me.deselect();
        }
    },
    
    getCurrentPosition: function() {
        
        
        var position = this.selecting ? this.nextSelection : this.selection;
        
        
        
        return position ? {
            view: position.view,
            record: position.record,
            row: position.rowIdx,
            columnHeader: position.column,
            
            
            column: position.view.getColumnManager().indexOf(position.column)
        } : position;
    },
    
    getPosition: function() {
        return (this.selecting ? this.nextSelection : this.selection) || null;
    },
    
    setCurrentPosition: function(pos, suppressEvent, 
    preventCheck) {
        if (pos && !pos.isCellContext) {
            pos = new Ext.grid.CellContext(this.view).setPosition({
                row: pos.row,
                
                
                column: typeof pos.column === 'number' ? this.view.getColumnManager().getColumns()[pos.column] : pos.column
            });
        }
        return this.setPosition(pos, suppressEvent, preventCheck);
    },
    
    setPosition: function(pos, suppressEvent, 
    preventCheck) {
        var me = this,
            last = me.selection;
        
        if (pos) {
            pos = pos.isCellContext ? pos.clone() : new Ext.grid.CellContext(me.view).setPosition(pos);
        }
        if (!preventCheck && last) {
            
            if (pos && (pos.record === last.record && pos.column === last.column && pos.view === last.view)) {
                pos = null;
            } else {
                me.onCellDeselect(me.selection, suppressEvent);
            }
        }
        if (pos) {
            me.nextSelection = pos;
            
            
            me.selecting = true;
            me.onCellSelect(me.nextSelection, suppressEvent);
            me.selecting = false;
            
            return (me.selection = pos);
        }
        
        return null;
    },
    isCellSelected: function(view, row, column) {
        var me = this,
            testPos,
            pos = me.getPosition();
        if (pos && pos.view === view) {
            testPos = new Ext.grid.CellContext(view).setPosition({
                row: row,
                
                
                column: typeof column === 'number' ? view.getColumnManager().getColumns()[column] : column
            });
            return (testPos.record === pos.record) && (testPos.column === pos.column);
        }
    },
    
    onStoreRemove: function(store, records, indices) {
        var me = this,
            pos = me.getPosition();
        me.callParent(arguments);
        if (pos && store.isMoving(pos.record)) {
            return;
        }
        if (pos && store.getCount() && store.indexOf(pos.record) !== -1) {
            pos.setRow(pos.record);
        } else {
            me.selection = null;
        }
    },
    onStoreClear: function() {
        this.callParent(arguments);
        this.selection = null;
    },
    onStoreAdd: function() {
        var me = this,
            pos = me.getPosition();
        me.callParent(arguments);
        if (pos) {
            pos.setRow(pos.record);
        } else {
            me.selection = null;
        }
    },
    
    onCellClick: function(view, cell, cellIndex, record, row, recordIndex, e) {
        
        if (recordIndex !== -1) {
            this.setPosition(e.position);
        }
    },
    
    
    onCellSelect: function(position, supressEvent) {
        if (position && position.rowIdx !== undefined && position.rowIdx > -1) {
            this.doSelect(position.record, 
            false, supressEvent);
        }
    },
    
    
    onCellDeselect: function(position, supressEvent) {
        if (position && position.rowIdx !== undefined) {
            this.doDeselect(position.record, supressEvent);
        }
    },
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            pos, eventName, view, nm;
        if (isSelected) {
            pos = me.nextSelection;
            eventName = 'select';
        } else {
            pos = me.selection;
            eventName = 'deselect';
        }
        
        
        
        view = pos.view || me.primaryView;
        if ((suppressEvent || me.fireEvent('before' + eventName, me, record, pos.rowIdx, pos.colIdx)) !== false && commitFn() !== false) {
            if (isSelected) {
                view.onCellSelect(pos);
            } else {
                view.onCellDeselect(pos);
                delete me.selection;
            }
            if (!suppressEvent) {
                me.fireEvent(eventName, me, record, pos.rowIdx, pos.colIdx);
            }
        }
    },
    onEditorTab: function(editingPlugin, e) {
        var me = this,
            direction = e.shiftKey ? 'left' : 'right',
            pos = editingPlugin.context,
            position = pos.view.walkCells(pos, direction, e, me.preventWrap);
        
        if (position) {
            
            if (editingPlugin.startEdit(position.record, position.column)) {
                me.wasEditing = false;
            } else 
            
            
            {
                position.view.getNavigationModel().setPosition(position, null, e);
                me.wasEditing = true;
            }
        }
    },
    refresh: function() {
        var pos = this.getPosition(),
            selRowIdx;
        
        if (pos && (selRowIdx = this.store.indexOf(this.selected.last())) !== -1) {
            pos.rowIdx = selRowIdx;
        }
    },
    
    onColumnMove: function(headerCt, header, fromIdx, toIdx) {
        var grid = headerCt.up('tablepanel');
        if (grid) {
            this.onViewRefresh(grid.view);
        }
    },
    onUpdate: function(record) {
        var me = this,
            pos;
        if (me.isSelected(record)) {
            pos = me.selecting ? me.nextSelection : me.selection;
            me.view.onCellSelect(pos);
        }
    },
    onViewRefresh: function(view) {
        var me = this,
            pos = me.getPosition(),
            newPos,
            headerCt = view.headerCt,
            record, column;
        
        
        if (pos && pos.view === view) {
            record = pos.record;
            column = pos.column;
            
            if (!column.isDescendantOf(headerCt)) {
                
                
                
                column = headerCt.queryById(column.id) || headerCt.down('[text="' + column.text + '"]') || headerCt.down('[dataIndex="' + column.dataIndex + '"]');
            }
            
            
            
            
            if (pos.record) {
                if (column && (view.store.indexOfId(record.getId()) !== -1)) {
                    newPos = new Ext.grid.CellContext(view).setPosition({
                        row: record,
                        column: column
                    });
                    me.setPosition(newPos);
                }
            } else {
                me.selection = null;
            }
        }
    },
    
    selectByPosition: function(position, suppressEvent) {
        this.setPosition(position, suppressEvent);
    }
});


Ext.define('Ext.selection.CheckboxModel', {
    alias: 'selection.checkboxmodel',
    extend: 'Ext.selection.RowModel',
    
    mode: 'MULTI',
    
    injectCheckbox: 0,
    
    checkOnly: false,
    
    showHeaderCheckbox: undefined,
    
    checkSelector: '.' + Ext.baseCSSPrefix + 'grid-row-checker',
    allowDeselect: true,
    headerWidth: 24,
    
    checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on',
    tdCls: Ext.baseCSSPrefix + 'grid-cell-special ' + Ext.baseCSSPrefix + 'grid-cell-row-checker',
    constructor: function() {
        var me = this;
        me.callParent(arguments);
        
        
        if (me.mode === 'SINGLE' && me.showHeaderCheckbox !== true) {
            me.showHeaderCheckbox = false;
        }
    },
    beforeViewRender: function(view) {
        var me = this,
            owner;
        me.callParent(arguments);
        
        if (!me.hasLockedHeader() || view.headerCt.lockedCt) {
            me.addCheckbox(view, true);
            owner = view.ownerCt;
            
            if (view.headerCt.lockedCt) {
                owner = owner.ownerCt;
            }
            me.mon(owner, 'reconfigure', me.onReconfigure, me);
        }
    },
    bindComponent: function(view) {
        this.sortable = false;
        this.callParent(arguments);
    },
    hasLockedHeader: function() {
        var views = this.views,
            vLen = views.length,
            v;
        for (v = 0; v < vLen; v++) {
            if (views[v].headerCt.lockedCt) {
                return true;
            }
        }
        return false;
    },
    
    addCheckbox: function(view, initial) {
        var me = this,
            checkbox = me.injectCheckbox,
            headerCt = view.headerCt;
        
        if (checkbox !== false) {
            if (checkbox === 'first') {
                checkbox = 0;
            } else if (checkbox === 'last') {
                checkbox = headerCt.getColumnCount();
            }
            Ext.suspendLayouts();
            if (view.getStore().isBufferedStore) {
                me.showHeaderCheckbox = false;
            }
            me.column = headerCt.add(checkbox, me.getHeaderConfig());
            Ext.resumeLayouts();
        }
        if (initial !== true) {
            view.refresh();
        }
    },
    
    onReconfigure: function(grid, store, columns) {
        if (columns) {
            this.addCheckbox(this.views[0]);
        }
    },
    
    toggleUiHeader: function(isChecked) {
        var view = this.views[0],
            headerCt = view.headerCt,
            checkHd = headerCt.child('gridcolumn[isCheckerHd]'),
            cls = this.checkerOnCls;
        if (checkHd) {
            if (isChecked) {
                checkHd.addCls(cls);
            } else {
                checkHd.removeCls(cls);
            }
        }
    },
    
    onHeaderClick: function(headerCt, header, e) {
        if (header === this.column) {
            e.stopEvent();
            var me = this,
                isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on');
            if (isChecked) {
                me.deselectAll();
            } else {
                me.selectAll();
            }
        }
    },
    
    getHeaderConfig: function() {
        var me = this,
            showCheck = me.showHeaderCheckbox !== false;
        return {
            isCheckerHd: showCheck,
            text: '&#160;',
            clickTargetName: 'el',
            width: me.headerWidth,
            sortable: false,
            draggable: false,
            resizable: false,
            hideable: false,
            menuDisabled: true,
            dataIndex: '',
            tdCls: me.tdCls,
            cls: showCheck ? Ext.baseCSSPrefix + 'column-header-checkbox ' : '',
            defaultRenderer: me.renderer.bind(me),
            editRenderer: me.editRenderer || me.renderEmpty,
            locked: me.hasLockedHeader()
        };
    },
    renderEmpty: function() {
        return '&#160;';
    },
    
    refresh: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },
    
    renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
        return '<div class="' + Ext.baseCSSPrefix + 'grid-row-checker" role="presentation">&#160;</div>';
    },
    selectByPosition: function(position, keepExisting) {
        if (!position.isCellContext) {
            position = new Ext.grid.CellContext(this.view).setPosition(position.row, position.column);
        }
        
        if (!this.checkOnly || position.column !== this.column) {
            this.callParent([
                position,
                keepExisting
            ]);
        }
    },
    
    onSelectChange: function() {
        this.callParent(arguments);
        if (!this.suspendChange) {
            this.updateHeaderState();
        }
    },
    
    onStoreLoad: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },
    onStoreAdd: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },
    onStoreRemove: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },
    onStoreRefresh: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },
    maybeFireSelectionChange: function(fireEvent) {
        if (fireEvent && !this.suspendChange) {
            this.updateHeaderState();
        }
        this.callParent(arguments);
    },
    resumeChanges: function() {
        this.callParent();
        if (!this.suspendChange) {
            this.updateHeaderState();
        }
    },
    
    updateHeaderState: function() {
        
        var me = this,
            store = me.store,
            storeCount = store.getCount(),
            views = me.views,
            hdSelectStatus = false,
            selectedCount = 0,
            selected, len, i;
        if (!store.isBufferedStore && storeCount > 0) {
            selected = me.selected;
            hdSelectStatus = true;
            for (i = 0 , len = selected.getCount(); i < len; ++i) {
                if (store.indexOfId(selected.getAt(i).id) === -1) {
                    break;
                }
                ++selectedCount;
            }
            hdSelectStatus = storeCount === selectedCount;
        }
        if (views && views.length) {
            me.toggleUiHeader(hdSelectStatus);
        }
    },
    vetoSelection: function(e) {
        var me = this,
            column = me.column,
            veto, isClick, isSpace;
        if (me.checkOnly) {
            isClick = e.type === 'click' && e.getTarget(me.checkSelector);
            isSpace = e.getKey() === e.SPACE && e.position.column === column;
            veto = !(isClick || isSpace);
        }
        return veto || me.callParent([
            e
        ]);
    },
    destroy: function() {
        this.column = null;
        this.callParent();
    },
    privates: {
        onBeforeNavigate: function(metaEvent) {
            var e = metaEvent.keyEvent;
            if (this.selectionMode !== 'SINGLE') {
                metaEvent.ctrlKey = metaEvent.ctrlKey || e.ctrlKey || (e.type === 'click' && !e.shiftKey) || e.getKey() === e.SPACE;
            }
        },
        selectWithEventMulti: function(record, e, isSelected) {
            var me = this;
            if (!e.shiftKey && !e.ctrlKey && e.getTarget(me.checkSelector)) {
                if (isSelected) {
                    me.doDeselect(record, true);
                } else {
                    me.doSelect(record, true);
                }
            } else {
                me.callParent([
                    record,
                    e,
                    isSelected
                ]);
            }
        }
    }
});


Ext.define('Ext.slider.Thumb', {
    requires: [
        'Ext.dd.DragTracker',
        'Ext.util.Format'
    ],
    overCls: Ext.baseCSSPrefix + 'slider-thumb-over',
    
    
    constructor: function(config) {
        var me = this;
        
        Ext.apply(me, config || {}, {
            cls: Ext.baseCSSPrefix + 'slider-thumb',
            
            constrain: false
        });
        me.callParent([
            config
        ]);
    },
    
    render: function() {
        var me = this;
        me.el = me.slider.innerEl.insertFirst(me.getElConfig());
        me.onRender();
    },
    onRender: function() {
        if (this.disabled) {
            this.disable();
        }
        this.initEvents();
    },
    getElConfig: function() {
        var me = this,
            slider = me.slider,
            style = {};
        style[slider.vertical ? 'bottom' : slider.horizontalProp] = slider.calculateThumbPosition(slider.normalizeValue(me.value)) + '%';
        return {
            style: style,
            id: this.id,
            cls: this.cls,
            role: 'presentation'
        };
    },
    
    move: function(v, animate) {
        var me = this,
            el = me.el,
            slider = me.slider,
            styleProp = slider.vertical ? 'bottom' : slider.horizontalProp,
            to, from, animCfg;
        v += '%';
        if (!animate) {
            el.dom.style[styleProp] = v;
        } else {
            to = {};
            to[styleProp] = v;
            if (!Ext.supports.GetPositionPercentage) {
                from = {};
                from[styleProp] = el.dom.style[styleProp];
            }
            
            animCfg = {
                target: el,
                duration: 350,
                from: from,
                to: to,
                scope: me,
                callback: me.onAnimComplete
            };
            if (animate !== true) {
                Ext.apply(animCfg, animate);
            }
            me.anim = new Ext.fx.Anim(animCfg);
        }
    },
    onAnimComplete: function() {
        this.anim = null;
    },
    
    enable: function() {
        var el = this.el;
        this.disabled = false;
        if (el) {
            el.removeCls(this.slider.disabledCls);
        }
    },
    
    disable: function() {
        var el = this.el;
        this.disabled = true;
        if (el) {
            el.addCls(this.slider.disabledCls);
        }
    },
    
    initEvents: function() {
        var me = this;
        me.tracker = new Ext.dd.DragTracker({
            el: me.el,
            onBeforeStart: me.onBeforeDragStart.bind(me),
            onStart: me.onDragStart.bind(me),
            onDrag: me.onDrag.bind(me),
            onEnd: me.onDragEnd.bind(me),
            tolerance: 3,
            autoStart: 300
        });
        me.el.hover(me.addOverCls, me.removeOverCls, me);
    },
    addOverCls: function() {
        var me = this;
        if (!me.disabled) {
            me.el.addCls(me.overCls);
        }
    },
    removeOverCls: function() {
        this.el.removeCls(this.overCls);
    },
    
    onBeforeDragStart: function(e) {
        var me = this,
            el = me.el,
            trackerXY = me.tracker.getXY(),
            delta = me.pointerOffset = el.getXY();
        if (me.disabled) {
            return false;
        } else {
            
            
            
            delta[0] += Math.floor(el.getWidth() / 2) - trackerXY[0];
            delta[1] += Math.floor(el.getHeight() / 2) - trackerXY[1];
            me.slider.promoteThumb(me);
            return true;
        }
    },
    
    onDragStart: function(e) {
        var me = this,
            slider = me.slider;
        slider.onDragStart(me, e);
        me.el.addCls(Ext.baseCSSPrefix + 'slider-thumb-drag');
        me.dragging = me.slider.dragging = true;
        me.dragStartValue = me.value;
        slider.fireEvent('dragstart', slider, e, me);
    },
    
    onDrag: function(e) {
        var me = this,
            slider = me.slider,
            index = me.index,
            newValue = me.getValueFromTracker(),
            above, below;
        
        if (newValue !== undefined) {
            if (me.constrain) {
                above = slider.thumbs[index + 1];
                below = slider.thumbs[index - 1];
                if (below !== undefined && newValue <= below.value) {
                    newValue = below.value;
                }
                if (above !== undefined && newValue >= above.value) {
                    newValue = above.value;
                }
            }
            slider.setValue(index, newValue, false);
            slider.fireEvent('drag', slider, e, me);
        }
    },
    getValueFromTracker: function() {
        var slider = this.slider,
            trackerXY = this.tracker.getXY(),
            trackPoint;
        trackerXY[0] += this.pointerOffset[0];
        trackerXY[1] += this.pointerOffset[1];
        trackPoint = slider.getTrackpoint(trackerXY);
        
        if (trackPoint !== undefined) {
            return slider.reversePixelValue(trackPoint);
        }
    },
    
    onDragEnd: function(e) {
        var me = this,
            slider = me.slider,
            value = me.value;
        slider.onDragEnd(me, e);
        me.el.removeCls(Ext.baseCSSPrefix + 'slider-thumb-drag');
        me.dragging = slider.dragging = false;
        slider.fireEvent('dragend', slider, e);
        if (me.dragStartValue != value) {
            slider.fireEvent('changecomplete', slider, value, me);
        }
    },
    destroy: function() {
        var me = this,
            anim = this.anim;
        if (anim) {
            anim.end();
        }
        me.el = me.tracker = me.anim = Ext.destroy(me.el, me.tracker);
    }
});


Ext.define('Ext.slider.Tip', {
    extend: 'Ext.tip.Tip',
    minWidth: 10,
    alias: 'widget.slidertip',
    
    offsets: null,
    
    align: null,
    
    position: '',
    defaultVerticalPosition: 'left',
    defaultHorizontalPosition: 'top',
    isSliderTip: true,
    init: function(slider) {
        var me = this,
            align, offsets;
        if (!me.position) {
            me.position = slider.vertical ? me.defaultVerticalPosition : me.defaultHorizontalPosition;
        }
        switch (me.position) {
            case 'top':
                offsets = [
                    0,
                    -10
                ];
                align = 'b-t?';
                break;
            case 'bottom':
                offsets = [
                    0,
                    10
                ];
                align = 't-b?';
                break;
            case 'left':
                offsets = [
                    -10,
                    0
                ];
                align = 'r-l?';
                break;
            case 'right':
                offsets = [
                    10,
                    0
                ];
                align = 'l-r?';
        }
        if (!me.align) {
            me.align = align;
        }
        if (!me.offsets) {
            me.offsets = offsets;
        }
        slider.on({
            scope: me,
            dragstart: me.onSlide,
            drag: me.onSlide,
            dragend: me.hide,
            destroy: me.destroy
        });
    },
    
    onSlide: function(slider, e, thumb) {
        var me = this;
        me.show();
        me.update(me.getText(thumb));
        me.el.alignTo(thumb.el, me.align, me.offsets);
    },
    
    getText: function(thumb) {
        return String(thumb.value);
    }
});


Ext.define('Ext.slider.Multi', {
    extend: 'Ext.form.field.Base',
    alias: 'widget.multislider',
    alternateClassName: 'Ext.slider.MultiSlider',
    requires: [
        'Ext.slider.Thumb',
        'Ext.slider.Tip',
        'Ext.Number',
        'Ext.util.Format',
        'Ext.Template'
    ],
    focusable: true,
    needArrowKeys: true,
    tabIndex: 0,
    focusCls: 'slider-focus',
    childEls: [
        'endEl',
        'innerEl'
    ],
    
    fieldSubTpl: [
        '<div id="{id}" role="{role}" {inputAttrTpl} class="' + Ext.baseCSSPrefix + 'slider {fieldCls} {vertical}',
        '{childElCls}"',
        '<tpl if="tabIdx != null"> tabindex="{tabIdx}"</tpl>',
        '<tpl if="isVertical"> aria-orientation="vertical"<tpl else> aria-orientation="horizontal"</tpl>',
        '>',
        '<div id="{cmpId}-endEl" data-ref="endEl" class="' + Ext.baseCSSPrefix + 'slider-end" role="presentation">',
        '<div id="{cmpId}-innerEl" data-ref="innerEl" class="' + Ext.baseCSSPrefix + 'slider-inner" role="presentation">',
        '{%this.renderThumbs(out, values)%}',
        '</div>',
        '</div>',
        '</div>',
        {
            renderThumbs: function(out, values) {
                var me = values.$comp,
                    i = 0,
                    thumbs = me.thumbs,
                    len = thumbs.length,
                    thumb, thumbConfig;
                for (; i < len; i++) {
                    thumb = thumbs[i];
                    thumbConfig = thumb.getElConfig();
                    thumbConfig.id = me.id + '-thumb-' + i;
                    Ext.DomHelper.generateMarkup(thumbConfig, out);
                }
            },
            disableFormats: true
        }
    ],
    horizontalProp: 'left',
    
    
    
    vertical: false,
    
    minValue: 0,
    
    maxValue: 100,
    
    decimalPrecision: 0,
    
    keyIncrement: 1,
    
    increment: 0,
    
    
    clickRange: [
        5,
        15
    ],
    
    clickToChange: true,
    
    animate: true,
    
    dragging: false,
    
    constrainThumbs: true,
    
    useTips: true,
    
    tipText: null,
    ariaRole: 'slider',
    
    defaultBindProperty: 'values',
    
    
    
    
    
    
    
    initValue: function() {
        var me = this,
            extValueFrom = Ext.valueFrom,
            
            values = extValueFrom(me.values, [
                extValueFrom(me.value, extValueFrom(me.minValue, 0))
            ]),
            i = 0,
            len = values.length;
        
        me.originalValue = values;
        
        for (; i < len; i++) {
            me.addThumb(me.normalizeValue(values[i]));
        }
    },
    
    initComponent: function() {
        var me = this,
            tipPlug, hasTip, p, pLen, plugins;
        
        me.thumbs = [];
        me.keyIncrement = Math.max(me.increment, me.keyIncrement);
        me.extraFieldBodyCls = Ext.baseCSSPrefix + 'slider-ct-' + (me.vertical ? 'vert' : 'horz');
        me.callParent();
        
        if (me.useTips) {
            if (Ext.isObject(me.useTips)) {
                tipPlug = Ext.apply({}, me.useTips);
            } else {
                tipPlug = me.tipText ? {
                    getText: me.tipText
                } : {};
            }
            plugins = me.plugins = me.plugins || [];
            pLen = plugins.length;
            for (p = 0; p < pLen; p++) {
                if (plugins[p].isSliderTip) {
                    hasTip = true;
                    break;
                }
            }
            if (!hasTip) {
                me.plugins.push(new Ext.slider.Tip(tipPlug));
            }
        }
    },
    
    addThumb: function(value) {
        var me = this,
            thumb = new Ext.slider.Thumb({
                ownerCt: me,
                value: value,
                slider: me,
                index: me.thumbs.length,
                constrain: me.constrainThumbs,
                disabled: !!me.readOnly
            });
        me.thumbs.push(thumb);
        
        if (me.rendered) {
            thumb.render();
        }
        return thumb;
    },
    
    promoteThumb: function(topThumb) {
        var thumbs = this.thumbStack || (this.thumbStack = Ext.Array.slice(this.thumbs)),
            ln = thumbs.length,
            zIndex = 10000,
            i;
        
        if (thumbs[0] !== topThumb) {
            Ext.Array.remove(thumbs, topThumb);
            thumbs.unshift(topThumb);
        }
        
        for (i = 0; i < ln; i++) {
            thumbs[i].el.setStyle('zIndex', zIndex);
            zIndex -= 1000;
        }
    },
    
    getSubTplData: function(fieldData) {
        var me = this;
        return Ext.apply(me.callParent(arguments), {
            $comp: me,
            isVertical: me.vertical,
            vertical: me.vertical ? Ext.baseCSSPrefix + 'slider-vert' : Ext.baseCSSPrefix + 'slider-horz',
            minValue: me.minValue,
            maxValue: me.maxValue,
            value: me.value,
            tabIdx: me.tabIndex,
            childElCls: ''
        });
    },
    onRender: function() {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            i = 0,
            thumb;
        me.callParent(arguments);
        for (i = 0; i < len; i++) {
            thumb = thumbs[i];
            thumb.el = me.el.getById(me.id + '-thumb-' + i);
            thumb.onRender();
        }
    },
    
    initEvents: function() {
        var me = this;
        me.callParent();
        me.mon(me.el, {
            scope: me,
            mousedown: me.onMouseDown,
            keydown: me.onKeyDown
        });
    },
    onDragStart: Ext.emptyFn,
    onDragEnd: Ext.emptyFn,
    
    getTrackpoint: function(xy) {
        var me = this,
            vertical = me.vertical,
            sliderTrack = me.innerEl,
            trackLength, result, positionProperty;
        if (vertical) {
            positionProperty = 'top';
            trackLength = sliderTrack.getHeight();
        } else {
            positionProperty = me.horizontalProp;
            trackLength = sliderTrack.getWidth();
        }
        xy = me.transformTrackPoints(sliderTrack.translatePoints(xy));
        result = Ext.Number.constrain(xy[positionProperty], 0, trackLength);
        return vertical ? trackLength - result : result;
    },
    transformTrackPoints: Ext.identityFn,
    
    
    
    checkChange: Ext.emptyFn,
    
    onMouseDown: function(e) {
        var me = this,
            thumbClicked = false,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            trackPoint;
        if (me.disabled) {
            return;
        }
        
        for (; !thumbClicked && i < len; i++) {
            thumbClicked = thumbClicked || e.target == thumbs[i].el.dom;
        }
        if (me.clickToChange && !thumbClicked) {
            trackPoint = me.getTrackpoint(e.getXY());
            if (trackPoint !== undefined) {
                me.onClickChange(trackPoint);
            }
        }
        me.focus();
    },
    
    onClickChange: function(trackPoint) {
        var me = this,
            thumb, index;
        
        
        
        thumb = me.getNearest(trackPoint);
        if (!thumb.disabled) {
            index = thumb.index;
            me.setValue(index, Ext.util.Format.round(me.reversePixelValue(trackPoint), me.decimalPrecision), undefined, true);
        }
    },
    
    getNearest: function(trackPoint) {
        var me = this,
            clickValue = me.reversePixelValue(trackPoint),
            nearestDistance = me.getRange() + 5,
            
            nearest = null,
            thumbs = me.thumbs,
            i = 0,
            len = thumbs.length,
            thumb, value, dist;
        for (; i < len; i++) {
            thumb = me.thumbs[i];
            value = thumb.value;
            dist = Math.abs(value - clickValue);
            if (Math.abs(dist) <= nearestDistance) {
                nearest = thumb;
                nearestDistance = dist;
            }
        }
        return nearest;
    },
    
    onKeyDown: function(e) {
        
        var me = this,
            k, val;
        if (me.disabled || me.thumbs.length !== 1) {
            e.preventDefault();
            return;
        }
        k = e.getKey();
        switch (k) {
            case e.UP:
            case e.RIGHT:
                e.stopEvent();
                val = e.ctrlKey ? me.maxValue : me.getValue(0) + me.keyIncrement;
                me.setValue(0, val, undefined, true);
                break;
            case e.DOWN:
            case e.LEFT:
                e.stopEvent();
                val = e.ctrlKey ? me.minValue : me.getValue(0) - me.keyIncrement;
                me.setValue(0, val, undefined, true);
                break;
        }
    },
    
    normalizeValue: function(v) {
        var me = this,
            snapFn = me.zeroBasedSnapping ? 'snap' : 'snapInRange';
        v = Ext.Number[snapFn](v, me.increment, me.minValue, me.maxValue);
        v = Ext.util.Format.round(v, me.decimalPrecision);
        v = Ext.Number.constrain(v, me.minValue, me.maxValue);
        return v;
    },
    
    setMinValue: function(val) {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb, i;
        me.minValue = val;
        for (i = 0; i < len; ++i) {
            thumb = thumbs[i];
            if (thumb.value < val) {
                me.setValue(i, val, false);
            }
        }
        me.syncThumbs();
    },
    
    setMaxValue: function(val) {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb, i;
        me.maxValue = val;
        for (i = 0; i < len; ++i) {
            thumb = thumbs[i];
            if (thumb.value > val) {
                me.setValue(i, val, false);
            }
        }
        me.syncThumbs();
    },
    
    setValue: function(index, value, animate, changeComplete) {
        var me = this,
            thumbs = me.thumbs,
            thumb, len, i, values;
        if (Ext.isArray(index)) {
            values = index;
            animate = value;
            for (i = 0 , len = values.length; i < len; ++i) {
                thumb = thumbs[i];
                if (thumb) {
                    me.setValue(i, values[i], animate);
                }
            }
            return me;
        }
        thumb = me.thumbs[index];
        
        value = me.normalizeValue(value);
        if (value !== thumb.value && me.fireEvent('beforechange', me, value, thumb.value, thumb) !== false) {
            thumb.value = value;
            if (me.rendered) {
                if (Ext.isDefined(animate)) {
                    animate = animate === false ? false : animate;
                } else {
                    animate = me.animate;
                }
                thumb.move(me.calculateThumbPosition(value), animate);
                me.fireEvent('change', me, value, thumb);
                me.checkDirty();
                if (changeComplete) {
                    me.fireEvent('changecomplete', me, value, thumb);
                }
            }
        }
        return me;
    },
    
    calculateThumbPosition: function(v) {
        var me = this,
            minValue = me.minValue,
            pos = (v - minValue) / me.getRange() * 100;
        if (isNaN(pos)) {
            pos = 0;
        }
        return pos;
    },
    
    getRatio: function() {
        var me = this,
            innerEl = me.innerEl,
            trackLength = me.vertical ? innerEl.getHeight() : innerEl.getWidth(),
            valueRange = me.getRange();
        return valueRange === 0 ? trackLength : (trackLength / valueRange);
    },
    getRange: function() {
        return this.maxValue - this.minValue;
    },
    
    reversePixelValue: function(pos) {
        return this.minValue + (pos / this.getRatio());
    },
    
    reversePercentageValue: function(pos) {
        return this.minValue + this.getRange() * (pos / 100);
    },
    
    onDisable: function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb, el, xy;
        me.callParent();
        for (; i < len; i++) {
            thumb = thumbs[i];
            el = thumb.el;
            thumb.disable();
            if (Ext.isIE) {
                
                
                xy = el.getXY();
                el.hide();
                me.innerEl.addCls(me.disabledCls).dom.disabled = true;
                if (!me.thumbHolder) {
                    me.thumbHolder = me.endEl.createChild({
                        role: 'presentation',
                        cls: Ext.baseCSSPrefix + 'slider-thumb ' + me.disabledCls
                    });
                }
                me.thumbHolder.show().setXY(xy);
            }
        }
    },
    
    onEnable: function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb, el;
        this.callParent();
        for (; i < len; i++) {
            thumb = thumbs[i];
            el = thumb.el;
            thumb.enable();
            if (Ext.isIE) {
                me.innerEl.removeCls(me.disabledCls).dom.disabled = false;
                if (me.thumbHolder) {
                    me.thumbHolder.hide();
                }
                el.show();
                me.syncThumbs();
            }
        }
    },
    
    syncThumbs: function() {
        if (this.rendered) {
            var thumbs = this.thumbs,
                length = thumbs.length,
                i = 0;
            for (; i < length; i++) {
                thumbs[i].move(this.calculateThumbPosition(thumbs[i].value));
            }
        }
    },
    
    getValue: function(index) {
        return Ext.isNumber(index) ? this.thumbs[index].value : this.getValues();
    },
    
    getValues: function() {
        var values = [],
            i = 0,
            thumbs = this.thumbs,
            len = thumbs.length;
        for (; i < len; i++) {
            values.push(thumbs[i].value);
        }
        return values;
    },
    getSubmitValue: function() {
        var me = this;
        return (me.disabled || !me.submitValue) ? null : me.getValue();
    },
    reset: function() {
        var me = this,
            arr = [].concat(me.originalValue),
            a = 0,
            aLen = arr.length,
            val;
        for (; a < aLen; a++) {
            val = arr[a];
            me.setValue(a, val);
        }
        me.clearInvalid();
        
        delete me.wasValid;
    },
    setReadOnly: function(readOnly) {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            i = 0;
        me.callParent(arguments);
        readOnly = me.readOnly;
        for (; i < len; ++i) {
            if (readOnly) {
                thumbs[i].disable();
            } else {
                thumbs[i].enable();
            }
        }
    },
    
    beforeDestroy: function() {
        var me = this,
            thumbs = me.thumbs,
            t = 0,
            tLen = thumbs.length,
            thumb;
        if (me.rendered) {
            for (; t < tLen; t++) {
                thumb = thumbs[t];
                Ext.destroy(thumb);
            }
        }
        me.callParent();
    }
});


Ext.define('Ext.slider.Single', {
    extend: 'Ext.slider.Multi',
    alias: [
        'widget.slider',
        'widget.sliderfield'
    ],
    alternateClassName: [
        'Ext.Slider',
        'Ext.form.SliderField',
        'Ext.slider.SingleSlider',
        'Ext.slider.Slider'
    ],
    
    defaultBindProperty: 'value',
    initComponent: function() {
        if (this.publishOnComplete) {
            this.valuePublishEvent = 'changecomplete';
        }
        this.callParent();
    },
    
    publishOnComplete: true,
    
    getValue: function() {
        
        return this.callParent([
            0
        ]);
    },
    
    setValue: function(value, animate) {
        var args = arguments,
            len = args.length;
        
        
        
        if (len == 1 || (len <= 3 && typeof args[1] != 'number')) {
            args = Ext.toArray(args);
            args.unshift(0);
        }
        return this.callParent(args);
    },
    
    getNearest: function() {
        
        return this.thumbs[0];
    }
});


Ext.define('Ext.slider.Widget', {
    extend: 'Ext.Widget',
    alias: 'widget.sliderwidget',
    
    requires: [
        'Ext.slider.Multi'
    ],
    cachedConfig: {
        
        vertical: false
    },
    config: {
        
        clickToChange: true,
        ui: 'widget',
        
        value: 0,
        
        minValue: 0,
        
        maxValue: 100,
        
        publishOnComplete: true,
        
        twoWayBindable: {
            value: 1
        }
    },
    decimalPrecision: 0,
    defaultBindProperty: 'value',
    element: {
        reference: 'element',
        cls: Ext.baseCSSPrefix + 'slider',
        listeners: {
            mousedown: 'onMouseDown',
            dragstart: 'cancelDrag',
            drag: 'cancelDrag',
            dragend: 'cancelDrag'
        },
        children: [
            {
                reference: 'endEl',
                cls: Ext.baseCSSPrefix + 'slider-end',
                children: [
                    {
                        reference: 'innerEl',
                        cls: Ext.baseCSSPrefix + 'slider-inner'
                    }
                ]
            }
        ]
    },
    thumbCls: Ext.baseCSSPrefix + 'slider-thumb',
    horizontalProp: 'left',
    
    
    animateOnSetValue: undefined,
    applyValue: function(value) {
        var me = this,
            animate = me.animateOnSetValue,
            i, len;
        if (Ext.isArray(value)) {
            value = Ext.Array.from(value);
            for (i = 0 , len = value.length; i < len; ++i) {
                me.setThumbValue(i, value[i] = me.normalizeValue(value[i]), animate, true);
            }
        } else {
            value = me.normalizeValue(value);
            me.setThumbValue(0, value, animate, true);
        }
        return value;
    },
    updateVertical: function(vertical, oldVertical) {
        this.element.removeCls(Ext.baseCSSPrefix + 'slider-' + (oldVertical ? 'vert' : 'horz'));
        this.element.addCls(Ext.baseCSSPrefix + 'slider-' + (vertical ? 'vert' : 'horz'));
    },
    doSetHeight: function(height) {
        this.callParent(arguments);
        this.endEl.dom.style.height = this.innerEl.dom.style.height = '100%';
    },
    cancelDrag: function(e) {
        
        e.stopPropagation();
    },
    getThumb: function(ordinal) {
        var me = this,
            thumbConfig,
            result = (me.thumbs || (me.thumbs = []))[ordinal];
        if (!result) {
            thumbConfig = {
                cls: me.thumbCls,
                style: {}
            };
            thumbConfig['data-thumbIndex'] = ordinal;
            result = me.thumbs[ordinal] = me.innerEl.createChild(thumbConfig);
        }
        return result;
    },
    getThumbPositionStyle: function() {
        return this.getVertical() ? 'bottom' : (this.rtl && Ext.rtl ? 'right' : 'left');
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    update: function() {
        var me = this,
            values = me.getValue(),
            len = values.length,
            i;
        for (i = 0; i < len; i++) {
            this.thumbs[i].dom.style[me.getThumbPositionStyle()] = me.calculateThumbPosition(values[i]) + '%';
        }
    },
    onMouseDown: function(e) {
        var me = this,
            thumb,
            trackPoint = e.getXY(),
            delta;
        if (!me.disabled && e.button === 0) {
            
            Ext.getDoc().on({
                scope: me,
                capture: true,
                selectstart: me.stopSelect
            });
            thumb = e.getTarget('.' + me.thumbCls, null, true);
            if (thumb) {
                me.animateOnSetValue = false;
                me.promoteThumb(thumb);
                me.captureMouse(me.onMouseMove, me.onMouseUp, [
                    thumb
                ], 1);
                delta = me.pointerOffset = thumb.getXY();
                
                
                
                delta[0] += Math.floor(thumb.getWidth() / 2) - trackPoint[0];
                delta[1] += Math.floor(thumb.getHeight() / 2) - trackPoint[1];
            } else {
                if (me.getClickToChange()) {
                    trackPoint = me.getTrackpoint(trackPoint);
                    if (trackPoint != null) {
                        me.onClickChange(trackPoint);
                    }
                }
            }
        }
    },
    
    onClickChange: function(trackPoint) {
        var me = this,
            thumb, index, value;
        
        
        
        thumb = me.getNearest(trackPoint);
        index = parseInt(thumb.getAttribute('data-thumbIndex'), 10);
        value = Ext.util.Format.round(me.reversePixelValue(trackPoint), me.decimalPrecision);
        if (index) {
            me.setThumbValue(index, value, undefined, true);
        } else {
            me.setValue(value);
        }
    },
    
    getNearest: function(trackPoint) {
        var me = this,
            clickValue = me.reversePixelValue(trackPoint),
            nearestDistance = me.getRange() + 5,
            
            nearest = null,
            thumbs = me.thumbs,
            i = 0,
            len = thumbs.length,
            thumb, value, dist;
        for (; i < len; i++) {
            thumb = thumbs[i];
            value = me.reversePercentageValue(parseInt(thumb.dom.style[me.getThumbPositionStyle()], 10));
            dist = Math.abs(value - clickValue);
            if (Math.abs(dist) <= nearestDistance) {
                nearest = thumb;
                nearestDistance = dist;
            }
        }
        return nearest;
    },
    
    promoteThumb: function(topThumb) {
        var thumbs = this.thumbStack || (this.thumbStack = Ext.Array.slice(this.thumbs)),
            ln = thumbs.length,
            zIndex = 10000,
            i;
        
        if (thumbs[0] !== topThumb) {
            Ext.Array.remove(thumbs, topThumb);
            thumbs.unshift(topThumb);
        }
        
        for (i = 0; i < ln; i++) {
            thumbs[i].el.setStyle('zIndex', zIndex);
            zIndex -= 1000;
        }
    },
    doMouseMove: function(e, thumb, changeComplete) {
        var me = this,
            trackerXY = e.getXY(),
            newValue, thumbIndex, trackPoint;
        trackerXY[0] += me.pointerOffset[0];
        trackerXY[1] += me.pointerOffset[1];
        trackPoint = me.getTrackpoint(trackerXY);
        
        if (trackPoint) {
            newValue = me.reversePixelValue(trackPoint);
            thumbIndex = parseInt(thumb.getAttribute('data-thumbIndex'), 10);
            if (thumbIndex || (!changeComplete && me.getPublishOnComplete())) {
                me.setThumbValue(thumbIndex, newValue, false, changeComplete);
            } else {
                me.setValue(newValue);
            }
        }
    },
    onMouseMove: function(e, thumb) {
        this.doMouseMove(e, thumb, false);
    },
    onMouseUp: function(e, thumb) {
        var me = this;
        me.doMouseMove(e, thumb, true);
        Ext.getDoc().un({
            scope: me,
            capture: true,
            selectstart: me.stopSelect
        });
        delete me.animateOnSetValue;
    },
    
    stopSelect: function(e) {
        e.stopEvent();
        return false;
    },
    
    setThumbValue: function(index, value, animate, changeComplete) {
        var me = this,
            thumb, thumbValue, len, i, values;
        if (Ext.isArray(index)) {
            values = index;
            animate = value;
            for (i = 0 , len = values.length; i < len; ++i) {
                me.setThumbValue(i, values[i], animate, changeComplete);
            }
            return me;
        }
        thumb = me.getThumb(index);
        thumbValue = me.reversePercentageValue(parseInt(thumb.dom.style[me.getThumbPositionStyle()], 10));
        
        value = me.normalizeValue(value);
        if (value !== thumbValue && me.fireEvent('beforechange', me, value, thumbValue, thumb) !== false) {
            if (me.element.dom) {
                
                
                me.element.set({
                    'aria-valuenow': value,
                    'aria-valuetext': value
                });
                me.moveThumb(thumb, me.calculateThumbPosition(value), Ext.isDefined(animate) ? animate !== false : me.animate);
                me.fireEvent('change', me, value, thumb);
            }
        }
        return me;
    },
    
    getValue: function(index) {
        var me = this,
            value;
        if (Ext.isNumber(index)) {
            value = me.thumbs[index].dom.style[me.getThumbPositionStyle()];
            value = me.reversePercentageValue(parseInt(value, 10));
        } else {
            value = me.getValues();
            if (value.length === 1) {
                value = value[0];
            }
        }
        return value;
    },
    
    getValues: function() {
        var me = this,
            values = [],
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length;
        for (; i < len; i++) {
            values.push(me.reversePercentageValue(parseInt(me.thumbs[i].dom.style[me.getThumbPositionStyle()], 10)));
        }
        return values;
    },
    
    moveThumb: function(thumb, v, animate) {
        var me = this,
            styleProp = me.getThumbPositionStyle(),
            to, from;
        v += '%';
        if (!animate) {
            thumb.dom.style[styleProp] = v;
        } else {
            to = {};
            to[styleProp] = v;
            if (!Ext.supports.GetPositionPercentage) {
                from = {};
                from[styleProp] = thumb.dom.style[styleProp];
            }
            new Ext.fx.Anim({
                target: thumb,
                duration: 350,
                from: from,
                to: to
            });
        }
    },
    
    normalizeValue: function(v) {
        var me = this,
            snapFn = me.zeroBasedSnapping ? 'snap' : 'snapInRange';
        v = Ext.Number[snapFn](v, me.increment, me.minValue, me.maxValue);
        v = Ext.util.Format.round(v, me.decimalPrecision);
        v = Ext.Number.constrain(v, me.minValue, me.maxValue);
        return v;
    },
    
    getTrackpoint: function(xy) {
        var me = this,
            vertical = me.getVertical(),
            sliderTrack = me.innerEl,
            trackLength, result, positionProperty;
        if (vertical) {
            positionProperty = 'top';
            trackLength = sliderTrack.getHeight();
        } else {
            positionProperty = 'left';
            trackLength = sliderTrack.getWidth();
        }
        xy = me.transformTrackPoints(sliderTrack.translatePoints(xy));
        result = Ext.Number.constrain(xy[positionProperty], 0, trackLength);
        return vertical ? trackLength - result : result;
    },
    transformTrackPoints: Ext.identityFn,
    
    calculateThumbPosition: function(v) {
        var me = this,
            pos = (v - me.getMinValue()) / me.getRange() * 100;
        if (isNaN(pos)) {
            pos = 0;
        }
        return pos;
    },
    
    getRatio: function() {
        var me = this,
            innerEl = me.innerEl,
            trackLength = me.getVertical() ? innerEl.getHeight() : innerEl.getWidth(),
            valueRange = me.getRange();
        return valueRange === 0 ? trackLength : (trackLength / valueRange);
    },
    getRange: function() {
        return this.getMaxValue() - this.getMinValue();
    },
    
    reversePixelValue: function(pos) {
        return this.getMinValue() + (pos / this.getRatio());
    },
    
    reversePercentageValue: function(pos) {
        return this.getMinValue() + this.getRange() * (pos / 100);
    },
    captureMouse: function(onMouseMove, onMouseUp, args, appendArgs) {
        var me = this,
            onMouseupWrap, listeners;
        onMouseMove = onMouseMove && Ext.Function.bind(onMouseMove, me, args, appendArgs);
        onMouseUp = onMouseUp && Ext.Function.bind(onMouseUp, me, args, appendArgs);
        onMouseupWrap = function() {
            Ext.getDoc().un(listeners);
            if (onMouseUp) {
                onMouseUp.apply(me, arguments);
            }
        };
        listeners = {
            mousemove: onMouseMove,
            mouseup: onMouseupWrap
        };
        
        Ext.getDoc().on(listeners);
    }
});

Ext.define('Ext.sparkline.Shape', {
    constructor: function(target, id, type, args) {
        this.target = target;
        this.id = id;
        this.type = type;
        this.args = args;
    },
    append: function() {
        this.target.appendShape(this);
        return this;
    }
});

Ext.define('Ext.sparkline.CanvasBase', {
    requires: [
        'Ext.sparkline.Shape'
    ],
    shapeCount: 0,
    _pxregex: /(\d+)(px)?\s*$/i,
    setWidth: function(width) {
        this.pixelWidth = width;
    },
    setHeight: function(height) {
        this.pixelHeight = height;
    },
    drawLine: function(x1, y1, x2, y2, lineColor, lineWidth) {
        return this.drawShape([
            [
                x1,
                y1
            ],
            [
                x2,
                y2
            ]
        ], lineColor, lineWidth);
    },
    drawShape: function(path, lineColor, fillColor, lineWidth) {
        return this._genShape('Shape', [
            path,
            lineColor,
            fillColor,
            lineWidth
        ]);
    },
    drawCircle: function(x, y, radius, lineColor, fillColor, lineWidth) {
        return this._genShape('Circle', [
            x,
            y,
            radius,
            lineColor,
            fillColor,
            lineWidth
        ]);
    },
    drawPieSlice: function(x, y, radius, startAngle, endAngle, lineColor, fillColor) {
        return this._genShape('PieSlice', [
            x,
            y,
            radius,
            startAngle,
            endAngle,
            lineColor,
            fillColor
        ]);
    },
    drawRect: function(x, y, width, height, lineColor, fillColor) {
        return this._genShape('Rect', [
            x,
            y,
            width,
            height,
            lineColor,
            fillColor
        ]);
    },
    getElement: function() {
        return this.el;
    },
    
    getLastShapeId: function() {
        return this.lastShapeId;
    },
    
    reset: function() {
        alert('reset not implemented');
    },
    
    _genShape: function(shapetype, shapeargs) {
        var id = this.shapeCount++;
        shapeargs.unshift(id);
        return new Ext.sparkline.Shape(this,id,shapetype,shapeargs);
    },
    
    appendShape: function(shape) {
        alert('appendShape not implemented');
    },
    
    replaceWithShape: function(shapeid, shape) {
        alert('replaceWithShape not implemented');
    },
    
    insertAfterShape: function(shapeid, shape) {
        alert('insertAfterShape not implemented');
    },
    
    removeShapeId: function(shapeid) {
        alert('removeShapeId not implemented');
    },
    
    getShapeAt: function(x, y) {
        alert('getShapeAt not implemented');
    },
    
    render: function() {
        alert('render not implemented');
    }
});


Ext.define('Ext.sparkline.CanvasCanvas', {
    extend: 'Ext.sparkline.CanvasBase',
    constructor: function(ownerSparkLine) {
        this.owner = ownerSparkLine;
    },
    setWidth: function(width) {
        this.callParent(arguments);
        this.owner.element.dom.width = width;
    },
    setHeight: function(height) {
        this.callParent(arguments);
        this.owner.element.dom.height = height;
    },
    onOwnerUpdate: function() {
        var me = this;
        me.el = me.owner.element;
        me.interact = !me.owner.initialConfig.disableInteraction;
        me.shapes = {};
        me.shapeseq = [];
        me.currentTargetShapeId = me.lastShapeId = null;
    },
    _getContext: function(lineColor, fillColor, lineWidth) {
        var context = this.el.dom.getContext('2d');
        if (lineColor != null) {
            context.strokeStyle = lineColor;
        }
        context.lineWidth = lineWidth || 1;
        if (fillColor != null) {
            context.fillStyle = fillColor;
        }
        return context;
    },
    reset: function() {
        var context = this._getContext();
        context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
        this.shapes = {};
        this.shapeseq = [];
        this.currentTargetShapeId = this.lastShapeId = null;
    },
    _drawShape: function(shapeid, path, lineColor, fillColor, lineWidth) {
        var context = this._getContext(lineColor, fillColor, lineWidth),
            i, plen;
        context.beginPath();
        context.moveTo(path[0][0] + 0.5, path[0][1] + 0.5);
        for (i = 1 , plen = path.length; i < plen; i++) {
            context.lineTo(path[i][0] + 0.5, path[i][1] + 0.5);
        }
        
        if (lineColor != null) {
            context.stroke();
        }
        if (fillColor != null) {
            context.fill();
        }
        if (this.targetX != null && this.targetY != null && context.isPointInPath(this.targetX, this.targetY)) {
            this.currentTargetShapeId = shapeid;
        }
    },
    _drawCircle: function(shapeid, x, y, radius, lineColor, fillColor, lineWidth) {
        var context = this._getContext(lineColor, fillColor, lineWidth);
        context.beginPath();
        context.arc(x, y, radius, 0, 2 * Math.PI, false);
        if (this.targetX != null && this.targetY != null && context.isPointInPath(this.targetX, this.targetY)) {
            this.currentTargetShapeId = shapeid;
        }
        if (lineColor !== undefined) {
            context.stroke();
        }
        if (fillColor !== undefined) {
            context.fill();
        }
    },
    _drawPieSlice: function(shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) {
        var context = this._getContext(lineColor, fillColor);
        context.beginPath();
        context.moveTo(x, y);
        context.arc(x, y, radius, startAngle, endAngle, false);
        context.lineTo(x, y);
        context.closePath();
        if (lineColor != null) {
            context.stroke();
        }
        if (fillColor) {
            context.fill();
        }
        if (this.targetX !== undefined && this.targetY !== undefined && context.isPointInPath(this.targetX, this.targetY)) {
            this.currentTargetShapeId = shapeid;
        }
    },
    _drawRect: function(shapeid, x, y, width, height, lineColor, fillColor) {
        return this._drawShape(shapeid, [
            [
                x,
                y
            ],
            [
                x + width,
                y
            ],
            [
                x + width,
                y + height
            ],
            [
                x,
                y + height
            ],
            [
                x,
                y
            ]
        ], lineColor, fillColor);
    },
    appendShape: function(shape) {
        this.shapes[shape.id] = shape;
        this.shapeseq.push(shape.id);
        this.lastShapeId = shape.id;
        return shape.id;
    },
    replaceWithShape: function(shapeid, shape) {
        var shapeseq = this.shapeseq,
            i;
        this.shapes[shape.id] = shape;
        for (i = shapeseq.length; i--; ) {
            if (shapeseq[i] == shapeid) {
                shapeseq[i] = shape.id;
            }
        }
        delete this.shapes[shapeid];
    },
    replaceWithShapes: function(shapeids, shapes) {
        var shapeseq = this.shapeseq,
            shapemap = {},
            sid, i, first;
        for (i = shapeids.length; i--; ) {
            shapemap[shapeids[i]] = true;
        }
        for (i = shapeseq.length; i--; ) {
            sid = shapeseq[i];
            if (shapemap[sid]) {
                shapeseq.splice(i, 1);
                delete this.shapes[sid];
                first = i;
            }
        }
        for (i = shapes.length; i--; ) {
            shapeseq.splice(first, 0, shapes[i].id);
            this.shapes[shapes[i].id] = shapes[i];
        }
    },
    insertAfterShape: function(shapeid, shape) {
        var shapeseq = this.shapeseq,
            i;
        for (i = shapeseq.length; i--; ) {
            if (shapeseq[i] === shapeid) {
                shapeseq.splice(i + 1, 0, shape.id);
                this.shapes[shape.id] = shape;
                return;
            }
        }
    },
    removeShapeId: function(shapeid) {
        var shapeseq = this.shapeseq,
            i;
        for (i = shapeseq.length; i--; ) {
            if (shapeseq[i] === shapeid) {
                shapeseq.splice(i, 1);
                break;
            }
        }
        delete this.shapes[shapeid];
    },
    getShapeAt: function(x, y) {
        this.targetX = x;
        this.targetY = y;
        this.render();
        return this.currentTargetShapeId;
    },
    render: function() {
        var shapeseq = this.shapeseq,
            shapes = this.shapes,
            shapeCount = shapeseq.length,
            context = this._getContext(),
            shapeid, shape, i;
        context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
        for (i = 0; i < shapeCount; i++) {
            shapeid = shapeseq[i];
            shape = shapes[shapeid];
            this['_draw' + shape.type].apply(this, shape.args);
        }
        if (!this.interact) {
            
            this.shapes = {};
            this.shapeseq = [];
        }
    }
});


Ext.define('Ext.sparkline.VmlCanvas', {
    extend: 'Ext.sparkline.CanvasBase',
    constructor: function(ownerSparkLine) {
        var me = this;
        me.owner = ownerSparkLine;
        ownerSparkLine.element = {
            tag: 'span',
            reference: 'element',
            listeners: {
                mouseenter: 'onMouseEnter',
                mouseleave: 'onMouseLeave',
                mousemove: 'onMouseMove'
            },
            style: {
                display: 'inline-block',
                position: 'relative',
                overflow: 'hidden',
                margin: '0px',
                padding: '0px',
                verticalAlign: 'top',
                cursor: 'default'
            },
            children: [
                {
                    tag: 'svml:group',
                    reference: 'groupEl',
                    coordorigin: '0 0',
                    coordsize: '0 0',
                    style: 'position:absolute;width:0;height:0;pointer-events:none'
                }
            ]
        };
    },
    setWidth: function(width) {
        var me = this;
        me.callParent(arguments);
        me.owner.groupEl.dom.coordsize = me.width + ' ' + (me.height || 0);
        me.owner.groupEl.dom.style.width = width + 'px';
    },
    setHeight: function(height) {
        var me = this;
        me.callParent(arguments);
        me.owner.groupEl.dom.coordsize = (me.width || 0) + ' ' + me.height;
        me.owner.groupEl.dom.style.height = height + 'px';
    },
    onOwnerUpdate: function() {
        var me = this;
        me.group = me.owner.groupEl;
        me.el = me.owner.element;
        me.prerender = [];
    },
    _drawShape: function(shapeid, path, lineColor, fillColor, lineWidth) {
        var vpath = [],
            initial, stroke, fill, closed, plen, i;
        for (i = 0 , plen = path.length; i < plen; i++) {
            vpath[i] = (path[i][0]) + ',' + (path[i][1]);
        }
        initial = vpath.splice(0, 1);
        lineWidth = lineWidth == null ? 1 : lineWidth;
        stroke = lineColor == null ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" ';
        fill = fillColor == null ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" ';
        closed = vpath[0] === vpath[vpath.length - 1] ? 'x ' : '';
        return [
            '<svml:shape coordorigin="0 0" coordsize="',
            this.pixelWidth,
            ' ',
            this.pixelHeight,
            '" id="jqsshape',
            shapeid,
            '" ',
            stroke,
            fill,
            ' style="position:absolute;height:',
            this.pixelHeight,
            'px;width:',
            this.pixelWidth,
            'px" ',
            ' path="m ',
            initial,
            ' l ',
            vpath.join(', '),
            ' ',
            closed,
            'e"></svml:shape>'
        ].join('');
    },
    _drawCircle: function(shapeid, x, y, radius, lineColor, fillColor, lineWidth) {
        var circumference = radius * 2,
            stroke, fill;
        x -= radius;
        y -= radius;
        stroke = lineColor == null ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" ';
        fill = fillColor == null ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" ';
        return [
            '<svml:oval id="jqsshape',
            shapeid,
            '" ',
            stroke,
            fill,
            ' style="position:absolute;top:',
            y,
            'px; left:',
            x,
            'px;width:',
            circumference,
            'px;height:',
            circumference,
            'px"></svml:oval>'
        ].join('');
    },
    _drawPieSlice: function(shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) {
        var vpath,
            width = this.pixelWidth,
            height = this.pixelHeight,
            startx, starty, endx, endy,
            stroke = lineColor == null ? ' stroked="false" ' : ' strokeWeight="1px" strokeColor="' + lineColor + '" ',
            fill = fillColor == null ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" ';
        
        if (startAngle === endAngle) {
            return '';
        }
        if ((endAngle - startAngle) === (2 * Math.PI)) {
            startAngle = 0;
            
            endAngle = (2 * Math.PI);
        }
        startx = x + Math.round(Math.cos(startAngle) * radius);
        starty = y + Math.round(Math.sin(startAngle) * radius);
        endx = x + Math.round(Math.cos(endAngle) * radius);
        endy = y + Math.round(Math.sin(endAngle) * radius);
        if (startx === endx && starty === endy) {
            if ((endAngle - startAngle) < Math.PI) {
                
                return '';
            }
            
            startx = endx = x + radius;
            starty = endy = y;
        }
        if (startx === endx && starty === endy && (endAngle - startAngle) < Math.PI) {
            return '';
        }
        vpath = [
            x - radius,
            y - radius,
            x + radius,
            y + radius,
            startx,
            starty,
            endx,
            endy
        ];
        return [
            '<svml:shape coordorigin="0 0" coordsize="',
            width,
            ' ',
            height,
            '" id="jqsshape',
            shapeid,
            '" ',
            stroke,
            fill,
            ' style="position:absolute;height:',
            height,
            'px;width:',
            width,
            'px" path="m ',
            x,
            ',',
            y,
            ' wa ',
            vpath.join(', '),
            ' x e"></svml:shape>'
        ].join('');
    },
    _drawRect: function(shapeid, x, y, width, height, lineColor, fillColor) {
        return this._drawShape(shapeid, [
            [
                x,
                y
            ],
            [
                x,
                y + height
            ],
            [
                x + width,
                y + height
            ],
            [
                x + width,
                y
            ],
            [
                x,
                y
            ]
        ], lineColor, fillColor);
    },
    reset: function() {
        Ext.fly(this.group).empty();
    },
    appendShape: function(shape) {
        this.prerender.push(this['_draw' + shape.type].apply(this, shape.args));
        this.lastShapeId = shape.id;
        return shape.id;
    },
    replaceWithShape: function(shapeid, shape) {
        var existing = this.el.getById('jqsshape' + shapeid, true),
            vel = this['_draw' + shape.type].apply(this, shape.args);
        existing.outerHTML = vel;
    },
    replaceWithShapes: function(shapeids, shapes) {
        
        var existing = this.el.getById('jqsshape' + shapeids[0], true),
            replace = '',
            slen = shapes.length,
            i;
        for (i = 0; i < slen; i++) {
            replace += this['_draw' + shapes[i].type].apply(this, shapes[i].args);
        }
        existing.outerHTML = replace;
        for (i = 1; i < shapeids.length; i++) {
            this.el.getById('jqsshape' + shapeids[i]).destroy();
        }
    },
    insertAfterShape: function(shapeid, shape) {
        var existing = this.el.getById('jqsshape' + shapeid, true),
            vel = this['_draw' + shape.type].apply(this, shape.args);
        existing.insertAdjacentHTML('afterEnd', vel);
    },
    removeShapeId: function(shapeid) {
        var existing = this.el.getById('jqsshape' + shapeid, true);
        this.group.removeChild(existing);
    },
    getShapeAt: function(x, y) {
        var shapeid = this.el.id.substr(8);
        return shapeid;
    },
    render: function() {
        this.group.dom.innerHTML = this.prerender.join('');
    }
}, function() {
    Ext.onReady(function() {
        var doc = document;
        if (doc.namespaces && !doc.namespaces.svml) {
            doc.namespaces.add("svml", "urn:schemas-microsoft-com:vml", '#default#VML');
        }
    });
});


Ext.define('Ext.sparkline.Base', {
    extend: 'Ext.Widget',
    requires: [
        'Ext.XTemplate',
        'Ext.sparkline.CanvasCanvas',
        'Ext.sparkline.VmlCanvas'
    ],
    cachedConfig: {
        baseCls: Ext.baseCSSPrefix + 'sparkline',
        
        lineColor: '#157fcc',
        
        fillColor: '#def',
        defaultPixelsPerValue: 3,
        tagValuesAttribute: 'values',
        enableTagOptions: false,
        enableHighlight: true,
        
        highlightColor: null,
        
        highlightLighten: 1.4,
        
        tooltipSkipNull: true,
        
        tooltipPrefix: '',
        
        tooltipSuffix: '',
        
        disableTooltips: false,
        disableInteraction: false,
        
        tipTpl: null
    },
    config: {
        
        values: null
    },
    element: {
        tag: 'canvas',
        reference: 'element',
        style: {
            display: 'inline-block',
            verticalAlign: 'top'
        },
        listeners: {
            mouseenter: 'onMouseEnter',
            mouseleave: 'onMouseLeave',
            mousemove: 'onMouseMove'
        },
        
        
        width: 0,
        height: 0
    },
    defaultBindProperty: 'values',
    
    
    redrawQueue: {},
    inheritableStatics: {
        sparkLineTipClass: Ext.baseCSSPrefix + 'sparkline-tip-target',
        onClassCreated: function(cls) {
            var proto = cls.prototype,
                configs = cls.getConfigurator().configs,
                config;
            
            for (config in configs) {
                
                if (config !== 'tipTpl') {
                    proto[Ext.Config.get(config).names.apply] = proto.applyConfigChange;
                }
            }
        }
    },
    constructor: function(config) {
        var me = this;
        
        me.canvas = Ext.supports.Canvas ? new Ext.sparkline.CanvasCanvas(me) : new Ext.sparkline.VmlCanvas(me);
        if (!me.getDisableTooltips()) {
            me.element.cls = Ext.sparkline.Base.sparkLineTipClass;
        }
        Ext.apply(me, config);
        me.callParent([
            config
        ]);
        
        me.el = me.element;
    },
    
    
    all: function(val, arr, ignoreNull) {
        var i;
        for (i = arr.length; i--; ) {
            if (ignoreNull && arr[i] === null) {
                
                continue;
            }
            if (arr[i] !== val) {
                return false;
            }
        }
        return true;
    },
    
    
    applyConfigChange: function(newValue) {
        var me = this;
        me.redrawQueue[me.getId()] = me;
        
        if (!me.redrawTimer) {
            Ext.sparkline.Base.prototype.redrawTimer = Ext.Function.requestAnimationFrame(me.processRedrawQueue);
        }
        return newValue;
    },
    
    
    applyTipTpl: function(tipTpl) {
        if (!tipTpl.isTemplate) {
            tipTpl = new Ext.XTemplate(tipTpl);
        }
        return tipTpl;
    },
    normalizeValue: function(val) {
        var nf;
        switch (val) {
            case 'undefined':
                val = undefined;
                break;
            case 'null':
                val = null;
                break;
            case 'true':
                val = true;
                break;
            case 'false':
                val = false;
                break;
            default:
                nf = parseFloat(val);
                if (val == nf) {
                    val = nf;
                };
        }
        return val;
    },
    normalizeValues: function(vals) {
        var i,
            result = [];
        for (i = vals.length; i--; ) {
            result[i] = this.normalizeValue(vals[i]);
        }
        return result;
    },
    doSetWidth: function(width) {
        var me = this,
            dom = me.element.dom;
        me.callParent(arguments);
        me.canvas.setWidth(width);
        me.width = width;
        if (me.height == null) {
            me.setHeight(parseInt(me.measurer.getCachedStyle(dom.parentNode, 'line-height'), 10));
        } else {
            me.redrawQueue[me.getId()] = me;
        }
    },
    doSetHeight: function(height) {
        var me = this;
        me.callParent(arguments);
        me.canvas.setHeight(height);
        me.height = height;
        me.redrawQueue[me.getId()] = me;
    },
    updateValues: function(values) {
        this.values = values;
    },
    redraw: function() {
        var me = this,
            tooltip;
        if (me.getValues()) {
            tooltip = me.tooltip;
            
            if (tooltip && tooltip.isVisible() && me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) {
                me.tooltip.triggerElement = me.el.dom;
            }
            me.onUpdate();
            me.canvas.onOwnerUpdate();
            me.renderGraph();
        }
    },
    onUpdate: Ext.emptyFn,
    
    renderGraph: function() {
        var ret = true;
        if (this.disabled) {
            this.canvas.reset();
            ret = false;
        }
        return ret;
    },
    onMouseEnter: function(e) {
        this.onMouseMove(e);
    },
    onMouseMove: function(e) {
        this.currentPageXY = e.getPoint();
        this.redraw();
    },
    onMouseLeave: function() {
        var me = this;
        me.currentPageXY = me.targetX = me.targetY = null;
        me.redraw();
    },
    updateDisplay: function() {
        var me = this,
            offset, tooltip, tooltipHTML, region;
        if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) {
            offset = me.canvas.el.getXY();
            region = me.getRegion(me.currentPageXY[0] - offset[0], me.currentPageXY[1] - offset[1]);
            if (region != null && !me.disableHighlight) {
                me.renderHighlight(region);
            }
            me.fireEvent('sparklineregionchange', me);
            tooltip = me.tooltip;
            if (region != null && tooltip) {
                tooltipHTML = me.getRegionTooltip(region);
                if (tooltipHTML) {
                    if (!me.lastTooltipHTML || tooltipHTML[0] !== me.lastTooltipHTML[0] || tooltipHTML[1] !== me.lastTooltipHTML[1]) {
                        tooltip.setTitle(tooltipHTML[0]);
                        tooltip.update(tooltipHTML[1]);
                        me.lastTooltipHTML = tooltipHTML;
                    }
                } else {
                    tooltip.hide();
                }
            }
        }
    },
    
    getRegion: Ext.emptyFn,
    
    getRegionTooltip: function(region) {
        var me = this,
            header = me.tooltipChartTitle,
            entries = [],
            tipTpl = me.getTipTpl(),
            fields, showFields, showFieldsKey, newFields, fv, formatter, fieldlen, j;
        fields = me.getRegionFields(region);
        formatter = me.tooltipFormatter;
        if (formatter) {
            return formatter(me, me, fields);
        }
        if (!tipTpl) {
            return '';
        }
        if (!Ext.isArray(fields)) {
            fields = [
                fields
            ];
        }
        showFields = me.tooltipFormatFieldlist;
        showFieldsKey = me.tooltipFormatFieldlistKey;
        if (showFields && showFieldsKey) {
            
            newFields = [];
            for (i = fields.length; i--; ) {
                fv = fields[i][showFieldsKey];
                if ((j = Ext.Array.indexOf(fv, showFields)) != -1) {
                    newFields[j] = fields[i];
                }
            }
            fields = newFields;
        }
        fieldlen = fields.length;
        for (j = 0; j < fieldlen; j++) {
            if (!fields[j].isNull || !me.getTooltipSkipNull()) {
                Ext.apply(fields[j], {
                    prefix: me.getTooltipPrefix(),
                    suffix: me.getTooltipSuffix()
                });
                entries.push(tipTpl.apply(fields[j]));
            }
        }
        if (header || entries.length) {
            return [
                header,
                entries.join('<br>')
            ];
        }
        return '';
    },
    getRegionFields: Ext.emptyFn,
    calcHighlightColor: function(color) {
        var me = this,
            highlightColor = me.getHighlightColor(),
            lighten = me.getHighlightLighten(),
            parse, mult, rgbnew, i;
        if (highlightColor) {
            return highlightColor;
        }
        if (lighten) {
            
            parse = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color) || /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);
            if (parse) {
                rgbnew = [];
                mult = color.length === 4 ? 16 : 1;
                for (i = 0; i < 3; i++) {
                    rgbnew[i] = Ext.Number.constrain(Math.round(parseInt(parse[i + 1], 16) * mult * lighten), 0, 255);
                }
                return 'rgb(' + rgbnew.join(',') + ')';
            }
        }
        return color;
    },
    destroy: function() {
        delete this.redrawQueue[this.getId()];
        this.callParent();
    }
}, function(cls) {
    var proto = cls.prototype;
    Ext.onReady(function() {
        proto.tooltip = new Ext.tip.ToolTip({
            id: 'sparklines-tooltip',
            target: document.body,
            delegate: '.' + cls.sparkLineTipClass,
            showDelay: 0,
            dismissDelay: 0,
            hideDelay: 400
        });
    });
    cls.onClassCreated(cls);
    proto.processRedrawQueue = function() {
        var redrawQueue = proto.redrawQueue,
            id;
        for (id in redrawQueue) {
            redrawQueue[id].redraw();
        }
        proto.redrawQueue = {};
        proto.redrawTimer = 0;
    };
});


Ext.define('Ext.sparkline.BarBase', {
    extend: 'Ext.sparkline.Base',
    renderHighlight: function(region) {
        this.renderRegion(region, true);
    },
    renderGraph: function() {
        var me = this,
            values = me.values,
            canvas = me.canvas,
            regionShapes = me.regionShapes || (me.regionShapes = {}),
            shapes, ids, i, j;
        if (!me.callParent()) {
            return;
        }
        for (i = values.length; i--; ) {
            shapes = me.renderRegion(i);
            if (shapes) {
                if (Ext.isArray(shapes)) {
                    ids = [];
                    for (j = shapes.length; j--; ) {
                        shapes[j].append();
                        ids.push(shapes[j].id);
                    }
                    regionShapes[i] = ids;
                } else {
                    shapes.append();
                    regionShapes[i] = shapes.id;
                }
            } else 
            {
                
                regionShapes[i] = null;
            }
        }
        
        if (me.currentPageXY) {
            me.currentRegion = null;
            me.updateDisplay();
        }
        canvas.render();
    }
});


Ext.define('Ext.sparkline.RangeMap', {
    constructor: function(map) {
        var key, range,
            rangelist = [];
        for (key in map) {
            if (map.hasOwnProperty(key) && typeof key === 'string' && key.indexOf(':') > -1) {
                range = key.split(':');
                range[0] = range[0].length === 0 ? -Infinity : parseFloat(range[0]);
                range[1] = range[1].length === 0 ? Infinity : parseFloat(range[1]);
                range[2] = map[key];
                rangelist.push(range);
            }
        }
        this.map = map;
        this.rangelist = rangelist || false;
    },
    get: function(value) {
        var rangelist = this.rangelist,
            i, range, result;
        if ((result = this.map[value]) !== undefined) {
            return result;
        }
        if (rangelist) {
            for (i = rangelist.length; i--; ) {
                range = rangelist[i];
                if (range[0] <= value && range[1] >= value) {
                    return range[2];
                }
            }
        }
    }
});


Ext.define('Ext.sparkline.Bar', {
    extend: 'Ext.sparkline.BarBase',
    requires: [
        'Ext.sparkline.RangeMap'
    ],
    alias: 'widget.sparklinebar',
    config: {
        
        barColor: '#3366cc',
        
        negBarColor: '#f44',
        
        stackedBarColor: [
            '#3366cc',
            '#dc3912',
            '#ff9900',
            '#109618',
            '#66aa00',
            '#dd4477',
            '#0099c6',
            '#990099'
        ],
        
        zeroColor: null,
        
        nullColor: null,
        
        zeroAxis: true,
        
        barWidth: 4,
        
        barSpacing: 1,
        
        chartRangeMin: null,
        
        chartRangeMax: null,
        
        chartRangeClip: false,
        
        colorMap: null,
        tipTpl: new Ext.XTemplate('&#9679; {prefix}{value}{suffix}')
    },
    remove: function(vals, filter) {
        var i, vl,
            result = [];
        for (i = 0 , vl = vals.length; i < vl; i++) {
            if (vals[i] !== filter) {
                result.push(vals[i]);
            }
        }
        return result;
    },
    
    
    all: function(arr, val, ignoreNull) {
        var i;
        for (i = arr.length; i--; ) {
            if (ignoreNull && arr[i] === null)  {
                
                continue;
            }
            
            if (arr[i] !== val) {
                return false;
            }
        }
        return true;
    },
    applyColorMap: function(colorMap) {
        var me = this;
        if (Ext.isArray(colorMap)) {
            me.colorMapByIndex = colorMap;
            me.colorMapByValue = null;
        } else {
            me.colorMapByIndex = null;
            me.colorMapByValue = colorMap;
            if (me.colorMapByValue && me.colorMapByValue.get == null) {
                me.colorMapByValue = new Ext.sparkline.RangeMap(colorMap);
            }
        }
        return colorMap;
    },
    onUpdate: function() {
        var me = this,
            values = me.values,
            barWidth = me.getBarWidth(),
            barSpacing = me.getBarSpacing(),
            chartRangeMin = me.getChartRangeMin(),
            chartRangeMax = me.getChartRangeMax(),
            chartRangeClip = me.getChartRangeClip(),
            stackMin = Infinity,
            stackMax = -Infinity,
            isStackString, groupMin, groupMax, stackRanges, numValues, i, vlen, range,
            zeroAxis = me.getZeroAxis(),
            xAxisOffset, min, max, clipMin, clipMax, stacked, vlist, j, slen, svals, val, yoffset, yMaxCalc,
            stackTotals = [],
            stackRangesNeg = [];
        
        for (i = 0 , vlen = values.length; i < vlen; i++) {
            val = values[i];
            isStackString = typeof (val) === 'string' && val.indexOf(':') > -1;
            if (isStackString || Ext.isArray(val)) {
                stacked = true;
                if (isStackString) {
                    val = values[i] = me.normalizeValues(val.split(':'));
                }
                val = me.remove(val, null);
                
                groupMin = Math.min.apply(Math, val);
                groupMax = Math.max.apply(Math, val);
                if (groupMin < stackMin) {
                    stackMin = groupMin;
                }
                if (groupMax > stackMax) {
                    stackMax = groupMax;
                }
            }
        }
        me.stacked = stacked;
        me.regionShapes = {};
        me.totalBarWidth = barWidth + barSpacing;
        me.width = (values.length * barWidth) + ((values.length - 1) * barSpacing);
        if (chartRangeClip) {
            clipMin = chartRangeMin == null ? -Infinity : chartRangeMin;
            clipMax = chartRangeMax == null ? Infinity : chartRangeMax;
        }
        numValues = [];
        stackRanges = stacked ? [] : numValues;
        for (i = 0 , vlen = values.length; i < vlen; i++) {
            if (stacked) {
                vlist = values[i];
                values[i] = svals = [];
                stackTotals[i] = 0;
                stackRanges[i] = stackRangesNeg[i] = 0;
                for (j = 0 , slen = vlist.length; j < slen; j++) {
                    val = svals[j] = chartRangeClip ? Ext.Number.constrain(vlist[j], clipMin, clipMax) : vlist[j];
                    if (val !== null) {
                        if (val > 0) {
                            stackTotals[i] += val;
                        }
                        if (stackMin < 0 && stackMax > 0) {
                            if (val < 0) {
                                stackRangesNeg[i] += Math.abs(val);
                            } else {
                                stackRanges[i] += val;
                            }
                        } else {
                            stackRanges[i] += Math.abs(val - (val < 0 ? stackMax : stackMin));
                        }
                        numValues.push(val);
                    }
                }
            } else {
                val = chartRangeClip ? Ext.Number.constrain(values[i], clipMin, clipMax) : values[i];
                val = values[i] = me.normalizeValue(val);
                if (val !== null) {
                    numValues.push(val);
                }
            }
        }
        me.max = max = Math.max.apply(Math, numValues);
        me.min = min = Math.min.apply(Math, numValues);
        me.stackMax = stackMax = stacked ? Math.max.apply(Math, stackTotals) : max;
        me.stackMin = stackMin = stacked ? Math.min.apply(Math, numValues) : min;
        if (chartRangeMin != null && (chartRangeClip || chartRangeMin < min)) {
            min = chartRangeMin;
        }
        if (chartRangeMax != null && (chartRangeClip || chartRangeMax > max)) {
            max = chartRangeMax;
        }
        if (min <= 0 && max >= 0 && zeroAxis) {
            xAxisOffset = 0;
        } else if (zeroAxis == false) {
            xAxisOffset = min;
        } else if (min > 0) {
            xAxisOffset = min;
        } else {
            xAxisOffset = max;
        }
        me.xAxisOffset = xAxisOffset;
        range = stacked ? (Math.max.apply(Math, stackRanges) + Math.max.apply(Math, stackRangesNeg)) : max - min;
        
        
        me.canvasHeightEf = (zeroAxis && min < 0) ? me.getHeight() - 2 : me.getHeight() - 1;
        if (min < xAxisOffset) {
            yMaxCalc = (stacked && max >= 0) ? stackMax : max;
            yoffset = (yMaxCalc - xAxisOffset) / range * me.getHeight();
            if (yoffset !== Math.ceil(yoffset)) {
                me.canvasHeightEf -= 2;
                yoffset = Math.ceil(yoffset);
            }
        } else {
            yoffset = me.getHeight();
        }
        me.yoffset = yoffset;
        me.range = range;
    },
    getRegion: function(x, y) {
        var result = Math.floor(x / this.totalBarWidth);
        return (result < 0 || result >= this.values.length) ? undefined : result;
    },
    getRegionFields: function(region) {
        var values = Ext.Array.from(this.values[region]),
            result = [],
            value, i;
        for (i = values.length; i--; ) {
            value = values[i];
            result.push({
                isNull: value === null,
                value: value,
                color: this.calcColor(i, value, region),
                offset: region
            });
        }
        return result;
    },
    calcColor: function(stacknum, value, valuenum) {
        var me = this,
            colorMapByIndex = me.colorMapByIndex,
            colorMapByValue = me.colorMapByValue,
            color, newColor,
            zeroColor = me.getZeroColor();
        if (this.stacked) {
            color = me.getStackedBarColor();
        } else {
            color = (value < 0) ? me.getNegBarColor() : me.getBarColor();
        }
        if (value === 0 && zeroColor !== undefined) {
            color = zeroColor;
        }
        if (colorMapByValue && (newColor = colorMapByValue.get(value))) {
            color = newColor;
        } else if (colorMapByIndex && colorMapByIndex.length > valuenum) {
            color = colorMapByIndex[valuenum];
        }
        return Ext.isArray(color) ? color[stacknum % color.length] : color;
    },
    
    renderRegion: function(valuenum, highlight) {
        var me = this,
            vals = me.values[valuenum],
            xaxisOffset = me.xAxisOffset,
            range = me.range,
            stacked = me.stacked,
            canvas = me.canvas,
            barWidth = me.getBarWidth(),
            x = valuenum * me.totalBarWidth,
            canvasHeightEf = me.canvasHeightEf,
            yoffset = me.yoffset,
            y, height, color, isNull, yoffsetNeg, i, valcount, val, minPlotted, allMin,
            nullColor = me.getNullColor();
        vals = Ext.isArray(vals) ? vals : [
            vals
        ];
        valcount = vals.length;
        val = vals[0];
        isNull = me.all(vals, null);
        allMin = me.all(vals, xaxisOffset, true);
        if (isNull) {
            if (nullColor) {
                color = highlight ? nullColor : me.calcHighlightColor(nullColor, me);
                y = (yoffset > 0) ? yoffset - 1 : yoffset;
                canvas.drawRect(x, y, barWidth - 1, 0, color, color).append();
            }
            return;
        }
        yoffsetNeg = yoffset;
        for (i = 0; i < valcount; i++) {
            val = vals[i];
            if (stacked && val === xaxisOffset) {
                if (!allMin || minPlotted) {
                    
                    continue;
                }
                minPlotted = true;
            }
            if (range > 0) {
                height = Math.floor(canvasHeightEf * ((Math.abs(val - xaxisOffset) / range))) + 1;
            } else {
                height = 1;
            }
            if (val < xaxisOffset || (val === xaxisOffset && yoffset === 0)) {
                y = yoffsetNeg;
                yoffsetNeg += height;
            } else {
                y = yoffset - height;
                yoffset -= height;
            }
            color = me.calcColor(i, val, valuenum);
            if (highlight) {
                color = me.calcHighlightColor(color, me);
            }
            canvas.drawRect(x, y, barWidth - 1, height - 1, color, color).append();
        }
    }
}, function(cls) {
    cls.onClassCreated(cls);
});


Ext.define('Ext.sparkline.Box', {
    extend: 'Ext.sparkline.Base',
    alias: 'widget.sparklinebox',
    config: {
        
        raw: false,
        
        boxLineColor: '#000',
        
        boxFillColor: '#cdf',
        
        whiskerColor: '#000',
        
        outlierLineColor: '#333',
        
        outlierFillColor: '#fff',
        
        medianColor: '#f00',
        
        showOutliers: true,
        
        outlierIQR: 1.5,
        
        spotRadius: 1.5,
        
        target: null,
        
        targetColor: '#4a2',
        
        chartRangeMin: null,
        
        chartRangeMax: null,
        tipTpl: new Ext.XTemplate('{field:this.fields}: {value}',{
            fields: function(v) {
                var fields = {
                        lq: 'Lower Quartile',
                        med: 'Median',
                        uq: 'Upper Quartile',
                        lo: 'Left Outlier',
                        ro: 'Right Outlier',
                        lw: 'Left Whisker',
                        rw: 'Right Whisker'
                    };
                return fields[v];
            }
        }),
        tooltipFormatFieldlistKey: 'field'
    },
    quartile: function(values, q) {
        var vl;
        if (q === 2) {
            vl = Math.floor(values.length / 2);
            return values.length % 2 ? values[vl] : (values[vl - 1] + values[vl]) / 2;
        } else {
            if (values.length % 2) {
                
                vl = (values.length * q + q) / 4;
                return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl - 1];
            } else {
                
                vl = (values.length * q + 2) / 4;
                return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl - 1];
            }
        }
    },
    
    applyValues: function(newValues) {
        newValues = Ext.Array.map(Ext.Array.from(newValues), Number);
        this.disabled = !(newValues && newValues.length);
        return newValues;
    },
    
    getRegion: function() {
        return 1;
    },
    getRegionFields: function() {
        var result = [
                {
                    field: 'lq',
                    value: this.quartiles[0]
                },
                {
                    field: 'med',
                    value: this.quartiles[1]
                },
                {
                    field: 'uq',
                    value: this.quartiles[2]
                }
            ];
        if (this.loutlier !== undefined) {
            result.push({
                field: 'lo',
                value: this.loutlier
            });
        }
        if (this.routlier !== undefined) {
            result.push({
                field: 'ro',
                value: this.routlier
            });
        }
        if (this.lwhisker !== undefined) {
            result.push({
                field: 'lw',
                value: this.lwhisker
            });
        }
        if (this.rwhisker !== undefined) {
            result.push({
                field: 'rw',
                value: this.rwhisker
            });
        }
        return result;
    },
    renderHighlight: Ext.emptyFn,
    renderGraph: function() {
        var me = this,
            canvas = me.canvas,
            values = me.values,
            vlen = values.length,
            canvasWidth = me.getWidth(),
            canvasHeight = me.getHeight(),
            chartRangeMin = me.getChartRangeMin(),
            chartRangeMax = me.getChartRangeMax(),
            minValue = chartRangeMin == null ? Math.min.apply(Math, values) : chartRangeMin,
            maxValue = chartRangeMax == null ? Math.max.apply(Math, values) : chartRangeMax,
            canvasLeft = 0,
            lwhisker, loutlier, iqr, q1, q2, q3, rwhisker, routlier, i, size, unitSize,
            spotRadius = me.getSpotRadius(),
            outlierLineColor = me.getOutlierLineColor(),
            outlierFillColor = me.getOutlierFillColor(),
            showOutliers = me.getShowOutliers(),
            outlierIQR = me.getOutlierIQR(),
            lineColor = me.getLineColor(),
            whiskerColor = me.getWhiskerColor(),
            targetColor = me.getTargetColor();
        if (!me.callParent()) {
            return;
        }
        if (me.raw) {
            if (showOutliers && values.length > 5) {
                loutlier = values[0];
                lwhisker = values[1];
                q1 = values[2];
                q2 = values[3];
                q3 = values[4];
                rwhisker = values[5];
                routlier = values[6];
            } else {
                lwhisker = values[0];
                q1 = values[1];
                q2 = values[2];
                q3 = values[3];
                rwhisker = values[4];
            }
        } else {
            values.sort(function(a, b) {
                return a - b;
            });
            q1 = me.quartile(values, 1);
            q2 = me.quartile(values, 2);
            q3 = me.quartile(values, 3);
            iqr = q3 - q1;
            if (showOutliers) {
                lwhisker = rwhisker = null;
                for (i = 0; i < vlen; i++) {
                    if (lwhisker == null && values[i] > q1 - (iqr * outlierIQR)) {
                        lwhisker = values[i];
                    }
                    if (values[i] < q3 + (iqr * outlierIQR)) {
                        rwhisker = values[i];
                    }
                }
                loutlier = values[0];
                routlier = values[vlen - 1];
            } else {
                lwhisker = values[0];
                rwhisker = values[vlen - 1];
            }
        }
        me.quartiles = [
            q1,
            q2,
            q3
        ];
        me.lwhisker = lwhisker;
        me.rwhisker = rwhisker;
        me.loutlier = loutlier;
        me.routlier = routlier;
        unitSize = canvasWidth / (maxValue - minValue + 1);
        if (showOutliers) {
            canvasLeft = Math.ceil(spotRadius);
            canvasWidth -= 2 * Math.ceil(spotRadius);
            unitSize = canvasWidth / (maxValue - minValue + 1);
            if (loutlier < lwhisker) {
                canvas.drawCircle((loutlier - minValue) * unitSize + canvasLeft, canvasHeight / 2, spotRadius, outlierLineColor, outlierFillColor).append();
            }
            if (routlier > rwhisker) {
                canvas.drawCircle((routlier - minValue) * unitSize + canvasLeft, canvasHeight / 2, spotRadius, outlierLineColor, outlierFillColor).append();
            }
        }
        
        canvas.drawRect(Math.round((q1 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight * 0.1), Math.round((q3 - q1) * unitSize), Math.round(canvasHeight * 0.8), me.getBoxLineColor(), me.getBoxFillColor()).append();
        
        canvas.drawLine(Math.round((lwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 2), Math.round((q1 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 2), lineColor).append();
        canvas.drawLine(Math.round((lwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 4), Math.round((lwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight - canvasHeight / 4), whiskerColor).append();
        
        canvas.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 2), Math.round((q3 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 2), lineColor).append();
        canvas.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight / 4), Math.round((rwhisker - minValue) * unitSize + canvasLeft), Math.round(canvasHeight - canvasHeight / 4), whiskerColor).append();
        
        canvas.drawLine(Math.round((q2 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight * 0.1), Math.round((q2 - minValue) * unitSize + canvasLeft), Math.round(canvasHeight * 0.9), me.getMedianColor()).append();
        if (me.target) {
            size = Math.ceil(me.spotRadius);
            canvas.drawLine(Math.round((me.target - minValue) * unitSize + canvasLeft), Math.round((canvasHeight / 2) - size), Math.round((me.target - minValue) * unitSize + canvasLeft), Math.round((canvasHeight / 2) + size), targetColor).append();
            canvas.drawLine(Math.round((me.target - minValue) * unitSize + canvasLeft - size), Math.round(canvasHeight / 2), Math.round((me.target - minValue) * unitSize + canvasLeft + size), Math.round(canvasHeight / 2), targetColor).append();
        }
        
        if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) {
            me.currentRegion = null;
            me.updateDisplay();
        }
        canvas.render();
    }
});


Ext.define('Ext.sparkline.Bullet', {
    extend: 'Ext.sparkline.Base',
    alias: 'widget.sparklinebullet',
    config: {
        
        targetColor: '#f33',
        
        targetWidth: 3,
        
        performanceColor: '#33f',
        
        rangeColors: [
            '#d3dafe',
            '#a8b6ff',
            '#7f94ff'
        ],
        
        base: null,
        tipTpl: new Ext.XTemplate('{fieldkey:this.fields} - {value}',{
            fields: function(v) {
                if (v === 'r') {
                    return 'Range';
                }
                if (v === 'p') {
                    return 'Performance';
                }
                if (v === 't') {
                    return 'Target';
                }
            }
        })
    },
    
    applyValues: function(newValues) {
        newValues = Ext.Array.map(Ext.Array.from(newValues), this.normalizeValue);
        this.disabled = !(newValues && newValues.length);
        return newValues;
    },
    onUpdate: function() {
        var me = this,
            values = me.values,
            min, max, vals,
            base = me.getBase();
        me.callParent(arguments);
        
        vals = values.slice();
        vals[0] = vals[0] === null ? vals[2] : vals[0];
        vals[1] = values[1] === null ? vals[2] : vals[1];
        min = Math.min.apply(Math, values);
        max = Math.max.apply(Math, values);
        if (base == null) {
            min = min < 0 ? min : 0;
        } else {
            min = base;
        }
        me.min = min;
        me.max = max;
        me.range = max - min;
        me.shapes = {};
        me.valueShapes = {};
        me.regiondata = {};
        if (!values.length) {
            me.disabled = true;
        }
    },
    getRegion: function(x, y) {
        var shapeid = this.canvas.getShapeAt(x, y);
        return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined;
    },
    getRegionFields: function(region) {
        return {
            fieldkey: region.substr(0, 1),
            value: this.values[region.substr(1)],
            region: region
        };
    },
    renderHighlight: function(region) {
        switch (region.substr(0, 1)) {
            case 'r':
                this.renderRange(region.substr(1), true).append();
                break;
            case 'p':
                this.renderPerformance(true).append();
                break;
            case 't':
                this.renderTarget(true).append();
                break;
        }
    },
    renderRange: function(region, highlight) {
        var rangeval = this.values[region],
            rangewidth = Math.round(this.getWidth() * ((rangeval - this.min) / this.range)),
            color = this.getRangeColors()[region - 2];
        if (highlight) {
            color = this.calcHighlightColor(color);
        }
        return this.canvas.drawRect(0, 0, rangewidth - 1, this.getHeight() - 1, color, color);
    },
    renderPerformance: function(highlight) {
        var perfval = this.values[1],
            perfwidth = Math.round(this.getWidth() * ((perfval - this.min) / this.range)),
            color = this.getPerformanceColor();
        if (highlight) {
            color = this.calcHighlightColor(color);
        }
        return this.canvas.drawRect(0, Math.round(this.getHeight() * 0.3), perfwidth - 1, Math.round(this.getHeight() * 0.4) - 1, color, color);
    },
    renderTarget: function(highlight) {
        var targetval = this.values[0],
            targetWidth = this.getTargetWidth(),
            x = Math.round(this.getWidth() * ((targetval - this.min) / this.range) - (targetWidth / 2)),
            targettop = Math.round(this.getHeight() * 0.1),
            targetheight = this.getHeight() - (targettop * 2),
            color = this.getTargetColor();
        if (highlight) {
            color = this.calcHighlightColor(color);
        }
        return this.canvas.drawRect(x, targettop, targetWidth - 1, targetheight - 1, color, color);
    },
    renderGraph: function() {
        var me = this,
            vlen = me.values.length,
            canvas = me.canvas,
            i, shape,
            shapes = me.shapes || (me.shapes = {}),
            valueShapes = me.valueShapes || (me.valueShapes = {});
        if (!me.callParent()) {
            return;
        }
        for (i = 2; i < vlen; i++) {
            shape = me.renderRange(i).append();
            shapes[shape.id] = 'r' + i;
            valueShapes['r' + i] = shape.id;
        }
        if (me.values[1] !== null) {
            shape = me.renderPerformance().append();
            shapes[shape.id] = 'p1';
            valueShapes.p1 = shape.id;
        }
        if (me.values[0] !== null) {
            shape = this.renderTarget().append();
            shapes[shape.id] = 't0';
            valueShapes.t0 = shape.id;
        }
        
        if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) {
            me.updateDisplay();
        }
        canvas.render();
    }
});


Ext.define('Ext.sparkline.Discrete', {
    extend: 'Ext.sparkline.BarBase',
    alias: 'widget.sparklinediscrete',
    config: {
        
        lineHeight: 'auto',
        
        thresholdColor: null,
        
        thresholdValue: 0,
        
        chartRangeMax: null,
        
        chartRangeMin: null,
        
        chartRangeClip: false,
        tipTpl: new Ext.XTemplate('{prefix}{value}{suffix}')
    },
    
    applyValues: function(newValues) {
        newValues = Ext.Array.map(Ext.Array.from(newValues), Number);
        this.disabled = !(newValues && newValues.length);
        return newValues;
    },
    onUpdate: function() {
        var me = this,
            values = me.values,
            chartRangeMin = me.getChartRangeMin(),
            chartRangeMax = me.getChartRangeMax(),
            chartRangeClip = me.getChartRangeClip();
        me.callParent(arguments);
        me.regionShapes = {};
        me.min = Math.min.apply(Math, values);
        me.max = Math.max.apply(Math, values);
        me.range = me.max - me.min;
        me.width = me.getWidth();
        me.interval = Math.floor(me.width / values.length);
        me.itemWidth = me.width / values.length;
        if (chartRangeMin != null && (chartRangeClip || chartRangeMin < me.min)) {
            me.min = chartRangeMin;
        }
        if (chartRangeMax != null && (chartRangeClip || chartRangeMax > me.max)) {
            me.max = chartRangeMax;
        }
        if (me.canvas) {
            if (me.getLineHeight() === 'auto') {
                me.setLineHeight(Math.round(me.getHeight() * 0.3));
            }
        }
    },
    getRegion: function(x, y) {
        return Math.floor(x / this.itemWidth);
    },
    getRegionFields: function(region) {
        return {
            isNull: this.values[region] === undefined,
            value: this.values[region],
            offset: region
        };
    },
    renderRegion: function(valuenum, highlight) {
        var me = this,
            values = me.values,
            min = me.min,
            max = me.max,
            range = me.range,
            interval = me.interval,
            canvas = me.canvas,
            canvasHeight = me.getHeight(),
            lineHeight = me.getLineHeight(),
            pheight = canvasHeight - lineHeight,
            ytop, val, color, x,
            thresholdColor = me.getThresholdColor();
        val = Ext.Number.constrain(values[valuenum], min, max);
        x = valuenum * interval;
        ytop = Math.round(pheight - pheight * ((val - min) / range));
        color = (thresholdColor && val < me.getThresholdValue()) ? thresholdColor : me.getLineColor();
        if (highlight) {
            color = me.calcHighlightColor(color);
        }
        canvas.drawLine(x, ytop, x, ytop + lineHeight, color).append();
    }
});


Ext.define('Ext.sparkline.Line', {
    extend: 'Ext.sparkline.Base',
    requires: [
        'Ext.sparkline.RangeMap'
    ],
    alias: 'widget.sparklineline',
    config: {
        
        spotColor: '#f80',
        
        highlightSpotColor: '#5f5',
        
        highlightLineColor: '#f22',
        
        spotRadius: 1.5,
        
        minSpotColor: '#f80',
        
        maxSpotColor: '#f80',
        
        lineWidth: 1,
        
        normalRangeMin: null,
        
        normalRangeMax: null,
        
        normalRangeColor: '#ccc',
        
        drawNormalOnTop: false,
        
        chartRangeMin: null,
        
        chartRangeMax: null,
        
        chartRangeMinX: null,
        
        chartRangeMaxX: null,
        tipTpl: new Ext.XTemplate('&#9679; {prefix}{y}{suffix}'),
        
        valueSpots: null
    },
    applyValueSpots: function(valueSpots) {
        if (valueSpots && !valueSpots.get) {
            valueSpots = new Ext.sparkline.RangeMap(valueSpots);
        }
        return valueSpots;
    },
    onUpdate: function() {
        this.vertices = [];
        this.regionMap = [];
        this.xvalues = [];
        this.yvalues = [];
        this.yminmax = [];
        this.hightlightSpotId = null;
    },
    getRegion: function(x, y) {
        var i,
            regionMap = this.regionMap;
        
        for (i = regionMap.length; i--; ) {
            if (regionMap[i] !== null && x >= regionMap[i][0] && x <= regionMap[i][1]) {
                return regionMap[i][2];
            }
        }
        return undefined;
    },
    getRegionFields: function(region) {
        return {
            isNull: this.yvalues[region] === null,
            x: this.xvalues[region],
            y: this.yvalues[region],
            color: this.getLineColor(),
            fillColor: this.getFillColor(),
            offset: region
        };
    },
    renderHighlight: function(region) {
        var me = this,
            canvas = me.canvas,
            vertex = me.vertices[region],
            spotRadius = me.getSpotRadius(),
            highlightSpotColor = me.getHighlightSpotColor(),
            highlightLineColor = me.getHighlightLineColor();
        if (!vertex) {
            return;
        }
        if (spotRadius && highlightSpotColor) {
            canvas.drawCircle(vertex[0], vertex[1], spotRadius, null, highlightSpotColor).append();
        }
        if (highlightLineColor) {
            canvas.drawLine(vertex[0], me.canvasTop, vertex[0], me.canvasTop + me.getHeight(), highlightLineColor).append();
        }
    },
    scanValues: function() {
        var me = this,
            values = me.values,
            valcount = values.length,
            xvalues = me.xvalues,
            yvalues = me.yvalues,
            yminmax = me.yminmax,
            i, val, isStr, isArray, sp;
        for (i = 0; i < valcount; i++) {
            val = values[i];
            isStr = typeof (values[i]) === 'string';
            isArray = typeof (values[i]) === 'object' && values[i] instanceof Array;
            sp = isStr && values[i].split(':');
            if (isStr && sp.length === 2) {
                
                xvalues.push(Number(sp[0]));
                yvalues.push(Number(sp[1]));
                yminmax.push(Number(sp[1]));
            } else if (isArray) {
                xvalues.push(val[0]);
                yvalues.push(val[1]);
                yminmax.push(val[1]);
            } else {
                xvalues.push(i);
                if (values[i] === null || values[i] === 'null') {
                    yvalues.push(null);
                } else {
                    yvalues.push(Number(val));
                    yminmax.push(Number(val));
                }
            }
        }
        if (me.xvalues) {
            xvalues = me.xvalues;
        }
        me.maxy = me.maxyorg = Math.max.apply(Math, yminmax);
        me.miny = me.minyorg = Math.min.apply(Math, yminmax);
        me.maxx = Math.max.apply(Math, xvalues);
        me.minx = Math.min.apply(Math, xvalues);
        me.xvalues = xvalues;
        me.yvalues = yvalues;
        me.yminmax = yminmax;
    },
    processRangeOptions: function() {
        var me = this,
            normalRangeMin = me.getNormalRangeMin(),
            normalRangeMax = me.getNormalRangeMax(),
            chartRangeMin = me.getChartRangeMin(),
            chartRangeMinX = me.getChartRangeMinX(),
            chartRangeMax = me.getChartRangeMax(),
            chartRangeMaxX = me.getChartRangeMaxX();
        if (normalRangeMin != null) {
            if (normalRangeMin < me.miny) {
                me.miny = normalRangeMin;
            }
            if (normalRangeMax > me.maxy) {
                me.maxy = normalRangeMax;
            }
        }
        if (chartRangeMin != null && (me.chartRangeClip || chartRangeMin < me.miny)) {
            me.miny = chartRangeMin;
        }
        if (chartRangeMax != null && (me.chartRangeClip || chartRangeMax > me.maxy)) {
            this.maxy = chartRangeMax;
        }
        if (chartRangeMinX != null && (me.chartRangeClipX || chartRangeMinX < me.minx)) {
            me.minx = chartRangeMinX;
        }
        if (chartRangeMaxX != null && (me.chartRangeClipX || chartRangeMaxX > me.maxx)) {
            me.maxx = chartRangeMaxX;
        }
    },
    drawNormalRange: function(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey) {
        var normalRangeMin = this.getNormalRangeMin(),
            normalRangeMax = this.getNormalRangeMax(),
            ytop = canvasTop + Math.round(canvasHeight - (canvasHeight * ((normalRangeMax - this.miny) / rangey))),
            height = Math.round((canvasHeight * (normalRangeMax - normalRangeMin)) / rangey);
        this.canvas.drawRect(canvasLeft, ytop, canvasWidth, height, undefined, this.normalRangeColor).append();
    },
    renderGraph: function() {
        var me = this,
            canvas = me.canvas,
            canvasWidth = me.getWidth(),
            canvasHeight = me.getHeight(),
            vertices = me.vertices,
            spotRadius = me.getSpotRadius(),
            regionMap = me.regionMap,
            rangeX, Y, yvallast, canvasTop, canvasLeft, vertex, path, paths, x, y, xNext, xPos, xPosNext, last, next, yValCount, lineShapes, fillShapes, plen,
            valueSpots = me.getValueSpots(),
            hlSpotsEnabled, color, xValues, yValues, i,
            spotColor = me.getSpotColor(),
            minSpotColor = me.getMinSpotColor(),
            maxSpotColor = me.getMaxSpotColor(),
            normalRangeMin = me.getNormalRangeMin(),
            drawNormalOnTop = me.getDrawNormalOnTop();
        if (!me.callParent()) {
            return;
        }
        me.scanValues();
        me.processRangeOptions();
        xValues = me.xvalues;
        yValues = me.yvalues;
        if (!me.yminmax.length || me.yvalues.length < 2) {
            
            return;
        }
        canvasTop = canvasLeft = 0;
        rangeX = me.maxx - me.minx === 0 ? 1 : me.maxx - me.minx;
        Y = me.maxy - me.miny === 0 ? 1 : me.maxy - me.miny;
        yvallast = me.yvalues.length - 1;
        if (spotRadius && (canvasWidth < (spotRadius * 4) || canvasHeight < (spotRadius * 4))) {
            spotRadius = 0;
        }
        if (spotRadius) {
            
            hlSpotsEnabled = me.getHighlightSpotColor() && !me.disableInteraction;
            if (hlSpotsEnabled || minSpotColor || (spotColor && yValues[yvallast] === me.miny)) {
                canvasHeight -= Math.ceil(spotRadius);
            }
            if (hlSpotsEnabled || maxSpotColor || (spotColor && yValues[yvallast] === me.maxy)) {
                canvasHeight -= Math.ceil(spotRadius);
                canvasTop += Math.ceil(spotRadius);
            }
            if (hlSpotsEnabled || ((minSpotColor || maxSpotColor) && (yValues[0] === me.miny || yValues[0] === me.maxy))) {
                canvasLeft += Math.ceil(spotRadius);
                canvasWidth -= Math.ceil(spotRadius);
            }
            if (hlSpotsEnabled || spotColor || (minSpotColor || maxSpotColor && (yValues[yvallast] === me.miny || yValues[yvallast] === me.maxy))) {
                canvasWidth -= Math.ceil(spotRadius);
            }
        }
        canvasHeight--;
        if (normalRangeMin != null && !drawNormalOnTop) {
            me.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, Y);
        }
        path = [];
        paths = [
            path
        ];
        last = next = null;
        yValCount = yValues.length;
        for (i = 0; i < yValCount; i++) {
            x = xValues[i];
            xNext = xValues[i + 1];
            y = yValues[i];
            xPos = canvasLeft + Math.round((x - me.minx) * (canvasWidth / rangeX));
            xPosNext = i < yValCount - 1 ? canvasLeft + Math.round((xNext - me.minx) * (canvasWidth / rangeX)) : canvasWidth;
            next = xPos + ((xPosNext - xPos) / 2);
            regionMap[i] = [
                last || 0,
                next,
                i
            ];
            last = next;
            if (y === null) {
                if (i) {
                    if (yValues[i - 1] !== null) {
                        path = [];
                        paths.push(path);
                    }
                    vertices.push(null);
                }
            } else {
                if (y < me.miny) {
                    y = me.miny;
                }
                if (y > me.maxy) {
                    y = me.maxy;
                }
                if (!path.length) {
                    
                    path.push([
                        xPos,
                        canvasTop + canvasHeight
                    ]);
                }
                vertex = [
                    xPos,
                    canvasTop + Math.round(canvasHeight - (canvasHeight * ((y - this.miny) / Y)))
                ];
                path.push(vertex);
                vertices.push(vertex);
            }
        }
        lineShapes = [];
        fillShapes = [];
        plen = paths.length;
        for (i = 0; i < plen; i++) {
            path = paths[i];
            if (path.length) {
                if (me.fillColor) {
                    path.push([
                        path[path.length - 1][0],
                        (canvasTop + canvasHeight)
                    ]);
                    fillShapes.push(path.slice(0));
                    path.pop();
                }
                
                
                if (path.length > 2) {
                    
                    path[0] = [
                        path[0][0],
                        path[1][1]
                    ];
                }
                lineShapes.push(path);
            }
        }
        
        plen = fillShapes.length;
        for (i = 0; i < plen; i++) {
            canvas.drawShape(fillShapes[i], me.fillColor, me.fillColor).append();
        }
        if (normalRangeMin != null && drawNormalOnTop) {
            me.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, Y);
        }
        plen = lineShapes.length;
        for (i = 0; i < plen; i++) {
            canvas.drawShape(lineShapes[i], me.getLineColor(), null, me.getLineWidth()).append();
        }
        if (spotRadius && valueSpots) {
            if (valueSpots.get == null) {
                valueSpots = new Ext.sparkline.RangeMap(valueSpots);
            }
            for (i = 0; i < yValCount; i++) {
                color = valueSpots.get(yValues[i]);
                if (color) {
                    canvas.drawCircle(canvasLeft + Math.round((xValues[i] - me.minx) * (canvasWidth / rangeX)), canvasTop + Math.round(canvasHeight - (canvasHeight * ((yValues[i] - me.miny) / Y))), spotRadius, null, color).append();
                }
            }
        }
        if (spotRadius && spotColor && yValues[yvallast] != null) {
            canvas.drawCircle(canvasLeft + Math.round((xValues[xValues.length - 1] - me.minx) * (canvasWidth / rangeX)), canvasTop + Math.round(canvasHeight - (canvasHeight * ((yValues[yvallast] - me.miny) / Y))), spotRadius, null, spotColor).append();
        }
        if (me.maxy !== me.minyorg) {
            if (spotRadius && minSpotColor) {
                x = xValues[Ext.Array.indexOf(yValues, me.minyorg)];
                canvas.drawCircle(canvasLeft + Math.round((x - me.minx) * (canvasWidth / rangeX)), canvasTop + Math.round(canvasHeight - (canvasHeight * ((me.minyorg - me.miny) / Y))), spotRadius, null, minSpotColor).append();
            }
            if (spotRadius && maxSpotColor) {
                x = xValues[Ext.Array.indexOf(yValues, me.maxyorg)];
                canvas.drawCircle(canvasLeft + Math.round((x - me.minx) * (canvasWidth / rangeX)), canvasTop + Math.round(canvasHeight - (canvasHeight * ((me.maxyorg - me.miny) / Y))), spotRadius, null, maxSpotColor).append();
            }
        }
        me.canvasTop = canvasTop;
        
        if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) {
            me.updateDisplay();
        }
        canvas.render();
    }
});


Ext.define('Ext.sparkline.Pie', {
    extend: 'Ext.sparkline.Base',
    alias: 'widget.sparklinepie',
    config: {
        
        offset: 0,
        
        sliceColors: [
            '#3366cc',
            '#dc3912',
            '#ff9900',
            '#109618',
            '#66aa00',
            '#dd4477',
            '#0099c6',
            '#990099'
        ],
        
        borderWidth: 0,
        
        borderColor: '#000',
        tipTpl: new Ext.XTemplate('&#9679; {value} ({percent:number("0.0")}%)')
    },
    
    applyValues: function(newValues) {
        newValues = Ext.Array.map(Ext.Array.from(newValues), Number);
        this.disabled = !(newValues && newValues.length);
        return newValues;
    },
    onUpdate: function() {
        var me = this,
            values = me.values,
            total = 0,
            i;
        me.callParent(arguments);
        me.shapes = {};
        
        me.valueShapes = {};
        
        if (values.length > 0) {
            for (i = values.length; i--; ) {
                total += values[i];
            }
        }
        me.total = total;
        me.radius = Math.floor(Math.min(me.getWidth(), me.getHeight()) / 2);
    },
    getRegion: function(x, y) {
        var shapeid = this.canvas.getShapeAt(x, y);
        return (shapeid != null && this.shapes[shapeid] != null) ? this.shapes[shapeid] : null;
    },
    getRegionFields: function(region) {
        var sliceColors = this.getSliceColors();
        return {
            isNull: this.values[region] == null,
            value: this.values[region],
            percent: this.values[region] / this.total * 100,
            color: sliceColors[region % sliceColors.length],
            offset: region
        };
    },
    renderHighlight: function(region) {
        this.renderSlice(region, true).append();
    },
    renderSlice: function(valuenum, highlight) {
        var me = this,
            canvas = me.canvas,
            radius = me.radius,
            borderWidth = me.getBorderWidth(),
            offset = me.getOffset(),
            circle = 2 * Math.PI,
            values = me.values,
            total = me.total,
            next = offset ? (2 * Math.PI) * (offset / 360) : 0,
            start, end, i, vlen, color,
            sliceColors = this.getSliceColors();
        vlen = values.length;
        for (i = 0; i < vlen; i++) {
            start = next;
            end = next;
            if (total > 0) {
                
                end = next + (circle * (values[i] / total));
            }
            if (valuenum === i) {
                color = sliceColors[i % sliceColors.length];
                if (highlight) {
                    color = me.calcHighlightColor(color);
                }
                return canvas.drawPieSlice(radius, radius, radius - borderWidth, start, end, null, color);
            }
            next = end;
        }
    },
    renderGraph: function() {
        var me = this,
            canvas = me.canvas,
            values = me.values,
            radius = me.radius,
            borderWidth = me.getBorderWidth(),
            shape, i,
            shapes = me.shapes || (me.shapes = {}),
            valueShapes = me.valueShapes || (me.valueShapes = {});
        if (!me.callParent()) {
            return;
        }
        if (borderWidth) {
            canvas.drawCircle(radius, radius, Math.floor(radius - (borderWidth / 2)), me.getBorderColor(), null, borderWidth).append();
        }
        for (i = values.length; i--; ) {
            if (values[i]) {
                
                shape = me.renderSlice(i).append();
                valueShapes[i] = shape.id;
                
                shapes[shape.id] = i;
            }
        }
        
        if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) {
            me.currentRegion = null;
            me.updateDisplay();
        }
        canvas.render();
    }
});


Ext.define('Ext.sparkline.TriState', {
    extend: 'Ext.sparkline.BarBase',
    requires: [
        'Ext.sparkline.RangeMap'
    ],
    alias: 'widget.sparklinetristate',
    config: {
        
        barWidth: 4,
        
        barSpacing: 1,
        
        posBarColor: '#6f6',
        
        negBarColor: '#f44',
        
        zeroBarColor: '#999',
        
        colorMap: {},
        tipTpl: new Ext.XTemplate('&#9679; {value:this.states}',{
            states: function(v) {
                var value = Number(v);
                if (value === -1) {
                    return 'Loss';
                }
                if (value === 0) {
                    return 'Draw';
                }
                if (value === 1) {
                    return 'Win';
                }
                return v;
            }
        })
    },
    applyColorMap: function(colorMap) {
        var me = this;
        if (Ext.isArray(colorMap)) {
            me.colorMapByIndex = colorMap;
            me.colorMapByValue = null;
        } else {
            me.colorMapByIndex = null;
            me.colorMapByValue = colorMap;
            if (me.colorMapByValue && me.colorMapByValue.get == null) {
                me.colorMapByValue = new Ext.sparkline.RangeMap(colorMap);
            }
        }
        return colorMap;
    },
    
    applyValues: function(newValues) {
        newValues = Ext.Array.map(Ext.Array.from(newValues), Number);
        this.disabled = !(newValues && newValues.length);
        return newValues;
    },
    onUpdate: function() {
        this.totalBarWidth = this.getBarWidth() + this.getBarSpacing();
    },
    getBarWidth: function() {
        var values = this.values;
        return this._barWidth || (this.getWidth() - (values.length - 1) * this.getBarSpacing()) / values.length;
    },
    getRegion: function(x, y) {
        return Math.floor(x / this.totalBarWidth);
    },
    getRegionFields: function(region) {
        return {
            isNull: this.values[region] == null,
            value: this.values[region],
            color: this.calcColor(this.values[region], region),
            offset: region
        };
    },
    calcColor: function(value, valuenum) {
        var me = this,
            values = me.values,
            colorMapByIndex = me.colorMapByIndex,
            colorMapByValue = me.colorMapByValue,
            color, newColor;
        if (colorMapByValue && (newColor = colorMapByValue.get(value))) {
            color = newColor;
        } else if (colorMapByIndex && colorMapByIndex.length > valuenum) {
            color = colorMapByIndex[valuenum];
        } else if (values[valuenum] < 0) {
            color = me.getNegBarColor();
        } else if (values[valuenum] > 0) {
            color = me.getPosBarColor();
        } else {
            color = me.getZeroBarColor();
        }
        return color;
    },
    renderRegion: function(valuenum, highlight) {
        var me = this,
            values = me.values,
            canvas = me.canvas,
            canvasHeight, height, halfHeight, x, y, color;
        canvasHeight = canvas.pixelHeight;
        halfHeight = Math.round(canvasHeight / 2);
        x = valuenum * me.totalBarWidth;
        if (values[valuenum] < 0) {
            y = halfHeight;
            height = halfHeight - 1;
        } else if (values[valuenum] > 0) {
            y = 0;
            height = halfHeight - 1;
        } else {
            y = halfHeight - 1;
            height = 2;
        }
        color = me.calcColor(values[valuenum], valuenum);
        if (color == null) {
            return;
        }
        if (highlight) {
            color = me.calcHighlightColor(color);
        }
        canvas.drawRect(x, y, me.getBarWidth() - 1, height - 1, color, color).append();
    }
});


Ext.define('Ext.state.CookieProvider', {
    extend: 'Ext.state.Provider',
    
    
    
    
    
    constructor: function(config) {
        var me = this;
        me.path = "/";
        me.expires = new Date(Ext.Date.now() + (1000 * 60 * 60 * 24 * 7));
        
        me.domain = null;
        me.secure = false;
        me.callParent(arguments);
        me.state = me.readCookies();
    },
    
    set: function(name, value) {
        var me = this;
        if (typeof value == "undefined" || value === null) {
            me.clear(name);
            return;
        }
        me.setCookie(name, value);
        me.callParent(arguments);
    },
    
    clear: function(name) {
        this.clearCookie(name);
        this.callParent(arguments);
    },
    
    readCookies: function() {
        var cookies = {},
            c = document.cookie + ";",
            re = /\s?(.*?)=(.*?);/g,
            prefix = this.prefix,
            len = prefix.length,
            matches, name, value;
        while ((matches = re.exec(c)) != null) {
            name = matches[1];
            value = matches[2];
            if (name && name.substring(0, len) == prefix) {
                cookies[name.substr(len)] = this.decodeValue(value);
            }
        }
        return cookies;
    },
    
    setCookie: function(name, value) {
        var me = this;
        document.cookie = me.prefix + name + "=" + me.encodeValue(value) + ((me.expires == null) ? "" : ("; expires=" + me.expires.toUTCString())) + ((me.path == null) ? "" : ("; path=" + me.path)) + ((me.domain == null) ? "" : ("; domain=" + me.domain)) + ((me.secure == true) ? "; secure" : "");
    },
    
    clearCookie: function(name) {
        var me = this;
        document.cookie = me.prefix + name + "=null; expires=Thu, 01-Jan-1970 00:00:01 GMT" + ((me.path == null) ? "" : ("; path=" + me.path)) + ((me.domain == null) ? "" : ("; domain=" + me.domain)) + ((me.secure == true) ? "; secure" : "");
    }
});


Ext.define('Ext.state.LocalStorageProvider', {
    extend: 'Ext.state.Provider',
    requires: [
        'Ext.util.LocalStorage'
    ],
    alias: 'state.localstorage',
    constructor: function() {
        var me = this;
        me.callParent(arguments);
        me.store = me.getStorageObject();
        if (me.store) {
            me.state = me.readLocalStorage();
        } else {
            me.state = {};
        }
    },
    readLocalStorage: function() {
        var store = this.store,
            data = {},
            keys = store.getKeys(),
            i = keys.length,
            key;
        while (i--) {
            key = keys[i];
            data[key] = this.decodeValue(store.getItem(key));
        }
        return data;
    },
    set: function(name, value) {
        var me = this;
        me.clear(name);
        if (value != null) {
            
            me.store.setItem(name, me.encodeValue(value));
            me.callParent(arguments);
        }
    },
    
    clear: function(name) {
        this.store.removeItem(name);
        this.callParent(arguments);
    },
    getStorageObject: function() {
        var prefix = this.prefix,
            id = prefix,
            n = id.length - 1;
        if (id.charAt(n) === '-') {
            id = id.substring(0, n);
        }
        return new Ext.util.LocalStorage({
            id: id,
            prefix: prefix
        });
    }
});


Ext.define('Ext.toolbar.Breadcrumb', {
    extend: 'Ext.Container',
    xtype: 'breadcrumb',
    requires: [
        'Ext.data.TreeStore',
        'Ext.button.Split'
    ],
    mixins: [
        'Ext.util.FocusableContainer'
    ],
    isBreadcrumb: true,
    baseCls: Ext.baseCSSPrefix + 'breadcrumb',
    layout: 'hbox',
    config: {
        
        buttonUI: 'plain-toolbar',
        
        displayField: 'text',
        
        overflowHandler: null,
        
        showIcons: null,
        
        showMenuIcons: null,
        
        store: null,
        
        useSplitButtons: true
    },
    renderConfig: {
        
        selection: 'root'
    },
    publishes: [
        'selection'
    ],
    twoWayBindable: [
        'selection'
    ],
    _breadcrumbCls: Ext.baseCSSPrefix + 'breadcrumb',
    _btnCls: Ext.baseCSSPrefix + 'breadcrumb-btn',
    _folderIconCls: Ext.baseCSSPrefix + 'breadcrumb-icon-folder',
    _leafIconCls: Ext.baseCSSPrefix + 'breadcrumb-icon-leaf',
    initComponent: function() {
        var me = this,
            layout = me.layout,
            overflowHandler = me.getOverflowHandler();
        if (typeof layout === 'string') {
            layout = {
                type: layout
            };
        }
        if (overflowHandler) {
            layout.overflowHandler = overflowHandler;
        }
        me.layout = layout;
        
        me.defaultButtonUI = me.getButtonUI();
        
        me._buttons = [];
        me.addCls([
            me._breadcrumbCls,
            me._breadcrumbCls + '-' + me.ui
        ]);
        me.callParent();
    },
    onDestroy: function() {
        var me = this;
        me._buttons = Ext.destroy(me._buttons);
        me.setStore(null);
        me.callParent();
    },
    afterComponentLayout: function() {
        var me = this,
            overflowHandler = me.layout.overflowHandler;
        me.callParent(arguments);
        if (overflowHandler && me.tooNarrow && overflowHandler.scrollToItem) {
            overflowHandler.scrollToItem(me.getSelection().get('depth'));
        }
    },
    applySelection: function(node) {
        var store = this.getStore();
        if (store) {
            node = (node === 'root') ? this.getStore().getRoot() : node;
        } else {
            node = null;
        }
        return node;
    },
    updateSelection: function(node) {
        var me = this,
            buttons = me._buttons,
            items = [],
            itemCount = me.items.getCount(),
            needsSync = me._needsSync,
            displayField = me.getDisplayField(),
            showIcons, glyph, iconCls, icon, newItemCount, currentNode, text, button, id, depth, i;
        Ext.suspendLayouts();
        if (node) {
            currentNode = node;
            depth = node.get('depth');
            newItemCount = depth + 1;
            i = depth;
            while (currentNode) {
                id = currentNode.getId();
                button = buttons[i];
                if (!needsSync && button && button._breadcrumbNodeId === id) {
                    
                    break;
                }
                text = currentNode.get(displayField);
                if (button) {
                    
                    button.setText(text);
                } else {
                    
                    button = buttons[i] = Ext.create({
                        xtype: me.getUseSplitButtons() ? 'splitbutton' : 'button',
                        ui: me.getButtonUI(),
                        cls: me._btnCls + ' ' + me._btnCls + '-' + me.ui,
                        text: text,
                        showEmptyMenu: true,
                        
                        menu: {
                            listeners: {
                                click: '_onMenuClick',
                                beforeshow: '_onMenuBeforeShow',
                                scope: this
                            }
                        },
                        handler: '_onButtonClick',
                        scope: me
                    });
                }
                showIcons = this.getShowIcons();
                if (showIcons !== false) {
                    glyph = currentNode.get('glyph');
                    icon = currentNode.get('icon');
                    iconCls = currentNode.get('iconCls');
                    if (glyph) {
                        button.setGlyph(glyph);
                        button.setIcon(null);
                        button.setIconCls(iconCls);
                    }
                    
                    else if (icon) {
                        button.setGlyph(null);
                        button.setIconCls(null);
                        button.setIcon(icon);
                    } else if (iconCls) {
                        button.setGlyph(null);
                        button.setIcon(null);
                        button.setIconCls(iconCls);
                    } else if (showIcons) {
                        
                        button.setGlyph(null);
                        button.setIcon(null);
                        button.setIconCls((currentNode.isLeaf() ? me._leafIconCls : me._folderIconCls) + '-' + me.ui);
                    } else {
                        
                        button.setGlyph(null);
                        button.setIcon(null);
                        button.setIconCls(null);
                    }
                }
                button.setArrowVisible(currentNode.hasChildNodes());
                button._breadcrumbNodeId = currentNode.getId();
                currentNode = currentNode.parentNode;
                i--;
            }
            if (newItemCount > itemCount) {
                
                items = buttons.slice(itemCount, depth + 1);
                me.add(items);
            } else {
                
                
                for (i = itemCount - 1; i >= newItemCount; i--) {
                    me.remove(me.items.items[i], false);
                }
            }
        } else {
            
            me.removeAll(false);
        }
        Ext.resumeLayouts(true);
        
        me.fireEvent('selectionchange', me, node);
        me._needsSync = false;
    },
    applyUseSplitButtons: function(useSplitButtons, oldUseSplitButtons) {
        if (this.rendered && useSplitButtons !== oldUseSplitButtons) {
            Ext.Error.raise("Cannot reconfigure 'useSplitButtons' config of Ext.toolbar.Breadcrumb after initial render");
        }
        return useSplitButtons;
    },
    applyStore: function(store) {
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
        }
        return store;
    },
    updateStore: function(store, oldStore) {
        this._needsSync = true;
        if (store && !this.isConfiguring) {
            this.setSelection(store.getRoot());
        }
    },
    updateOverflowHandler: function(overflowHandler) {
        if (overflowHandler === 'menu') {
            Ext.Error.raise("Using Menu overflow with breadcrumb is not currently supported.");
        }
    },
    privates: {
        
        _onButtonClick: function(button, e) {
            if (this.getUseSplitButtons()) {
                this.setSelection(this.getStore().getNodeById(button._breadcrumbNodeId));
            }
        },
        
        _onMenuClick: function(menu, item, e) {
            this.setSelection(this.getStore().getNodeById(item._breadcrumbNodeId));
        },
        
        _onMenuBeforeShow: function(menu) {
            var me = this,
                node = me.getStore().getNodeById(menu.ownerCmp._breadcrumbNodeId),
                displayField = me.getDisplayField(),
                showMenuIcons = me.getShowMenuIcons(),
                childNodes, child, glyph, items, i, icon, iconCls, ln, item;
            if (node.hasChildNodes()) {
                childNodes = node.childNodes;
                items = [];
                for (i = 0 , ln = childNodes.length; i < ln; i++) {
                    child = childNodes[i];
                    item = {
                        text: child.get(displayField),
                        _breadcrumbNodeId: child.getId()
                    };
                    if (showMenuIcons !== false) {
                        glyph = child.get('glyph');
                        icon = child.get('icon');
                        iconCls = child.get('iconCls');
                        if (glyph) {
                            item.glyph = glyph;
                            item.iconCls = iconCls;
                        }
                        
                        else if (icon) {
                            item.icon = icon;
                        } else if (iconCls) {
                            item.iconCls = iconCls;
                        } else if (showMenuIcons) {
                            
                            item.iconCls = (child.isLeaf() ? me._leafIconCls : me._folderIconCls) + '-' + me.ui;
                        }
                    }
                    items.push(item);
                }
                menu.removeAll();
                menu.add(items);
            } else {
                
                return false;
            }
        }
    }
});


Ext.define('Ext.toolbar.Fill', {
    extend: 'Ext.Component',
    
    
    requires: [
        'Ext.toolbar.Toolbar'
    ],
    alias: 'widget.tbfill',
    alternateClassName: 'Ext.Toolbar.Fill',
    ariaRole: 'presentation',
    
    isFill: true,
    flex: 1
});


Ext.define('Ext.toolbar.Spacer', {
    extend: 'Ext.Component',
    
    
    requires: [
        'Ext.toolbar.Toolbar'
    ],
    alias: 'widget.tbspacer',
    alternateClassName: 'Ext.Toolbar.Spacer',
    baseCls: Ext.baseCSSPrefix + 'toolbar-spacer',
    ariaRole: 'presentation'
});


Ext.define('Ext.view.DragZone', {
    extend: 'Ext.dd.DragZone',
    containerScroll: false,
    constructor: function(config) {
        var me = this,
            view, ownerCt, el;
        Ext.apply(me, config);
        
        
        
        
        
        if (!me.ddGroup) {
            me.ddGroup = 'view-dd-zone-' + me.view.id;
        }
        
        
        
        
        
        
        
        view = me.view;
        ownerCt = view.ownerCt;
        
        
        if (ownerCt) {
            el = ownerCt.getTargetEl().dom;
        } else {
            el = view.el.dom.parentNode;
        }
        me.callParent([
            el
        ]);
        me.ddel = document.createElement('div');
        me.ddel.className = Ext.baseCSSPrefix + 'grid-dd-wrap';
    },
    init: function(id, sGroup, config) {
        var me = this,
            
            triggerEvent = Ext.supports.touchScroll ? 'itemlongpress' : 'itemmousedown',
            eventSpec = {
                scope: me
            };
        eventSpec[triggerEvent] = me.onItemMouseDown;
        me.initTarget(id, sGroup, config);
        me.view.mon(me.view, eventSpec);
    },
    onValidDrop: function(target, e, id) {
        this.callParent();
        
        target.el.focus();
    },
    onItemMouseDown: function(view, record, item, index, e) {
        if (!this.isPreventDrag(e, record, item, index)) {
            
            
            
            if (view.focusRow) {
                view.focusRow(record);
            }
            this.handleMouseDown(e);
        }
    },
    
    isPreventDrag: function(e, record, item, index) {
        return false;
    },
    getDragData: function(e) {
        var view = this.view,
            item = e.getTarget(view.getItemSelector());
        if (item) {
            return {
                copy: view.copy || (view.allowCopy && e.ctrlKey),
                event: e,
                view: view,
                ddel: this.ddel,
                item: item,
                records: view.getSelectionModel().getSelection(),
                fromPosition: Ext.fly(item).getXY()
            };
        }
    },
    onInitDrag: function(x, y) {
        var me = this,
            data = me.dragData,
            view = data.view,
            selectionModel = view.getSelectionModel(),
            record = view.getRecord(data.item);
        
        
        if (!selectionModel.isSelected(record)) {
            selectionModel.selectWithEvent(record, me.DDMInstance.mousedownEvent);
        }
        data.records = selectionModel.getSelection();
        Ext.fly(me.ddel).setHtml(me.getDragText());
        me.proxy.update(me.ddel);
        me.onStartDrag(x, y);
        return true;
    },
    getDragText: function() {
        var count = this.dragData.records.length;
        return Ext.String.format(this.dragText, count, count === 1 ? '' : 's');
    },
    getRepairXY: function(e, data) {
        return data ? data.fromPosition : false;
    }
});


Ext.define('Ext.tree.ViewDragZone', {
    extend: 'Ext.view.DragZone',
    isPreventDrag: function(e, record) {
        return (record.get('allowDrag') === false) || !!e.getTarget(this.view.expanderSelector);
    },
    getDragText: function() {
        var records = this.dragData.records,
            count = records.length,
            text = records[0].get(this.displayField),
            suffix = 's';
        if (count === 1 && text) {
            return text;
        } else if (!text) {
            suffix = '';
        }
        return Ext.String.format(this.dragText, count, suffix);
    },
    afterRepair: function() {
        var me = this,
            view = me.view,
            selectedRowCls = view.selectedItemCls,
            records = me.dragData.records,
            r,
            rLen = records.length,
            fly = Ext.fly,
            item;
        if (Ext.enableFx && me.repairHighlight) {
            
            for (r = 0; r < rLen; r++) {
                
                
                item = view.getNode(records[r]);
                
                
                fly(item.firstChild).highlight(me.repairHighlightColor, {
                    listeners: {
                        beforeanimate: function() {
                            if (view.isSelected(item)) {
                                fly(item).removeCls(selectedRowCls);
                            }
                        },
                        afteranimate: function() {
                            if (view.isSelected(item)) {
                                fly(item).addCls(selectedRowCls);
                            }
                        }
                    }
                });
            }
        }
        me.dragging = false;
    }
});


Ext.define('Ext.tree.ViewDropZone', {
    extend: 'Ext.view.DropZone',
    
    allowParentInserts: false,
    
    allowContainerDrops: false,
    
    appendOnly: false,
    
    expandDelay: 500,
    indicatorCls: Ext.baseCSSPrefix + 'tree-ddindicator',
    
    expandNode: function(node) {
        var view = this.view;
        this.expandProcId = false;
        if (!node.isLeaf() && !node.isExpanded()) {
            view.expand(node);
            this.expandProcId = false;
        }
    },
    
    queueExpand: function(node) {
        this.expandProcId = Ext.Function.defer(this.expandNode, this.expandDelay, this, [
            node
        ]);
    },
    
    cancelExpand: function() {
        if (this.expandProcId) {
            clearTimeout(this.expandProcId);
            this.expandProcId = false;
        }
    },
    getPosition: function(e, node) {
        var view = this.view,
            record = view.getRecord(node),
            y = e.getY(),
            noAppend = record.isLeaf(),
            noBelow = false,
            region = Ext.fly(node).getRegion(),
            fragment;
        
        if (record.isRoot()) {
            return 'append';
        }
        
        if (this.appendOnly) {
            return noAppend ? false : 'append';
        }
        if (!this.allowParentInserts) {
            noBelow = record.hasChildNodes() && record.isExpanded();
        }
        fragment = (region.bottom - region.top) / (noAppend ? 2 : 3);
        if (y >= region.top && y < (region.top + fragment)) {
            return 'before';
        } else if (!noBelow && (noAppend || (y >= (region.bottom - fragment) && y <= region.bottom))) {
            return 'after';
        } else {
            return 'append';
        }
    },
    isValidDropPoint: function(node, position, dragZone, e, data) {
        if (!node || !data.item) {
            return false;
        }
        var view = this.view,
            targetNode = view.getRecord(node),
            draggedRecords = data.records,
            dataLength = draggedRecords.length,
            ln = draggedRecords.length,
            i, record;
        
        if (!(targetNode && position && dataLength)) {
            return false;
        }
        
        for (i = 0; i < ln; i++) {
            record = draggedRecords[i];
            if (record.isNode && record.contains(targetNode)) {
                return false;
            }
        }
        
        if (position === 'append' && targetNode.get('allowDrop') === false) {
            return false;
        } else if (position !== 'append' && targetNode.parentNode.get('allowDrop') === false) {
            return false;
        }
        
        if (Ext.Array.contains(draggedRecords, targetNode)) {
            return false;
        }
        return view.fireEvent('nodedragover', targetNode, position, data, e) !== false;
    },
    onNodeOver: function(node, dragZone, e, data) {
        var position = this.getPosition(e, node),
            returnCls = this.dropNotAllowed,
            view = this.view,
            targetNode = view.getRecord(node),
            indicator = this.getIndicator(),
            indicatorY = 0;
        
        this.cancelExpand();
        if (position === 'append' && !this.expandProcId && !Ext.Array.contains(data.records, targetNode) && !targetNode.isLeaf() && !targetNode.isExpanded()) {
            this.queueExpand(targetNode);
        }
        if (this.isValidDropPoint(node, position, dragZone, e, data)) {
            this.valid = true;
            this.currentPosition = position;
            this.overRecord = targetNode;
            indicator.setWidth(Ext.fly(node).getWidth());
            indicatorY = Ext.fly(node).getY() - Ext.fly(view.el).getY() - 1;
            
            if (view.touchScroll === 2) {
                indicatorY += view.getScrollY();
            }
            
            if (position === 'before') {
                returnCls = targetNode.isFirst() ? Ext.baseCSSPrefix + 'tree-drop-ok-above' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
                indicator.showAt(0, indicatorY);
                dragZone.proxy.show();
            } else if (position === 'after') {
                returnCls = targetNode.isLast() ? Ext.baseCSSPrefix + 'tree-drop-ok-below' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
                indicatorY += Ext.fly(node).getHeight();
                indicator.showAt(0, indicatorY);
                dragZone.proxy.show();
            } else {
                returnCls = Ext.baseCSSPrefix + 'tree-drop-ok-append';
                
                indicator.hide();
            }
        } else {
            this.valid = false;
        }
        this.currentCls = returnCls;
        return returnCls;
    },
    
    onNodeOut: function(n, dd, e, data) {
        this.valid = false;
        this.getIndicator().hide();
    },
    onContainerOver: function(dd, e, data) {
        return this.allowContainerDrops ? this.dropAllowed : e.getTarget('.' + this.indicatorCls) ? this.currentCls : this.dropNotAllowed;
    },
    
    
    onContainerDrop: function(dragZone, e, data) {
        if (this.allowContainerDrops) {
            this.valid = true;
            this.currentPosition = 'append';
            this.overRecord = this.view.store.getRoot();
            this.onNodeDrop(this.overRecord, dragZone, e, data);
        }
    },
    notifyOut: function() {
        this.callParent(arguments);
        this.cancelExpand();
    },
    handleNodeDrop: function(data, targetNode, position) {
        var me = this,
            targetView = me.view,
            parentNode = targetNode ? targetNode.parentNode : targetView.panel.getRootNode(),
            Model = targetView.store.getModel(),
            records, i, len, record, insertionMethod, argList, needTargetExpand, transferData;
        
        if (data.copy) {
            records = data.records;
            data.records = [];
            for (i = 0 , len = records.length; i < len; i++) {
                record = records[i];
                if (record.isNode) {
                    data.records.push(record.copy());
                } else {
                    
                    data.records.push(new Model(Ext.apply({}, record.data)));
                }
            }
        }
        
        me.cancelExpand();
        
        
        
        
        if (position === 'before') {
            insertionMethod = parentNode.insertBefore;
            argList = [
                null,
                targetNode
            ];
            targetNode = parentNode;
        } else if (position === 'after') {
            if (targetNode.nextSibling) {
                insertionMethod = parentNode.insertBefore;
                argList = [
                    null,
                    targetNode.nextSibling
                ];
            } else {
                insertionMethod = parentNode.appendChild;
                argList = [
                    null
                ];
            }
            targetNode = parentNode;
        } else {
            if (!(targetNode.isExpanded() || targetNode.isLoading())) {
                needTargetExpand = true;
            }
            insertionMethod = targetNode.appendChild;
            argList = [
                null
            ];
        }
        
        transferData = function() {
            var color, n;
            
            Ext.suspendLayouts();
            
            for (i = 0 , len = data.records.length; i < len; i++) {
                record = data.records[i];
                if (!record.isNode) {
                    if (record.isModel) {
                        record = new Model(record.data,record.getId());
                    } else {
                        record = new Model(record);
                    }
                    data.records[i] = record;
                }
                argList[0] = record;
                insertionMethod.apply(targetNode, argList);
                
                targetView.getNavigationModel().setPosition(record);
            }
            
            if (me.sortOnDrop) {
                targetNode.sort(targetNode.getOwnerTree().store.getSorters().sortFn);
            }
            Ext.resumeLayouts(true);
            
            
            
            if (Ext.enableFx && me.dropHighlight) {
                color = me.dropHighlightColor;
                for (i = 0; i < len; i++) {
                    n = targetView.getNode(data.records[i]);
                    if (n) {
                        Ext.fly(n).highlight(color);
                    }
                }
            }
        };
        
        if (needTargetExpand) {
            targetNode.expand(false, transferData);
        }
        
        
        
        
        
        else if (targetNode.isLoading()) {
            targetNode.on({
                expand: transferData,
                delay: 1,
                single: true
            });
        } else 
        {
            transferData();
        }
    }
});


Ext.define('Ext.tree.plugin.TreeViewDragDrop', {
    extend: 'Ext.plugin.Abstract',
    alias: 'plugin.treeviewdragdrop',
    uses: [
        'Ext.tree.ViewDragZone',
        'Ext.tree.ViewDropZone'
    ],
    
    
    
    
    dragText: '{0} selected node{1}',
    
    
    allowParentInserts: false,
    
    allowContainerDrops: false,
    
    appendOnly: false,
    
    ddGroup: "TreeDD",
    
    containerScroll: false,
    
    
    
    
    expandDelay: 1000,
    
    enableDrop: true,
    
    enableDrag: true,
    
    nodeHighlightColor: 'c3daf9',
    
    nodeHighlightOnDrop: Ext.enableFx,
    
    
    displayField: 'text',
    
    
    
    
    init: function(view) {
        view.on('render', this.onViewRender, this, {
            single: true
        });
    },
    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },
    onViewRender: function(view) {
        var me = this,
            scrollEl;
        if (me.enableDrag) {
            if (me.containerScroll) {
                scrollEl = view.getEl();
            }
            me.dragZone = new Ext.tree.ViewDragZone(Ext.apply({
                view: view,
                ddGroup: me.dragGroup || me.ddGroup,
                dragText: me.dragText,
                displayField: me.displayField,
                repairHighlightColor: me.nodeHighlightColor,
                repairHighlight: me.nodeHighlightOnRepair,
                scrollEl: scrollEl
            }, me.dragZone));
        }
        if (me.enableDrop) {
            me.dropZone = new Ext.tree.ViewDropZone(Ext.apply({
                view: view,
                ddGroup: me.dropGroup || me.ddGroup,
                allowContainerDrops: me.allowContainerDrops,
                appendOnly: me.appendOnly,
                allowParentInserts: me.allowParentInserts,
                expandDelay: me.expandDelay,
                dropHighlightColor: me.nodeHighlightColor,
                dropHighlight: me.nodeHighlightOnDrop,
                sortOnDrop: me.sortOnDrop,
                containerScroll: me.containerScroll
            }, me.dropZone));
        }
    }
}, function() {
    var proto = this.prototype;
    proto.nodeHighlightOnDrop = proto.nodeHighlightOnRepair = Ext.enableFx;
});


Ext.define('Ext.util.CSS', function() {
    var CSS,
        rules = null,
        doc = document,
        camelRe = /(-[a-z])/gi,
        camelFn = function(m, a) {
            return a.charAt(1).toUpperCase();
        };
    return {
        singleton: true,
        rules: rules,
        initialized: false,
        constructor: function() {
            
            CSS = this;
        },
        
        createStyleSheet: function(cssText, id) {
            var ss,
                head = doc.getElementsByTagName('head')[0],
                styleEl = doc.createElement('style');
            styleEl.setAttribute('type', 'text/css');
            if (id) {
                styleEl.setAttribute('id', id);
            }
            
            ss = styleEl.styleSheet;
            if (ss) {
                head.appendChild(styleEl);
                ss.cssText = cssText;
            } else {
                styleEl.appendChild(doc.createTextNode(cssText));
                head.appendChild(styleEl);
                ss = styleEl.sheet;
            }
            CSS.cacheStyleSheet(ss);
            return ss;
        },
        
        removeStyleSheet: function(id) {
            var existing = doc.getElementById(id);
            if (existing) {
                existing.parentNode.removeChild(existing);
            }
        },
        
        swapStyleSheet: function(id, url) {
            var ss;
            CSS.removeStyleSheet(id);
            ss = doc.createElement("link");
            ss.setAttribute("rel", "stylesheet");
            ss.setAttribute("type", "text/css");
            ss.setAttribute("id", id);
            ss.setAttribute("href", url);
            doc.getElementsByTagName("head")[0].appendChild(ss);
        },
        
        cacheStyleSheet: function(ss) {
            if (!rules) {
                rules = CSS.rules = {};
            }
            try {
                
                var ssRules = ss.cssRules || ss.rules,
                    i = ssRules.length - 1,
                    imports = ss.imports,
                    len = imports ? imports.length : 0,
                    rule, j;
                
                for (j = 0; j < len; ++j) {
                    CSS.cacheStyleSheet(imports[j]);
                }
                for (; i >= 0; --i) {
                    rule = ssRules[i];
                    
                    if (rule.styleSheet) {
                        CSS.cacheStyleSheet(rule.styleSheet);
                    }
                    CSS.cacheRule(rule, ss);
                }
            } catch (e) {}
        },
        cacheRule: function(cssRule, styleSheet) {
            
            if (cssRule.styleSheet) {
                return CSS.cacheStyleSheet(cssRule.styleSheet);
            }
            var selectorText = cssRule.selectorText,
                selectorCount, j;
            if (selectorText) {
                
                selectorText = selectorText.split(',');
                selectorCount = selectorText.length;
                for (j = 0; j < selectorCount; j++) {
                    
                    
                    rules[Ext.String.trim(selectorText[j]).toLowerCase()] = {
                        parentStyleSheet: styleSheet,
                        cssRule: cssRule
                    };
                }
            }
        },
        
        getRules: function(refreshCache) {
            var result = {},
                selector;
            if (rules === null || refreshCache) {
                CSS.refreshCache();
            }
            for (selector in rules) {
                result[selector] = rules[selector].cssRule;
            }
            return result;
        },
        
        refreshCache: function() {
            var ds = doc.styleSheets,
                i = 0,
                len = ds.length;
            rules = CSS.rules = {};
            for (; i < len; i++) {
                try {
                    if (!ds[i].disabled) {
                        CSS.cacheStyleSheet(ds[i]);
                    }
                } catch (e) {}
            }
        },
        
        getRule: function(selector, refreshCache, rawCache) {
            var i, result;
            if (!rules || refreshCache) {
                CSS.refreshCache();
            }
            if (!Ext.isArray(selector)) {
                result = rules[selector.toLowerCase()];
                if (result && !rawCache) {
                    result = result.cssRule;
                }
                return result || null;
            }
            for (i = 0; i < selector.length; i++) {
                if (rules[selector[i]]) {
                    return rawCache ? rules[selector[i].toLowerCase()] : rules[selector[i].toLowerCase()].cssRule;
                }
            }
            return null;
        },
        
        createRule: function(styleSheet, selector, cssText) {
            var result,
                ruleSet = styleSheet.cssRules || styleSheet.rules,
                index = ruleSet.length;
            if (styleSheet.insertRule) {
                styleSheet.insertRule(selector + ' {' + cssText + '}', index);
            } else {
                styleSheet.addRule(selector, cssText || ' ');
            }
            CSS.cacheRule(result = ruleSet[index], styleSheet);
            return result;
        },
        
        updateRule: function(selector, property, value) {
            var rule, i, styles;
            if (!Ext.isArray(selector)) {
                rule = CSS.getRule(selector);
                if (rule) {
                    
                    if (arguments.length === 2) {
                        styles = Ext.Element.parseStyles(property);
                        for (property in styles) {
                            rule.style[property.replace(camelRe, camelFn)] = styles[property];
                        }
                    } else {
                        rule.style[property.replace(camelRe, camelFn)] = value;
                    }
                    return true;
                }
            } else {
                for (i = 0; i < selector.length; i++) {
                    if (CSS.updateRule(selector[i], property, value)) {
                        return true;
                    }
                }
            }
            return false;
        },
        deleteRule: function(selector) {
            var rule = CSS.getRule(selector, false, true),
                styleSheet, index;
            if (rule) {
                styleSheet = rule.parentStyleSheet;
                index = Ext.Array.indexOf(styleSheet.cssRules || styleSheet.rules, rule.cssRule);
                if (styleSheet.deleteRule) {
                    styleSheet.deleteRule(index);
                } else {
                    styleSheet.removeRule(index);
                }
                delete rules[selector];
            }
        }
    };
});


Ext.define('Ext.util.Cookies', {
    singleton: true,
    
    set: function(name, value) {
        var argv = arguments,
            argc = arguments.length,
            expires = (argc > 2) ? argv[2] : null,
            path = (argc > 3) ? argv[3] : '/',
            domain = (argc > 4) ? argv[4] : null,
            secure = (argc > 5) ? argv[5] : false;
        document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toUTCString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
    },
    
    get: function(name) {
        var parts = document.cookie.split('; '),
            len = parts.length,
            item, i, ret;
        
        
        
        
        
        for (i = 0; i < len; ++i) {
            item = parts[i].split('=');
            if (item[0] === name) {
                ret = item[1];
                return ret ? unescape(ret) : '';
            }
        }
        return null;
    },
    
    clear: function(name, path) {
        if (this.get(name)) {
            path = path || '/';
            document.cookie = name + '=' + '; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=' + path;
        }
    }
});


Ext.define('Ext.view.MultiSelectorSearch', {
    extend: 'Ext.panel.Panel',
    xtype: 'multiselector-search',
    layout: 'fit',
    floating: true,
    resizable: true,
    minWidth: 200,
    minHeight: 200,
    border: true,
    defaultListenerScope: true,
    referenceHolder: true,
    
    searchText: 'Search...',
    initComponent: function() {
        var me = this,
            owner = me.owner,
            items = me.makeItems(),
            i, item, records, store;
        me.dockedItems = me.makeDockedItems();
        me.items = items;
        store = Ext.data.StoreManager.lookup(me.store);
        for (i = items.length; i--; ) {
            if ((item = items[i]).xtype === 'grid') {
                item.store = store;
                item.isSearchGrid = true;
                item.selModel = item.selModel || {
                    type: 'checkboxmodel',
                    pruneRemoved: false,
                    listeners: {
                        selectionchange: 'onSelectionChange'
                    }
                };
                Ext.merge(item, me.grid);
                if (!item.columns) {
                    item.hideHeaders = true;
                    item.columns = [
                        {
                            flex: 1,
                            dataIndex: me.field
                        }
                    ];
                }
                break;
            }
        }
        me.callParent();
        records = me.getOwnerStore().getRange();
        if (!owner.convertSelectionRecord.$nullFn) {
            for (i = records.length; i--; ) {
                records[i] = owner.convertSelectionRecord(records[i]);
            }
        }
        if (store.isLoading() || (store.loadCount === 0 && !store.getCount())) {
            store.on('load', function() {
                if (!me.isDestroyed) {
                    me.selectRecords(records);
                }
            }, null, {
                single: true
            });
        } else {
            me.selectRecords(records);
        }
    },
    getOwnerStore: function() {
        return this.owner.getStore();
    },
    afterShow: function() {
        var searchField = this.lookupReference('searchField');
        this.callParent(arguments);
        if (searchField) {
            searchField.focus();
        }
    },
    
    getSearchStore: function() {
        var searchGrid = this.lookupReference('searchGrid');
        return searchGrid.getStore();
    },
    makeDockedItems: function() {
        return [
            {
                xtype: 'textfield',
                reference: 'searchField',
                dock: 'top',
                hideFieldLabel: true,
                emptyText: this.searchText,
                triggers: {
                    clear: {
                        cls: Ext.baseCSSPrefix + 'form-clear-trigger',
                        handler: 'onClearSearch',
                        hidden: true
                    }
                },
                listeners: {
                    change: 'onSearchChange',
                    buffer: 300
                }
            }
        ];
    },
    makeItems: function() {
        return [
            {
                xtype: 'grid',
                reference: 'searchGrid',
                trailingBufferZone: 2,
                leadingBufferZone: 2,
                viewConfig: {
                    deferEmptyText: false,
                    emptyText: 'No results.'
                }
            }
        ];
    },
    selectRecords: function(records) {
        var searchGrid = this.lookupReference('searchGrid');
        return searchGrid.getSelectionModel().select(records);
    },
    deselectRecords: function(records) {
        var searchGrid = this.lookupReference('searchGrid');
        return searchGrid.getSelectionModel().deselect(records);
    },
    search: function(text) {
        var me = this,
            filter = me.searchFilter,
            filters = me.getSearchStore().getFilters();
        if (text) {
            filters.beginUpdate();
            if (filter) {
                filter.setValue(text);
            } else {
                me.searchFilter = filter = new Ext.util.Filter({
                    id: 'search',
                    property: me.field,
                    value: text
                });
            }
            filters.add(filter);
            filters.endUpdate();
        } else if (filter) {
            filters.remove(filter);
        }
    },
    privates: {
        onClearSearch: function() {
            var searchField = this.lookupReference('searchField');
            searchField.setValue(null);
            searchField.focus();
        },
        onSearchChange: function(searchField) {
            var value = searchField.getValue(),
                trigger = searchField.getTrigger('clear');
            trigger.setHidden(!value);
            this.search(value);
        },
        onSelectionChange: function(selModel, selection) {
            var owner = this.owner,
                store = owner.getStore(),
                data = store.data,
                remove = 0,
                map = {},
                add, i, id, record;
            for (i = selection.length; i--; ) {
                record = selection[i];
                id = record.id;
                map[id] = record;
                if (!data.containsKey(id)) {
                    (add || (add = [])).push(owner.convertSearchRecord(record));
                }
            }
            for (i = data.length; i--; ) {
                record = data.getAt(i);
                if (!map[record.id]) {
                    (remove || (remove = [])).push(record);
                }
            }
            if (add || remove) {
                data.splice(data.length, remove, add);
            }
        }
    }
});


Ext.define('Ext.view.MultiSelector', {
    extend: 'Ext.grid.Panel',
    xtype: 'multiselector',
    config: {
        
        search: {
            xtype: 'multiselector-search',
            width: 200,
            height: 200,
            store: {
                autoLoad: true
            }
        }
    },
    
    fieldName: 'name',
    
    fieldTitle: null,
    
    removeRowText: '✖',
    
    removeRowTip: 'Remove this item',
    emptyText: 'Nothing selected',
    
    addToolText: 'Search for items to add',
    initComponent: function() {
        var me = this,
            emptyText = me.emptyText,
            store = me.getStore(),
            search = me.getSearch(),
            fieldTitle = me.fieldTitle,
            searchStore, model;
        if (!search) {
            Ext.Error.raise('The search configuration is required for the multi selector');
        }
        searchStore = search.store;
        if (searchStore.isStore) {
            model = searchStore.getModel();
        } else {
            model = searchStore.model;
        }
        if (!store) {
            me.store = {
                model: model
            };
        }
        if (emptyText && !me.viewConfig) {
            me.viewConfig = {
                deferEmptyText: false,
                emptyText: emptyText
            };
        }
        if (!me.columns) {
            me.hideHeaders = !fieldTitle;
            me.columns = [
                {
                    text: fieldTitle,
                    dataIndex: me.fieldName,
                    flex: 1
                },
                me.makeRemoveRowColumn()
            ];
        }
        me.callParent();
    },
    addTools: function() {
        this.addTool({
            type: 'plus',
            tooltip: this.addToolText,
            callback: 'onShowSearch',
            scope: this
        });
    },
    convertSearchRecord: Ext.identityFn,
    convertSelectionRecord: Ext.identityFn,
    makeRemoveRowColumn: function() {
        var me = this;
        return {
            width: 22,
            menuDisabled: true,
            tdCls: Ext.baseCSSPrefix + 'multiselector-remove',
            processEvent: me.processRowEvent.bind(me),
            renderer: me.renderRemoveRow,
            updater: Ext.emptyFn,
            scope: me
        };
    },
    processRowEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {
        if (e.type !== 'click') {
            return;
        }
        if (Ext.fly(cell).hasCls(Ext.baseCSSPrefix + 'multiselector-remove')) {
            this.store.remove(record);
            if (this.searchPopup) {
                this.searchPopup.deselectRecords(record);
            }
        }
    },
    renderRemoveRow: function() {
        return '<span data-qtip="' + this.removeRowTip + '" role="button">' + this.removeRowText + '</span>';
    },
    beforeDestroy: function() {
        Ext.un({
            mousedown: 'onDismissSearch',
            scope: this
        });
        this.callParent();
    },
    privates: {
        onDismissSearch: function(e) {
            var searchPopup = this.searchPopup;
            if (searchPopup && !(searchPopup.owns(e.getTarget()) || this.owns(e.getTarget()))) {
                Ext.un({
                    mousedown: 'onDismissSearch',
                    scope: this
                });
                searchPopup.hide();
            }
        },
        onShowSearch: function(panel, tool) {
            var me = this,
                searchPopup = me.searchPopup,
                store = me.getStore();
            if (!searchPopup) {
                searchPopup = Ext.merge({
                    owner: me,
                    field: me.fieldName,
                    floating: true
                }, me.getSearch());
                me.searchPopup = searchPopup = me.add(searchPopup);
                
                
                if (store.getCount()) {
                    searchPopup.selectRecords(store.getRange());
                }
            }
            searchPopup.showBy(me, 'tl-tr?');
            Ext.on({
                mousedown: 'onDismissSearch',
                scope: me
            });
        }
    }
});



Ext.define('Ext.window.Toast', {
    extend: 'Ext.window.Window',
    xtype: 'toast',
    isToast: true,
    cls: Ext.baseCSSPrefix + 'toast',
    bodyPadding: 10,
    autoClose: true,
    plain: false,
    draggable: false,
    resizable: false,
    shadow: false,
    focus: Ext.emptyFn,
    
    anchor: null,
    
    useXAxis: false,
    
    align: 'br',
    
    
    animate: true,
    
    spacing: 6,
    
    
    
    paddingX: 30,
    paddingY: 10,
    slideInAnimation: 'easeIn',
    slideBackAnimation: 'bounceOut',
    slideInDuration: 1500,
    slideBackDuration: 1000,
    hideDuration: 500,
    autoCloseDelay: 3000,
    stickOnClick: true,
    stickWhileHover: true,
    closeOnMouseDown: false,
    
    isHiding: false,
    isFading: false,
    destroyAfterHide: false,
    closeOnMouseOut: false,
    
    xPos: 0,
    yPos: 0,
    initComponent: function() {
        var me = this;
        me.updateAlignment(me.align);
        me.setAnchor(me.anchor);
        me.callParent();
    },
    onRender: function() {
        var me = this;
        me.callParent(arguments);
        me.el.hover(me.onMouseEnter, me.onMouseLeave, me);
        
        if (me.closeOnMouseDown) {
            Ext.getDoc().on('mousedown', me.onDocumentMousedown, me);
        }
    },
    
    alignmentProps: {
        br: {
            paddingFactorX: -1,
            paddingFactorY: -1,
            siblingAlignment: "br-br",
            anchorAlign: "tr-br"
        },
        bl: {
            paddingFactorX: 1,
            paddingFactorY: -1,
            siblingAlignment: "bl-bl",
            anchorAlign: "tl-bl"
        },
        tr: {
            paddingFactorX: -1,
            paddingFactorY: 1,
            siblingAlignment: "tr-tr",
            anchorAlign: "br-tr"
        },
        tl: {
            paddingFactorX: 1,
            paddingFactorY: 1,
            siblingAlignment: "tl-tl",
            anchorAlign: "bl-tl"
        },
        b: {
            paddingFactorX: 0,
            paddingFactorY: -1,
            siblingAlignment: "b-b",
            useXAxis: 0,
            anchorAlign: "t-b"
        },
        t: {
            paddingFactorX: 0,
            paddingFactorY: 1,
            siblingAlignment: "t-t",
            useXAxis: 0,
            anchorAlign: "b-t"
        },
        l: {
            paddingFactorX: 1,
            paddingFactorY: 0,
            siblingAlignment: "l-l",
            useXAxis: 1,
            anchorAlign: "r-l"
        },
        r: {
            paddingFactorX: -1,
            paddingFactorY: 0,
            siblingAlignment: "r-r",
            useXAxis: 1,
            anchorAlign: "l-r"
        },
        
        x: {
            br: {
                anchorAlign: "bl-br"
            },
            bl: {
                anchorAlign: "br-bl"
            },
            tr: {
                anchorAlign: "tl-tr"
            },
            tl: {
                anchorAlign: "tr-tl"
            }
        }
    },
    updateAlignment: function(align) {
        var me = this,
            alignmentProps = me.alignmentProps,
            props = alignmentProps[align],
            xprops = alignmentProps.x[align];
        if (xprops && me.useXAxis) {
            Ext.applyIf(me, xprops);
        }
        Ext.applyIf(me, props);
    },
    getXposAlignedToAnchor: function() {
        var me = this,
            align = me.align,
            anchor = me.anchor,
            anchorEl = anchor && anchor.el,
            el = me.el,
            xPos = 0;
        
        if (anchorEl && anchorEl.dom) {
            if (!me.useXAxis) {
                
                xPos = el.getLeft();
            }
            
            
            else if (align === 'br' || align === 'tr' || align === 'r') {
                xPos += anchorEl.getAnchorXY('r')[0];
                xPos -= (el.getWidth() + me.paddingX);
            } else {
                xPos += anchorEl.getAnchorXY('l')[0];
                xPos += me.paddingX;
            }
        }
        return xPos;
    },
    getYposAlignedToAnchor: function() {
        var me = this,
            align = me.align,
            anchor = me.anchor,
            anchorEl = anchor && anchor.el,
            el = me.el,
            yPos = 0;
        
        if (anchorEl && anchorEl.dom) {
            if (me.useXAxis) {
                
                yPos = el.getTop();
            }
            
            
            else if (align === 'br' || align === 'bl' || align === 'b') {
                yPos += anchorEl.getAnchorXY('b')[1];
                yPos -= (el.getHeight() + me.paddingY);
            } else {
                yPos += anchorEl.getAnchorXY('t')[1];
                yPos += me.paddingY;
            }
        }
        return yPos;
    },
    getXposAlignedToSibling: function(sibling) {
        var me = this,
            align = me.align,
            el = me.el,
            xPos;
        if (!me.useXAxis) {
            xPos = el.getLeft();
        } else if (align === 'tl' || align === 'bl' || align === 'l') {
            
            xPos = (sibling.xPos + sibling.el.getWidth() + sibling.spacing);
        } else {
            
            xPos = (sibling.xPos - el.getWidth() - me.spacing);
        }
        return xPos;
    },
    getYposAlignedToSibling: function(sibling) {
        var me = this,
            align = me.align,
            el = me.el,
            yPos;
        if (me.useXAxis) {
            yPos = el.getTop();
        } else if (align === 'tr' || align === 'tl' || align === 't') {
            
            yPos = (sibling.yPos + sibling.el.getHeight() + sibling.spacing);
        } else {
            
            yPos = (sibling.yPos - el.getHeight() - sibling.spacing);
        }
        return yPos;
    },
    getToasts: function() {
        var anchor = this.anchor,
            alignment = this.anchorAlign,
            activeToasts = anchor.activeToasts || (anchor.activeToasts = {});
        return activeToasts[alignment] || (activeToasts[alignment] = []);
    },
    setAnchor: function(anchor) {
        var me = this,
            Toast;
        me.anchor = anchor = ((typeof anchor === 'string') ? Ext.getCmp(anchor) : anchor);
        
        
        if (!anchor) {
            Toast = Ext.window.Toast;
            me.anchor = Toast.bodyAnchor || (Toast.bodyAnchor = {
                el: Ext.getBody()
            });
        }
    },
    beforeShow: function() {
        var me = this;
        if (me.stickOnClick) {
            me.body.on('click', function() {
                me.cancelAutoClose();
            });
        }
        if (me.autoClose) {
            if (!me.closeTask) {
                me.closeTask = new Ext.util.DelayedTask(me.doAutoClose,me);
            }
            me.closeTask.delay(me.autoCloseDelay);
        }
        
        me.el.setX(-10000);
        me.el.setOpacity(1);
    },
    afterShow: function() {
        var me = this,
            el = me.el,
            activeToasts, sibling, length, xy;
        me.callParent(arguments);
        activeToasts = me.getToasts();
        length = activeToasts.length;
        sibling = length && activeToasts[length - 1];
        if (sibling) {
            el.alignTo(sibling.el, me.siblingAlignment, [
                0,
                0
            ]);
            me.xPos = me.getXposAlignedToSibling(sibling);
            me.yPos = me.getYposAlignedToSibling(sibling);
        } else {
            el.alignTo(me.anchor.el, me.anchorAlign, [
                (me.paddingX * me.paddingFactorX),
                (me.paddingY * me.paddingFactorY)
            ], false);
            me.xPos = me.getXposAlignedToAnchor();
            me.yPos = me.getYposAlignedToAnchor();
        }
        Ext.Array.include(activeToasts, me);
        if (me.animate) {
            
            
            xy = el.getXY();
            el.animate({
                from: {
                    x: xy[0],
                    y: xy[1]
                },
                to: {
                    x: me.xPos,
                    y: me.yPos,
                    opacity: 1
                },
                easing: me.slideInAnimation,
                duration: me.slideInDuration,
                dynamic: true
            });
        } else {
            me.setLocalXY(me.xPos, me.yPos);
        }
    },
    onDocumentMousedown: function(e) {
        if (this.isVisible() && !this.owns(e.getTarget())) {
            this.hide();
        }
    },
    slideBack: function() {
        var me = this,
            anchor = me.anchor,
            anchorEl = anchor && anchor.el,
            el = me.el,
            activeToasts = me.getToasts(),
            index = Ext.Array.indexOf(activeToasts, me);
        
        if (!me.isHiding && el && el.dom && anchorEl && anchorEl.isVisible()) {
            if (index) {
                me.xPos = me.getXposAlignedToSibling(activeToasts[index - 1]);
                me.yPos = me.getYposAlignedToSibling(activeToasts[index - 1]);
            } else {
                me.xPos = me.getXposAlignedToAnchor();
                me.yPos = me.getYposAlignedToAnchor();
            }
            me.stopAnimation();
            if (me.animate) {
                el.animate({
                    to: {
                        x: me.xPos,
                        y: me.yPos
                    },
                    easing: me.slideBackAnimation,
                    duration: me.slideBackDuration,
                    dynamic: true
                });
            }
        }
    },
    update: function() {
        var me = this;
        if (me.isVisible()) {
            me.isHiding = true;
            me.hide();
        }
        
        me.callParent(arguments);
        me.show();
    },
    cancelAutoClose: function() {
        var closeTask = this.closeTask;
        if (closeTask) {
            closeTask.cancel();
        }
    },
    doAutoClose: function() {
        var me = this;
        if (!(me.stickWhileHover && me.mouseIsOver)) {
            
            me.close();
        } else {
            
            me.closeOnMouseOut = true;
        }
    },
    onMouseEnter: function() {
        this.mouseIsOver = true;
    },
    onMouseLeave: function() {
        var me = this;
        me.mouseIsOver = false;
        if (me.closeOnMouseOut) {
            me.closeOnMouseOut = false;
            me.close();
        }
    },
    removeFromAnchor: function() {
        var me = this,
            activeToasts, index;
        if (me.anchor) {
            activeToasts = me.getToasts();
            index = Ext.Array.indexOf(activeToasts, me);
            if (index !== -1) {
                Ext.Array.erase(activeToasts, index, 1);
                
                for (; index < activeToasts.length; index++) {
                    activeToasts[index].slideBack();
                }
            }
        }
    },
    getFocusEl: Ext.emptyFn,
    hide: function() {
        var me = this,
            el = me.el;
        me.cancelAutoClose();
        if (me.isHiding) {
            if (!me.isFading) {
                me.callParent(arguments);
                
                me.removeFromAnchor();
                me.isHiding = false;
            }
        } else {
            
            me.isHiding = true;
            me.isFading = true;
            me.cancelAutoClose();
            if (el) {
                if (me.animate) {
                    el.fadeOut({
                        opacity: 0,
                        easing: 'easeIn',
                        duration: me.hideDuration,
                        listeners: {
                            afteranimate: function() {
                                me.isFading = false;
                                me.hide(me.animateTarget, me.doClose, me);
                            }
                        }
                    });
                } else {
                    me.isFading = false;
                    me.hide(me.animateTarget, me.doClose, me);
                }
            }
        }
        return me;
    }
}, function(Toast) {
    Ext.toast = function(message, title, align, iconCls) {
        var config = message,
            toast;
        if (Ext.isString(message)) {
            config = {
                title: title,
                html: message,
                iconCls: iconCls
            };
            if (align) {
                config.align = align;
            }
        }
        toast = new Toast(config);
        toast.show();
        return toast;
    };
});

})(this.Ext5 || (this.Ext5 = {}));
