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

Copyright (c) 2011-2018 Sencha Inc

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

This version of Sencha Ext JS 6.5.3.57 is licensed commercially for a limited period for evaluation 
purposes only. Production use or use beyond the applicable evaluation period is prohibited 
under this license.

If your trial has expired, please contact the sales department at http://www.sencha.com/contact.

Version: 6.5.3.57 Build date: 2018-01-23 06:16:04 (79adf230ffe196c51f938f82140c44b1174dc392)

*/
// @tag core


var Ext = Ext || {};


Ext.Boot = Ext.Boot || (function(emptyFn) {
    var doc = document,
        _emptyArray = [],
        _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: 'UTF-8'
        },
        _assetConfig = {},
        cssRe = /\.css(?:\?|$)/i,
        resolverEl = doc.createElement('a'),
        isBrowser = typeof window !== 'undefined',
        _environment = {
            browser: isBrowser,
            node: !isBrowser && (typeof require === 'function'),
            phantom: (window && (window._phantom || window.callPhantom)) || /PhantomJS/.test(window.navigator.userAgent)
        },
        _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;
        },
        _merge = function() {
            var lowerCase = false,
                obj = Array.prototype.shift.call(arguments),
                index, i, len, value;
            if (typeof arguments[arguments.length - 1] === 'boolean') {
                lowerCase = Array.prototype.pop.call(arguments);
            }
            len = arguments.length;
            for (index = 0; index < len; index++) {
                value = arguments[index];
                if (typeof value === 'object') {
                    for (i in value) {
                        obj[lowerCase ? i.toLowerCase() : i] = value[i];
                    }
                }
            }
            return obj;
        },
        _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;
        },
        
        Boot = {
            loading: 0,
            loaded: 0,
            apply: _apply,
            env: _environment,
            config: _config,
            
            assetConfig: _assetConfig,
            
            
            scripts: {},
            
            
            currentFile: null,
            suspendedQueue: [],
            currentRequest: null,
            
            
            syncMode: false,
            
            
            debug: _debug,
            
            
            useElements: true,
            listeners: [],
            Request: Request,
            Entry: Entry,
            allowMultipleBrowsers: false,
            browserNames: {
                ie: 'IE',
                firefox: 'Firefox',
                safari: 'Safari',
                chrome: 'Chrome',
                opera: 'Opera',
                dolfin: 'Dolfin',
                edge: 'Edge',
                webosbrowser: 'webOSBrowser',
                chromeMobile: 'ChromeMobile',
                chromeiOS: 'ChromeiOS',
                silk: 'Silk',
                other: 'Other'
            },
            osNames: {
                ios: 'iOS',
                android: 'Android',
                windowsPhone: 'WindowsPhone',
                webos: 'webOS',
                blackberry: 'BlackBerry',
                rimTablet: 'RIMTablet',
                mac: 'MacOS',
                win: 'Windows',
                tizen: 'Tizen',
                linux: 'Linux',
                bada: 'Bada',
                chromeOS: 'ChromeOS',
                other: 'Other'
            },
            browserPrefixes: {
                ie: 'MSIE ',
                edge: 'Edge/',
                firefox: 'Firefox/',
                chrome: 'Chrome/',
                safari: 'Version/',
                opera: 'OPR/',
                dolfin: 'Dolfin/',
                webosbrowser: 'wOSBrowser/',
                chromeMobile: 'CrMo/',
                chromeiOS: 'CriOS/',
                silk: 'Silk/'
            },
            
            
            browserPriority: [
                'edge',
                'opera',
                'dolfin',
                'webosbrowser',
                'silk',
                'chromeiOS',
                'chromeMobile',
                'ie',
                'firefox',
                'safari',
                'chrome'
            ],
            osPrefixes: {
                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/',
                chromeOS: 'CrOS '
            },
            fallbackOSPrefixes: {
                windows: 'win',
                mac: 'mac',
                linux: 'linux'
            },
            devicePrefixes: {
                iPhone: 'iPhone',
                iPod: 'iPod',
                iPad: 'iPad'
            },
            maxIEVersion: 12,
            
            detectPlatformTags: function() {
                var me = this,
                    ua = navigator.userAgent,
                    isMobile = /Mobile(\/|\s)/.test(ua),
                    element = document.createElement('div'),
                    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;
                    },
                    
                    getBrowsers = function() {
                        var browsers = {},
                            maxIEVersion, prefix, value, key, index, len, match, version, matched;
                        
                        
                        
                        len = me.browserPriority.length;
                        for (index = 0; index < len; index++) {
                            key = me.browserPriority[index];
                            if (!matched) {
                                value = me.browserPrefixes[key];
                                match = ua.match(new RegExp('(' + value + ')([\\w\\._]+)'));
                                version = match && match.length > 1 ? parseInt(match[2]) : 0;
                                if (version) {
                                    matched = true;
                                }
                            } else {
                                version = 0;
                            }
                            browsers[key] = version;
                        }
                        
                        if (browsers.ie) {
                            var mode = document.documentMode;
                            if (mode >= 8) {
                                browsers.ie = mode;
                            }
                        }
                        
                        version = browsers.ie || false;
                        maxIEVersion = Math.max(version, me.maxIEVersion);
                        for (index = 8; index <= maxIEVersion; ++index) {
                            prefix = 'ie' + index;
                            browsers[prefix + 'm'] = version ? version <= index : 0;
                            browsers[prefix] = version ? version === index : 0;
                            browsers[prefix + 'p'] = version ? version >= index : 0;
                        }
                        return browsers;
                    },
                    
                    getOperatingSystems = function() {
                        var systems = {},
                            value, key, keys, index, len, match, matched, version, activeCount;
                        keys = _getKeys(me.osPrefixes);
                        len = keys.length;
                        for (index = 0 , activeCount = 0; index < len; index++) {
                            key = keys[index];
                            value = me.osPrefixes[key];
                            match = ua.match(new RegExp('(' + value + ')([^\\s;]+)'));
                            matched = match ? match[1] : null;
                            
                            
                            if (matched && (matched === 'HTC_' || matched === 'Silk/')) {
                                version = 2.3;
                            } else {
                                version = match && match.length > 1 ? parseFloat(match[match.length - 1]) : 0;
                            }
                            if (version) {
                                activeCount++;
                            }
                            systems[key] = version;
                        }
                        keys = _getKeys(me.fallbackOSPrefixes);
                        
                        
                        len = keys.length;
                        for (index = 0; index < len; index++) {
                            key = keys[index];
                            
                            if (activeCount === 0) {
                                value = me.fallbackOSPrefixes[key];
                                match = ua.toLowerCase().match(new RegExp(value));
                                systems[key] = match ? true : 0;
                            } else {
                                systems[key] = 0;
                            }
                        }
                        return systems;
                    },
                    
                    getDevices = function() {
                        var devices = {},
                            value, key, keys, index, len, match;
                        keys = _getKeys(me.devicePrefixes);
                        len = keys.length;
                        for (index = 0; index < len; index++) {
                            key = keys[index];
                            value = me.devicePrefixes[key];
                            match = ua.match(new RegExp(value));
                            devices[key] = match ? true : 0;
                        }
                        return devices;
                    },
                    browsers = getBrowsers(),
                    systems = getOperatingSystems(),
                    devices = getDevices(),
                    platformParams = Boot.loadPlatformsParam();
                
                
                _merge(_tags, browsers, systems, devices, platformParams, true);
                _tags.phone = !!((_tags.iphone || _tags.ipod) || (!_tags.silk && (_tags.android && (_tags.android < 3 || isMobile))) || (_tags.blackberry && isMobile) || (_tags.windowsphone));
                _tags.tablet = !!(!_tags.phone && (_tags.ipad || _tags.android || _tags.silk || _tags.rimtablet || (_tags.ie10 && /; Touch/.test(ua))));
                _tags.touch = 
                
                isEventSupported('touchend') || 
                
                
                navigator.maxTouchPoints || 
                navigator.msMaxTouchPoints;
                _tags.desktop = !_tags.phone && !_tags.tablet;
                _tags.cordova = _tags.phonegap = !!(window.PhoneGap || window.Cordova || window.cordova);
                _tags.webview = /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i.test(ua);
                _tags.androidstock = (_tags.android <= 4.3) && (_tags.safari || _tags.silk);
                
                _merge(_tags, platformParams, true);
            },
            
            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.platformTags.split(",");
                    for (tmplen = tmpArray.length , i = 0; i < tmplen; i++) {
                        platform = tmpArray[i].split(":");
                        name = platform[0];
                        enabled = true;
                        if (platform.length > 1) {
                            enabled = platform[1];
                            if (enabled === 'false' || enabled === '0') {
                                enabled = false;
                            }
                        }
                        platforms[name] = enabled;
                    }
                }
                return platforms;
            },
            filterPlatform: function(platform, excludes) {
                platform = _emptyArray.concat(platform || _emptyArray);
                excludes = _emptyArray.concat(excludes || _emptyArray);
                var plen = platform.length,
                    elen = excludes.length,
                    include = (!plen && elen),
                    
                    i, tag;
                for (i = 0; i < plen && !include; i++) {
                    tag = platform[i];
                    include = !!_tags[tag];
                }
                for (i = 0; i < elen && include; i++) {
                    tag = excludes[i];
                    include = !_tags[tag];
                }
                return include;
            },
            init: function() {
                var scriptEls = doc.getElementsByTagName('script'),
                    script = scriptEls[0],
                    len = scriptEls.length,
                    re = /\/ext(\-[a-z\-]+)?\.js$/,
                    entry, src, state, baseUrl, key, n, origin;
                
                Boot.hasReadyState = ("readyState" in script);
                Boot.hasAsync = ("async" in script);
                Boot.hasDefer = ("defer" in script);
                Boot.hasOnLoad = ("onload" in script);
                
                Boot.isIE8 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && !Boot.hasOnLoad;
                Boot.isIE9 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad;
                Boot.isIE10p = Boot.hasReadyState && Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad;
                if (Boot.isIE8) {
                    Boot.isIE10 = false;
                    Boot.isIE10m = true;
                } else {
                    Boot.isIE10 = (new Function('/*@cc_on return @_jscript_version @*/')()) === 10;
                    Boot.isIE10m = Boot.isIE10 || Boot.isIE9 || Boot.isIE8;
                }
                
                Boot.isIE11 = Boot.isIE10p && !Boot.isIE10;
                
                
                
                for (n = 0; n < len; n++) {
                    src = (script = scriptEls[n]).src;
                    if (!src) {
                        
                        continue;
                    }
                    state = script.readyState || null;
                    
                    if (!baseUrl && re.test(src)) {
                        baseUrl = src;
                    }
                    if (!Boot.scripts[key = Boot.canonicalUrl(src)]) {
                        
                        
                        
                        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.baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
                origin = window.location.origin || window.location.protocol + "//" + window.location.hostname + (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, canonicalPath) {
                var key, entry;
                
                
                key = canonicalPath ? url : Boot.canonicalUrl(url);
                entry = Boot.scripts[key];
                if (!entry) {
                    entry = Boot.create(url, key, cfg);
                    if (canonicalPath) {
                        entry.canonicalPath = true;
                    }
                }
                return entry;
            },
            registerContent: function(url, type, content) {
                var cfg = {
                        content: content,
                        loaded: true,
                        css: type === 'css'
                    };
                return Boot.getEntry(url, cfg);
            },
            processRequest: function(request, sync) {
                request.loadEntries(sync);
            },
            load: function(request) {
                
                
                
                var request = new Request(request);
                if (request.sync || Boot.syncMode) {
                    return Boot.loadSync(request);
                }
                
                
                if (Boot.currentRequest) {
                    
                    
                    
                    
                    
                    
                    request.getEntries();
                    Boot.suspendedQueue.push(request);
                } else {
                    Boot.currentRequest = request;
                    Boot.processRequest(request, false);
                }
                return Boot;
            },
            loadSync: function(request) {
                
                
                
                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) {
                            
                            
                            
                            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) {
                
                if (!('length' in indexMap)) {
                    var indexArray = [],
                        index;
                    for (index in indexMap) {
                        if (!isNaN(+index)) {
                            indexArray[+index] = indexMap[index];
                        }
                    }
                    indexMap = indexArray;
                }
                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.onreadystatechange = emptyFn;
                            xhr = null;
                        }
                    };
                if (async) {
                    xhr.onreadystatechange = readyStateChange;
                }
                try {
                    
                    
                    
                    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(this, cfg);
        delete this.url;
        this.urls = urls;
        this.charset = charset;
    }
    
    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(item, indexMap, loadOrder, includeUses, skipLoaded) {
            var resolved = [],
                queue = [
                    item
                ],
                itemIndex = item.idx,
                queue, entry, dependencies, depIndex, i, len;
            if (indexMap[itemIndex]) {
                
                return resolved;
            }
            
            
            
            
            
            
            
            indexMap[itemIndex] = resolved[itemIndex] = true;
            while (item = queue.shift()) {
                
                if (item.canonicalPath) {
                    entry = Boot.getEntry(item.path, null, true);
                } else {
                    entry = Boot.getEntry(this.prepareUrl(item.path));
                }
                if (!(skipLoaded && entry.done)) {
                    if (includeUses && item.uses && item.uses.length) {
                        dependencies = item.requires.concat(item.uses);
                    } else {
                        dependencies = item.requires;
                    }
                    for (i = 0 , len = dependencies.length; i < len; i++) {
                        depIndex = dependencies[i];
                        if (!indexMap[depIndex]) {
                            indexMap[depIndex] = resolved[depIndex] = true;
                            queue.push(loadOrder[depIndex]);
                        }
                    }
                }
            }
            return resolved;
        },
        getPathsFromIndexes: function(indexes, loadOrder) {
            var paths = [],
                index, len;
            
            for (index = 0 , len = indexes.length; index < len; index++) {
                if (indexes[index]) {
                    paths.push(loadOrder[index].path);
                }
            }
            return paths;
        },
        expandUrl: function(url, loadOrder, loadOrderMap, indexMap, includeUses, skipLoaded) {
            var item, resolved;
            if (loadOrder) {
                item = loadOrderMap[url];
                if (item) {
                    resolved = this.getLoadIndexes(item, indexMap, loadOrder, includeUses, skipLoaded);
                    if (resolved.length) {
                        return this.getPathsFromIndexes(resolved, loadOrder);
                    }
                }
            }
            return [
                url
            ];
        },
        expandUrls: function(urls, includeUses) {
            var me = this,
                loadOrder = me.loadOrder,
                expanded = [],
                expandMap = {},
                indexMap = [],
                loadOrderMap, tmpExpanded, i, len, t, tlen, tUrl;
            if (typeof urls === "string") {
                urls = [
                    urls
                ];
            }
            if (loadOrder) {
                loadOrderMap = me.loadOrderMap;
                if (!loadOrderMap) {
                    loadOrderMap = me.loadOrderMap = me.createLoadOrderMap(loadOrder);
                }
            }
            for (i = 0 , len = urls.length; i < len; i++) {
                
                
                
                
                tmpExpanded = this.expandUrl(urls[i], loadOrder, loadOrderMap, indexMap, includeUses, false);
                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,
                loadOrderMap, item, i, entry, urls, url;
            if (!entries) {
                entries = [];
                urls = me.getUrls();
                
                if (me.loadOrder) {
                    loadOrderMap = me.loadOrderMap;
                }
                for (i = 0; i < urls.length; i++) {
                    url = me.prepareUrl(urls[i]);
                    if (loadOrderMap) {
                        item = loadOrderMap[url];
                    }
                    entry = Boot.getEntry(url, {
                        buster: me.buster,
                        charset: me.charset
                    }, item && item.canonicalPath);
                    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, entries, 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) {
                
                
                
                while ((listener = listeners.shift())) {
                    listener(this);
                }
            }
        }
    };
    function Entry(cfg) {
        
        
        
        if (cfg.$isEntry) {
            return cfg;
        }
        
        
        
        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 (Boot.config.disableCaching) {
            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(this, cfg);
        this.charset = charset;
        this.buster = buster;
        this.requests = [];
    }
    
    Entry.prototype = {
        $isEntry: true,
        done: false,
        evaluated: false,
        loaded: false,
        isCrossDomain: function() {
            var me = this;
            if (me.crossDomain === undefined) {
                
                
                
                me.crossDomain = (me.getLoadUrl().indexOf(Boot.origin) !== 0);
            }
            return me.crossDomain;
        },
        isCss: function() {
            var me = this;
            if (me.css === undefined) {
                if (me.url) {
                    var assetConfig = Boot.assetConfig[me.url];
                    me.css = assetConfig ? assetConfig.type === "css" : cssRe.test(me.url);
                } else {
                    me.css = false;
                }
            }
            return this.css;
        },
        getElement: function(tag) {
            var me = this,
                el = me.el;
            if (!el) {
                
                
                
                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 (me.charset) {
                        el.charset = me.charset;
                    }
                    if (Boot.hasAsync) {
                        el.async = false;
                    }
                }
                me.el = el;
            }
            return el;
        },
        getLoadUrl: function() {
            var me = this,
                url;
            url = me.canonicalPath ? me.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();
            me.preserve = true;
            el.onerror = function() {
                me.error = true;
                if (callback) {
                    callback();
                    callback = null;
                }
            };
            if (Boot.isIE10m) {
                el.onreadystatechange = function() {
                    if (this.readyState === 'loaded' || this.readyState === 'complete') {
                        if (callback) {
                            callback();
                            callback = this.onreadystatechange = this.onerror = null;
                        }
                    }
                };
            } else {
                el.onload = function() {
                    callback();
                    callback = this.onload = this.onerror = null;
                };
            }
            
            el[me.prop] = me.getLoadUrl();
        },
        onLoadElementReady: function() {
            Boot.getHead().appendChild(this.getElement());
            this.evaluated = true;
        },
        inject: function(content, asset) {
            
            
            
            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.el.onerror = me.el.onload = emptyFn;
                    me.el = null;
                    me.loaded = me.evaluated = me.done = true;
                    me.notifyRequests();
                };
            me.createLoadElement(function() {
                complete();
            });
            me.evaluateLoadElement();
            
            
            
            return false;
        },
        loadElement: function() {
            var me = this,
                complete = function() {
                    me.el.onerror = me.el.onload = emptyFn;
                    me.el = null;
                    me.loaded = me.evaluated = me.done = true;
                    me.notifyRequests();
                };
            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 (Boot.isIE10 || me.isCrossDomain()) {
                        return me.loadCrossDomain();
                    }
                    
                    
                    
                    else if (!me.isCss() && Boot.hasReadyState) {
                        me.createLoadElement(function() {
                            me.loaded = true;
                            me.notifyRequests();
                        });
                    } else if (Boot.useElements && 
                    !(me.isCss() && _environment.phantom)) {
                        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) {
                
                
                
                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 || '/');
    };
    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;
    }());
}



Ext.setResourcePath = function(poolName, path) {
    var manifest = Ext.manifest || (Ext.manifest = {}),
        paths = manifest.resources || (manifest.resources = {});
    if (manifest) {
        if (typeof poolName !== 'string') {
            Ext.apply(paths, poolName);
        } else {
            paths[poolName] = path;
        }
        manifest.resources = paths;
    }
};
Ext.getResourcePath = function(path, poolName, packageName) {
    if (typeof path !== 'string') {
        poolName = path.pool;
        packageName = path.packageName;
        path = path.path;
    }
    var manifest = Ext.manifest,
        paths = manifest && manifest.resources,
        poolPath = paths[poolName],
        output = [];
    if (poolPath == null) {
        poolPath = paths.path;
        if (poolPath == null) {
            poolPath = 'resources';
        }
    }
    if (poolPath) {
        output.push(poolPath);
    }
    if (packageName) {
        output.push(packageName);
    }
    output.push(path);
    return output.join('/');
};



var Ext = Ext || {};


(function() {
    var global = this,
        objectPrototype = Object.prototype,
        toString = objectPrototype.toString,
        enumerables = [
            
            'valueOf',
            'toLocaleString',
            'toString',
            'constructor'
        ],
        emptyFn = Ext.fireIdle = 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})?\)\\?\/$/,
        elevateArgs, elevateFn, elevateRet, elevateScope;
    Ext.global = global;
    Ext.$nextIid = 0;
    
    Ext.now = Date.now || (Date.now = function() {
        return +new Date();
    });
    
    Ext.ticks = (global.performance && global.performance.now) ? function() {
        return performance.now();
    } : 
    Ext.now;
    Ext._startTime = Ext.ticks();
    
    emptyFn.$nullFn = identityFn.$nullFn = emptyFn.$emptyFn = identityFn.$identityFn = privateFn.$nullFn = true;
    privateFn.$privacy = 'framework';
    
    emptyFn.$noClearOnDestroy = identityFn.$noClearOnDestroy = true;
    privateFn.$noClearOnDestroy = true;
    
    
    Ext['suspendLayouts'] = Ext['resumeLayouts'] = emptyFn;
    
    for (i in {
        toString: 1
    }) {
        enumerables = null;
    }
    
    Ext.enumerables = enumerables;
    
    Ext.apply = function(object, config, defaults) {
        if (object) {
            if (defaults) {
                Ext.apply(object, defaults);
            }
            if (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;
    };
    
    function addInstanceOverrides(target, owner, overrides) {
        var name, value;
        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;
            }
        }
    }
    Ext.buildSettings = Ext.apply({
        baseCSSPrefix: 'x-'
    }, Ext.buildSettings || {});
    Ext.apply(Ext, {
        
        idSeed: 0,
        
        idPrefix: 'ext-',
        
        isRobot: false,
        
        isSecure: /^https/i.test(window.location.protocol),
        
        enableGarbageCollector: false,
        
        enableListenerCollection: true,
        
        name: Ext.sandboxName || 'Ext',
        
        privateFn: privateFn,
        
        emptyFn: emptyFn,
        
        identityFn: identityFn,
        
        frameStartTime: Ext.now(),
        
        manifest: manifest,
        
        
        debugConfig: Ext.debugConfig || manifest.debug || {
            hooks: {
                '*': true
            }
        },
        
        
        enableAria: true,
        startsWithHashRe: /^#/,
        
        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.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(),
        
        
        emptyArray: Object.freeze ? Object.freeze([]) : [],
        
        baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
        
        $eventNameMap: {},
        
        
        $vendorEventRe: /^(DOMMouse|Moz.+|MS.+|webkit.+)/,
        
        
        canonicalEventName: function(name) {
            return Ext.$eventNameMap[name] || (Ext.$eventNameMap[name] = (Ext.$vendorEventRe.test(name) ? name : name.toLowerCase()));
        },
        
        applyIf: function(object, config) {
            if (object && config && typeof config === 'object') {
                for (var property in config) {
                    if (object[property] === undefined) {
                        object[property] = config[property];
                    }
                }
            }
            return object;
        },
        
        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.destroyed) {
                        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,
                    privates;
                if (owner && owner.$isClass) {
                    
                    privates = overrides.privates;
                    if (privates) {
                        overrides = Ext.apply({}, overrides);
                        delete overrides.privates;
                        addInstanceOverrides(target, owner, privates);
                    }
                    addInstanceOverrides(target, owner, overrides);
                } 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(obj) {
            return toString.call(obj) === '[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 && 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, cloneDom) {
            if (item == null) {
                return item;
            }
            
            
            
            if (cloneDom !== false && 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], cloneDom);
                }
            }
            
            else if (type === '[object Object]' && item.constructor === Object) {
                clone = {};
                for (key in item) {
                    clone[key] = Ext.clone(item[key], cloneDom);
                }
                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
        },
        ariaWarn: function(target, msg) {
            
            
            
            if (Ext.enableAria && !Ext.slicer) {
                if (!Ext.ariaWarn.first) {
                    Ext.ariaWarn.first = true;
                    Ext.log.warn("WAI-ARIA compatibility warnings can be suppressed " + "by adding the following to application startup code:");
                    Ext.log.warn("    Ext.ariaWarn = Ext.emptyFn;");
                }
                Ext.log.warn({
                    msg: msg,
                    dump: target
                });
            }
        },
        
        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;
            };
        })(),
        
        doElevate: function() {
            var fn = elevateFn,
                args = elevateArgs,
                scope = elevateScope;
            
            
            elevateFn = elevateArgs = elevateScope = null;
            elevateRet = args ? fn.apply(scope, args) : fn.call(scope);
            
            
            Ext.fireIdle();
        },
        
        elevate: function(fn, scope, args, 
        timer) 
        {
            var ret;
            if (args && !args.length) {
                args = null;
            }
            Ext._suppressIdle = false;
            
            if (timer) {
                timer.tick();
            }
            
            if (Ext.elevateFunction) {
                elevateFn = fn;
                elevateScope = scope;
                elevateArgs = args;
                
                Ext.elevateFunction(Ext.doElevate);
                ret = elevateRet;
                elevateRet = null;
            } else {
                ret = args ? fn.apply(scope, args) : fn.call(scope);
                Ext.fireIdle();
            }
            
            if (timer) {
                timer.tock();
            }
            
            return ret;
        },
        
        Timer: {
            all: {},
            track: false,
            created: function(kind, id, info) {
                if (!Ext.Timer.track) {
                    return null;
                }
                var timer = Ext.apply({
                        kind: kind,
                        id: id,
                        done: false,
                        firing: false,
                        creator: new Error().stack,
                        tick: Ext.Timer.tick,
                        tock: Ext.Timer.tock
                    }, info);
                var timers = Ext.Timer.all[kind] || (Ext.Timer.all[kind] = {});
                timers[timer.id] = timer;
                if (Ext.Timer.hook) {
                    Ext.Timer.hook(timer);
                }
                return timer;
            },
            get: function(id, kind) {
                kind = kind || 'timeout';
                var timers = Ext.Timer.all[kind];
                return timers && timers[id] || null;
            },
            cancel: function(kind, id) {
                var timers = Ext.Timer.all[kind];
                var timer = timers && timers[id];
                if (timer) {
                    timer.cancelled = true;
                    delete timers[id];
                }
            },
            tick: function() {
                if (Ext.Timer.firing) {
                    
                    
                    
                    Ext.log.error('Previous timer state not cleaned up properly: ' + Ext.Timer.firing.creator);
                }
                if (this.kind !== 'interval') {
                    this.done = true;
                    delete Ext.Timer.all[this.kind][this.id];
                }
                this.firing = true;
                Ext.Timer.firing = this;
            },
            tock: function() {
                this.firing = false;
                if (Ext.Timer.firing === this) {
                    Ext.Timer.firing = null;
                }
            }
        },
        
        
        getExpando: function(target, id) {
            var expandos = target.$expandos;
            return expandos && expandos[id] || null;
        },
        
        setExpando: function(target, id, value) {
            var expandos = target.$expandos;
            if (value !== undefined) {
                (expandos || (target.$expandos = {}))[id] = value;
            } else if (expandos) {
                delete expandos[id];
            }
        }
    });
    
    Ext.returnTrue.$nullFn = Ext.returnId.$nullFn = true;
}());





Ext.platformTags.classic = !(Ext.platformTags.modern = Ext.isModern = 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 === Ext.raise) {
                method = method.caller;
            }
            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.raise('The method "' + fail.$owner.$className + '.' + fail.$name + '" has been removed. ' + suggestion);
    }
    return fail;
    
    return Ext.emptyFn;
};

Ext.raise = function() {
    Ext.Error.raise.apply(Ext.Error, arguments);
};


(function(skipNotify) {
    if (skipNotify || 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;
            }
        };
    
    notify.$skipTimerCheck = true;
    
    
    
    setInterval(notify, 1000);
}(!!window.__UNIT_TESTING__));



Ext.Array = (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 len = arguments.length,
                pos = fixArrayIndex(array, index),
                removed;
            if (len < 3) {
                removeCount = array.length - pos;
            }
            removed = array.slice(index, fixArrayIndex(array, pos + removeCount));
            if (len < 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;
            },
            
            findInsertionIndex: function(item, items, comparatorFn, index) {
                var len = items.length,
                    beforeCheck, afterCheck;
                comparatorFn = comparatorFn || ExtArray.lexicalCompare;
                if (index < len) {
                    beforeCheck = index > 0 ? comparatorFn(items[index - 1], item) : 0;
                    afterCheck = index < len - 1 ? comparatorFn(item, items[index]) : 0;
                    if (beforeCheck < 1 && afterCheck < 1) {
                        return index;
                    }
                }
                return ExtArray.binarySearch(items, item, comparatorFn);
            },
            
            forEach: ('forEach' in arrayPrototype) ? function(array, fn, scope) {
                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 array ? arrayPrototype.indexOf.call(array, item, from) : -1;
            } : function(array, item, from) {
                var i,
                    length = array ? array.length : 0;
                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 len = array.length,
                    results = new Array(len),
                    i;
                for (i = 0; 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;
            },
            
            reduce: Array.prototype.reduce ? function(array, reduceFn, initialValue) {
                if (arguments.length === 3) {
                    return Array.prototype.reduce.call(array, reduceFn, initialValue);
                }
                return Array.prototype.reduce.call(array, reduceFn);
            } : function(array, reduceFn, initialValue) {
                array = Object(array);
                
                if (!Ext.isFunction(reduceFn)) {
                    Ext.raise('Invalid parameter: expected a function.');
                }
                
                var index = 0,
                    length = array.length >>> 0,
                    reduced = initialValue;
                if (arguments.length < 3) {
                    while (true) {
                        if (index in array) {
                            reduced = array[index++];
                            break;
                        }
                        if (++index >= length) {
                            throw new TypeError('Reduce of empty array with no initial value');
                        }
                    }
                }
                for (; index < length; ++index) {
                    if (index in array) {
                        reduced = reduceFn(reduced, array[index], index, array);
                    }
                }
                return reduced;
            },
            
            
            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(strings, getKey, scope) {
                if (!strings) {
                    return null;
                }
                var map = {},
                    i = strings.length;
                if (typeof strings === 'string') {
                    map[strings] = 1;
                } else if (!getKey) {
                    while (i--) {
                        map[strings[i]] = i + 1;
                    }
                } else if (typeof getKey === 'string') {
                    while (i--) {
                        map[strings[i][getKey]] = i + 1;
                    }
                } else {
                    while (i--) {
                        map[getKey.call(scope, strings[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);
            },
            move: function(array, fromIdx, toIdx) {
                if (toIdx === fromIdx) {
                    return;
                }
                var item = array[fromIdx],
                    incr = toIdx > fromIdx ? 1 : -1,
                    i;
                for (i = fromIdx; i != toIdx; i += incr) {
                    array[i] = array[i + incr];
                }
                array[toIdx] = item;
            },
            
            replace: replace,
            
            splice: splice,
            
            push: function(target) {
                var args = arguments,
                    len = args.length,
                    i, newItem;
                if (target === undefined) {
                    target = [];
                } else if (!Ext.isArray(target)) {
                    target = [
                        target
                    ];
                }
                for (i = 1; i < len; i++) {
                    newItem = args[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.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.raise('Expected a falsey value for ' + property + ' but was ' + b);
        }
    },
    
    truthy: function(b, msg) {
        if (!b) {
            Ext.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.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.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.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.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.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;
        },
        fromCharCode = String.fromCharCode,
        ExtString;
    return ExtString = {
        
        fromCodePoint: String.fromCodePoint || function() {
            var codePoint,
                result = '',
                codeUnits = [],
                index = -1,
                length = arguments.length;
            while (++index < length) {
                codePoint = Number(arguments[index]);
                if (!isFinite(codePoint) || 
                codePoint < 0 || 
                codePoint > 1114111 || 
                Math.floor(codePoint) !== codePoint) 
                {
                    Ext.raise('Invalid code point: ' + codePoint);
                }
                if (codePoint <= 65535) {
                    
                    codeUnits.push(codePoint);
                } else {
                    
                    
                    codePoint -= 65536;
                    codeUnits.push((codePoint >> 10) + 55296, (codePoint % 1024) + 56320);
                }
                if (index + 1 === length) {
                    result += fromCharCode(codeUnits);
                    codeUnits.length = 0;
                }
            }
            return result;
        },
        
        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,
        monthInfo = {
            F: true,
            m: true,
            M: true,
            n: true
        },
        yearInfo = {
            o: true,
            Y: true,
            y: true
        },
        
        
        
        
        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 != null) ? v : null;"
        ].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];
        });
    }
    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",
        
        DAYS_IN_WEEK: 7,
        
        MONTHS_IN_YEAR: 12,
        
        MAX_DAYS_IN_MONTH: 31,
        SUNDAY: 0,
        MONDAY: 1,
        TUESDAY: 2,
        WEDNESDAY: 3,
        THURSDAY: 4,
        FRIDAY: 5,
        SATURDAY: 6,
        
        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',
        
        defaultTimeFormat: 'h:i A',
        
        firstDayOfWeek: 0,
        
        weekendDays: [
            0,
            6
        ],
        
        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, ''));
        },
        
        isMonthFormat: function(format) {
            return !!monthInfo[format];
        },
        
        isYearFormat: function(format) {
            return !!yearInfo[format];
        },
        
        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(year, month, day, hour, minute, second, millisecond) {
            
            hour = hour || 0;
            minute = minute || 0;
            second = second || 0;
            millisecond = millisecond || 0;
            
            var dt = utilDate.add(new nativeDate(year < 100 ? 100 : year, month - 1, day, hour, minute, second, millisecond), utilDate.YEAR, year < 100 ? year - 100 : 0);
            return year === dt.getFullYear() && month === dt.getMonth() + 1 && day === dt.getDate() && hour === dt.getHours() && minute === dt.getMinutes() && second === dt.getSeconds() && millisecond === 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{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 (isNaN(date.getTime())) {
                return date;
            }
            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, preventDstAdjust) {
            var d = utilDate.clone(date),
                base = 0,
                day, decimalValue;
            if (!interval || value === 0) {
                return d;
            }
            decimalValue = value - parseInt(value, 10);
            value = parseInt(value, 10);
            if (value) {
                switch (interval.toLowerCase()) {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    case utilDate.MILLI:
                        if (preventDstAdjust) {
                            d.setMilliseconds(d.getMilliseconds() + value);
                        } else {
                            d.setTime(d.getTime() + value);
                        };
                        break;
                    case utilDate.SECOND:
                        if (preventDstAdjust) {
                            d.setSeconds(d.getSeconds() + value);
                        } else {
                            d.setTime(d.getTime() + value * 1000);
                        };
                        break;
                    case utilDate.MINUTE:
                        if (preventDstAdjust) {
                            d.setMinutes(d.getMinutes() + value);
                        } else {
                            d.setTime(d.getTime() + value * 60 * 1000);
                        };
                        break;
                    case utilDate.HOUR:
                        if (preventDstAdjust) {
                            d.setHours(d.getHours() + value);
                        } else {
                            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, preventDstAdjust) {
            return utilDate.add(date, interval, -value, preventDstAdjust);
        },
        
        between: function(date, start, end) {
            var t = date.getTime();
            return start.getTime() <= t && t <= end.getTime();
        },
        
        isWeekend: function(date) {
            return Ext.Array.indexOf(this.weekendDays, date.getDay()) > -1;
        },
        
        utcToLocal: function(d) {
            return new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds());
        },
        
        localToUtc: function(d) {
            return utilDate.utc(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
        },
        
        utc: function(year, month, day, hour, min, s, ms) {
            return new Date(Date.UTC(year, month, day, hour || 0, min || 0, s || 0, ms || 0));
        },
        
        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;
                    };
                    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;
                case utilDate.SECOND:
                    num.setUTCSeconds(num.getUTCSeconds() - num.getUTCSeconds() % step);
                    num.setUTCMilliseconds(0);
                    return num;
                case utilDate.MINUTE:
                    num.setUTCMinutes(num.getUTCMinutes() - num.getUTCMinutes() % step);
                    num.setUTCSeconds(0);
                    num.setUTCMilliseconds(0);
                    return num;
                case utilDate.HOUR:
                    num.setUTCHours(num.getUTCHours() - num.getUTCHours() % step);
                    num.setUTCMinutes(0);
                    num.setUTCSeconds(0);
                    num.setUTCMilliseconds(0);
                    return num;
                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;
                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;
                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;
            }
        }
    };
    utilDate.parseCodes.C = utilDate.parseCodes.c;
    return utilDate;
}());


Ext.Function = (function() {
    
    
    
    
    var lastTime = 0,
        animFrameId,
        animFrameHandlers = [],
        animFrameNoArgs = [],
        idSource = 0,
        animFrameMap = {},
        slice = Array.prototype.slice,
        win = window,
        global = Ext.global,
        
        hasImmediate = !Ext.disableImmediate && !!(global.setImmediate && global.clearImmediate),
        requestAnimFrame = win.requestAnimationFrame || win.webkitRequestAnimationFrame || win.mozRequestAnimationFrame || win.oRequestAnimationFrame || function(callback) {
            var currTime = Ext.now(),
                timeToCall = Math.max(0, 16 - (currTime - lastTime)),
                timerFn = function() {
                    callback(currTime + timeToCall);
                },
                id;
            
            timerFn.$origFn = callback.$origFn || callback;
            timerFn.$skipTimerCheck = timerFn.$origFn.$skipTimerCheck;
            
            id = win.setTimeout(timerFn, timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        },
        fireHandlers = function() {
            var len = animFrameHandlers.length,
                id, i, handler;
            animFrameId = null;
            
            var timer;
            
            
            for (i = 0; i < len; i++) {
                handler = animFrameHandlers[i];
                id = handler[3];
                
                if (animFrameMap[id]) {
                    delete animFrameMap[id];
                    
                    timer = Ext.Timer.get(id, 'raf');
                    if (timer) {
                        timer.tick();
                    }
                    
                    handler[0].apply(handler[1] || global, handler[2] || animFrameNoArgs);
                    
                    if (timer) {
                        timer.tock();
                    }
                }
            }
            
            
            
            animFrameHandlers = animFrameHandlers.slice(len);
        },
        fireElevatedHandlers = function() {
            Ext.elevate(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 fn.bind(scope);
                }
                var method = fn;
                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 || global, callArgs);
                };
            },
            
            bindCallback: function(callback, scope, args, delay, caller) {
                return function() {
                    var a = slice.call(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) {
                var newMethod, prop;
                newMethod = function() {
                    return method.apply(this, arguments);
                };
                for (prop in method) {
                    if (method.hasOwnProperty(prop)) {
                        newMethod[prop] = method[prop];
                    }
                }
                return newMethod;
            },
            
            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;
                        return (newFn.apply(scope || me || global, args) !== false) ? origFn.apply(me || global, args) : returnValue;
                    };
                }
            },
            
            createDelayed: function(fn, delay, scope, args, appendArgs) {
                var boundFn = fn;
                if (scope || args) {
                    boundFn = Ext.Function.bind(fn, scope, args, appendArgs);
                }
                return function() {
                    var me = this,
                        args = slice.call(arguments),
                        timerFn, timerId;
                    
                    var timer;
                    
                    timerFn = function() {
                        Ext.elevate(boundFn, me, args, 
                        timer);
                    };
                    
                    timerId = setTimeout(timerFn, delay);
                    
                    timerFn.$origFn = fn.$origFn || fn;
                    timerFn.$skipTimerCheck = timerFn.$origFn.$skipTimerCheck;
                    timer = Ext.Timer.created('timeout', timerId, {
                        type: 'createDelayed',
                        fn: fn,
                        timerFn: timerFn
                    });
                };
            },
            
            
            defer: function(fn, millis, scope, args, appendArgs) {
                var timerId = 0,
                    timerFn, boundFn;
                
                var timer;
                
                if (!scope && !args && !appendArgs) {
                    boundFn = fn;
                } else {
                    boundFn = Ext.Function.bind(fn, scope, args, appendArgs);
                }
                if (millis > 0) {
                    timerFn = function() {
                        Ext.elevate(boundFn, 
                        null, null, timer);
                    };
                    
                    timerId = setTimeout(timerFn, millis);
                    
                    timerFn.$origFn = fn.$origFn || fn;
                    timerFn.$skipTimerCheck = timerFn.$origFn.$skipTimerCheck;
                    timer = Ext.Timer.created('timeout', timerId, {
                        type: 'defer',
                        fn: fn,
                        timerFn: timerFn
                    });
                } else 
                {
                    boundFn();
                }
                return timerId;
            },
            
            interval: function(fn, millis, scope, args, appendArgs) {
                var timerFn, timerId, boundFn;
                
                var timer;
                
                boundFn = Ext.Function.bind(fn, scope, args, appendArgs);
                timerFn = function() {
                    Ext.elevate(boundFn, 
                    null, null, timer);
                };
                
                timerId = setInterval(timerFn, millis);
                
                timerFn.$origFn = boundFn.$origFn || fn;
                timerFn.$skipTimerCheck = timerFn.$origFn.$skipTimerCheck;
                timer = Ext.Timer.created('interval', timerId, {
                    type: 'interval',
                    fn: fn,
                    timerFn: timerFn
                });
                
                return timerId;
            },
            
            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,
                    result = function() {
                        var callArgs = args || slice.call(arguments, 0),
                            me = scope || this,
                            timerFn;
                        
                        var timer;
                        
                        if (timerId) {
                            Ext.undefer(timerId);
                        }
                        timerFn = function() {
                            Ext.elevate(fn, me, callArgs, 
                            timer);
                        };
                        
                        result.timer = timerId = setTimeout(timerFn, buffer);
                        
                        timerFn.$origFn = fn.$origFn || fn;
                        timerFn.$skipTimerCheck = timerFn.$origFn.$skipTimerCheck;
                        timer = Ext.Timer.created('timeout', timerId, {
                            type: 'createBuffered',
                            fn: fn,
                            timerFn: timerFn
                        });
                    };
                
                return result;
            },
            
            createAnimationFrame: function(fn, scope, args, queueStrategy) {
                var boundFn, timerId;
                queueStrategy = queueStrategy || 3;
                boundFn = function() {
                    var timerFn,
                        callArgs = args || slice.call(arguments, 0);
                    scope = scope || this;
                    if (queueStrategy === 3 && timerId) {
                        ExtFunction.cancelAnimationFrame(timerId);
                    }
                    if ((queueStrategy & 1) || !timerId) {
                        timerFn = function() {
                            timerId = boundFn.timerId = null;
                            fn.apply(scope, callArgs);
                        };
                        
                        timerFn.$origFn = fn.$origFn || fn;
                        timerFn.$skipTimerCheck = timerFn.$origFn.$skipTimerCheck;
                        
                        timerId = boundFn.timerId = ExtFunction.requestAnimationFrame(timerFn);
                    }
                };
                return boundFn;
            },
            
            requestAnimationFrame: function(fn, scope, args) {
                var id = ++idSource,
                    
                    handler = slice.call(arguments, 0);
                handler[3] = id;
                animFrameMap[id] = 1;
                
                
                Ext.Timer.created('raf', id, {
                    type: 'raf',
                    fn: fn
                });
                
                
                
                animFrameHandlers.push(handler);
                if (!animFrameId) {
                    animFrameId = requestAnimFrame(fireElevatedHandlers);
                }
                return id;
            },
            cancelAnimationFrame: function(id) {
                
                
                
                delete animFrameMap[id];
                
                Ext.Timer.cancel('raf', id);
            },
            
            
            createThrottled: function(fn, interval, scope) {
                var lastCallTime = 0,
                    elapsed, lastArgs, timerId,
                    execute = function() {
                        fn.apply(scope, lastArgs);
                        lastCallTime = Ext.now();
                        lastArgs = timerId = null;
                    };
                
                execute.$origFn = fn.$origFn || fn;
                execute.$skipTimerCheck = execute.$origFn.$skipTimerCheck;
                
                return function() {
                    
                    if (!scope) {
                        scope = this;
                    }
                    elapsed = Ext.now() - lastCallTime;
                    lastArgs = Ext.Array.slice(arguments);
                    
                    
                    if (elapsed >= interval) {
                        Ext.undefer(timerId);
                        execute();
                    }
                    
                    else if (!timerId) {
                        timerId = Ext.defer(execute, interval - elapsed);
                    }
                };
            },
            
            createBarrier: function(count, fn, scope) {
                var barrierFn = function() {
                        if (!--count) {
                            fn.apply(scope, arguments);
                        }
                    };
                
                barrierFn.$origFn = fn.$origFn || fn;
                barrierFn.$skipTimerCheck = barrierFn.$origFn.$skipTimerCheck;
                
                return barrierFn;
            },
            
            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);
                });
            },
            interceptAfterOnce: function(object, methodName, fn, scope) {
                var origMethod = object[methodName],
                    newMethod;
                newMethod = function() {
                    var ret;
                    if (origMethod) {
                        origMethod.apply(this, arguments);
                    }
                    ret = fn.apply(scope || this, arguments);
                    object[methodName] = origMethod;
                    object = methodName = fn = scope = origMethod = newMethod = null;
                    return ret;
                };
                object[methodName] = newMethod;
                return newMethod;
            },
            makeCallback: function(callback, scope) {
                
                if (!scope[callback]) {
                    if (scope.$className) {
                        Ext.raise('No method "' + callback + '" on ' + scope.$className);
                    }
                    Ext.raise('No method "' + callback + '"');
                }
                
                return function() {
                    return scope[callback].apply(scope, arguments);
                };
            },
            
            memoize: function(fn, scope, hashFn) {
                var memo = {},
                    isFunc = hashFn && Ext.isFunction(hashFn);
                return function(value) {
                    var key = isFunc ? hashFn.apply(scope, arguments) : value;
                    if (!(key in memo)) {
                        memo[key] = fn.apply(scope, arguments);
                    }
                    return memo[key];
                };
            },
            
            _stripCommentRe: /(\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\/)|(\/\/.*)/g,
            
            toCode: function(fn) {
                var s = fn ? fn.toString() : '';
                
                s = s.replace(ExtFunction._stripCommentRe, '');
                
                return s;
            },
            
            
            
            fireElevatedHandlers: function() {
                fireElevatedHandlers();
            }
        };
    
    
    
    Ext.asap = hasImmediate ? function(fn, scope, parameters) {
        var boundFn = fn,
            timerFn, timerId;
        
        var timer;
        
        if (scope != null || parameters != null) {
            boundFn = ExtFunction.bind(fn, scope, parameters);
        }
        timerFn = function() {
            Ext.elevate(boundFn, 
            null, null, timer);
        };
        
        timerId = setImmediate(timerFn);
        
        timerFn.$origFn = fn.$origFn || fn;
        timerFn.$skipTimerCheck = timerFn.$origFn.$skipTimerCheck;
        timer = Ext.Timer.created('asap', timerId, {
            type: 'asap',
            fn: fn,
            timerFn: timerFn
        });
        
        return timerId;
    } : function(fn, scope, parameters) {
        var boundFn = fn,
            timerFn, timerId;
        
        var timer;
        
        if (scope != null || parameters != null) {
            boundFn = ExtFunction.bind(fn, scope, parameters);
        }
        timerFn = function() {
            Ext.elevate(boundFn, 
            null, null, timer);
        };
        
        timerId = setTimeout(timerFn, 0, true);
        
        timerFn.$origFn = fn.$origFn || fn;
        timerFn.$skipTimerCheck = timerFn.$origFn.$skipTimerCheck;
        timer = Ext.Timer.created('timeout', timerId, {
            type: 'asap',
            fn: fn,
            timerFn: timerFn
        });
        
        return timerId;
    };
    
    Ext.unasap = hasImmediate ? function(id) {
        if (id) {
            clearImmediate(id);
            
            Ext.Timer.cancel('asap', id);
        }
        
        return null;
    } : function(id) {
        return Ext.undefer(id);
    };
    
    Ext.asapCancel = function(id) {
        return Ext.unasap(id);
    };
    
    Ext.defer = ExtFunction.defer;
    
    Ext.undefer = function(id) {
        if (id) {
            clearTimeout(id);
            
            Ext.Timer.cancel('timeout', id);
        }
        
        return null;
    };
    
    Ext.interval = ExtFunction.interval;
    
    Ext.uninterval = function(id) {
        if (id) {
            clearInterval(id);
            
            Ext.Timer.cancel('interval', id);
        }
        
        return null;
    };
    
    Ext.pass = ExtFunction.pass;
    
    Ext.bind = ExtFunction.bind;
    Ext.raf = function() {
        return ExtFunction.requestAnimationFrame.apply(ExtFunction, arguments);
    };
    Ext.unraf = function(id) {
        ExtFunction.cancelAnimationFrame(id);
    };
    return ExtFunction;
})();


Ext.Number = (new function() {
    
    
    
    
    var ExtNumber = this,
        isToFixedBroken = (0.9).toFixed() !== '1',
        math = Math,
        ClipDefault = {
            count: false,
            inclusive: false,
            wrap: true
        };
    
    Number.MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER || -(math.pow(2, 53) - 1);
    Number.MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || math.pow(2, 53) - 1;
    Ext.apply(ExtNumber, {
        MIN_SAFE_INTEGER: Number.MIN_SAFE_INTEGER,
        MAX_SAFE_INTEGER: Number.MAX_SAFE_INTEGER,
        
        floatRe: /^[-+]?(?:\d+|\d*\.\d*)(?:[Ee][+-]?\d+)?$/,
        intRe: /^[-+]?\d+(?:[Ee]\+?\d+)?$/,
        Clip: {
            DEFAULT: ClipDefault,
            COUNT: Ext.applyIf({
                count: true
            }, ClipDefault),
            INCLUSIVE: Ext.applyIf({
                inclusive: true
            }, ClipDefault),
            NOWRAP: Ext.applyIf({
                wrap: false
            }, ClipDefault)
        },
        
        parseFloat: function(value) {
            if (value === undefined) {
                value = null;
            }
            if (value !== null && typeof value !== 'number') {
                value = String(value);
                value = ExtNumber.floatRe.test(value) ? +value : null;
                if (isNaN(value)) {
                    value = null;
                }
            }
            return value;
        },
        
        parseInt: function(value) {
            if (value === undefined) {
                value = null;
            }
            if (typeof value === 'number') {
                value = Math.floor(value);
            } else if (value !== null) {
                value = String(value);
                value = ExtNumber.intRe.test(value) ? +value : null;
            }
            return value;
        },
        binarySearch: function(array, value, begin, end) {
            if (begin === undefined) {
                begin = 0;
            }
            if (end === undefined) {
                end = array.length;
            }
            --end;
            var middle, midVal;
            while (begin <= end) {
                middle = (begin + end) >>> 1;
                
                midVal = array[middle];
                if (value === midVal) {
                    return middle;
                }
                if (midVal < value) {
                    begin = middle + 1;
                } else {
                    end = middle - 1;
                }
            }
            return begin;
        },
        bisectTuples: function(array, value, index, begin, end) {
            if (begin === undefined) {
                begin = 0;
            }
            if (end === undefined) {
                end = array.length;
            }
            --end;
            var middle, midVal;
            while (begin <= end) {
                middle = (begin + end) >>> 1;
                
                midVal = array[middle][index];
                if (value === midVal) {
                    return middle;
                }
                if (midVal < value) {
                    begin = middle + 1;
                } else {
                    end = middle - 1;
                }
            }
            return begin;
        },
        
        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;
        },
        
        roundToNearest: function(value, interval) {
            interval = interval || 1;
            return interval * math.round(value / interval);
        },
        
        roundToPrecision: function(value, precision) {
            var factor = math.pow(10, precision || 1);
            return math.round(value * factor) / factor;
        },
        
        truncateToPrecision: function(value, precision) {
            var factor = math.pow(10, precision || 1);
            return parseInt(value * factor, 10) / factor;
        },
        
        sign: math.sign || function(x) {
            x = +x;
            
            if (x === 0 || isNaN(x)) {
                return x;
            }
            return (x > 0) ? 1 : -1;
        },
        
        log10: math.log10 || function(x) {
            return math.log(x) * math.LOG10E;
        },
        
        isEqual: function(n1, n2, epsilon) {
            
            if (!(typeof n1 === 'number' && typeof n2 === 'number' && typeof epsilon === 'number')) {
                Ext.raise("All parameters should be valid numbers.");
            }
            
            return math.abs(n1 - n2) < epsilon;
        },
        
        isFinite: Number.isFinite || function(value) {
            return typeof value === 'number' && isFinite(value);
        },
        isInteger: Number.isInteger || function(value) {
            
            
            return ~~(value + 0) === value;
        },
        
        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() {},
        queryRe = /^\?/,
        keyRe = /(\[):?([^\]]*)\]/g,
        nameRe = /^([^\[]+)/,
        plusRe = /\+/g,
        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(queryRe, '').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 = components[0];
                        name = name.replace(plusRe, '%20');
                        name = decodeURIComponent(name);
                        value = components[1];
                        if (value !== undefined) {
                            value = value.replace(plusRe, '%20');
                            value = decodeURIComponent(value);
                        } else {
                            value = '';
                        }
                        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(keyRe);
                            matchedName = name.match(nameRe);
                            
                            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;
                if (object) {
                    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,
                    args = arguments,
                    ln = args.length,
                    mergeFn = ExtObject.merge,
                    cloneFn = Ext.clone,
                    object, key, value, sourceKey;
                for (; i < ln; i++) {
                    object = args[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 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
        },
        owner: {
            isOwner: 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 (callback[2] === '.') {
                
                
                if (callback.substr(0, 2) !== 'up') {
                    Ext.raise('Invalid callback method name "' + callback + '"');
                }
                if (scope) {
                    Ext.raise('Callback "up" syntax is incompatible with scopes');
                }
                if (!caller || !Ext.isFunction(caller.up)) {
                    Ext.raise('Callback "up" syntax requires a caller with "up" method');
                }
                
                callback = callback.substr(3);
                
                for (scope = caller.up(); scope && !scope[callback]; scope = scope.up()) {}
                
                
                if (!scope || !Ext.isFunction(scope[callback])) {
                    Ext.raise('No such method "' + callback + '" found up() from ' + scope.getId ? scope.getId() : scope.id);
                }
            }
            
            else if ((!scope || namedScope) && caller) {
                scope = caller.resolveListenerScope(namedScope ? scope : defaultScope);
            }
            
            if (!scope || !Ext.isObject(scope)) {
                Ext.raise('Named method "' + callback + '" requires a scope object');
            }
            if (!Ext.isFunction(scope[callback])) {
                Ext.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 {
                ret = args ? callback.apply(scope, args) : 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' || from === '0') ? false : Boolean(from);
                case 'null':
                    return isString && (!from || from === 'null') ? null : false;
                case 'undefined':
                    return isString && (!from || from === 'undefined') ? undefined : false;
                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;
    },
    
    copy: 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 (source.hasOwnProperty(name) || (usePrototypeKeys && name in source)) {
                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;
    },
    
    copyIf: 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 (!(name in destination) && (name in source)) {
                destination[name] = source[name];
            }
        }
        return destination;
    },
    
    extend: (function() {
        
        var objectConstructor = Object.prototype.constructor,
            inlineOverrides = function(o) {
                var m;
                for (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.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;
        };
    }()),
    
    isOnline: function() {
        return Ext.global.navigator.onLine;
    },
    
    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);
        }
    },
    _resourcePoolRe: /^[<]([^<>@:]*)(?:[@]([^<>@:]+))?[>](.+)$/,
    
    resolveResource: function(url) {
        var ret = url,
            m;
        if (url && url.charAt(0) === '<') {
            m = Ext._resourcePoolRe.exec(url);
            if (m) {
                ret = Ext.getResourcePath(m[3], m[1], m[2]);
            }
        }
        return ret;
    },
    
    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.raise("getScrollbarSize called before DomReady");
        }
        
        var scrollbarSize = Ext._scrollbarSize;
        if (force || !scrollbarSize) {
            var db = document.body,
                div = document.createElement('div'),
                h, w;
            div.style.width = div.style.height = '100px';
            div.style.overflow = 'scroll';
            div.style.position = 'absolute';
            db.appendChild(div);
            
            
            Ext._scrollbarSize = scrollbarSize = {
                width: w = div.offsetWidth - div.clientWidth,
                height: h = div.offsetHeight - div.clientHeight
            };
            scrollbarSize.reservedWidth = w ? 'calc(100% - ' + w + 'px)' : '';
            scrollbarSize.reservedHeight = h ? 'calc(100% - ' + h + 'px)' : '';
            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.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) {
            
            if (!instance && !classReference) {
                Ext.raise('[Ext.factory] Cannot determine type of class to create');
            }
            
            return instance || Ext.create(classReference);
        }
        
        if (!Ext.isObject(config)) {
            Ext.raise("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);
    },
    
    convertKeyedItems: function(items, defaultProperty, functionProperty) {
        if (items && !items.isInstance && Ext.isObject(items)) {
            var obj = items,
                item, itemId, value;
            items = [];
            
            if (obj.xtype || obj.xclass || obj.itemId || obj.id) {
                items.push(obj);
            } else {
                for (itemId in obj) {
                    item = obj[itemId];
                    if (item) {
                        if (item === true) {
                            item = {};
                        } else if (typeof item === 'function') {
                            
                            if (!functionProperty) {
                                Ext.raise('Function not expected here');
                            }
                            
                            value = item;
                            item = {};
                            item[functionProperty] = value;
                        } else if (typeof item === 'string') {
                            value = item;
                            item = {};
                            item[defaultProperty || 'xtype'] = value;
                        } else {
                            item = Ext.apply({}, item);
                        }
                        item.itemId = itemId;
                        items.push(item);
                    }
                }
            }
        }
        return items;
    },
    
    weightSortFn: function(lhs, rhs) {
        return (lhs.weight || 0) - (rhs.weight || 0);
    },
    
    concat: function(a, b) {
        var noB = b == null,
            E = Ext.emptyArray;
        return (a == null) ? (noB ? a : E.concat(b)) : (noB ? E.concat(a) : E.concat(a, b));
    },
    
    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 = dumpObject(value, level + 1, maxLevel, withFunctions);
                    } else if (Ext.isObject(value)) {
                        member = 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,
                prefix, stack, fn, out, max;
            log.indent = indent;
            if (typeof message !== 'string') {
                options = message;
                message = options.msg || '';
                level = options.level || level;
                dump = options.dump;
                stack = options.stack;
                prefix = options.prefix;
                fn = options.fn;
                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('');
            }
            if (prefix) {
                message = prefix + ' - ' + message;
            }
            message = indent ? Ext.String.repeat(' ', log.indentSize * indent) + message : message;
            
            if (level !== 'log') {
                message = '[' + level.charAt(0).toUpperCase() + '] ' + message;
            }
            if (fn) {
                message += '\nCaller: ' + fn.toString();
            }
            
            
            
            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,
        toolkitNames = {
            classic: 1,
            modern: 1
        },
        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: 'core',
                touch: 'modern'
            },
            to: {
                ext: [
                    'extjs'
                ],
                'core': [
                    'core'
                ],
                modern: [
                    'touch'
                ]
            }
        },
        
        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) {
            if (packageName in toolkitNames) {
                Ext.toolkit = packageName;
            }
            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];
        if (pkg && pkg.version) {
            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', '6.5.3.57');
        Ext.setVersion('core', '6.5.3.57');
    }
})(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,
        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());
    },
    getEventedSetter: function() {
        return this.eventedSetter || (this.root.eventedSetter = this.makeEventedSetter());
    },
    
    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;
    },
    makeEventedSetter: function() {
        var name = this.name,
            names = this.names,
            prefixedName = names.internal,
            getName = names.get,
            applyName = names.apply,
            updateName = names.update,
            changeEventName = names.changeEvent,
            updateFn = function(me, value, oldValue, internalName) {
                me[internalName] = value;
                if (me[updateName]) {
                    me[updateName](value, oldValue);
                }
            },
            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])) {
                    if (me.isConfiguring) {
                        me[internalName] = value;
                        if (me[updateName]) {
                            me[updateName](value, oldValue);
                        }
                    } else {
                        me.fireEventedAction(changeEventName, [
                            me,
                            value,
                            oldValue
                        ], updateFn, me, [
                            me,
                            value,
                            oldValue,
                            internalName
                        ]);
                    }
                }
            }
            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,
            superCfg = cls.superclass ? cls.superclass.self.$config : null;
        
        me.cls = cls;
        
        me.superCfg = superCfg;
        if (superCfg) {
            
            me.configs = ExtObject.chain(superCfg.configs);
            
            me.cachedConfigs = ExtObject.chain(superCfg.cachedConfigs);
            
            me.initMap = ExtObject.chain(superCfg.initMap);
            
            me.values = ExtObject.chain(superCfg.values);
            me.needsFork = superCfg.needsFork;
            
            
            
            
            me.deprecations = ExtObject.chain(superCfg.deprecations);
        } else 
        {
            me.configs = {};
            me.cachedConfigs = {};
            me.initMap = {};
            me.values = {};
            
            me.deprecations = {};
        }
        
        prototype.config = prototype.defaultConfig = me.values;
        cls.$config = me;
    };
    Ext.Configurator.prototype = {
        self: Ext.Configurator,
        needsFork: false,
        
        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;
                isCached = false;
                if (meta) {
                    isCached = !!meta.cached;
                    value = meta.$value;
                    isObject = value && value.constructor === Object;
                }
                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.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.getter || cfg.getGetter();
                    }
                    if (!prototype[s = names.set]) {
                        prototype[s] = (meta && meta.evented) ? (cfg.eventedSetter || cfg.getEventedSetter()) : (cfg.setter || 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 (!me.needsFork && value && (value.constructor === Object || value instanceof Array)) {
                    me.needsFork = true;
                }
                
                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;
            }
        },
        
        addDeprecations: function(configs) {
            var me = this,
                deprecations = me.deprecations,
                className = (me.cls.$className || '') + '#',
                message, newName, oldName;
            for (oldName in configs) {
                newName = configs[oldName];
                
                
                
                
                
                
                
                
                
                if (!newName) {
                    message = 'This config has been removed.';
                } else if (!(message = newName.message)) {
                    message = 'This config has been renamed to "' + newName + '"';
                }
                deprecations[oldName] = className + oldName + ': ' + message;
            }
        },
        
        
        configure: function(instance, instanceConfig) {
            var me = this,
                configs = me.configs,
                
                deprecations = me.deprecations,
                
                initMap = me.initMap,
                initListMap = me.initListMap,
                initList = me.initList,
                prototype = me.cls.prototype,
                values = me.values,
                remaining = 0,
                firstInstance = !initList,
                cachedInitList, cfg, getter, i, internalName, ln, names, name, value, isCached, valuesKey, field;
            values = me.needsFork ? ExtObject.fork(values) : ExtObject.chain(values);
            
            instance.isConfiguring = true;
            if (firstInstance) {
                
                
                me.initList = initList = [];
                me.initListMap = initListMap = {};
                instance.isFirstInstance = true;
                for (name in initMap) {
                    cfg = configs[name];
                    isCached = cfg.cached;
                    if (initMap[name]) {
                        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.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 (deprecations[name]) {
                        Ext.log.warn(deprecations[name]);
                        if (!cfg) {
                            
                            
                            
                            
                            
                            continue;
                        }
                    }
                    
                    if (!cfg) {
                        
                        field = instance.self.prototype[name];
                        if (instance.$configStrict && (typeof field === 'function') && !field.$nullFn) {
                            
                            Ext.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, false);
                            }
                        }
                    }
                    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, false);
                        }
                    }
                }
                baseConfig[name] = value;
            }
            return baseConfig;
        },
        
        reconfigure: function(instance, instanceConfig, options) {
            var currentConfig = instance.config,
                configList = [],
                strict = instance.$configStrict && !(options && options.strict === false),
                configs = this.configs,
                defaults = options && options.defaults,
                cfg, getter, i, len, name, names, prop;
            for (name in instanceConfig) {
                cfg = configs[name];
                if (defaults && instance.hasOwnProperty(cfg && instance.$configPrefixed ? cfg.names.internal : name)) {
                    
                    continue;
                }
                currentConfig[name] = instanceConfig[name];
                
                if (this.deprecations[name]) {
                    
                    Ext.log.warn(this.deprecations[name]);
                    if (!cfg) {
                        
                        continue;
                    }
                }
                
                if (cfg) {
                    
                    
                    instance[cfg.names.get] = cfg.initGetter || cfg.getInitGetter();
                } else {
                    
                    
                    
                    
                    
                    
                    
                    prop = instance.self.prototype[name];
                    if (strict) {
                        if ((typeof prop === 'function') && !prop.$nullFn) {
                            
                            Ext.Error.raise("Cannot override method " + name + " on " + instance.$className + " instance.");
                            
                            
                            continue;
                        } else 
                        {
                            if (name !== 'type') {
                                Ext.log.warn('No such config "' + name + '" for class ' + instance.$className);
                            }
                        }
                    }
                }
                
                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 {
                        
                        instance[name] = instanceConfig[name];
                    }
                }
            }
        },
        
        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 = [],
        
        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.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.raise(message);
                    },
                    set: function(value) {
                        Ext.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,
        Reaper;
    Ext.Reaper = Reaper = {
        delay: 100,
        queue: [],
        timer: null,
        add: function(obj) {
            if (!Reaper.timer) {
                Reaper.timer = Ext.defer(Reaper.tick, Reaper.delay);
            }
            Reaper.queue.push(obj);
        },
        flush: function() {
            if (Reaper.timer) {
                Ext.undefer(Reaper.timer);
                Reaper.timer = null;
            }
            var queue = Reaper.queue,
                n = queue.length,
                i, obj;
            Reaper.queue = [];
            for (i = 0; i < n; ++i) {
                obj = queue[i];
                if (obj && obj.$reap) {
                    obj.$reap();
                }
            }
        },
        tick: function() {
            Reaper.timer = null;
            Reaper.flush();
        }
    };
    
    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),
                
                configurator = me.getConfigurator(),
                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.configs;
                    if (names) {
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        configurator.addDeprecations(names);
                    }
                    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(parentClass) {
            var me = this,
                parentPrototype = parentClass.prototype,
                prototype, name, statics;
            prototype = me.prototype = Ext.Object.chain(parentPrototype);
            prototype.self = me;
            me.superclass = prototype.superclass = parentPrototype;
            if (!parentClass.$isClass) {
                for (name in BasePrototype) {
                    if (name in prototype) {
                        prototype[name] = BasePrototype[name];
                    }
                }
            }
            
            
            statics = parentPrototype.$inheritableStatics;
            if (statics) {
                for (name in statics) {
                    if (!me.hasOwnProperty(name)) {
                        me[name] = parentClass[name];
                    }
                }
            }
            
            if (parentClass.$onExtended) {
                me.$onExtended = parentClass.$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 me = this,
                proto = me.prototype,
                inheritableStatics = me.$inheritableStatics,
                name, member, current;
            if (!inheritableStatics) {
                inheritableStatics = Ext.apply({}, proto.$inheritableStatics);
                me.$inheritableStatics = proto.$inheritableStatics = inheritableStatics;
            }
            
            var className = Ext.getClassName(me) + '.';
            
            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    current = me[name];
                    
                    if (typeof member == 'function') {
                        member.name = className + name;
                    }
                    
                    if (typeof current === 'function' && !current.$isClass && !current.$nullFn) {
                        member.$previous = current;
                    }
                    me[name] = member;
                    inheritableStatics[name] = true;
                }
            }
            return me;
        },
        
        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.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 (members.platformConfig) {
                me.addPlatformConfig(members);
            }
            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;
        },
        addPlatformConfig: function(data) {
            var me = this,
                prototype = me.prototype,
                platformConfigs = data.platformConfig,
                added, classConfigs, configs, configurator, keys, name, value, i, ln;
            delete prototype.platformConfig;
            
            if (platformConfigs instanceof Array) {
                throw new Error('platformConfigs must be specified as an object.');
            }
            
            configurator = me.getConfigurator();
            classConfigs = configurator.configs;
            
            keys = Ext.getPlatformConfigKeys(platformConfigs);
            
            
            
            
            
            
            
            
            
            
            for (i = 0 , ln = keys.length; i < ln; ++i) {
                configs = platformConfigs[keys[i]];
                added = null;
                for (name in configs) {
                    value = configs[name];
                    
                    if (name in classConfigs) {
                        
                        (added || (added = {}))[name] = value;
                    } else {
                        
                        prototype[name] = value;
                    }
                }
                if (added) {
                    configurator.add(added);
                }
            }
        },
        
        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, mixinName, mixinValue, mixins, mixinStatics, staticName;
            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 (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' || key === '$inheritableStatics') && (prototype[key] === undefined)) {
                    prototype[key] = mixinValue;
                }
            }
            
            
            statics = mixin.$inheritableStatics;
            if (statics) {
                mixinStatics = {};
                for (staticName in statics) {
                    if (!me.hasOwnProperty(staticName)) {
                        mixinStatics[staticName] = mixinClass[staticName];
                    }
                }
                me.addInheritableStatics(mixinStatics);
            }
            
            
            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,
        
        destroyed: false,
        
        clearPropertiesOnDestroy: true,
        
        clearPrototypeOnDestroy: 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;
        },
        
        
        initConfig: function(instanceConfig) {
            var me = this,
                cfg = me.self.getConfigurator();
            me.initConfig = Ext.emptyFn;
            
            me.initialConfig = instanceConfig || {};
            cfg.configure(me, instanceConfig);
            return me;
        },
        beforeInitConfig: Ext.emptyFn,
        
        getConfig: function(name, peek, ifInitialized) {
            var me = this,
                ret, cfg, propName;
            if (name) {
                cfg = me.self.$config.configs[name];
                if (cfg) {
                    propName = me.$configPrefixed ? cfg.names.internal : name;
                    
                    
                    
                    
                    
                    if (ifInitialized) {
                        ret = me.hasOwnProperty(propName) ? me[propName] : null;
                    } else if (peek) {
                        
                        
                        
                        ret = me.hasOwnProperty(propName) ? me[propName] : me.config[name];
                    } else {
                        ret = me[cfg.names.get]();
                    }
                } else {
                    ret = me[name];
                }
            } else {
                ret = me.getCurrentConfig();
            }
            return ret;
        },
        
        destroyMembers: function() {
            var me = this,
                configs = me.self.$config.configs,
                len = arguments.length,
                cfg, name, value, i;
            for (i = 0; i < len; i++) {
                name = arguments[i];
                cfg = configs[name];
                name = cfg && me.$configPrefixed ? cfg.names.internal : name;
                value = me.hasOwnProperty(name) && me[name];
                if (value) {
                    Ext.destroy(value);
                    me[name] = null;
                }
            }
        },
        freezeConfig: function(name) {
            var me = this,
                config = Ext.Config.get(name),
                names = config.names,
                value = me[names.get]();
            me[names.set] = function(v) {
                
                if (v !== value) {
                    Ext.raise('Cannot change frozen config "' + name + '"');
                }
                
                return me;
            };
            
            if (!Ext.isIE8) {
                Object.defineProperty(me, me.$configPrefixed ? names.internal : name, {
                    get: function() {
                        return value;
                    },
                    set: function(v) {
                        if (v !== value) {
                            Ext.raise('Cannot change frozen config "' + name + '"');
                        }
                    }
                });
            }
        },
        
        
        setConfig: function(name, value, options) {
            
            
            
            
            
            var me = this,
                configurator, config, prop;
            if (name) {
                configurator = me.self.getConfigurator();
                if (typeof name === 'string') {
                    config = configurator.configs[name];
                    if (!config) {
                        if (me.$configStrict) {
                            prop = me.self.prototype[name];
                            if ((typeof prop === 'function') && !prop.$nullFn) {
                                
                                Ext.Error.raise("Cannot override method " + name + " on " + me.$className + " instance.");
                                
                                return me;
                            } else 
                            {
                                if (name !== 'type') {
                                    Ext.log.warn('No such config "' + name + '" for class ' + me.$className);
                                }
                            }
                        }
                        
                        config = Ext.Config.map[name] || Ext.Config.get(name);
                    }
                    if (me[config.names.set]) {
                        me[config.names.set](value);
                    } else {
                        
                        me[name] = value;
                    }
                } else {
                    
                    
                    configurator.reconfigure(me, name, options || value);
                }
            }
            return me;
        },
        
        getCurrentConfig: function() {
            var cfg = this.self.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.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.destroyed) {
                        value.destroy();
                    } else if (value.parentNode && 'nodeType' in value) {
                        value.parentNode.removeChild(value);
                    }
                }
                me[link] = null;
            }
            return me;
        },
        $reap: function() {
            var me = this,
                protectedProps = me.$noClearOnDestroy,
                props, prop, value, type, i, len;
            
            
            props = Ext.Object.getKeys(me);
            for (i = 0 , len = props.length; i < len; i++) {
                prop = props[i];
                if (!protectedProps || !protectedProps[prop]) {
                    value = me[prop];
                    type = typeof value;
                    
                    
                    
                    
                    if (type === 'object' || (type === 'function' && !value.$noClearOnDestroy)) {
                        me[prop] = null;
                    }
                }
            }
            me.$nulled = true;
            
            
            
            if (Object.setPrototypeOf) {
                if (me.clearPrototypeOnDestroy && !me.$vetoClearingPrototypeOnDestroy) {
                    Object.setPrototypeOf(me, null);
                }
            }
        },
        
        
        destroy: function() {
            var me = this,
                links = me.$links,
                clearPropertiesOnDestroy = me.clearPropertiesOnDestroy;
            if (links) {
                me.$links = null;
                me.unlink(Ext.Object.getKeys(links));
            }
            me.destroy = Ext.emptyFn;
            
            me.isDestroyed = me.destroyed = true;
            
            
            if (clearPropertiesOnDestroy === true) {
                
                if (!me.isObservable) {
                    me.$reap();
                }
            } else if (clearPropertiesOnDestroy) {
                
                if (clearPropertiesOnDestroy !== 'async') {
                    Ext.raise('Invalid value for clearPropertiesOnDestroy');
                }
                
                Reaper.add(me);
            }
        }
    });
    
    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.raise(msg);
        }
    };
    Ext.Reaper.tick.$skipTimerCheck = true;
    
    return Base;
}(Ext.Function.flexSetter));


(function(LRU, prototype) {
    
    
    
    (Ext.util || (Ext.util = {})).LRU = LRU = 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;
    };
    LRU.prototype = prototype = {
        
        count: 0,
        
        add: function(key, value) {
            var me = this,
                map = me.map,
                entry = map[key];
            if (entry) {
                me.unlink(entry);
                --me.count;
            }
            map[key] = entry = {
                
                id: ++me.seed,
                
                key: key,
                value: value
            };
            me.link(entry);
            ++me.count;
            return entry;
        },
        
        clear: function(fn, scope) {
            var me = this,
                head = me.head,
                entry = head.next;
            head.next = head.prev = head;
            me.count = 0;
            if (fn && !fn.$nullFn) {
                for (; entry !== head; entry = entry.next) {
                    fn.call(scope || me, entry.key, entry.value);
                }
            }
        },
        
        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;
                }
            }
        },
        
        prune: function(fn, scope) {
            var me = this,
                entry = me.head.prev,
                ret;
            if (me.count) {
                ret = entry.value;
                me.unlink(entry);
                --me.count;
                if (fn) {
                    fn.call(scope || me, entry.key, ret);
                }
            }
            return ret;
        },
        
        remove: function(key) {
            var me = this,
                map = me.map,
                entry = map[key],
                value;
            if (entry) {
                me.unlink(entry);
                value = entry.value;
                delete map[key];
                --me.count;
            }
            return value;
        },
        
        touch: function(key) {
            var me = this,
                head = me.head,
                entry = me.map[key];
            if (entry && entry.prev !== head) {
                
                
                me.unlink(entry);
                me.link(entry);
            }
        },
        
        trim: function(size, fn, scope) {
            while (this.count > size) {
                this.prune(fn, scope);
            }
        },
        
        
        
        link: function(entry) {
            var head = this.head,
                first = head.next;
            entry.next = first;
            entry.prev = head;
            head.next = entry;
            first.prev = entry;
        },
        
        unlink: function(entry) {
            var next = entry.next,
                prev = entry.prev;
            prev.next = next;
            next.prev = prev;
        }
    };
    prototype.destroy = function() {
        this.clear.apply(this, arguments);
    };
}());


(function(LRU, fn, Cache) {
    
    
    
    
    Ext.util.Cache = Cache = function(config) {
        LRU.call(this, config);
    };
    fn.prototype = LRU.prototype;
    Cache.prototype = Ext.apply(new fn(), {
        
        maxSize: 100,
        
        
        clear: function() {
            LRU.prototype.clear.call(this, this.evict);
        },
        
        get: function(key) {
            var me = this,
                entry = me.map[key],
                value;
            if (entry) {
                value = entry.value;
                me.touch(key);
            } else {
                value = me.miss.apply(me, arguments);
                me.add(key, value);
                me.trim(me.maxSize, me.evict);
            }
            return value;
        },
        
        
        
        evict: Ext.emptyFn
    });
}(Ext.util.LRU, function() {}));


(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) { try { return (' + code + '); } catch(e) { return false;}}');
    };
    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('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',
            
            
            '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 = {};
    me.nameToPrefix = {};
};
Ext.Inventory.prototype = {
    _array1: [
        0
    ],
    prefixes: null,
    dotRe: /\./g,
    wildcardRe: /\*/g,
    addAlias: function(className, alias, update) {
        return this.addMapping(className, alias, this.aliasToName, this.nameToAliases, update);
    },
    addAlternate: function(className, alternate) {
        return this.addMapping(className, alternate, this.alternateToName, this.nameToAlternates);
    },
    addMapping: function(className, alternate, toName, nameTo, update) {
        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 (!update && toName[a] && ('Ext.Gadget' !== 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.nameToPrefix[className] || (me.nameToPrefix[className] = 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;
        } else if (className in this.nameToPrefix) {
            return this.nameToPrefix[className];
        }
        var prefixes = this.getPrefixes(),
            length = className.length,
            items, currChar, currSubstr, prefix, j, jlen;
        
        
        
        while (length-- > 0) {
            items = prefixes[length];
            if (items) {
                currChar = className.charAt(length);
                if (currChar !== '.') {
                    
                    continue;
                }
                currSubstr = className.substring(0, length);
                for (j = 0 , jlen = items.length; j < jlen; j++) {
                    prefix = items[j];
                    if (prefix === className.substring(0, length)) {
                        return prefix;
                    }
                }
            }
        }
        return '';
    },
    getPrefixes: function() {
        var me = this,
            prefixes = me.prefixes,
            names, name, nameLength, items, i, len;
        if (!prefixes) {
            names = me.names.slice(0);
            me.prefixes = prefixes = [];
            for (i = 0 , len = names.length; i < len; i++) {
                name = names[i];
                nameLength = name.length;
                items = prefixes[nameLength] || (prefixes[nameLength] = []);
                items.push(name);
            }
        }
        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];
        delete me.nameToPrefix[name];
        if (aliases) {
            for (i = aliases.length; i--; ) {
                
                
                
                if (name === aliasToName[a = aliases[i]]) {
                    delete aliasToName[a];
                }
            }
        }
        if (alternates) {
            for (i = alternates.length; i--; ) {
                
                if (name === alternateToName[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[0], 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;
        me.nameToPrefix = {};
        return me;
    })
};



Ext.ClassManager = (function(Class, alias, arraySlice, arrayFrom, global) {
    
    
    
    
    
    var makeCtor = Ext.Class.makeCtor,
        nameLookupStack = [],
        namespaceCache = {
            Ext: {
                name: 'Ext',
                value: Ext
            }
        },
        
        
        Manager = Ext.apply(new Ext.Inventory(), {
            
            classes: {},
            
            classCount: 0,
            
            classState: {},
            
            
            existCache: {},
            
            instantiators: [],
            
            isCreated: function(className) {
                
                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;
                }
                if (!Manager.lookupName(className, false)) {
                    return false;
                }
                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);
                }
            },
            
            $namespaceCache: namespaceCache,
            
            addRootNamespaces: function(namespaces) {
                for (var name in namespaces) {
                    namespaceCache[name] = {
                        name: name,
                        value: namespaces[name]
                    };
                }
            },
            
            clearNamespaceCache: function() {
                nameLookupStack.length = 0;
                for (var name in namespaceCache) {
                    if (!namespaceCache[name].value) {
                        delete namespaceCache[name];
                    }
                }
            },
            
            getNamespaceEntry: function(namespace) {
                if (typeof namespace !== 'string') {
                    return namespace;
                }
                
                var entry = namespaceCache[namespace],
                    i;
                if (!entry) {
                    i = namespace.lastIndexOf('.');
                    if (i < 0) {
                        entry = {
                            name: namespace
                        };
                    } else {
                        entry = {
                            name: namespace.substring(i + 1),
                            parent: Manager.getNamespaceEntry(namespace.substring(0, i))
                        };
                    }
                    namespaceCache[namespace] = entry;
                }
                return entry;
            },
            
            lookupName: function(namespace, autoCreate) {
                var entry = Manager.getNamespaceEntry(namespace),
                    scope = Ext.global,
                    i = 0,
                    e, parent;
                
                for (e = entry; e; e = e.parent) {
                    
                    
                    
                    nameLookupStack[i++] = e;
                }
                while (scope && i-- > 0) {
                    
                    e = nameLookupStack[i];
                    parent = scope;
                    scope = e.value || scope[e.name];
                    if (!scope && autoCreate) {
                        parent[e.name] = scope = {};
                    }
                }
                return scope;
            },
            
            setNamespace: function(namespace, value) {
                var entry = Manager.getNamespaceEntry(namespace),
                    scope = Ext.global;
                if (entry.parent) {
                    scope = Manager.lookupName(entry.parent, true);
                }
                scope[entry.name] = value;
                return value;
            },
            
            setXType: function(cls, xtype) {
                var className = cls.$className,
                    C = className ? cls : Manager.get(className = cls),
                    proto = C.prototype,
                    xtypes = proto.xtypes,
                    xtypesChain = proto.xtypesChain,
                    xtypesMap = proto.xtypesMap;
                if (!proto.hasOwnProperty('xtypes')) {
                    proto.xtypes = xtypes = [];
                    proto.xtypesChain = xtypesChain = xtypesChain ? xtypesChain.slice(0) : [];
                    proto.xtypesMap = xtypesMap = Ext.apply({}, xtypesMap);
                }
                Manager.addAlias(className, 'widget.' + xtype, true);
                xtypes.push(xtype);
                xtypesChain.push(xtype);
                xtypesMap[xtype] = true;
            },
            
            
            set: function(name, value) {
                var targetName = Manager.getName(value);
                Manager.classes[name] = Manager.setNamespace(name, value);
                
                Manager.classCount++;
                
                if (targetName && targetName !== name) {
                    Manager.addAlternate(targetName, name);
                }
                return Manager;
            },
            
            get: function(name) {
                return Manager.classes[name] || Manager.lookupName(name, false);
            },
            
            addNameAliasMappings: function(aliases) {
                Manager.addAlias(aliases);
            },
            
            addNameAlternateMappings: function(alternates) {
                Manager.addAlternate(alternates);
            },
            
            getByAlias: function(alias) {
                return Manager.get(Manager.getNameByAlias(alias));
            },
            
            getByConfig: function(config, aliasPrefix) {
                var xclass = config.xclass,
                    name;
                if (xclass) {
                    name = xclass;
                } else {
                    name = config.xtype;
                    if (name) {
                        aliasPrefix = 'widget.';
                    } else {
                        name = config.type;
                    }
                    name = Manager.getNameByAlias(aliasPrefix + name);
                }
                return Manager.get(name);
            },
            
            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 = 1,
                    
                    dependenciesLoaded,
                    classReady = function() {
                        var cls, dependencies, i, key, temp;
                        if (!dependenciesLoaded) {
                            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);
                                        }
                                    }
                                }
                            }
                            dependenciesLoaded = 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 = overriddenClassName.$isClass ? overriddenClassName : 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);
                        }
                    };
                
                if (className) {
                    Manager.overrideMap[className] = true;
                }
                
                
                
                if ('compatibility' in data) {
                    compat = data.compatibility;
                    if (!compat) {
                        
                        compat = false;
                    } else if (typeof compat === 'number') {
                        
                        compat = true;
                    } else if (typeof compat !== 'boolean') {
                        compat = Ext.checkVersion(compat);
                    }
                }
                if (compat) {
                    
                    if (overriddenClassName.$isClass) {
                        classReady();
                    } else {
                        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);
            },
            
            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('platformConfig', function(name, Class, data) {
        Class.addPlatformConfig(data);
    });
    
    
    
    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;
            
            if (classes[className]) {
                Manager.classCount--;
            }
            
            delete classes[className];
            delete Manager.existCache[className];
            delete Manager.classState[className];
            Manager.removeName(className);
            
            
            Ext.Factory.clearCaches();
            var entry = Manager.getNamespaceEntry(className),
                scope = entry.parent ? Manager.lookupName(entry.parent, false) : Ext.global,
                entryName;
            if (scope) {
                entryName = entry.name;
                
                try {
                    delete scope[entryName];
                } catch (e) {
                    scope[entryName] = undefined;
                }
            }
            
            
            return entryName;
        },
        
        
        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: function() {
            var root = global,
                i;
            for (i = arguments.length; i-- > 0; ) {
                root = Manager.lookupName(arguments[i], true);
            }
            return root;
        }
    });
    
    Ext.addRootNamespaces = Manager.addRootNamespaces;
    
    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.interceptAfterOnce(cls, 'onClassCreated', function() {
            var cls = this,
                prototype = cls.prototype,
                mixins = prototype.mixins,
                key, mixin;
            
            Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor#afterClassCreated', arguments);
            
            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 = {})).Browser = function(userAgent, publish) {
    
    
    
    
    var me = this,
        browserPrefixes = Ext.Boot.browserPrefixes,
        browserNames = Ext.Boot.browserNames,
        enginePrefixes = me.enginePrefixes,
        engineNames = me.engineNames,
        browserMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(browserPrefixes).join(')|(?:') + '))([\\w\\._]+)')),
        engineMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(enginePrefixes).join(')|(?:') + '))([\\w\\._]+)')),
        browserName = browserNames.other,
        engineName = engineNames.other,
        browserVersion = '',
        engineVersion = '',
        majorVer = '',
        isWebView = false,
        edgeRE = /(Edge\/)([\w.]+)/,
        ripple = '',
        i, prefix, name;
    
    me.userAgent = userAgent;
    
    this.is = function(name) {
        
        
        return !!this.is[name];
    };
    
    
    if (/Edge\//.test(userAgent)) {
        browserMatch = userAgent.match(edgeRE);
        engineMatch = userAgent.match(edgeRE);
    }
    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 (browserName && browserVersion) {
        Ext.setVersion(browserName, browserVersion);
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    if (userAgent.match(/FB/) && browserName === 'Other') {
        browserName = browserNames.safari;
        engineName = engineNames.webkit;
    }
    
    else if (userAgent.match(/Android.*Chrome/g)) {
        browserName = 'ChromeMobile';
    } else {
        browserMatch = userAgent.match(/OPR\/(\d+.\d+)/);
        if (browserMatch) {
            browserName = 'Opera';
            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 = document.documentMode || parseInt(majorVer, 10);
            for (i = 7; i <= 11; ++i) {
                prefix = 'isIE' + i;
                Ext[prefix] = majorVer === i;
                Ext[prefix + 'm'] = majorVer <= i;
                Ext[prefix + 'p'] = majorVer >= i;
            }
        }
        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);
    
    try {
        ripple = window.top.ripple;
    } catch (e) {}
    
    this.setFlag('Ripple', !!document.getElementById("tinyhippos-injected") && !Ext.isEmpty(ripple));
    this.setFlag('WebWorks', !!window.blackberry);
    if (window.PhoneGap !== undefined || window.Cordova !== undefined || window.cordova !== undefined) {
        isWebView = true;
        this.setFlag('PhoneGap');
        this.setFlag('Cordova');
    }
    
    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,
    engineNames: {
        edge: 'Edge',
        webkit: 'WebKit',
        gecko: 'Gecko',
        presto: 'Presto',
        trident: 'Trident',
        other: 'Other'
    },
    enginePrefixes: {
        edge: 'Edge/',
        webkit: 'AppleWebKit/',
        gecko: 'Gecko/',
        presto: 'Presto/',
        trident: 'Trident/'
    },
    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: ''
    },
    
    
    name: null,
    
    version: null,
    
    engineName: null,
    
    engineVersion: null,
    setFlag: function(name, value, publish) {
        if (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;
    }
};

(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 = Ext.Boot.osNames,
        prefixes = Ext.Boot.osPrefixes,
        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 (userAgent.match(/ipad/i)) {
        platform = 'iPad';
    }
    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");
        }
        if (this.is.Android4) {
            browserScope.setFlag("AndroidStock");
            browserScope.setFlag("AndroidStock4");
        }
    }
};
Ext.env.OS.prototype = {
    constructor: Ext.env.OS,
    
    is: function(name) {
        return !!this[name];
    },
    
    name: null,
    
    version: null,
    setFlag: function(name, value) {
        if (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;
    Ext.isApple = Ext.isMac || Ext.isiOS;
    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|ChromeOS/.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.Android && 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);
        return (style || element.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, names, test, vector, value;
        
        
        
        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;
            names = test.names;
            if (value === undefined) {
                if (!isReady && test.ready) {
                    
                    notRun.push(test);
                    
                    continue;
                }
                value = test.fn.call(me, doc, div);
            }
            
            if (name) {
                supports[name] = has[name] = value;
            } else if (names) {
                while (names.length) {
                    name = names.pop();
                    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: 'CloneNodeCopiesExpando',
            fn: function() {
                var el = document.createElement('div');
                el.expandoProp = {};
                return el.cloneNode().expandoProp === el.expandoProp;
            }
        },
        {
            
            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: 'CSS3NegationSelector',
            fn: function(doc) {
                try {
                    doc.querySelectorAll("foo:not(bar)");
                } catch (e) {
                    return false;
                }
                return true;
            }
        },
        {
            
            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: 'Touch',
            fn: function() {
                
                var maxTouchPoints = navigator.msMaxTouchPoints || navigator.maxTouchPoints;
                
                
                
                
                
                
                
                
                
                if (Ext.browser.is.Chrome && Ext.browser.version.isLessThanOrEqual(39)) {
                    return (Ext.supports.TouchEvents && maxTouchPoints !== 1) || maxTouchPoints > 1;
                } else {
                    return Ext.supports.TouchEvents || maxTouchPoints > 0;
                }
            }
        },
        {
            
            name: 'PointerEvents',
            fn: function() {
                return !!(window.PointerEvent && !Ext.supports.TouchEvents);
            }
        },
        {
            
            name: 'MSPointerEvents',
            fn: function() {
                return Ext.isIE10;
            }
        },
        {
            
            name: 'TouchEvents',
            fn: function() {
                return this.isEventSupported('touchend');
            }
        },
        {
            
            name: 'TouchAction',
            ready: true,
            fn: function(doc, div) {
                if (!window.getComputedStyle) {
                    return 0;
                }
                var values = [
                        'pan-x',
                        'pan-y',
                        'pinch-zoom',
                        'double-tap-zoom'
                    ],
                    flags = [
                        1,
                        2,
                        4,
                        8
                    ],
                    ln = values.length,
                    flag = 0,
                    i, value;
                for (i = 0; i < ln; i++) {
                    value = values[i];
                    div.style.touchAction = value;
                    if (getComputedStyle(div).touchAction === value) {
                        flag |= flags[i];
                    }
                }
                return flag;
            }
        },
        {
            
            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: 'Css3dTransforms',
            fn: function() {
                
                return this.has('CssTransforms') && this.isStyleSupported('perspective');
            }
        },
        
        
        {
            
            name: 'CssTransforms',
            fn: function() {
                return this.isStyleSupported('transform');
            }
        },
        {
            name: 'CssTransformNoPrefix',
            fn: function() {
                return this.isStyleSupportedWithoutPrefix('transform');
            }
        },
        {
            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: 'XmlQuerySelector',
            fn: function() {
                var xmlString = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><root></root>',
                    xmlDoc;
                
                if (window.ActiveXObject) {
                    xmlDoc = new ActiveXObject("Microsoft.xmlDOM");
                    xmlDoc.async = false;
                    xmlDoc.loadXML(xmlString);
                } else if (window.DOMParser) {
                    var parser = new DOMParser();
                    xmlDoc = parser.parseFromString(xmlString, 'text/xml');
                }
                return xmlDoc ? !!xmlDoc.lastChild.querySelector : 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: '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 = doc.createElement('div'),
                    style = el.style;
                if (el.getBoundingClientRect) {
                    
                    
                    
                    style.position = 'absolute';
                    style.top = "0";
                    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('div', true);
                    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,
                    style = div.style,
                    pos = style.position;
                
                style.position = 'absolute';
                outerBox.offsetHeight;
                style.position = pos;
                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.isIE || Ext.isEdge) || (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.firefoxVersion < 52);
            }
        },
        
        {
            name: 'AsyncFocusEvents',
            fn: function() {
                
                
                
                return Ext.asyncFocus = !!Ext.isIE;
            }
        },
        
        
        {
            name: 'accessibility',
            ready: true,
            fn: function(doc) {
                var body = doc.body,
                    div, img, style, supports, bgImg;
                function getColor(colorTxt) {
                    var values = [],
                        colorValue = 0,
                        regex, match;
                    if (colorTxt.indexOf('rgb(') !== -1) {
                        values = colorTxt.replace('rgb(', '').replace(')', '').split(', ');
                    } else if (colorTxt.indexOf('#') !== -1) {
                        regex = colorTxt.length === 7 ? /^#(\S\S)(\S\S)(\S\S)$/ : /^#(\S)(\S)(\S)$/;
                        match = colorTxt.match(regex);
                        if (match) {
                            values = [
                                '0x' + match[1],
                                '0x' + match[2],
                                '0x' + match[3]
                            ];
                        }
                    }
                    for (var i = 0; i < values.length; i++) {
                        colorValue += parseInt(values[i]);
                    }
                    return colorValue;
                }
                div = doc.createElement('div');
                img = doc.createElement('img');
                style = div.style;
                Ext.apply(style, {
                    width: '2px',
                    position: 'absolute',
                    clip: 'rect(1px,1px,1px,1px)',
                    borderWidth: '1px',
                    borderStyle: 'solid',
                    borderTopTolor: '#f00',
                    borderRightColor: '#ff0',
                    backgroundColor: '#fff',
                    backgroundImage: 'url(' + Ext.BLANK_IMAGE_URL + ')'
                });
                img.alt = '';
                img.src = Ext.BLANK_IMAGE_URL;
                div.appendChild(img);
                body.appendChild(div);
                
                style = div.currentStyle || div.style;
                bgImg = style.backgroundImage;
                supports = {
                    
                    
                    
                    
                    Images: img.offsetWidth === 1 && img.readyState !== 'uninitialized',
                    BackgroundImages: !(bgImg !== null && (bgImg === "none" || bgImg === "url(invalid-url:)")),
                    BorderColors: style.borderTopColor !== style.borderRightColor,
                    LightOnDark: getColor(style.color) - getColor(style.backgroundColor) > 0
                };
                Ext.supports.HighContrastMode = !supports.BackgroundImages;
                body.removeChild(div);
                div = img = null;
                return supports;
            }
        },
        {
            
            name: 'ViewportUnits',
            ready: true,
            fn: function(doc) {
                
                
                if (Ext.isIE8) {
                    return false;
                }
                
                var body = doc.body,
                    div = document.createElement('div'),
                    style = div.currentStyle || div.style,
                    width, divWidth;
                body.appendChild(div);
                Ext.apply(style, {
                    width: '50vw'
                });
                width = parseInt(window.innerWidth / 2, 10);
                divWidth = parseInt((window.getComputedStyle ? getComputedStyle(div, null) : div.currentStyle).width, 10);
                body.removeChild(div);
                div = null;
                return width === divWidth;
            }
        },
        {
            name: 'CSSVariables',
            ready: false,
            fn: function(doc) {
                
                
                if (!window.getComputedStyle) {
                    return false;
                }
                
                var style = window.getComputedStyle(doc.documentElement);
                return style.getPropertyValue && !!style.getPropertyValue('--x-supports-variables');
            }
        },
        {
            
            name: 'Selectors2',
            ready: false,
            fn: function(doc) {
                try {
                    return !!doc.querySelectorAll(':scope');
                } catch (e) {
                    return false;
                }
            }
        },
        {
            
            name: 'CSSScrollSnap',
            ready: false,
            fn: function(doc) {
                var style = doc.documentElement.style;
                return 'scrollSnapType' in style || 'webkitScrollSnapType' in style || 'msScrollSnapType' in style;
            }
        },
        
        {
            name: 'TranslateYCausesHorizontalScroll',
            ready: true,
            fn: function(doc, div) {
                div.innerHTML = '<div style="position: relative; overflow: auto; height: 200px; width: 200px;">' + '<div>' + '<div style="transform: translateY(260px); width: 50px;">a</div>' + '</div>' + '</div>';
                return div.firstChild.scrollWidth > div.firstChild.clientWidth;
            }
        },
        {
            
            name: 'PercentageSizeFlexBug',
            ready: true,
            fn: function(doc, div) {
                if (Ext.isIE9m) {
                    return false;
                }
                var style = div.style;
                style.display = 'flex';
                style.flexDirection = 'column';
                style.height = style.width = '100px';
                div.innerHTML = '<div style="flex: 1 1;"><div style="height:50%"></div></div>';
                return div.firstChild.firstChild.offsetHeight !== 50;
            }
        },
        {
            
            name: 'CannotScrollExactHeight',
            fn: function() {
                return Ext.isIE10p;
            }
        },
        {
            
            name: 'WebKitInputTableBoxModelBug',
            ready: true,
            fn: function(doc, div) {
                var table = document.createElement('div'),
                    cell = document.createElement('div'),
                    input = document.createElement('input'),
                    tableStyle = table.style,
                    cellStyle = cell.style,
                    inputStyle = input.style,
                    body = doc.body,
                    hasBug;
                input.type = 'text';
                tableStyle.display = 'table';
                tableStyle.height = '100px';
                cellStyle.display = 'table-cell';
                inputStyle.border = '0';
                inputStyle.padding = '10px';
                inputStyle.boxSizing = 'border-box';
                inputStyle.height = '100%';
                cell.appendChild(input);
                table.appendChild(cell);
                body.appendChild(table);
                hasBug = input.offsetHeight === 80;
                body.removeChild(table);
                return hasBug;
            }
        },
        {
            
            name: 'PassiveEventListener',
            fn: function(doc, div) {
                var supportsPassive = false,
                    options;
                try {
                    options = Object.defineProperty({}, 'passive', {
                        get: function() {
                            supportsPassive = true;
                        }
                    });
                    window.addEventListener('e', null, options);
                    window.removeEventListener('e', null, options);
                } catch (e) {}
                return supportsPassive;
            }
        },
        {
            
            name: 'CSSMinContent',
            ready: true,
            fn: function(doc, div) {
                
                
                
                div.innerHTML = '<div style="height:4px;width:4px;min-height:-webkit-min-content;min-height:-moz-min-content;min-height:min-content"><div style="height:8px;width:8px"></div></div>';
                return div.firstChild.offsetHeight === 8;
            }
        },
        {
            name: 'ComputedSizeIncludesPadding',
            ready: true,
            fn: function(doc, div) {
                var ret = false,
                    bd = document.body,
                    el, w;
                if (window.getComputedStyle) {
                    el = document.createElement('div');
                    el.style.cssText = 'width:10px;padding:2px;' + '-webkit-box-sizing:border-box;box-sizing:border-box;';
                    bd.appendChild(el);
                    w = window.getComputedStyle(el, null).width;
                    ret = w === '10px';
                    bd.removeChild(el);
                }
                return ret;
            }
        },
        0
    ]
};

Ext.feature.tests.pop();

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.ticks();
            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.ticks();
            me.invokeAll();
            Ext._afterReadyTime = Ext.ticks();
        }
    },
    
    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)) {
                    Ext.undefer(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);
    };
    
    Ext.onInternalReady = function(fn, scope, options) {
        Ready.on(fn, scope, Ext.apply({
            priority: 1000
        }, 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 = {},
        _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: {},
        missingCount: 0,
        missingQueue: {},
        
        syncModeEnabled: false,
        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, classes, className, idx, baseUrl, loadlen, l, loadItem;
            
            if (src.indexOf("packages/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;
                        loadItem.canonicalPath = true;
                    }
                }
            }
            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 state = Manager.classState,
                missingClassNames = [],
                urls = [],
                urlByClass = {},
                numClasses = classNames.length,
                url, className, i, numMissing;
            for (i = 0; i < numClasses; ++i) {
                className = Manager.resolveName(classNames[i]);
                if (!Manager.isCreated(className)) {
                    missingClassNames.push(className);
                    if (!state[className]) {
                        urlByClass[className] = Loader.getPath(className);
                        urls.push(urlByClass[className]);
                    }
                }
            }
            
            
            numMissing = missingClassNames.length;
            if (numMissing) {
                Loader.missingCount += numMissing;
                Manager.onCreated(function() {
                    if (callback) {
                        Ext.callback(callback, scope, arguments);
                    }
                    Loader.checkReady();
                }, Loader, missingClassNames);
                if (!_config.enabled) {
                    Ext.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,
                        _urlByClass: urlByClass
                    });
                } 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(request) {
            var options = this,
                entries = request.entries || [],
                onError = options.onError,
                error, entry, i;
            Loader.hasFileLoadError = true;
            --Loader.scriptsLoading;
            if (onError) {
                for (i = 0; i < entries.length; i++) {
                    entry = entries[i];
                    if (entry.error) {
                        error = new Error('Failed to load: ' + entry.url);
                        break;
                    }
                }
                error = error || new Error('Failed to load');
                onError.call(options.userScope, options, error, request);
            } else 
            {
                Ext.log.error("[Ext.Loader] Some requested files failed to load.");
            }
            
            Loader.checkReady();
        },
        onLoadSuccess: function() {
            var options = this,
                onLoad = options.onLoad,
                classNames = options._classNames,
                urlByClass = options._urlByClass,
                state = Manager.classState,
                missingQueue = Loader.missingQueue,
                className, i, len;
            --Loader.scriptsLoading;
            if (onLoad) {
                
                onLoad.call(options.userScope, options);
            }
            
            
            
            
            for (i = 0 , len = classNames.length; i < len; i++) {
                className = classNames[i];
                
                
                
                
                
                
                
                
                if (!state[className]) {
                    missingQueue[className] = urlByClass[className];
                }
            }
            Loader.checkReady();
        },
        
        
        
        reportMissingClasses: function() {
            if (!Loader.syncModeEnabled && !Loader.scriptsLoading && Loader.isLoading && !Loader.hasFileLoadError) {
                var missingQueue = Loader.missingQueue,
                    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);
        },
        
        checkMissingQueue: function() {
            var missingQueue = Loader.missingQueue,
                newQueue = {},
                name,
                missing = 0;
            for (name in missingQueue) {
                
                
                
                if (!(Manager.classState[name] || Manager.isCreated(name))) {
                    newQueue[name] = missingQueue[name];
                    missing++;
                }
            }
            Loader.missingCount = missing;
            Loader.missingQueue = newQueue;
        },
        
        checkReady: function() {
            var wasLoading = Loader.isLoading,
                isLoading;
            Loader.checkMissingQueue();
            isLoading = Loader.missingCount + Loader.scriptsLoading;
            if (isLoading && !wasLoading) {
                Ready.block();
                Loader.isLoading = !!isLoading;
            } else if (!isLoading && wasLoading) {
                Loader.triggerReady();
            }
            
            if (!Loader.scriptsLoading && Loader.missingCount) {
                
                
                Ext.defer(function() {
                    if (!Loader.scriptsLoading && Loader.missingCount) {
                        Ext.log.error('[Loader] The following classes failed to load:');
                        for (var name in Loader.missingQueue) {
                            Ext.log.error('[Loader] ' + name + ' from ' + Loader.missingQueue[name]);
                        }
                    }
                }, 1000);
            }
        }
    });
    
    
    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 manifestClasses = Ext.manifest && Ext.manifest.classes,
            deadlockPath = [],
            detectDeadlock;
        
        if (className && (!manifestClasses || !manifestClasses[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);
                var requires = _requiresMap[cls],
                    dep, i, ln;
                if (requires) {
                    if (Ext.Array.contains(requires, className)) {
                        Ext.Error.raise("Circular requirement detected! '" + className + "' and '" + deadlockPath[1] + "' mutually require each other. Path: " + deadlockPath.join(' -> ') + " -> " + deadlockPath[0]);
                    }
                    for (i = 0 , ln = requires.length; i < ln; i++) {
                        dep = requires[i];
                        if (!isInHistory[dep]) {
                            detectDeadlock(requires[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 uses = data.uses,
            classNames;
        if (uses) {
            classNames = Manager.getNamesByExpression(data.uses);
            Loader.addUsedClasses(classNames);
        }
    });
    Manager.onCreated(Loader.historyPush);
    
    Loader.init();
}());


Ext._endTime = Ext.ticks();



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.raise('Use of "beforeHooks" is deprecated - use "before" instead');
                }
                if (mixinConfig.hooks) {
                    Ext.raise('Use of "hooks" is deprecated - use "after" instead');
                }
                if (mixinConfig.afterHooks) {
                    Ext.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() {
            me.id = null;
            if (!(scope && scope.destroyed)) {
                args ? fn.apply(scope, args) : fn.call(scope);
            }
            if (fireIdleEvent === false) {
                Ext._suppressIdle = true;
            }
        };
    
    
    if (fn) {
        call.$origFn = fn.$origFn || fn;
        call.$skipTimerCheck = call.$origFn.$skipTimerCheck;
    }
    
    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;
        me.delayTime = delay;
        
        if (fn) {
            call.$origFn = fn.$origFn || fn;
            call.$skipTimerCheck = call.$origFn.$skipTimerCheck;
        }
        
        if (!me.id) {
            if (delay === -1) {
                me.id = Ext.raf(call);
            } else {
                me.id = Ext.defer(call, delay || 1);
            }
        }
        
        return me.id;
    };
    
    me.cancel = function() {
        if (me.id) {
            if (me.delayTime === -1) {
                Ext.unraf(me.id);
            } else {
                Ext.undefer(me.id);
            }
            me.id = null;
        }
    };
    me.flush = function() {
        if (me.id) {
            me.cancel();
            
            
            var was = fireIdleEvent;
            fireIdleEvent = true;
            call();
            fireIdleEvent = was;
        }
    };
    
    me.stop = function(stopFn, stopScope) {
        
        
        
        if (stopFn && stopFn === fn && (!stopScope || stopScope === scope)) {
            me.cancel();
        }
    };
};



Ext.define('Ext.util.Event', function() {
    var arraySlice = Array.prototype.slice,
        arrayInsert = Ext.Array.insert,
        toArray = Ext.Array.toArray,
        fireArgs = {};
    return {
        
        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, managedListeners;
            
            if (scope && !Ext._namedScopes[scope] && (typeof fn === 'string') && (typeof scope[fn] !== 'function')) {
                Ext.raise("No method named '" + fn + "' found on scope object");
            }
            
            if (me.findListener(fn, scope) === -1) {
                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) {
                    listeners[length] = listener;
                } else {
                    arrayInsert(listeners, index, [
                        listener
                    ]);
                }
                if (observable.isElement) {
                    
                    
                    
                    
                    
                    
                    observable._getPublisher(eventName, options.translate === false).subscribe(observable, eventName, options.delegated !== false, options.capture);
                }
                
                if (manager) {
                    
                    
                    managedListeners = manager.managedListeners || (manager.managedListeners = []);
                    managedListeners.push({
                        item: me.observable,
                        ename: (options && options.managedName) || me.name,
                        fn: fn,
                        scope: scope,
                        options: options
                    });
                }
                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.onFrame) {
                    handler = me.createAnimFrame(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;
        },
        removeListener: function(fn, scope, index) {
            var me = this,
                removed = false,
                observable = me.observable,
                eventName = me.name,
                listener, options, manager, managedListeners, managedListener, i;
            index = index != null ? index : me.findListener(fn, scope);
            if (index !== -1) {
                listener = me.listeners[index];
                if (me.firing) {
                    me.listeners = me.listeners.slice(0);
                }
                
                
                
                me.listeners.splice(index, 1);
                
                
                if (me._highestNegativePriorityIndex) {
                    if (index < me._highestNegativePriorityIndex) {
                        me._highestNegativePriorityIndex--;
                    } else if (index === me._highestNegativePriorityIndex && index === me.listeners.length) {
                        delete me._highestNegativePriorityIndex;
                    }
                }
                if (listener) {
                    options = listener.o;
                    
                    
                    
                    if (listener.task) {
                        listener.task.cancel();
                        delete listener.task;
                    }
                    
                    i = listener.tasks && listener.tasks.length;
                    if (i) {
                        while (i--) {
                            listener.tasks[i].cancel();
                        }
                        delete listener.tasks;
                    }
                    
                    listener.fireFn.timerId = Ext.undefer(listener.fireFn.timerId);
                    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 (observable.isElement) {
                        observable._getPublisher(eventName, options.translate === false).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;
        },
        fireDelegated: function(firingObservable, args) {
            this.firingObservable = firingObservable;
            return this.fire.apply(this, args);
        },
        fire: function() {
            var me = this,
                CQ = Ext.ComponentQuery,
                listeners = me.listeners,
                count = listeners.length,
                observable = me.observable,
                isElement = observable.isElement,
                isComponent = observable.isComponent,
                firingObservable = me.firingObservable,
                options, delegate, fireInfo, i, args, listener, len, delegateEl, currentTarget, type, chained, firingArgs, e, fireFn, fireScope;
            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];
                    
                    
                    
                    if (!listener) {
                        
                        continue;
                    }
                    options = listener.o;
                    if (isElement) {
                        if (currentTarget) {
                            
                            
                            e.setCurrentTarget(currentTarget);
                        }
                        
                        
                        
                        
                        type = listener.type;
                        if (type) {
                            
                            
                            
                            
                            
                            
                            chained = e;
                            e = args[0] = chained.chain({
                                type: type,
                                isGesture: false
                            });
                        }
                        
                        
                        Ext.EventObject = e;
                    }
                    firingArgs = args;
                    if (options) {
                        delegate = options.delegate;
                        if (delegate) {
                            if (isElement) {
                                
                                
                                delegateEl = e.getTarget(typeof delegate === 'function' ? delegate : '#' + e.currentTarget.id + ' ' + delegate);
                                if (delegateEl) {
                                    args[1] = delegateEl;
                                    
                                    
                                    currentTarget = e.currentTarget;
                                    e.setCurrentTarget(delegateEl);
                                } else {
                                    
                                    continue;
                                }
                            } else if (isComponent && !CQ.is(firingObservable, delegate, observable)) {
                                
                                continue;
                            }
                        }
                        if (isElement) {
                            if (options.preventDefault) {
                                e.preventDefault();
                            }
                            if (options.stopPropagation) {
                                e.stopPropagation();
                            }
                            if (options.stopEvent) {
                                e.stopEvent();
                            }
                        }
                        args[len] = options;
                        if (options.args) {
                            firingArgs = options.args.concat(args);
                        }
                    }
                    fireInfo = me.getFireInfo(listener);
                    fireFn = fireInfo.fn;
                    fireScope = fireInfo.scope;
                    
                    fireInfo.fn = fireInfo.scope = null;
                    
                    
                    
                    if (fireScope && fireScope.destroyed) {
                        me.removeListener(fireFn, fireScope, i);
                        fireFn = null;
                        
                        
                        
                        if (fireScope.$className !== 'Ext.container.Monitor') {
                            (Ext.raiseOnDestroyed ? Ext.raise : Ext.log.warn)({
                                msg: 'Attempting to fire "' + me.name + '" event on destroyed ' + (fireScope.$className || 'object') + ' instance with id: ' + (fireScope.id || 'unknown'),
                                instance: fireScope
                            });
                        }
                    }
                    
                    
                    if (fireFn && fireFn.apply(fireScope, firingArgs) === false) {
                        Ext.EventObject = null;
                        return (me.firing = false);
                    }
                    
                    
                    if (options) {
                        args.length--;
                    }
                    if (chained) {
                        
                        
                        
                        e = args[0] = chained;
                        chained = null;
                    }
                    
                    
                    Ext.EventObject = 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.raise('Unable to dynamically resolve scope for "' + listener.ev.name + '" listener on ' + this.observable.id);
                }
                if (!Ext.isFunction(scope[fn])) {
                    Ext.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.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.raise('Unable to dynamically resolve method "' + name + '" on ' + this.observable.$className);
            }
            
            return fireArgs;
        },
        createAnimFrame: function(handler, listener, o, scope, wrapped) {
            var fireInfo;
            if (!wrapped) {
                fireInfo = listener.ev.getFireInfo(listener, true);
                handler = fireInfo.fn;
                scope = fireInfo.scope;
                
                fireInfo.fn = fireInfo.scope = null;
            }
            return Ext.Function.createAnimationFrame(handler, scope, o.args);
        },
        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;
                        
                        fireInfo.fn = fireInfo.scope = null;
                    }
                    return handler.apply(scope, arguments);
                }
            };
        },
        createBuffered: function(handler, listener, o, scope, wrapped) {
            listener.task = new Ext.util.DelayedTask();
            return function() {
                
                
                
                if (listener.task) {
                    var fireInfo;
                    
                    if (Ext.Timer.track) {
                        o.$delayedTask = listener.task;
                    }
                    
                    
                    if (!wrapped) {
                        fireInfo = listener.ev.getFireInfo(listener, true);
                        handler = fireInfo.fn;
                        scope = fireInfo.scope;
                        
                        fireInfo.fn = fireInfo.scope = null;
                    }
                    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;
                    
                    fireInfo.fn = fireInfo.scope = null;
                }
                if (!listener.tasks) {
                    listener.tasks = [];
                }
                listener.tasks.push(task);
                
                if (Ext.Timer.track) {
                    o.$delayedTask = task;
                }
                
                
                task.delay(o.delay || 10, handler, scope, toArray(arguments));
            };
        },
        createSingle: function(handler, listener, o, scope, wrapped) {
            return function() {
                var event = listener.ev,
                    observable = event.observable,
                    fn = listener.fn,
                    fireInfo;
                
                
                
                
                if (observable) {
                    if (!observable.destroyed) {
                        observable.removeListener(event.name, fn, scope);
                    }
                } else {
                    event.removeListener(fn, scope);
                }
                if (!wrapped) {
                    fireInfo = event.getFireInfo(listener, true);
                    handler = fireInfo.fn;
                    scope = fireInfo.scope;
                    
                    fireInfo.fn = fireInfo.scope = null;
                }
                return handler.apply(scope, arguments);
            };
        }
    };
});



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 = this.id = prefix + (++uniqueIds[prefix]);
        }
        this.getUniqueId = this.getOptimizedId;
        return id;
    },
    setId: function(id) {
        
        this.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);
        },
        
        
        protectedProps = [
            'events',
            'hasListeners',
            'managedListeners',
            'eventedBeforeEventNames'
        ];
    ListenerRemover.prototype.destroy = function() {
        var me = this,
            args = me.args,
            observable = me.observable,
            
            
            elementName = args[0].element || (args[3] && args[3].element);
        
        if (elementName) {
            
            if (Ext.Array.indexOf(observable.referenceList, elementName) === -1) {
                Ext.Logger.error("Destroying event listener with an invalid element reference of '" + elementName + "' for this component. Available values are: '" + observable.referenceList.join("', '") + "'", observable);
            }
            
            observable = observable[elementName];
        }
        
        if (!observable.destroyed) {
            observable[me.managedListeners ? 'mun' : 'un'].apply(observable, me.args);
        }
        me.destroy = Ext.emptyFn;
    };
    return {
        extend: Ext.Mixin,
        mixinConfig: {
            id: 'observable',
            after: {
                destroy: 'destroyObservable'
            }
        },
        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, i, len;
                
                
                
                
                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();
                }
                
                scope = T.prototype.$noClearOnDestroy || {};
                for (i = 0 , len = protectedProps.length; i < len; i++) {
                    scope[protectedProps[i]] = true;
                }
                T.prototype.$noClearOnDestroy = scope;
            }
        },
        
        
        
        isObservable: true,
        
        $vetoClearingPrototypeOnDestroy: true,
        
        eventsSuspended: 0,
        
        constructor: function(config) {
            var me = this,
                self = me.self,
                declaredListeners, listeners, bubbleEvents, len, i;
            
            
            
            
            if (me.$observableInitialized) {
                return;
            }
            me.$observableInitialized = true;
            
            
            
            
            
            
            
            
            me.hasListeners = me.hasListeners = new me.HasListeners();
            me.eventedBeforeEventNames = {};
            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) {
                    
                    
                    
                    
                    
                    
                    for (i = 0 , len = listeners.length; i < len; ++i) {
                        me.addListener(listeners[i]);
                    }
                } else {
                    me.addListener(listeners);
                }
            }
            bubbleEvents = (config && config.bubbleEvents) || me.bubbleEvents;
            if (bubbleEvents) {
                me.enableBubble(bubbleEvents);
            }
            if (me.$applyConfigs) {
                
                if (config) {
                    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 (item.$observableDestroyed) {
                return;
            }
            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.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) {
            
            if (typeof fn === 'string' && !scope) {
                fn = this[fn];
            }
            
            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);
        },
        $eventedController: {
            _paused: 1,
            pause: function() {
                ++this._paused;
            },
            resume: function() {
                var me = this,
                    fn = me.fn,
                    scope = me.scope,
                    fnArgs = me.fnArgs,
                    owner = me.owner,
                    args, ret;
                if (!--me._paused) {
                    if (fn) {
                        args = Ext.Array.slice(fnArgs || me.args);
                        if (fnArgs === false) {
                            
                            args.shift();
                        }
                        me.fn = null;
                        
                        args.push(me);
                        if (Ext.isFunction(fn)) {
                            ret = fn.apply(scope, args);
                        } else if (scope && Ext.isString(fn) && Ext.isFunction(scope[fn])) {
                            ret = scope[fn].apply(scope, args);
                        }
                        if (ret === false) {
                            return false;
                        }
                    }
                    if (!me._paused) {
                        
                        return me.owner.fireEventArgs(me.eventName, me.args);
                    }
                }
            }
        },
        
        fireEventedAction: function(eventName, args, fn, scope, fnArgs) {
            var me = this,
                eventedBeforeEventNames = me.eventedBeforeEventNames,
                beforeEventName = eventedBeforeEventNames[eventName] || (eventedBeforeEventNames[eventName] = 'before' + eventName),
                controller = Ext.apply({
                    owner: me,
                    eventName: eventName,
                    fn: fn,
                    scope: scope,
                    fnArgs: fnArgs,
                    args: args
                }, me.$eventedController),
                value;
            args.push(controller);
            value = me.fireEventArgs(beforeEventName, args);
            args.pop();
            if (value === false) {
                return false;
            }
            return controller.resume();
        },
        
        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(eventName, fn, scope, options, order, caller) {
            var me = this,
                namedScopes = Ext._namedScopes,
                config, namedScope, isClassListener, innerScope, eventOptions;
            
            if (typeof eventName !== 'string') {
                options = eventName;
                scope = options.scope;
                namedScope = scope && namedScopes[scope];
                isClassListener = namedScope && namedScope.isSelf;
                
                
                eventOptions = ((me.isComponent || me.isWidget) && options.element) ? me.$elementEventOptions : me.$eventOptions;
                for (eventName in options) {
                    config = options[eventName];
                    if (!eventOptions[eventName]) {
                        
                        innerScope = config.scope;
                        
                        
                        
                        
                        
                        if (innerScope && isClassListener) {
                            namedScope = namedScopes[innerScope];
                            if (namedScope && namedScope.isController) {
                                innerScope = 'self.controller';
                            }
                        }
                        me.doAddListener(eventName, config.fn || config, innerScope || scope, config.fn ? config : options, order, caller);
                    }
                }
                if (options && options.destroyable) {
                    return new ListenerRemover(me, options);
                }
            } else {
                me.doAddListener(eventName, fn, scope, options, order, caller);
                if (options && options.destroyable) {
                    return new ListenerRemover(me, eventName, fn, scope, options);
                }
            }
            return me;
        },
        
        removeListener: function(eventName, fn, scope, eventOptions) {
            var me = this,
                config, options;
            if (typeof eventName !== 'string') {
                options = eventName;
                
                
                eventOptions = eventOptions || me.$eventOptions;
                for (eventName in options) {
                    if (options.hasOwnProperty(eventName)) {
                        config = options[eventName];
                        if (!me.$eventOptions[eventName]) {
                            me.doRemoveListener(eventName, config.fn || config, config.scope || options.scope);
                        }
                    }
                }
            } else {
                me.doRemoveListener(eventName, fn, scope);
            }
            return me;
        },
        
        onBefore: function(eventName, fn, scope, options) {
            return this.addListener(eventName, fn, scope, options, 'before');
        },
        
        onAfter: function(eventName, fn, scope, options) {
            return this.addListener(eventName, fn, scope, options, 'after');
        },
        
        unBefore: function(eventName, fn, scope, options) {
            return this.removeListener(eventName, fn, scope, options, 'before');
        },
        
        unAfter: function(eventName, fn, scope, options) {
            return this.removeListener(eventName, fn, scope, options, 'after');
        },
        
        addBeforeListener: function() {
            return this.onBefore.apply(this, arguments);
        },
        
        addAfterListener: function() {
            return this.onAfter.apply(this, arguments);
        },
        
        removeBeforeListener: function() {
            return this.unBefore.apply(this, arguments);
        },
        
        removeAfterListener: function() {
            return this.unAfter.apply(this, arguments);
        },
        
        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.events = null;
            }
            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,
                i, len;
            if (managedListeners) {
                
                
                me.managedListeners = null;
                for (i = 0 , len = managedListeners.length; i < len; i++) {
                    me.removeManagedListenerItem(true, managedListeners[i]);
                }
                managedListeners.length = 0;
            }
            me.managedListeners = 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))) {
                
                
                if (!managedListener.item.destroyed) {
                    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(eventName) {
            eventName = Ext.canonicalEventName(eventName);
            return !!this.hasListeners[eventName];
        },
        
        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, ename;
            for (i = 0; i < len; i++) {
                ename = Ext.canonicalEventName(arguments[i]);
                event = events[ename];
                
                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, newName,
                relayers = {};
            if (Ext.isObject(events)) {
                for (i in events) {
                    newName = events[i];
                    relayers[i] = me.createRelayer(newName);
                }
            } else {
                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();
            this.destroyObservable(true);
        },
        destroyObservable: function(skipClearListeners) {
            var me = this,
                clearPropertiesOnDestroy = me.clearPropertiesOnDestroy;
            if (me.$observableDestroyed) {
                return;
            }
            if (!skipClearListeners) {
                me.clearListeners();
            }
            
            
            
            
            
            if (me.destroyed) {
                if (clearPropertiesOnDestroy) {
                    if (clearPropertiesOnDestroy === true && !me.$nulled) {
                        me.$reap();
                    }
                    
                    
                    
                    
                    
                    if (!me.clearPrototypeOnDestroy) {
                        me.fireEvent = me.fireEventArgs = me.fireAction = me.fireEventedAction = Ext.emptyFn;
                    }
                    
                    
                    me.events = me.managedListeners = me.eventedBeforeEventNames = null;
                    me.$observableDestroyed = true;
                }
                
                
                
                
                
                if (me.clearPrototypeOnDestroy && Object.setPrototypeOf && !me.$alreadyNulled) {
                    Object.setPrototypeOf(me, null);
                    me.$alreadyNulled = true;
                }
            }
        },
        
        privates: {
            doAddListener: function(ename, fn, scope, options, order, caller, manager) {
                var me = this,
                    ret = false,
                    event, 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.raise("Cannot add '" + ename + "' listener to " + me.$className + " instance.  No function specified.");
                }
                
                event = (me.events || (me.events = {}))[ename];
                if (!event || !event.isEvent) {
                    event = me._initEvent(ename);
                }
                if (fn !== emptyFn) {
                    
                    
                    
                    if (!manager && (scope && scope.isObservable && (scope !== me))) {
                        manager = scope;
                    }
                    if (event.addListener(fn, scope, options, caller, manager)) {
                        
                        
                        me.hasListeners._incr_(ename);
                        ret = true;
                    }
                }
                return ret;
            },
            doRemoveListener: function(ename, fn, scope) {
                var me = this,
                    ret = false,
                    events = me.events,
                    event;
                ename = Ext.canonicalEventName(ename);
                event = events && events[ename];
                
                if (!fn) {
                    Ext.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);
                        ret = true;
                    }
                }
                return ret;
            },
            _initEvent: function(eventName) {
                return (this.events[eventName] = new Ext.util.Event(this, eventName));
            }
        },
        deprecated: {
            '5.0': {
                methods: {
                    addEvents: null
                }
            }
        }
    };
}, 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, count) {
            
            
            
            if (count == null) {
                count = 1;
            }
            if (!(this[ev] -= count)) {
                
                
                
                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 event = (this.methodEvents = this.methodEvents || {})[method],
            returnValue, v, cancel,
            me = this,
            makeCall;
        if (!event) {
            me.methodEvents[method] = event = {};
            event.originalFn = me[method];
            event.methodName = method;
            event.before = [];
            event.after = [];
            makeCall = function(fn, scope, args) {
                scope = scope || me;
                if (typeof fn === 'string') {
                    fn = scope[fn];
                }
                if ((v = fn.apply(scope, 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;
                    }
                }
            };
            me[method] = function() {
                var args = Array.prototype.slice.call(arguments, 0),
                    argsLen = args.length,
                    b, i, len;
                returnValue = v = undefined;
                cancel = false;
                for (i = 0 , len = event.before.length; i < len; i++) {
                    b = event.before[i];
                    if (b.extraArgs) {
                        args.push.apply(args, b.extraArgs);
                    }
                    makeCall(b.fn, b.scope, args);
                    args.length = argsLen;
                    if (cancel || b.preventDefault) {
                        return returnValue;
                    }
                }
                if ((v = event.originalFn.apply(me, args)) !== undefined) {
                    returnValue = v;
                }
                for (i = 0 , len = event.after.length; i < len; i++) {
                    b = event.after[i];
                    if (b.extraArgs) {
                        args.push.apply(args, b.extraArgs);
                    }
                    makeCall(b.fn, b.scope, args);
                    args.length = argsLen;
                    if (cancel || b.preventDefault) {
                        return returnValue;
                    }
                }
                return returnValue;
            };
        }
        return event;
    }
    Ext.apply(proto, {
        onClassMixedIn: prepareMixin,
        
        beforeMethod: function(method, fn, scope, preventDefault, extraArgs) {
            getMethodEvent.call(this, method).before.push({
                fn: fn,
                scope: scope,
                extraArgs: extraArgs,
                preventDefault: preventDefault
            });
        },
        
        afterMethod: function(method, fn, scope, preventDefault, extraArgs) {
            getMethodEvent.call(this, method).after.push({
                fn: fn,
                scope: scope,
                extraArgs: extraArgs,
                preventDefault: preventDefault
            });
        },
        removeMethodListener: function(method, fn, scope) {
            var e = getMethodEvent.call(this, 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;
    }
}, function(HashMap) {
    var prototype = HashMap.prototype;
    
    prototype.removeByKey = prototype.removeAtKey;
});


Ext.define('Ext.AbstractManager', {
    
    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.raise('Key is undefined. Please ensure the item has a key before registering the item.');
        }
        if (this.all.containsKey(key)) {
            Ext.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.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.promise.Consequence', function(Consequence) {
    return {
        
        promise: null,
        
        deferred: null,
        
        onFulfilled: null,
        
        onRejected: null,
        
        onProgress: null,
        
        constructor: function(onFulfilled, onRejected, onProgress) {
            var me = this;
            me.onFulfilled = onFulfilled;
            me.onRejected = onRejected;
            me.onProgress = onProgress;
            me.deferred = new Ext.promise.Deferred();
            me.promise = me.deferred.promise;
        },
        
        trigger: function(action, value) {
            var me = this,
                deferred = me.deferred;
            switch (action) {
                case 'fulfill':
                    me.propagate(value, me.onFulfilled, deferred, deferred.resolve);
                    break;
                case 'reject':
                    me.propagate(value, me.onRejected, deferred, deferred.reject);
                    break;
            }
        },
        
        update: function(progress) {
            if (Ext.isFunction(this.onProgress)) {
                progress = this.onProgress(progress);
            }
            this.deferred.update(progress);
        },
        
        propagate: function(value, callback, deferred, deferredMethod) {
            if (Ext.isFunction(callback)) {
                this.schedule(function() {
                    try {
                        deferred.resolve(callback(value));
                    } catch (e) {
                        deferred.reject(e);
                    }
                });
            } else {
                deferredMethod.call(this.deferred, value);
            }
        },
        
        schedule: function(callback) {
            var n = Consequence.queueSize++;
            Consequence.queue[n] = callback;
            if (!n) {
                
                Ext.asap(Consequence.dispatch);
            }
        },
        statics: {
            
            queue: new Array(10000),
            
            queueSize: 0,
            
            dispatch: function() {
                var queue = Consequence.queue,
                    fn, i;
                
                for (i = 0; i < Consequence.queueSize; ++i) {
                    fn = queue[i];
                    queue[i] = null;
                    
                    fn();
                }
                Consequence.queueSize = 0;
            }
        }
    };
}, 
function(Consequence) {
    Consequence.dispatch.$skipTimerCheck = true;
});




Ext.define('Ext.promise.Deferred', {
    
    promise: null,
    
    consequences: [],
    
    completed: false,
    
    completionAction: null,
    
    completionValue: null,
    constructor: function() {
        var me = this;
        me.promise = new Ext.promise.Promise(me);
        me.consequences = [];
        me.completed = false;
        me.completionAction = null;
        me.completionValue = null;
    },
    
    then: function(onFulfilled, onRejected, onProgress) {
        var me = this,
            consequence = new Ext.promise.Consequence(onFulfilled, onRejected, onProgress);
        if (me.completed) {
            consequence.trigger(me.completionAction, me.completionValue);
        } else {
            me.consequences.push(consequence);
        }
        return consequence.promise;
    },
    
    resolve: function(value) {
        var me = this,
            isHandled, thenFn;
        if (me.completed) {
            return;
        }
        try {
            if (value === me.promise) {
                throw new TypeError('A Promise cannot be resolved with itself.');
            }
            if (value != null && (typeof value === 'object' || Ext.isFunction(value)) && Ext.isFunction(thenFn = value.then)) {
                isHandled = false;
                try {
                    thenFn.call(value, function(value) {
                        if (!isHandled) {
                            isHandled = true;
                            me.resolve(value);
                        }
                    }, function(error) {
                        if (!isHandled) {
                            isHandled = true;
                            me.reject(error);
                        }
                    });
                } catch (e1) {
                    if (!isHandled) {
                        me.reject(e1);
                    }
                }
            } else {
                me.complete('fulfill', value);
            }
        } catch (e2) {
            me.reject(e2);
        }
    },
    
    reject: function(reason) {
        if (this.completed) {
            return;
        }
        this.complete('reject', reason);
    },
    
    update: function(progress) {
        var consequences = this.consequences,
            consequence, i, len;
        if (this.completed) {
            return;
        }
        for (i = 0 , len = consequences.length; i < len; i++) {
            consequence = consequences[i];
            consequence.update(progress);
        }
    },
    
    complete: function(action, value) {
        var me = this,
            consequences = me.consequences,
            consequence, i, len;
        me.completionAction = action;
        me.completionValue = value;
        me.completed = true;
        for (i = 0 , len = consequences.length; i < len; i++) {
            consequence = consequences[i];
            consequence.trigger(me.completionAction, me.completionValue);
        }
        me.consequences = null;
    }
});



Ext.define('Ext.promise.Promise', function(ExtPromise) {
    var Deferred;
    return {
        statics: {
            
            CancellationError: Ext.global.CancellationError || Error,
            _ready: function() {
                
                Deferred = Ext.promise.Deferred;
            },
            
            all: function(promisesOrValues) {
                
                if (!(Ext.isArray(promisesOrValues) || ExtPromise.is(promisesOrValues))) {
                    Ext.raise('Invalid parameter: expected an Array or Promise of an Array.');
                }
                
                return ExtPromise.when(promisesOrValues).then(function(promisesOrValues) {
                    var deferred = new Deferred(),
                        remainingToResolve = promisesOrValues.length,
                        results = new Array(remainingToResolve),
                        index, promiseOrValue, resolve, i, len;
                    if (!remainingToResolve) {
                        deferred.resolve(results);
                    } else {
                        resolve = function(item, index) {
                            return ExtPromise.when(item).then(function(value) {
                                results[index] = value;
                                if (!--remainingToResolve) {
                                    deferred.resolve(results);
                                }
                                return value;
                            }, function(reason) {
                                return deferred.reject(reason);
                            });
                        };
                        for (index = i = 0 , len = promisesOrValues.length; i < len; index = ++i) {
                            promiseOrValue = promisesOrValues[index];
                            if (index in promisesOrValues) {
                                resolve(promiseOrValue, index);
                            } else {
                                remainingToResolve--;
                            }
                        }
                    }
                    return deferred.promise;
                });
            },
            
            is: function(value) {
                return value != null && (typeof value === 'object' || Ext.isFunction(value)) && Ext.isFunction(value.then);
            },
            
            race: function(promises) {
                
                if (!Ext.isArray(promises)) {
                    Ext.raise('Invalid parameter: expected an Array.');
                }
                
                var deferred = new Deferred(),
                    len = promises.length,
                    i;
                for (i = 0; i < len; ++i) {
                    deferred.resolve(promises[i]);
                }
                return deferred.promise;
            },
            
            rethrowError: function(error) {
                Ext.asap(function() {
                    throw error;
                });
            },
            
            when: function(value) {
                var deferred = new Deferred();
                deferred.resolve(value);
                return deferred.promise;
            }
        },
        
        owner: null,
        
        constructor: function(owner) {
            this.owner = owner;
        },
        
        then: function(onFulfilled, onRejected, onProgress, scope) {
            var ref;
            if (arguments.length === 1 && Ext.isObject(arguments[0])) {
                ref = arguments[0];
                onFulfilled = ref.success;
                onRejected = ref.failure;
                onProgress = ref.progress;
                scope = ref.scope;
            }
            if (scope) {
                if (onFulfilled) {
                    onFulfilled = onFulfilled.bind(scope);
                }
                if (onRejected) {
                    onRejected = onRejected.bind(scope);
                }
                if (onProgress) {
                    onProgress = onProgress.bind(scope);
                }
            }
            return this.owner.then(onFulfilled, onRejected, onProgress);
        },
        
        'catch': function(onRejected, scope) {
            var ref;
            if (arguments.length === 1 && Ext.isObject(arguments[0])) {
                ref = arguments[0];
                onRejected = ref.fn;
                scope = ref.scope;
            }
            if (scope != null) {
                onRejected = onRejected.bind(scope);
            }
            return this.owner.then(null, onRejected);
        },
        
        otherwise: function(onRejected, scope) {
            return this['catch'].apply(this, arguments);
        },
        
        always: function(onCompleted, scope) {
            var ref;
            if (arguments.length === 1 && Ext.isObject(arguments[0])) {
                ref = arguments[0];
                onCompleted = ref.fn;
                scope = ref.scope;
            }
            if (scope != null) {
                onCompleted = onCompleted.bind(scope);
            }
            return this.owner.then(function(value) {
                try {
                    onCompleted();
                } catch (e) {
                    ExtPromise.rethrowError(e);
                }
                return value;
            }, function(reason) {
                try {
                    onCompleted();
                } catch (e) {
                    ExtPromise.rethrowError(e);
                }
                throw reason;
            });
        },
        
        done: function() {
            this.owner.then(null, ExtPromise.rethrowError);
        },
        
        cancel: function(reason) {
            if (reason == null) {
                reason = null;
            }
            this.owner.reject(new this.self.CancellationError(reason));
        },
        
        log: function(identifier) {
            if (identifier == null) {
                identifier = '';
            }
            return this.owner.then(function(value) {
                Ext.log("" + (identifier || 'Promise') + " resolved with value: " + value);
                return value;
            }, function(reason) {
                Ext.log("" + (identifier || 'Promise') + " rejected with reason: " + reason);
                throw reason;
            });
        }
    };
}, function(ExtPromise) {
    ExtPromise._ready();
});


Ext.define('Ext.Promise', function() {
    var Polyfiller;
    return {
        statics: {
            _ready: function() {
                
                Polyfiller = Ext.promise.Promise;
            },
            
            all: function() {
                return Polyfiller.all.apply(Polyfiller, arguments);
            },
            
            race: function() {
                return Polyfiller.race.apply(Polyfiller, arguments);
            },
            
            reject: function(reason) {
                var deferred = new Ext.promise.Deferred();
                deferred.reject(reason);
                return deferred.promise;
            },
            
            resolve: function(value) {
                var deferred = new Ext.promise.Deferred();
                deferred.resolve(value);
                return deferred.promise;
            }
        },
        constructor: function(action) {
            var deferred = new Ext.promise.Deferred();
            action(deferred.resolve.bind(deferred), deferred.reject.bind(deferred));
            return deferred.promise;
        }
    };
}, function(ExtPromise) {
    var P = Ext.global.Promise;
    if (P && P.resolve && !Ext.useExtPromises) {
        Ext.Promise = P;
    } else {
        ExtPromise._ready();
    }
});



Ext.define('Ext.Deferred', function(Deferred) {
    var ExtPromise, rejected, resolved, when;
    return {
        extend: Ext.promise.Deferred,
        statics: {
            _ready: function() {
                
                ExtPromise = Ext.promise.Promise;
                when = Ext.Promise.resolve;
            },
            
            all: function() {
                return ExtPromise.all.apply(ExtPromise, arguments);
            },
            
            any: function(promisesOrValues) {
                
                if (!(Ext.isArray(promisesOrValues) || ExtPromise.is(promisesOrValues))) {
                    Ext.raise('Invalid parameter: expected an Array or Promise of an Array.');
                }
                
                return Deferred.some(promisesOrValues, 1).then(function(array) {
                    return array[0];
                }, function(error) {
                    if (error instanceof Error && error.message === 'Too few Promises were resolved.') {
                        Ext.raise('No Promises were resolved.');
                    } else {
                        throw error;
                    }
                });
            },
            
            delay: function(promiseOrValue, milliseconds) {
                var deferred;
                if (arguments.length === 1) {
                    milliseconds = promiseOrValue;
                    promiseOrValue = undefined;
                }
                milliseconds = Math.max(milliseconds, 1);
                deferred = new Deferred();
                deferred.timeoutId = Ext.defer(function() {
                    delete deferred.timeoutId;
                    deferred.resolve(promiseOrValue);
                }, milliseconds);
                return deferred.promise;
            },
            
            getCachedRejected: function() {
                if (!rejected) {
                    
                    rejected = Ext.Promise.reject();
                }
                return rejected;
            },
            
            getCachedResolved: function() {
                if (!resolved) {
                    
                    resolved = Ext.Promise.resolve();
                }
                return resolved;
            },
            
            map: function(promisesOrValues, mapFn) {
                
                if (!(Ext.isArray(promisesOrValues) || ExtPromise.is(promisesOrValues))) {
                    Ext.raise('Invalid parameter: expected an Array or Promise of an Array.');
                }
                if (!Ext.isFunction(mapFn)) {
                    Ext.raise('Invalid parameter: expected a function.');
                }
                
                return Deferred.resolved(promisesOrValues).then(function(promisesOrValues) {
                    var deferred, index, promiseOrValue, remainingToResolve, resolve, results, i, len;
                    remainingToResolve = promisesOrValues.length;
                    results = new Array(promisesOrValues.length);
                    deferred = new Deferred();
                    if (!remainingToResolve) {
                        deferred.resolve(results);
                    } else {
                        resolve = function(item, index) {
                            return Deferred.resolved(item).then(function(value) {
                                return mapFn(value, index, results);
                            }).then(function(value) {
                                results[index] = value;
                                if (!--remainingToResolve) {
                                    deferred.resolve(results);
                                }
                                return value;
                            }, function(reason) {
                                return deferred.reject(reason);
                            });
                        };
                        for (index = i = 0 , len = promisesOrValues.length; i < len; index = ++i) {
                            promiseOrValue = promisesOrValues[index];
                            if (index in promisesOrValues) {
                                resolve(promiseOrValue, index);
                            } else {
                                remainingToResolve--;
                            }
                        }
                    }
                    return deferred.promise;
                });
            },
            
            memoize: function(fn, scope, hashFn) {
                var memoizedFn = Ext.Function.memoize(fn, scope, hashFn);
                return function() {
                    return Deferred.all(Ext.Array.slice(arguments)).then(function(values) {
                        return memoizedFn.apply(scope, values);
                    });
                };
            },
            
            parallel: function(fns, scope) {
                if (scope == null) {
                    scope = null;
                }
                var args = Ext.Array.slice(arguments, 2);
                return Deferred.map(fns, function(fn) {
                    if (!Ext.isFunction(fn)) {
                        throw new Error('Invalid parameter: expected a function.');
                    }
                    return fn.apply(scope, args);
                });
            },
            
            pipeline: function(fns, initialValue, scope) {
                if (scope == null) {
                    scope = null;
                }
                return Deferred.reduce(fns, function(value, fn) {
                    if (!Ext.isFunction(fn)) {
                        throw new Error('Invalid parameter: expected a function.');
                    }
                    return fn.call(scope, value);
                }, initialValue);
            },
            
            race: function() {
                return ExtPromise.race.apply(ExtPromise, arguments);
            },
            
            reduce: function(values, reduceFn, initialValue) {
                
                if (!(Ext.isArray(values) || ExtPromise.is(values))) {
                    Ext.raise('Invalid parameter: expected an Array or Promise of an Array.');
                }
                if (!Ext.isFunction(reduceFn)) {
                    Ext.raise('Invalid parameter: expected a function.');
                }
                
                var initialValueSpecified = arguments.length === 3;
                return Deferred.resolved(values).then(function(promisesOrValues) {
                    var reduceArguments = [
                            promisesOrValues,
                            function(previousValueOrPromise, currentValueOrPromise, currentIndex) {
                                return Deferred.resolved(previousValueOrPromise).then(function(previousValue) {
                                    return Deferred.resolved(currentValueOrPromise).then(function(currentValue) {
                                        return reduceFn(previousValue, currentValue, currentIndex, promisesOrValues);
                                    });
                                });
                            }
                        ];
                    if (initialValueSpecified) {
                        reduceArguments.push(initialValue);
                    }
                    return Ext.Array.reduce.apply(Ext.Array, reduceArguments);
                });
            },
            
            rejected: function(reason) {
                var deferred = new Ext.Deferred();
                deferred.reject(reason);
                return deferred.promise;
            },
            
            resolved: function(promiseOrValue) {
                var deferred = new Ext.Deferred();
                deferred.resolve(promiseOrValue);
                return deferred.promise;
            },
            
            sequence: function(fns, scope) {
                if (scope == null) {
                    scope = null;
                }
                var args = Ext.Array.slice(arguments, 2);
                return Deferred.reduce(fns, function(results, fn) {
                    if (!Ext.isFunction(fn)) {
                        throw new Error('Invalid parameter: expected a function.');
                    }
                    return Deferred.resolved(fn.apply(scope, args)).then(function(result) {
                        results.push(result);
                        return results;
                    });
                }, []);
            },
            
            some: function(promisesOrValues, howMany) {
                
                if (!(Ext.isArray(promisesOrValues) || ExtPromise.is(promisesOrValues))) {
                    Ext.raise('Invalid parameter: expected an Array or Promise of an Array.');
                }
                if (!Ext.isNumeric(howMany) || howMany <= 0) {
                    Ext.raise('Invalid parameter: expected a positive integer.');
                }
                
                return Deferred.resolved(promisesOrValues).then(function(promisesOrValues) {
                    var deferred, index, onReject, onResolve, promiseOrValue, remainingToReject, remainingToResolve, values, i, len;
                    values = [];
                    remainingToResolve = howMany;
                    remainingToReject = (promisesOrValues.length - remainingToResolve) + 1;
                    deferred = new Deferred();
                    if (promisesOrValues.length < howMany) {
                        deferred.reject(new Error('Too few Promises were resolved.'));
                    } else {
                        onResolve = function(value) {
                            if (remainingToResolve > 0) {
                                values.push(value);
                            }
                            remainingToResolve--;
                            if (remainingToResolve === 0) {
                                deferred.resolve(values);
                            }
                            return value;
                        };
                        onReject = function(reason) {
                            remainingToReject--;
                            if (remainingToReject === 0) {
                                deferred.reject(new Error('Too few Promises were resolved.'));
                            }
                            return reason;
                        };
                        for (index = i = 0 , len = promisesOrValues.length; i < len; index = ++i) {
                            promiseOrValue = promisesOrValues[index];
                            if (index in promisesOrValues) {
                                Deferred.resolved(promiseOrValue).then(onResolve, onReject);
                            }
                        }
                    }
                    return deferred.promise;
                });
            },
            
            timeout: function(promiseOrValue, milliseconds) {
                var deferred = new Deferred(),
                    timeoutId;
                timeoutId = Ext.defer(function() {
                    if (timeoutId) {
                        deferred.reject(new Error('Promise timed out.'));
                    }
                }, milliseconds);
                Deferred.resolved(promiseOrValue).then(function(value) {
                    Ext.undefer(timeoutId);
                    timeoutId = null;
                    deferred.resolve(value);
                }, function(reason) {
                    Ext.undefer(timeoutId);
                    timeoutId = null;
                    deferred.reject(reason);
                });
                return deferred.promise;
            }
        }
    };
}, function(Deferred) {
    Deferred._ready();
});



Ext.Factory = function(type) {
    var me = this;
    me.aliasPrefix = type + '.';
    me.cache = {};
    me.name = type.replace(me.fixNameRe, me.fixNameFn);
    me.type = type;
    
    me.creator = 'create' + Ext.String.capitalize(me.name);
};
Ext.Factory.prototype = {
    
    
    defaultProperty: 'type',
    
    
    instanceProp: 'isInstance',
    
    
    
    typeProperty: 'type',
    
    create: function(config, defaultType) {
        var me = this,
            Manager = Ext.ClassManager,
            cache = me.cache,
            typeProperty = me.typeProperty,
            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[typeProperty];
        }
        if (defaultType && defaultType.constructor === Object) {
            config = Ext.apply({}, config, defaultType);
            defaultType = defaultType[typeProperty];
        }
        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.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 = {};
        this.instanceCache = {};
    },
    
    hook: function(fn) {
        var me = this,
            original = me.create;
        me.create = function(config, defaultType) {
            var ret = fn.call(me, original, config, defaultType);
            if (ret === undefined) {
                ret = original.call(me, config, defaultType);
            }
            return ret;
        };
    },
    
    update: function(instance, config, creator, creatorMethod, defaultsConfig) {
        var me = this,
            aliases, defaults, reuse, type;
        
        
        if (!config || config.isInstance) {
            
            if (config && !config[me.instanceProp]) {
                Ext.raise('Config instance failed ' + me.instanceProp + ' requirement');
            }
            
            if (instance && instance !== config) {
                instance.destroy();
            }
            return config;
        }
        if (typeof config === 'string') {
            type = config;
            config = {};
            config[me.defaultProperty] = type;
        }
        
        if (instance) {
            if (config === true) {
                return instance;
            }
            if (!(type = config.xclass)) {
                if (!(type = config.xtype)) {
                    type = config[me.typeProperty];
                    if (type) {
                        
                        type = me.aliasPrefix + type;
                        aliases = instance.self.prototype;
                        
                        
                        
                        if (aliases.hasOwnProperty('alias')) {
                            aliases = aliases.alias;
                            if (aliases) {
                                reuse = aliases === type || aliases.indexOf(type) > -1;
                            }
                        }
                    }
                } else {
                    
                    reuse = instance.isXType(type, 
                    true);
                }
            } else {
                
                reuse = instance.$className === type;
            }
            if (reuse) {
                instance.setConfig(config);
                return instance;
            }
            instance.destroy();
        }
        if (config === true) {
            config = {};
        }
        if (creator) {
            if (defaultsConfig) {
                defaults = Ext.Config.map[defaultsConfig];
                defaults = creator[defaults.names.get]();
                if (defaults) {
                    config = Ext.merge(Ext.clone(defaults), config);
                }
            }
            creatorMethod = creatorMethod || me.creator;
            if (creator[creatorMethod]) {
                config = creator[creatorMethod](config);
                
                if (!config) {
                    Ext.raise('Missing return value from ' + creatorMethod + ' on class ' + creator.$className);
                }
            }
        }
        
        return me.create(config);
    }
};

Ext.Factory.define = function(type, config) {
    var Factory = Ext.Factory,
        cacheable = config && config.cacheable,
        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 = function(config, defaultType) {
            
            
            return factory.create(config, defaultType);
        };
        if (cacheable) {
            factory.instanceCache = {};
            factory.hook(function(original, config, defaultType) {
                var cache = this.instanceCache,
                    v;
                if (typeof config === 'string' && !(v = cache[config])) {
                    v = original.call(this, config, defaultType);
                    
                    
                    if (v.cacheable !== false) {
                        cache[config] = v;
                        
                        
                        
                        Ext.Object.freeze(v);
                    }
                }
                
                return v;
            });
        }
        fn.instance = factory;
        
        fn.update = function(instance, config, creator, creatorMethod, defaultsConfig) {
            return factory.update(instance, config, creator, creatorMethod, defaultsConfig);
        };
    }
    return fn;
};
Ext.Factory.clearCaches = function() {
    var Factory = Ext.Factory,
        key, item;
    for (key in Factory) {
        item = Factory[key];
        item = item.instance;
        if (item) {
            item.clearCache();
        }
    }
};
Ext.Factory.on = function(name, fn) {
    Ext.Factory[name].instance.hook(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.data.request.Base', {
    mixins: [
        Ext.mixin.Factoryable
    ],
    
    
    factoryConfig: {
        type: 'request',
        defaultType: 'ajax'
    },
    
    result: null,
    success: null,
    timer: null,
    constructor: function(config) {
        var me = this;
        
        
        
        
        Ext.apply(me, config.options || {}, config.ownerConfig);
        me.id = ++Ext.data.Connection.requestId;
        me.owner = config.owner;
        me.options = config.options;
        me.requestOptions = config.requestOptions;
    },
    
    start: function() {
        var me = this,
            timeout = me.getTimeout();
        if (timeout && me.async) {
            me.timer = Ext.defer(me.onTimeout, timeout, me);
        }
    },
    abort: function() {
        var me = this;
        me.clearTimer();
        if (!me.timedout) {
            me.aborted = true;
        }
        me.abort = Ext.emptyFn;
    },
    createDeferred: function() {
        var me = this,
            result = me.result,
            d = new Ext.Deferred();
        if (me.completed) {
            if (me.success) {
                d.resolve(result);
            } else {
                d.reject(result);
            }
        }
        me.deferred = d;
        return d;
    },
    getDeferred: function() {
        return this.deferred || this.createDeferred();
    },
    getPromise: function() {
        return this.getDeferred().promise;
    },
    
    then: function() {
        var promise = this.getPromise();
        return promise.then.apply(promise, arguments);
    },
    
    onComplete: function() {
        var me = this,
            deferred = me.deferred,
            result = me.result;
        me.clearTimer();
        if (deferred) {
            if (me.success) {
                deferred.resolve(result);
            } else {
                deferred.reject(result);
            }
        }
        me.completed = true;
    },
    onTimeout: function() {
        var me = this;
        me.timedout = true;
        me.timer = null;
        me.abort(true);
    },
    getTimeout: function() {
        return this.timeout;
    },
    clearTimer: function() {
        this.timer = Ext.undefer(this.timer);
    },
    destroy: function() {
        var me = this;
        me.abort();
        me.owner = me.options = me.requestOptions = me.result = null;
        me.callParent();
    },
    privates: {
        
        createException: function() {
            var me = this,
                result;
            result = {
                request: me,
                requestId: me.id,
                status: me.aborted ? -1 : 0,
                statusText: me.aborted ? 'transaction aborted' : 'communication failure',
                getResponseHeader: me._getHeader,
                getAllResponseHeaders: me._getHeaders
            };
            if (me.aborted) {
                result.aborted = true;
            }
            if (me.timedout) {
                result.timedout = true;
            }
            return result;
        },
        _getHeader: function(name) {
            var headers = this.headers;
            return headers && headers[name.toLowerCase()];
        },
        _getHeaders: function() {
            return this.headers;
        }
    }
});


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.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.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.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.request.Ajax', {
    extend: Ext.data.request.Base,
    alias: 'request.ajax',
    statics: {
        
        parseStatus: function(status, response) {
            var len;
            if (response) {
                
                if (response.responseType === 'arraybuffer') {
                    len = response.byteLength;
                } else if (response.responseText) {
                    len = response.responseText.length;
                }
            }
            
            status = status == 1223 ? 204 : status;
            var success = (status >= 200 && status < 300) || status == 304 || (status == 0 && Ext.isNumber(len)),
                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
            };
        }
    },
    start: function(data) {
        var me = this,
            options = me.options,
            requestOptions = me.requestOptions,
            isXdr = me.isXdr,
            xhr, headers;
        xhr = me.xhr = me.openRequest(options, requestOptions, me.async, me.username, me.password);
        
        if (!isXdr) {
            headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);
        }
        if (me.async) {
            if (!isXdr) {
                xhr.onreadystatechange = me.bindStateChange();
            }
        }
        if (isXdr) {
            me.processXdrRequest(me, xhr);
        }
        
        me.callParent([
            data
        ]);
        
        xhr.send(data);
        if (!me.async) {
            return me.onComplete();
        }
        return me;
    },
    
    abort: function(force) {
        var me = this,
            xhr = me.xhr;
        if (force || me.isLoading()) {
            
            try {
                xhr.onreadystatechange = null;
            } catch (e) {
                
                
                xhr.onreadystatechange = Ext.emptyFn;
            }
            xhr.abort();
            me.callParent([
                force
            ]);
            me.onComplete();
            me.cleanup();
        }
    },
    
    cleanup: function() {
        this.xhr = null;
        delete this.xhr;
    },
    isLoading: function() {
        var me = this,
            xhr = me.xhr,
            state = xhr && xhr.readyState,
            C = Ext.data.flash && Ext.data.flash.BinaryXhr;
        if (!xhr || me.aborted || me.timedout) {
            return false;
        }
        
        
        if (C && xhr instanceof C) {
            return state !== 4;
        }
        return state !== 0 && state !== 4;
    },
    
    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.isXdr) {
                xhr.open(requestOptions.method, requestOptions.url);
            } else {
                xhr.open(requestOptions.method, requestOptions.url, async);
            }
        }
        if (options.binary || me.binary) {
            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.withCredentials) {
            xhr.withCredentials = true;
        }
        return xhr;
    },
    
    newRequest: function(options) {
        var me = this,
            xhr;
        if (options.binaryData) {
            
            if (window.Uint8Array) {
                
                xhr = me.getXhrInstance();
            } else {
                
                xhr = new Ext.data.flash.BinaryXhr();
            }
        } else if (me.cors && Ext.isIE9m) {
            xhr = me.getXdrInstance();
            me.isXdr = true;
        } else {
            xhr = me.getXhrInstance();
            me.isXdr = false;
        }
        return xhr;
    },
    
    setupHeaders: function(xhr, options, data, params) {
        var me = this,
            headers = Ext.apply({}, options.headers || {}, me.defaultHeaders),
            contentType = me.defaultPostHeader,
            jsonData = options.jsonData,
            xmlData = options.xmlData,
            type = 'Content-Type',
            useHeader = me.useDefaultXhrHeader,
            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.defaultXhrHeader;
        }
        
        
        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.owner.fireEvent('exception', key, header);
        }
        return headers;
    },
    
    getXdrInstance: function() {
        var xdr;
        if (Ext.ieVersion >= 8) {
            xdr = new XDomainRequest();
        } else {
            Ext.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;
    }()),
    processXdrRequest: function(request, xhr) {
        var me = this;
        
        delete request.headers;
        request.contentType = request.options.contentType || me.defaultXdrContentType;
        xhr.onload = me.bindStateChange(true);
        xhr.onerror = xhr.ontimeout = me.bindStateChange(false);
    },
    processXdrResponse: function(response, xhr) {
        
        response.getAllResponseHeaders = function() {
            return [];
        };
        response.getResponseHeader = function() {
            return '';
        };
        response.contentType = xhr.contentType || this.defaultXdrContentType;
    },
    bindStateChange: function(xdrResult) {
        var me = this;
        return function() {
            Ext.elevate(function() {
                me.onStateChange(xdrResult);
            });
        };
    },
    onStateChange: function(xdrResult) {
        var me = this,
            xhr = me.xhr;
        
        if ((xhr && xhr.readyState == 4) || me.isXdr) {
            me.clearTimer();
            me.onComplete(xdrResult);
            me.cleanup();
        }
    },
    
    onComplete: function(xdrResult) {
        var me = this,
            owner = me.owner,
            options = me.options,
            xhr = me.xhr,
            failure = {
                success: false,
                isException: false
            },
            result, success, response;
        if (!xhr || me.destroyed) {
            return me.result = failure;
        }
        try {
            result = Ext.data.request.Ajax.parseStatus(xhr.status, xhr);
            if (result.success) {
                
                
                
                result.success = xhr.readyState === 4;
            }
        } catch (e) {
            
            
            result = failure;
        }
        success = me.success = me.isXdr ? xdrResult : result.success;
        if (success) {
            response = me.createResponse(xhr);
            if (owner.hasListeners.requestcomplete) {
                owner.fireEvent('requestcomplete', owner, response, options);
            }
            if (options.success) {
                Ext.callback(options.success, options.scope, [
                    response,
                    options
                ]);
            }
        } else {
            if (result.isException || me.aborted || me.timedout) {
                response = me.createException(xhr);
            } else {
                response = me.createResponse(xhr);
            }
            if (owner.hasListeners.requestexception) {
                owner.fireEvent('requestexception', owner, response, options);
            }
            if (options.failure) {
                Ext.callback(options.failure, options.scope, [
                    response,
                    options
                ]);
            }
        }
        me.result = response;
        if (options.callback) {
            Ext.callback(options.callback, options.scope, [
                options,
                success,
                response
            ]);
        }
        owner.onRequestComplete(me);
        me.callParent([
            xdrResult
        ]);
        return response;
    },
    
    createResponse: function(xhr) {
        var me = this,
            isXdr = me.isXdr,
            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);
            }
        }
        response = {
            request: me,
            requestId: me.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 (me.binary) {
            response.responseBytes = me.getByteArray(xhr);
        } else {
            
            
            
            
            response.responseText = xhr.responseText;
            response.responseXML = xhr.responseXML;
        }
        return response;
    },
    destroy: function() {
        this.xhr = null;
        this.callParent();
    },
    privates: {
        
        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;
        }
    }
});


Ext.define('Ext.data.request.Form', {
    extend: Ext.data.request.Base,
    alias: 'request.form',
    start: function(data) {
        var me = this,
            options = me.options,
            requestOptions = me.requestOptions;
        
        me.callParent([
            data
        ]);
        me.form = me.upload(options.form, requestOptions.url, requestOptions.data, options);
        return me;
    },
    abort: function(force) {
        var me = this,
            frame;
        if (me.isLoading()) {
            try {
                frame = me.frame.dom;
                if (frame.stop) {
                    frame.stop();
                } else {
                    frame.document.execCommand('Stop');
                }
            } catch (e) {}
        }
        
        me.callParent([
            force
        ]);
        me.onComplete();
        me.cleanup();
    },
    
    cleanup: function() {
        var me = this,
            frame = me.frame;
        if (frame) {
            
            frame.un('load', me.onComplete, me);
            Ext.removeNode(frame);
        }
        me.frame = me.form = null;
    },
    isLoading: function() {
        return !!this.frame;
    },
    
    upload: function(form, url, params, options) {
        form = Ext.getDom(form);
        options = options || {};
        var frameDom = document.createElement('iframe'),
            frame = Ext.get(frameDom),
            id = frame.id,
            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;
        
        frame.set({
            name: id,
            cls: Ext.baseCSSPrefix + 'hidden-display',
            src: Ext.SSL_SECURE_URL,
            tabIndex: -1
        });
        document.body.appendChild(frameDom);
        document.body.appendChild(form);
        
        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);
                    }
                }
            }
        }
        this.frame = frame;
        frame.on({
            load: this.onComplete,
            scope: this,
            
            single: !Ext.isOpera
        });
        form.submit();
        document.body.removeChild(form);
        
        Ext.fly(form).set(buf);
        for (hLen = hiddens.length , h = 0; h < hLen; h++) {
            Ext.removeNode(hiddens[h]);
        }
        return form;
    },
    getDoc: function() {
        var frame = this.frame.dom;
        return (frame && (frame.contentWindow.document || frame.contentDocument)) || (window.frames[frame.id] || {}).document;
    },
    getTimeout: function() {
        
        
        
        return this.options.timeout;
    },
    
    onComplete: function() {
        var me = this,
            frame = me.frame,
            owner = me.owner,
            options = me.options,
            callback, doc, success, contentNode, response;
        
        if (!frame) {
            return;
        }
        if (me.aborted || me.timedout) {
            me.result = response = me.createException();
            response.responseXML = null;
            response.responseText = Ext.encode({
                success: false,
                message: Ext.String.trim(response.statusText)
            });
            response.request = me;
            callback = options.failure;
            success = false;
        } else {
            try {
                doc = me.getDoc();
                
                me.result = response = {
                    responseText: '',
                    responseXML: null,
                    request: me
                };
                
                
                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;
                    response.status = 200;
                } else {
                    Ext.raise("Could not acquire a suitable connection for the file upload service.");
                }
            } catch (e) {
                me.result = response = me.createException();
                
                response.status = 400;
                response.statusText = (e.message || e.description) + '';
                response.responseText = Ext.encode({
                    success: false,
                    message: Ext.String.trim(response.statusText)
                });
                response.responseXML = null;
                callback = options.failure;
                success = false;
            }
        }
        me.frame = null;
        me.success = success;
        owner.fireEvent(success ? 'requestcomplete' : 'requestexception', owner, response, options);
        Ext.callback(callback, options.scope, [
            response,
            options
        ]);
        Ext.callback(options.callback, options.scope, [
            options,
            success,
            response
        ]);
        owner.onRequestComplete(me);
        
        Ext.asap(frame.destroy, frame);
        me.callParent();
    },
    destroy: function() {
        this.cleanup();
        this.callParent();
    }
});


Ext.define('Ext.data.Connection', {
    mixins: {
        observable: Ext.mixin.Observable
    },
    statics: {
        requestId: 0
    },
    enctypeRe: /multipart\/form-data/i,
    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,
            requestOptions, request;
        if (me.fireEvent('beforerequest', me, options) !== false) {
            requestOptions = me.setOptions(options, options.scope || Ext.global);
            request = me.createRequest(options, requestOptions);
            return request.start(requestOptions.data);
        }
        
        request = {
            status: -1,
            statusText: 'Request cancelled in beforerequest event handler'
        };
        Ext.callback(options.callback, options.scope, [
            options,
            false,
            request
        ]);
        return Ext.Deferred.rejected([
            options,
            false,
            request
        ]);
    },
    createRequest: function(options, requestOptions) {
        var me = this,
            type = options.type || requestOptions.type,
            request;
        
        if (!type) {
            type = me.isFormUpload(options) ? 'form' : 'ajax';
        }
        
        if (options.autoAbort || me.getAutoAbort()) {
            me.abort();
        }
        
        
        
        
        
        
        
        request = Ext.Factory.request({
            type: type,
            owner: me,
            options: options,
            requestOptions: requestOptions,
            ownerConfig: me.getConfig()
        });
        me.requests[request.id] = request;
        me.latestId = request.id;
        return request;
    },
    
    isFormUpload: function(options) {
        var form = this.getForm(options);
        if (form) {
            return options.isUpload || this.enctypeRe.test(form.getAttribute('enctype'));
        }
        return false;
    },
    
    getForm: function(options) {
        return Ext.getDom(options.form);
    },
    
    setOptions: function(options, scope) {
        var me = this,
            params = options.params || {},
            extraParams = me.getExtraParams(),
            urlParams = options.urlParams,
            url = options.url || me.getUrl(),
            cors = options.cors,
            jsonData = options.jsonData,
            method, disableCache, data;
        if (cors !== undefined) {
            me.setCors(cors);
        }
        
        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.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;
    },
    
    isLoading: function(request) {
        if (!request) {
            request = this.getLatest();
        }
        return request ? request.isLoading() : false;
    },
    
    abort: function(request) {
        if (!request) {
            request = this.getLatest();
        }
        if (request && request.isLoading()) {
            request.abort();
        }
    },
    
    abortAll: function() {
        var requests = this.requests,
            id;
        for (id in requests) {
            this.abort(requests[id]);
        }
    },
    
    getLatest: function() {
        var id = this.latestId,
            request;
        if (id) {
            request = this.requests[id];
        }
        return request || null;
    },
    
    clearTimeout: function(request) {
        if (!request) {
            request = this.getLatest();
        }
        if (request) {
            request.clearTimer();
        }
    },
    onRequestComplete: function(request) {
        delete this.requests[request.id];
    },
    
    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() {
        var me = this;
        me.queue = [];
        me.taskQueue = [];
        me.runningQueue = [];
        me.idleQueue = [];
        me.isRunning = false;
        me.isIdle = true;
        me.run = me.run.bind(me);
        
        
        
        
        
        
        if (Ext.os.is.iOS) {
            
            me.watch.$skipTimerCheck = true;
            
            me.watchdogTimer = Ext.interval(me.watch, 500, me);
        }
    },
    
    start: function(fn, scope, args) {
        var me = this;
        me.queue.push(arguments);
        if (!me.isRunning) {
            if (me.hasOwnProperty('idleTimer')) {
                Ext.undefer(me.idleTimer);
                delete me.idleTimer;
            }
            if (me.hasOwnProperty('idleQueueTimer')) {
                Ext.undefer(me.idleQueueTimer);
                delete me.idleQueueTimer;
            }
            me.isIdle = false;
            me.isRunning = true;
            
            me.startCountTime = Ext.now();
            me.count = 0;
            
            me.doStart();
        }
    },
    clear: function() {
        var me = this;
        Ext.undefer(me.idleTimer);
        Ext.undefer(me.idleQueueTimer);
        Ext.unraf(me.animationFrameId);
        me.idleTimer = me.idleQueueTimer = me.animationFrameId = null;
        me.queue.length = me.taskQueue.length = me.runningQueue.length = me.idleQueue.length = 0;
        me.isRunning = false;
        me.isIdle = true;
        
        me.startCountTime = Ext.now();
        me.count = 0;
    },
    
    watch: function() {
        if (this.isRunning && Ext.now() - this.lastRunTime >= 500) {
            this.run();
        }
    },
    run: function() {
        var me = this,
            item, element;
        
        me.animationFrameId = null;
        if (!me.isRunning) {
            return;
        }
        var queue = me.runningQueue,
            now = Ext.now(),
            i, ln;
        me.lastRunTime = now;
        me.frameStartTime = now;
        
        
        
        
        
        i = me.queue.length;
        while (i--) {
            item = me.queue[i];
            element = item[1] && item[1].getElement && item[1].getElement();
            if (element && element.destroyed) {
                me.queue.splice(i, 1);
            }
        }
        queue.push.apply(queue, me.queue);
        
        for (i = 0 , ln = queue.length; i < ln; i++) {
            me.invoke(queue[i]);
        }
        queue.length = 0;
        
        var elapse = me.frameStartTime - me.startCountTime,
            count = ++me.count;
        if (elapse >= 200) {
            me.onFpsChanged(count * 1000 / elapse, count, elapse);
            me.startCountTime = me.frameStartTime;
            me.count = 0;
        }
        
        if (!me.queue.length) {
            me.stop();
        }
        
        if (me.isRunning) {
            me.doIterate();
        }
    },
    
    onFpsChanged: Ext.emptyFn,
    onStop: Ext.emptyFn,
    
    doStart: function() {
        if (!this.animationFrameId) {
            this.animationFrameId = Ext.raf(this.run);
        }
        this.lastRunTime = Ext.now();
    },
    doIterate: function() {
        if (!this.animationFrameId) {
            this.animationFrameId = Ext.raf(this.run);
        }
    },
    doStop: function() {
        if (this.animationFrameId) {
            Ext.unraf(this.animationFrameId);
        }
        this.animationFrameId = null;
    },
    
    stop: function(fn, scope, args) {
        var me = this;
        if (!me.isRunning) {
            return;
        }
        var queue = me.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) {
            me.doStop();
            
            me.onStop();
            
            me.isRunning = false;
            if (me.idleQueue.length && !me.idleTimer) {
                me.idleTimer = Ext.defer(me.whenIdle, 100, me);
            }
        }
    },
    onIdle: function(fn, scope, args) {
        var me = this,
            listeners = me.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 (me.isIdle) {
            me.processIdleQueue();
        } else if (!me.idleTimer) {
            me.idleTimer = Ext.defer(me.whenIdle, 100, me);
        }
    },
    unIdle: function(fn, scope, args) {
        var me = this,
            listeners = me.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;
            }
        }
        if (!listeners.length && me.idleTimer) {
            Ext.undefer(me.idleTimer);
            delete me.idleTimer;
        }
        if (!listeners.length && me.idleQueueTimer) {
            Ext.undefer(me.idleQueueTimer);
            delete me.idleQueueTimer;
        }
        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() {
        delete this.idleTimer;
        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() {
        var styleTpl = {
                color: 'white',
                'background-color': 'black',
                'text-align': 'center',
                'font-family': 'sans-serif',
                'font-size': '8px',
                'font-weight': 'normal',
                'font-style': 'normal',
                'line-height': '20px',
                '-webkit-font-smoothing': 'antialiased',
                'zIndex': 100000,
                position: 'absolute'
            };
        Ext.getBody().append([
            
            {
                style: Ext.applyIf({
                    bottom: '50px',
                    left: 0,
                    width: '50px',
                    height: '20px'
                }, styleTpl),
                html: 'Average'
            },
            {
                style: Ext.applyIf({
                    'background-color': 'red',
                    'font-size': '18px',
                    'line-height': '50px',
                    bottom: 0,
                    left: 0,
                    width: '50px',
                    height: '50px'
                }, styleTpl),
                id: '__averageFps',
                html: '0'
            },
            
            {
                style: Ext.applyIf({
                    bottom: '50px',
                    left: '50px',
                    width: '50px',
                    height: '20px'
                }, styleTpl),
                html: 'Min (Last 1k)'
            },
            {
                style: Ext.applyIf({
                    'background-color': 'orange',
                    'font-size': '18px',
                    'line-height': '50px',
                    bottom: 0,
                    left: '50px',
                    width: '50px',
                    height: '50px'
                }, styleTpl),
                id: '__minFps',
                html: '0'
            },
            
            {
                style: Ext.applyIf({
                    bottom: '50px',
                    left: '100px',
                    width: '50px',
                    height: '20px'
                }, styleTpl),
                html: 'Max (Last 1k)'
            },
            {
                style: Ext.applyIf({
                    'background-color': 'maroon',
                    'font-size': '18px',
                    'line-height': '50px',
                    bottom: 0,
                    left: '100px',
                    width: '50px',
                    height: '50px'
                }, styleTpl),
                id: '__maxFps',
                html: '0'
            },
            
            {
                style: Ext.applyIf({
                    bottom: '50px',
                    left: '150px',
                    width: '50px',
                    height: '20px'
                }, styleTpl),
                html: 'Current'
            },
            {
                style: Ext.applyIf({
                    'background-color': 'green',
                    'font-size': '18px',
                    'line-height': '50px',
                    bottom: 0,
                    left: '150px',
                    width: '50px',
                    height: '50px'
                }, styleTpl),
                id: '__currentFps',
                html: '0'
            }
        ]);
        Ext.AnimationQueue.resetFps();
    },
    resetFps: function() {
        var currentFps = Ext.get('__currentFps'),
            averageFps = Ext.get('__averageFps'),
            minFps = Ext.get('__minFps'),
            maxFps = Ext.get('__maxFps'),
            min = 1000,
            max = 0,
            count = 0,
            sum = 0;
        if (!currentFps) {
            return;
        }
        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.onReady(this.showFps.bind(this));
    }
});



Ext.define('Ext.mixin.Bufferable', function(Bufferable) {
    return {
        extend: Ext.Mixin,
        mixinConfig: {
            id: 'bufferable',
            before: {
                destroy: 'cancelAllCalls'
            },
            extended: function(baseClass, derivedClass, classBody) {
                var bufferableMethods = classBody.bufferableMethods;
                if (bufferableMethods) {
                    delete classBody.bufferableMethods;
                    Bufferable.processClass(derivedClass, bufferableMethods);
                }
            }
        },
        afterClassMixedIn: function(targetClass) {
            Bufferable.processClass(targetClass);
        },
        privates: {
            
            cancelAllCalls: function() {
                var bufferables = this.bufferables,
                    name;
                if (bufferables) {
                    for (name in bufferables) {
                        bufferables[name].cancel();
                        delete bufferables[name];
                    }
                }
            },
            
            cancelBufferedCall: function(name, invoke) {
                var bufferables = this.bufferables,
                    timer = bufferables && bufferables[name];
                if (timer) {
                    timer[invoke ? 'invoke' : 'cancel']();
                }
                return !!timer;
            },
            
            flushBufferedCall: function(name) {
                return this.cancelBufferedCall(name, true);
            },
            
            initBufferables: function() {
                var me = this,
                    methods = me.hasOwnProperty('bufferableMethods') && me.bufferableMethods,
                    classMethods;
                if (methods) {
                    Bufferable._canonicalize(methods);
                    classMethods = me.self.prototype.bufferableMethods;
                    me.bufferableMethods = Ext.merge(Ext.clone(classMethods), methods);
                }
                return (me.bufferables = {});
            },
            
            isCallPending: function(name) {
                var bufferables = this.bufferables,
                    timer = bufferables && bufferables[name];
                return !!timer;
            },
            statics: {
                SINGLE: {
                    single: true
                },
                _canonicalize: function(methods) {
                    var t, def, s, name;
                    for (name in methods) {
                        s = Ext.String.capitalize(name);
                        def = methods[name];
                        t = typeof def;
                        if (t === 'number' || t === 'string') {
                            
                            
                            
                            
                            methods[name] = def = {
                                delay: def
                            };
                        }
                        if (typeof (t = def.delay) === 'string') {
                            
                            
                            
                            def[t] = true;
                            delete def.delay;
                        }
                        def.capitalized = s;
                        def.name = name;
                        if (!def.fn) {
                            def.fn = 'do' + s;
                        }
                        if (!def.flag) {
                            def.flag = 'is' + s + 'Pending';
                        }
                    }
                },
                _canceller: function() {
                    var timer = this,
                        
                        id = timer.id;
                    if (id) {
                        if (timer.delay) {
                            Ext.undefer(id);
                        } else if (timer.asap) {
                            Ext.unasap(id);
                        } else if (timer.idle) {
                            Ext.un('idle', id, null, Bufferable.SINGLE);
                        } else if (timer.raf) {
                            Ext.unraf(id);
                        }
                        timer.id = null;
                    }
                    timer.args = null;
                    timer.target[timer.flag] = false;
                },
                _invoker: function() {
                    var timer = this,
                        
                        args = timer.args || Ext.emptyArray,
                        target = timer.target;
                    
                    ++timer.invokes;
                    
                    timer.cancel();
                    target[timer.fn].apply(target, args);
                },
                delayCall: function(target, def, args) {
                    var bufferables = target.bufferables || target.initBufferables(),
                        name = def.name,
                        timer = bufferables[name] || (bufferables[name] = Ext.apply({
                            
                            calls: 0,
                            invokes: 0,
                            
                            args: null,
                            cancel: Bufferable._canceller,
                            id: null,
                            target: target,
                            invoke: Bufferable._invoker
                        }, def)),
                        delay = def.delay,
                        exec = function() {
                            if (timer.id) {
                                timer.id = null;
                                timer.invoke();
                            }
                        };
                    if (timer.id) {
                        timer.cancel();
                    }
                    timer.args = args;
                    
                    ++timer.calls;
                    
                    target[timer.flag] = true;
                    if (delay) {
                        timer.id = Ext.defer(exec, delay);
                    } else if (def.asap) {
                        timer.id = Ext.asap(exec);
                    } else if (def.idle) {
                        timer.id = exec;
                        Ext.on('idle', exec, null, Bufferable.SINGLE);
                    } else if (def.raf) {
                        timer.id = Ext.raf(exec);
                    } else {
                        
                        timer.invoke();
                    }
                },
                processClass: function(cls, bufferableMethods) {
                    var proto = cls.prototype,
                        inherited = proto.bufferableMethods,
                        def, name;
                    if (bufferableMethods) {
                        
                        Bufferable._canonicalize(bufferableMethods);
                        if (inherited) {
                            
                            
                            
                            inherited = Ext.merge(Ext.clone(inherited), bufferableMethods);
                        }
                        proto.bufferableMethods = inherited || bufferableMethods;
                    } else {
                        
                        
                        
                        bufferableMethods = inherited;
                        Bufferable._canonicalize(bufferableMethods);
                        
                        proto.bufferables = null;
                    }
                    if (bufferableMethods) {
                        for (name in bufferableMethods) {
                            if (!proto[name]) {
                                def = bufferableMethods[name];
                                Bufferable.processMethod(proto, def, Array.prototype.slice);
                            }
                        }
                    }
                },
                processMethod: function(proto, def, slice) {
                    var name = def.name,
                        cap = def.capitalized;
                    proto[name] = function() {
                        return Bufferable.delayCall(this, def, slice.call(arguments));
                    };
                    proto['cancel' + cap] = function() {
                        return this.cancelBufferedCall(name);
                    };
                    proto['flush' + cap] = function() {
                        return this.flushBufferedCall(name);
                    };
                }
            }
        }
    };
});




Ext.define('Ext.ComponentManager', {
    alternateClassName: 'Ext.ComponentMgr',
    singleton: true,
    mixins: [
        Ext.mixin.Bufferable
    ],
    count: 0,
    referencesDirty: true,
    referenceRepairs: 0,
    typeName: 'xtype',
    bufferableMethods: {
        handleDocumentMouseDown: 'asap'
    },
    
    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);
        me.all = {};
        me.byInstanceId = {};
        me.holders = {};
        me.names = {};
        me.references = {};
        me.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,
            id = component.getId(),
            onAvailableCallbacks = me.onAvailableCallbacks;
        
        if (id === undefined) {
            Ext.raise('Component id is undefined. Please ensure the component has an id.');
        }
        if (id in me.all) {
            Ext.raise('Duplicate component id "' + id + '"');
        }
        if (component.$iid in me.byInstanceId) {
            Ext.raise('Duplicate component instance id "' + component.$iid + '"');
        }
        
        me.all[id] = component;
        me.byInstanceId[component.$iid] = component;
        if (component.reference) {
            me.references[id] = component;
        }
        if (component.name && component.nameable) {
            me.names[id] = component;
        }
        if (component.nameHolder || component.referenceHolder) {
            me.holders[id] = component;
        }
        ++me.count;
        if (!me.hasFocusListener) {
            me.installFocusListener();
        }
        onAvailableCallbacks = onAvailableCallbacks && onAvailableCallbacks[id];
        if (onAvailableCallbacks && onAvailableCallbacks.length) {
            me.notifyAvailable(component);
        }
    },
    unregister: function(component) {
        var me = this,
            all = me.all,
            byInstanceId = me.byInstanceId,
            holders = me.holders,
            references = me.references,
            names = me.names,
            id = component.getId();
        if (id in holders) {
            
            
            
            holders[id] = null;
            delete holders[id];
        }
        if (id in names) {
            names[id] = null;
            delete names[id];
        }
        if (id in references) {
            references[id] = null;
            delete references[id];
        }
        all[id] = null;
        delete all[id];
        id = component.$iid;
        byInstanceId[id] = null;
        delete byInstanceId[id];
        --me.count;
    },
    markReferencesDirty: function() {
        var me = this,
            holders = me.holders,
            key;
        if (!me.referencesDirty) {
            
            for (key in holders) {
                holders[key].refs = holders[key].nameRefs = null;
            }
            me.referencesDirty = true;
        }
    },
    fixReferences: function() {
        var me = this,
            references = me.references,
            names = me.names,
            key;
        if (me.referencesDirty) {
            ++me.referenceRepairs;
            for (key in references) {
                references[key]._fixReference();
            }
            for (key in names) {
                names[key]._fixName();
            }
            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) {
        Ext.Object.each(this.all, fn, scope);
    },
    
    getCount: function() {
        return this.count;
    },
    
    getAll: function() {
        return Ext.Object.getValues(this.all);
    },
    
    getActiveComponent: function() {
        return Ext.Component.from(Ext.dom.Element.getActiveElement());
    },
    
    onGlobalFocus: function(e) {
        var me = this,
            event = e.event,
            toComponent = event.toComponent = e.toComponent = Ext.Component.from(e.toElement),
            fromComponent = event.fromComponent = e.fromComponent = Ext.Component.from(e.fromElement),
            commonAncestor = me.getCommonAncestor(fromComponent, toComponent),
            targetComponent;
        
        if (toComponent !== fromComponent) {
            if (fromComponent && !fromComponent.destroyed && !fromComponent.isDestructing()) {
                if (fromComponent.handleBlurEvent) {
                    fromComponent.handleBlurEvent(e);
                }
                
                for (targetComponent = fromComponent; targetComponent && targetComponent !== commonAncestor; targetComponent = targetComponent.getRefOwner()) {
                    if (!(targetComponent.destroyed || targetComponent.destroying)) {
                        e.type = 'focusleave';
                        targetComponent.onFocusLeave(event);
                    }
                }
            }
            if (toComponent && !toComponent.destroyed && !toComponent.isDestructing()) {
                if (toComponent.handleFocusEvent) {
                    toComponent.handleFocusEvent(e);
                }
                
                for (targetComponent = toComponent; targetComponent && targetComponent !== commonAncestor; targetComponent = targetComponent.getRefOwner()) {
                    e.type = 'focusenter';
                    targetComponent.onFocusEnter(event);
                }
            }
        }
        for (targetComponent = commonAncestor; targetComponent; targetComponent = targetComponent.getRefOwner()) {
            if (!(targetComponent.destroying || targetComponent.destroyed)) {
                targetComponent.onFocusMove(e);
            }
        }
    },
    getCommonAncestor: function(compA, compB) {
        if (compA === compB) {
            return compA;
        }
        while (compA && !(compA.isAncestor(compB) || compA === compB)) {
            compA = compA.getRefOwner();
        }
        return compA;
    },
    privates: {
        
        doHandleDocumentMouseDown: function(e) {
            var floatedSelector = Ext.Widget.prototype.floatedSelector,
                targetFloated;
            
            
            if (floatedSelector) {
                targetFloated = Ext.Component.from(e.getTarget(floatedSelector, Ext.getBody()));
                
                if (targetFloated) {
                    targetFloated.toFront(true);
                }
            }
        },
        installFocusListener: function() {
            var me = this;
            Ext.on('focus', me.onGlobalFocus, me);
            me.hasFocusListener = true;
        },
        clearAll: function() {
            this.all = {};
            this.names = {};
            this.references = {};
            this.onAvailableCallbacks = {};
        },
        
        from: function(el, limit, selector) {
            if (el && el.isEvent) {
                el = el.target;
            }
            var target = Ext.getDom(el),
                cache = this.all,
                depth = 0,
                topmost, cmpId, cmp;
            if (typeof limit !== 'number') {
                topmost = Ext.getDom(limit);
                limit = Number.MAX_VALUE;
            }
            while (target && target.nodeType === 1 && depth < limit && target !== topmost) {
                cmpId = target.getAttribute('data-componentid') || target.id;
                if (cmpId) {
                    cmp = cache[cmpId];
                    if (cmp && (!selector || Ext.ComponentQuery.is(cmp, selector))) {
                        return cmp;
                    }
                    
                    depth++;
                }
                target = target.parentNode;
            }
            return null;
        }
    }
}, function() {
    var ComponentManager = Ext.ComponentManager;
    
    ComponentManager.fromElement = ComponentManager.from;
    
    Ext.getCmp = function(id) {
        return ComponentManager.get(id);
    };
    Ext.iidToCmp = function(iid) {
        return ComponentManager.byInstanceId[iid] || null;
    };
    Ext.doEv = function(node, e) {
        var cmp, method, event;
        
        
        cmp = Ext.Component.from(e.target);
        if (cmp && !cmp.destroying && !cmp.destroyed && cmp.getEventHandlers) {
            method = cmp.getEventHandlers()[e.type];
            if (method && cmp[method]) {
                event = new Ext.event.Event(e);
                return cmp[method](event);
            }
        }
        return true;
    };
});


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([
            initial
        ]);
    },
    
    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
}, 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.destroyed && 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 && candidate.self.getConfigurator && 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 ? !!propValue : queryOperators[operator](Ext.coerce(propValue, compareTo), compareTo)) {
                    result.push(candidate);
                }
            }
            return result;
        },
        
        filterById = function(items, id, idOnly) {
            var result = [],
                i = 0,
                length = items.length,
                candidate, check;
            for (; i < length; i++) {
                candidate = items[i];
                check = idOnly ? candidate.id : candidate.getItemId();
                if (check === 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],
                        
                        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, root) {
            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, root, op);
                if (result) {
                    return result;
                }
            }
            return false;
        },
        _is: function(component, root, operations) {
            var len = operations.length,
                active = [
                    component
                ],
                operation, i, j, mode, 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);
                    }
                } else {
                    active = filterItems(active, operation);
                }
                
                if (active.length === 0) {
                    return false;
                }
            }
            
            
            if (root) {
                if (!mode) {
                    
                    
                    active = getAncestors(active);
                }
                if (active.length > 0) {
                    
                    
                    active = filterItems(active, {
                        method: filterById,
                        args: [
                            root.id,
                            true
                        ]
                    });
                }
                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(cq, {
        
        cache: new Ext.util.LruCache({
            maxSize: 100
        }),
        
        pseudos: {
            not: function(components, selector) {
                var 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 && c.isFocusable()) {
                        results.push(c);
                    }
                }
                return results;
            },
            
            canfocus: function(cmps, value) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;
                for (; i < len; i++) {
                    c = cmps[i];
                    if (c.canFocus && c.canFocus(false, value)) {
                        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,
                    len = m[2] - 0,
                    i, n, nodeIndex;
                for (i = 0; n = c[i]; i++) {
                    
                    nodeIndex = i + 1;
                    if (f === 1) {
                        if (len === 0 || nodeIndex === len) {
                            result.push(n);
                        }
                    } else if ((nodeIndex + len) % f === 0) {
                        result.push(n);
                    }
                }
                return result;
            },
            scrollable: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;
                for (; i < len; i++) {
                    c = cmps[i];
                    
                    if (c.scrollable || c._scrollable) {
                        results.push(c);
                    }
                }
                return results;
            },
            visible: function(cmps, deep) {
                deep = deep === 'true';
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;
                for (; i < len; i++) {
                    c = cmps[i];
                    
                    if (c.isVisible(deep)) {
                        results.push(c);
                    }
                }
                return results;
            }
        },
        
        query: function(selector, root) {
            
            if (!selector) {
                return Ext.ComponentManager.getAll();
            }
            var results = [],
                noDupResults = [],
                dupMatcher = {},
                query = cq.cache.get(selector),
                resultsLn, cmp, i;
            if (!query) {
                query = cq.cache.add(selector, cq.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) {
            cq._visit(true, selector, root, fn, scope, extraArgs);
        },
        
        visitPostOrder: function(selector, root, fn, scope, extraArgs) {
            cq._visit(false, selector, root, fn, scope, extraArgs);
        },
        
        _visit: function(preOrder, selector, root, fn, scope, extraArgs) {
            var query = cq.cache.get(selector),
                callArgs = [
                    root
                ],
                children,
                len = 0,
                i, rootMatch;
            if (!query) {
                query = cq.cache.add(selector, cq.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 (cq._visit.call(cq, 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, root) {
            if (!selector) {
                return true;
            }
            if (typeof selector === 'function') {
                return selector(component);
            } else {
                var query = cq.cache.get(selector);
                if (!query) {
                    query = cq.cache.add(selector, cq.parse(selector));
                }
                return query.is(component, root);
            }
        },
        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.raise('Invalid ComponentQuery selector: ""');
                }
                
                operations.push(cq._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.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.all = function() {
        return cq.query.apply(cq, arguments);
    };
    
    Ext.first = function() {
        var matches = cq.query.apply(cq, arguments);
        return (matches && matches[0]) || null;
    };
});


Ext.define('Ext.Evented', {
    alternateClassName: 'Ext.EventedBase',
    mixins: [
        Ext.mixin.Observable
    ],
    initialized: false,
    constructor: function(config) {
        
        
        this.callParent();
        
        this.mixins.observable.constructor.call(this, config);
        this.initialized = true;
    },
    onClassExtended: function(cls, data) {
        if (!data.hasOwnProperty('eventedConfig')) {
            return;
        }
        var config = data.config,
            eventedConfig = data.eventedConfig,
            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.eventedSetter || cfg.getEventedSetter();
            }
        }
    }
});


Ext.define('Ext.util.Positionable', {
    mixinId: 'positionable',
    _positionTopLeft: [
        'position',
        'top',
        'left'
    ],
    
    
    
    
    
    clippedCls: Ext.baseCSSPrefix + 'clipped',
    afterSetPosition: Ext.emptyFn,
    
    
    
    
    
    getAnchorToXY: function() {
        Ext.raise("getAnchorToXY is not implemented in " + this.$className);
    },
    
    getBorderPadding: function() {
        Ext.raise("getBorderPadding is not implemented in " + this.$className);
    },
    
    getLocalX: function() {
        Ext.raise("getLocalX is not implemented in " + this.$className);
    },
    
    getLocalXY: function() {
        Ext.raise("getLocalXY is not implemented in " + this.$className);
    },
    
    getLocalY: function() {
        Ext.raise("getLocalY is not implemented in " + this.$className);
    },
    
    getX: function() {
        Ext.raise("getX is not implemented in " + this.$className);
    },
    
    getXY: function() {
        Ext.raise("getXY is not implemented in " + this.$className);
    },
    
    getY: function() {
        Ext.raise("getY is not implemented in " + this.$className);
    },
    
    setLocalX: function() {
        Ext.raise("setLocalX is not implemented in " + this.$className);
    },
    
    setLocalXY: function() {
        Ext.raise("setLocalXY is not implemented in " + this.$className);
    },
    
    setLocalY: function() {
        Ext.raise("setLocalY is not implemented in " + this.$className);
    },
    
    setX: function() {
        Ext.raise("setX is not implemented in " + this.$className);
    },
    
    setXY: function() {
        Ext.raise("setXY is not implemented in " + this.$className);
    },
    
    setY: function() {
        Ext.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, size) {
        var region = this.getRegion();
        region.setPosition(0, 0);
        region.translateBy(extraX || 0, extraY || 0);
        if (size) {
            region.setWidth(size.width);
            region.setHeight(size.height);
        }
        return region.getAnchorPoint(anchor);
    },
    
    convertPositionSpec: function(posSpec) {
        return Ext.util.Region.getAlignInfo(posSpec);
    },
    
    getAlignToXY: function(alignToEl, position, offsets) {
        var newRegion = this.getAlignToRegion(alignToEl, position, offsets);
        return [
            newRegion.x,
            newRegion.y
        ];
    },
    getAlignToRegion: function(alignToEl, posSpec, offset, minHeight) {
        var me = this,
            inside, newRegion, bodyScroll;
        alignToEl = Ext.fly(alignToEl.el || alignToEl);
        if (!alignToEl || !alignToEl.dom) {
            
            Ext.raise({
                sourceClass: 'Ext.util.Positionable',
                sourceMethod: 'getAlignToXY',
                msg: 'Attempted to align an element that doesn\'t exist'
            });
        }
        
        posSpec = me.convertPositionSpec(posSpec);
        
        if (posSpec.constrain) {
            
            
            if (posSpec.constrain === '!') {
                inside = alignToEl;
            } else {
                
                
                
                inside = me.constrainTo || me.container || me.el.parent();
            }
            inside = Ext.fly(inside.el || inside).getConstrainRegion();
        }
        if (alignToEl === Ext.getBody()) {
            bodyScroll = alignToEl.getScroll();
            offset = [
                bodyScroll.left,
                bodyScroll.top
            ];
        }
        newRegion = me.getRegion().alignTo({
            target: alignToEl.getRegion(),
            inside: inside,
            minHeight: minHeight,
            offset: offset,
            align: posSpec,
            axisLock: true
        });
        return newRegion;
    },
    
    getAnchorXY: function(anchor, local, size) {
        var me = this,
            region = me.getRegion(),
            el = me.el,
            isViewport = el.dom.nodeName === 'BODY' || el.dom.nodeType === 9,
            scroll = el.getScroll();
        if (local) {
            region.setPosition(0, 0);
        } else if (isViewport) {
            region.setPosition(scroll.left, scroll.top);
        }
        if (size) {
            region.setWidth(size.width);
            region.setHeight(size.height);
        }
        return region.getAnchorPoint(anchor);
    },
    
    getBox: function(contentBox, local) {
        var me = this,
            xy = local ? me.getLocalXY() : me.getXY(),
            x = xy[0],
            y = xy[1],
            w, h, borderPadding, beforeX, beforeY;
        
        if (me.el.dom.nodeName === 'BODY' || me.el.dom.nodeType === 9) {
            w = Ext.Element.getViewportWidth();
            h = Ext.Element.getViewportHeight();
        } else {
            w = me.getWidth();
            h = me.getHeight();
        }
        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();
        if (local && proposedConstrainPosition) {
            proposedConstrainPosition = me.reverseTranslateXY(proposedConstrainPosition);
        }
        vector = ((me.constrainHeader && me.header.rendered) ? me.header : me).getConstrainVector(constrainTo, proposedConstrainPosition, proposedSize);
        
        if (vector) {
            xy = proposedPosition || me.getPosition(local);
            xy[0] += vector[0];
            xy[1] += vector[1];
        }
        return xy;
    },
    
    getConstrainRegion: function() {
        var me = this,
            el = me.el,
            isBody = el.dom.nodeName === 'BODY',
            dom = el.dom,
            borders = el.getBorders(),
            pos = el.getXY(),
            left = pos[0] + borders.beforeX,
            top = pos[1] + borders.beforeY,
            scroll, width, height;
        
        if (isBody) {
            scroll = el.getScroll();
            left = scroll.left;
            top = scroll.top;
            width = Ext.Element.getViewportWidth();
            height = Ext.Element.getViewportHeight();
        } else {
            width = dom.clientWidth;
            height = dom.clientHeight;
        }
        return new Ext.util.Region(top, left + width, top + height, left);
    },
    
    getConstrainVector: function(constrainTo, proposedPosition, proposedSize) {
        var me = this,
            thisRegion = me.getRegion(),
            vector = [
                0,
                0
            ],
            shadowSize = (me.shadow && me.constrainShadow && !me.shadowDisabled) ? me.el.shadow.getShadowSize() : undefined,
            overflowed = false,
            constraintInsets = me.constraintInsets;
        if (!(constrainTo instanceof Ext.util.Region)) {
            constrainTo = Ext.get(constrainTo.el || constrainTo);
            
            
            constrainTo = constrainTo.getConstrainRegion();
        }
        
        if (constraintInsets) {
            constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets);
            constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.left);
        }
        
        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 = offsetsTo.isRegion ? [
                offsetsTo.x,
                offsetsTo.y
            ] : Ext.fly(offsetsTo.el || offsetsTo).getXY();
        return [
            o[0] - e[0],
            o[1] - e[1]
        ];
    },
    
    getRegion: function(contentBox, local) {
        var box = this.getBox(contentBox, local);
        return new Ext.util.Region(box.top, box.right, box.bottom, box.left);
    },
    
    getClientRegion: function() {
        var me = this,
            el = me.el,
            dom = el.dom,
            viewContentBox = me.getBox(true),
            scrollbarHeight = dom.offsetHeight > dom.clientHeight,
            scrollbarWidth = dom.offsetWidth > dom.clientWidth,
            padding, scrollSize, isRTL;
        if (scrollbarHeight || scrollbarWidth) {
            scrollSize = Ext.getScrollbarSize();
            
            
            if (scrollbarWidth) {
                scrollbarWidth = scrollSize.width;
                isRTL = el.getStyle('direction') === 'rtl' && !Ext.supports.rtlVertScrollbarOnRight;
                if (isRTL) {
                    padding = el.getPadding('l');
                    viewContentBox.left -= padding + Math.max(padding, scrollbarWidth);
                } else {
                    padding = el.getPadding('r');
                    viewContentBox.right += padding - Math.max(padding, scrollbarWidth);
                }
            }
            
            
            if (scrollbarHeight) {
                scrollbarHeight = scrollSize.height;
                padding = el.getPadding('b');
                viewContentBox.bottom += padding - Math.max(padding, scrollbarHeight);
            }
        }
        
        return new Ext.util.Region(viewContentBox.top, viewContentBox.right, viewContentBox.bottom, viewContentBox.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
        };
    },
    
    reverseTranslateXY: function(xy) {
        var coords = xy,
            el = this.el,
            dom = el.dom,
            offsetParent = dom.offsetParent,
            relative, offsetParentXY, x, y;
        if (offsetParent) {
            relative = el.isStyle('position', 'relative') , offsetParentXY = Ext.fly(offsetParent).getXY() , x = xy[0] + offsetParentXY[0] + offsetParent.clientLeft;
            y = xy[1] + offsetParentXY[1] + offsetParent.clientTop;
            if (relative) {
                
                
                x += el.getPadding('l');
                y += el.getPadding('t');
            }
            coords = [
                x,
                y
            ];
        }
        return coords;
    },
    privates: {
        
        clipTo: function(clippingEl, sides) {
            var clippingRegion,
                el = this.el,
                floaterRegion = el.getRegion(),
                overflow, i,
                clipValues = [],
                clippedCls = this.clippedCls,
                clipStyle, clipped, shadow;
            
            if (clippingEl.isRegion) {
                clippingRegion = clippingEl;
            } else {
                clippingRegion = (clippingEl.isComponent ? clippingEl.el : Ext.fly(clippingEl)).getConstrainRegion();
            }
            
            if (!sides) {
                sides = 15;
            }
            
            if (sides & 1 && (overflow = clippingRegion.top - floaterRegion.top) > 0) {
                clipValues[0] = overflow;
                clipped = true;
            } else {
                clipValues[0] = -10000;
            }
            if (sides & 2 && (overflow = floaterRegion.right - clippingRegion.right) > 0) {
                clipValues[1] = Math.max(0, el.getWidth() - overflow);
                clipped = true;
            } else {
                clipValues[1] = 10000;
            }
            if (sides & 4 && (overflow = floaterRegion.bottom - clippingRegion.bottom) > 0) {
                clipValues[2] = Math.max(0, el.getHeight() - overflow);
                clipped = true;
            } else {
                clipValues[2] = 10000;
            }
            if (sides & 8 && (overflow = clippingRegion.left - floaterRegion.left) > 0) {
                clipValues[3] = overflow;
                clipped = true;
            } else {
                clipValues[3] = -10000;
            }
            clipStyle = 'rect(';
            for (i = 0; i < 4; ++i) {
                
                
                clipStyle += Ext.Element.addUnits(clipValues[i], 'px');
                clipStyle += (i === 3) ? ')' : ',';
            }
            el.dom.style.clip = clipStyle;
            
            
            el.addCls(clippedCls);
            
            
            
            
            if ((shadow = el.shadow) && (el = shadow.el) && el.dom) {
                clipValues[2] -= shadow.offsets.y;
                clipValues[3] -= shadow.offsets.x;
                clipStyle = 'rect(';
                for (i = 0; i < 4; ++i) {
                    
                    
                    clipStyle += Ext.Element.addUnits(clipValues[i], 'px');
                    clipStyle += (i === 3) ? ')' : ',';
                }
                el.dom.style.clip = clipStyle;
                
                
                if (clipped && !Ext.supports.CSS3BoxShadow) {
                    el.dom.style.display = 'none';
                } else {
                    el.dom.style.display = '';
                    
                    
                    el.addCls(clippedCls);
                }
            }
        },
        
        clearClip: function() {
            var el = this.el,
                clippedCls = this.clippedCls;
            el.dom.style.clip = Ext.isIE8 ? 'auto' : '';
            
            
            el.removeCls(clippedCls);
            
            if (el.shadow && el.shadow.el && el.shadow.el.dom) {
                el.shadow.el.dom.style.clip = Ext.isIE8 ? 'auto' : '';
                
                
                if (!Ext.supports.CSS3BoxShadow) {
                    el.dom.style.display = '';
                    
                    
                    el.removeCls(clippedCls);
                }
            }
        }
    }
});


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);
        Ext.getDetachedBody().dom.appendChild(el.dom);
    },
    
    reset: function() {
        var cache = this.cache,
            i = cache.length;
        while (i--) {
            cache[i].destroy();
        }
        this.cache = [];
    }
});


Ext.define('Ext.dom.Underlay', {
    
    
    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) {
            if (el.dom) {
                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 = x + offsets.x;
                y = y + offsets.y;
                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,
            isDirectEvent = Ext.event.publisher.Dom.instance.directEvents[name],
            captures, directs, directCaptures;
        options = options || {};
        if (options.delegated === false || isDirectEvent) {
            if (isDirectEvent && options.delegate) {
                options.capture = true;
            }
            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', {
    isEventPublisher: true,
    $vetoClearingPrototypeOnDestroy: true,
    
    handledEvents: [],
    statics: {
        
        publishers: {},
        
        publishersByEvent: {}
    },
    constructor: function() {
        var me = this,
            type = me.type;
        
        me.handles = {};
        
        if (!type) {
            Ext.raise("Event publisher '" + me.$className + "' defined without a 'type' property.");
        }
        if (me.self.instance) {
            Ext.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;
        }
    },
    
    subscribe: function() {
        Ext.raise("Ext.event.publisher.Publisher subclass '" + this.$className + '" has no subscribe method.');
    },
    unsubscribe: function() {
        Ext.raise("Ext.event.publisher.Publisher subclass '" + this.$className + '" has no unsubscribe method.');
    },
    
    fire: function(element, eventName, args) {
        var event;
        if (element.hasListeners[eventName]) {
            event = element.events[eventName];
            if (event) {
                event.fire.apply(event, args);
            }
        }
    }
});


Ext.define('Ext.util.Offset', {
    
    statics: {
        fromObject: function(obj) {
            if (obj instanceof this) {
                return obj;
            }
            if (typeof obj === 'number') {
                return new this(obj, obj);
            }
            if (obj.length) {
                return new this(obj[0], obj[1]);
            }
            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.raise('Offset must be an instance of Ext.util.Offset');
        }
        
        return (this.x === offset.x && this.y === offset.y);
    },
    add: function(offset) {
        
        if (!(offset instanceof this.statics())) {
            Ext.raise('Offset must be an instance of Ext.util.Offset');
        }
        
        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', function() {
    var ExtUtil = Ext.util,
        constrainRe = /([^\?!]*)(!|\?)?$/,
        alignRe = /^(?:(?:([trbl])(\d+))|(tl|t|tc|tr|l|c|r|bl|b|bc|br))(?:-(?:(?:([trbl])(\d+))|(tl|t|tc|tr|l|c|r|bl|b|bc|br)))?$/i,
        
        
        
        
        
        
        LTROffsetFactors = {
            l: 0,
            r: 100,
            t: 0,
            b: 100,
            c: 50
        },
        RTLOffsetFactors = {
            l: 100,
            r: 0,
            t: 0,
            b: 100,
            c: 50
        },
        relativePositions = {
            b: 0,
            l: 1,
            t: 2,
            r: 3
        },
        alignMap = {
            "tl-tr": "l0-r0",
            "tl-r": "l0-r50",
            "bl-r": "l100-r50",
            "bl-br": "l100-r100",
            "tr-tl": "r0-l0",
            "tr-l": "r0-l50",
            "br-l": "r100-l50",
            "br-bl": "r100-l100"
        },
        rtlAlignMap = {
            "tl-tr": "r0-l0",
            "tl-r": "r0-l50",
            "bl-r": "r100-l50",
            "bl-br": "r100-l100",
            "tr-tl": "l0-r0",
            "tr-l": "l0-r50",
            "br-l": "l100-r50",
            "br-bl": "l100-r100"
        },
        adjustParams = [],
        zeroOffset = new ExtUtil.Offset(0, 0),
        parseRegion = function(box) {
            var Region = ExtUtil.Region,
                type = typeof box,
                top, right, bottom, left;
            if (box == null) {
                return Region.EMPTY;
            }
            if (box.isRegion) {
                return box;
            }
            if (box.isElement || box.nodeType === 1) {
                return this.getRegion(box);
            }
            if (type === 'string') {
                box = box.split(' ');
                switch (box.length) {
                    case 1:
                        box[1] = box[2] = box[3] = box[0];
                        break;
                    case 2:
                        box[2] = box[0];
                        box[3] = box[1];
                        break;
                    case 3:
                        box[3] = box[1];
                }
                top = parseInt(box[0], 10) || 0;
                right = parseInt(box[1], 10) || 0;
                bottom = parseInt(box[2], 10) || 0;
                left = parseInt(box[3], 10) || 0;
            } else if (type === 'number') {
                top = right = bottom = left = box;
            } else if (typeof box.x === 'number') {
                top = box.y;
                left = box.x;
                if (typeof box.right === 'number') {
                    right = box.right;
                    bottom = box.bottom;
                } else {
                    right = left + box.width;
                    bottom = top + box.height;
                }
            } else 
            {
                Ext.raise('Not convertible to a Region: ' + box);
            }
            
            return new Region(top, right, bottom, left);
        },
        magnitude = [
            -1,
            1,
            1,
            -1
        ],
        
        
        
        addAnchorOffset = function(target, anchorSize, relativePosition) {
            
            if (relativePosition != null && anchorSize) {
                adjustParams[0] = adjustParams[1] = adjustParams[2] = adjustParams[3] = 0;
                adjustParams[relativePosition] = anchorSize.y * magnitude[relativePosition];
                target = ExtUtil.Region.from(target);
                target.adjust.apply(target, adjustParams);
            }
            return target;
        },
        
        calculateAnchorPosition = function(target, result, relativePosition, anchorSize, inside) {
            var anchorWidth = Math.ceil(anchorSize.x),
                minOverlap = Math.ceil(anchorWidth / 2) + 3,
                min, max, anchorPos, isBefore, overlapLine, x, y;
            
            if (inside && !inside.intersect(target)) {
                return;
            }
            if (relativePosition != null) {
                
                if (relativePosition & 1) {
                    
                    if (result.getHeight() < anchorWidth + 4) {
                        return;
                    }
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    min = target.top + minOverlap - result.height;
                    max = target.bottom - minOverlap - 1;
                    result.setPosition(result.x, Math.min(Math.max(result.y, min), max));
                    
                    
                    min = result.top + 2;
                    max = result.bottom - (anchorWidth + 2);
                    isBefore = relativePosition === 3;
                    x = isBefore ? result.right : result.left;
                    overlapLine = new ExtUtil.Region(Math.max(result.top, target.top), x, Math.min(result.bottom, target.bottom), x);
                    
                    anchorPos = new ExtUtil.Region(0, 0, 0, 0).setWidth(anchorSize.y).setHeight(anchorWidth).alignTo({
                        target: overlapLine,
                        align: isBefore ? 'l-r' : 'r-l',
                        overlap: true
                    });
                    
                    anchorPos.setPosition(anchorPos.x, Math.min(Math.max(anchorPos.y, min), max));
                    anchorPos.position = isBefore ? 'right' : 'left';
                } else 
                {
                    
                    if (result.getWidth() < anchorWidth + 4) {
                        return;
                    }
                    
                    
                    min = target.left + minOverlap - result.width;
                    max = target.right - minOverlap - 1;
                    result.setPosition(Math.min(Math.max(result.x, min), max), result.y);
                    
                    
                    min = result.left + 2;
                    max = result.right - (anchorWidth + 2);
                    
                    isBefore = relativePosition === 0;
                    y = isBefore ? result.bottom : result.top;
                    overlapLine = new ExtUtil.Region(y, Math.min(result.right, target.right), y, Math.max(result.left, target.left));
                    
                    anchorPos = new ExtUtil.Region(0, 0, 0, 0).setWidth(anchorWidth).setHeight(anchorSize.y).alignTo({
                        target: overlapLine,
                        align: isBefore ? 't-b' : 'b-t',
                        overlap: true
                    });
                    
                    anchorPos.setPosition(Math.min(Math.max(anchorPos.x, min), max), anchorPos.y);
                    anchorPos.position = isBefore ? 'bottom' : 'top';
                }
                
                if (inside && !inside.contains(anchorPos)) {
                    return;
                }
                result.anchor = anchorPos;
                result.anchor.align = relativePosition;
            }
        },
        checkMinHeight = function(minHeight, result, target, inside) {
            var newHeight;
            if (minHeight && inside) {
                
                if (result.top >= target.bottom && result.bottom > inside.bottom) {
                    result.setHeight(Math.max(result.getHeight() + inside.bottom - result.bottom, minHeight));
                    result.constrainHeight = true;
                }
                
                else if (result.bottom <= target.top && result.top < inside.top) {
                    newHeight = Math.max(result.getHeight() + result.top - inside.top, minHeight);
                    result.adjust(result.getHeight() - newHeight);
                    result.constrainHeight = true;
                }
                
                else if (result.getHeight() > inside.getHeight()) {
                    result.setHeight(Math.max(minHeight, inside.getHeight()));
                    result.setPosition(result.x, 0);
                    result.constrainHeight = true;
                }
            }
        },
        checkMinWidth = function(minWidth, result, target, inside) {
            var newWidth;
            if (minWidth && inside) {
                
                if (result.left >= target.right && result.right > inside.right) {
                    result.setWidth(Math.max(result.getWidth() + inside.right - result.right, minWidth));
                    result.constrainWidth = true;
                }
                
                else if (result.right <= target.left && result.left < inside.left) {
                    newWidth = Math.max(result.getWidth() + result.left - inside.left, minWidth);
                    result.adjust(0, 0, 0, result.getWidth() - newWidth);
                    result.constrainWidth = true;
                }
                
                else if (result.getWidth() > inside.getWidth()) {
                    result.setWidth(Math.max(minWidth, inside.getWidth()));
                    result.setPosition(0, result.y);
                    result.constrainWidth = true;
                }
            }
        };
    return {
        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);
            },
            
            getAlignInfo: function(align, rtl) {
                if (typeof align === 'object') {
                    return align;
                }
                align = align ? ((align.indexOf('-') < 0) ? 'tl-' + align : align) : 'tl-bl';
                
                constrain = constrainRe.exec(align);
                align = constrain[1];
                
                align = (rtl ? rtlAlignMap : alignMap)[align] || align;
                var offsetFactors = rtl ? RTLOffsetFactors : LTROffsetFactors,
                    constrain,
                    parts = alignRe.exec(align),
                    result;
                
                if (!parts) {
                    Ext.raise({
                        sourceClass: 'Ext.util.Region',
                        sourceMethod: 'getAlignInfo',
                        position: align,
                        msg: 'Attempted to align an element with an invalid position: "' + align + '"'
                    });
                }
                
                result = {
                    myEdge: parts[1],
                    myOffset: parts[2],
                    otherEdge: parts[4],
                    otherOffset: parts[5],
                    constrain: constrain[2]
                };
                
                
                if (parts[3]) {
                    result.myEdge = parts[3][0];
                    result.myOffset = offsetFactors[parts[3][1]];
                    if (result.myOffset == null) {
                        result.myOffset = 50;
                    }
                }
                if (parts[6]) {
                    result.otherEdge = parts[6][0];
                    result.otherOffset = offsetFactors[parts[6][1]];
                    if (result.otherOffset == null) {
                        result.otherOffset = 50;
                    }
                }
                
                result.position = relativePositions[result.myEdge];
                return result;
            }
        },
        
        
        constructor: function(top, right, bottom, left) {
            var me = this;
            me.y = me.top = me[1] = top;
            me.right = right;
            me.bottom = bottom;
            me.x = me.left = me[0] = left;
            me.height = me.bottom - me.top;
            me.width = me.right - me.left;
        },
        
        setPosition: function(x, y) {
            
            if (arguments.length === 1) {
                y = x[1];
                x = x[0];
            }
            return this.translateBy(x - this.x, y - this.y);
        },
        
        contains: function(region) {
            var me = this;
            return (region.x >= me.x && (region.right || region.x) <= me.right && region.y >= me.y && (region.bottom || region.y) <= 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(targetRegion) {
            var me = this,
                constrain = Ext.Number.constrain;
            me.top = me.y = constrain(me.top, targetRegion.y, targetRegion.bottom);
            me.bottom = constrain(me.bottom, targetRegion.y, targetRegion.bottom);
            me.left = me.x = constrain(me.left, targetRegion.x, targetRegion.right);
            me.right = constrain(me.right, targetRegion.x, targetRegion.right);
            me.height = me.bottom - me.top;
            me.width = me.right - me.left;
            return me;
        },
        
        adjust: function(top, right, bottom, left) {
            var me = this;
            me.top = me.y += top || 0;
            me.left = me.x += left || 0;
            me.right += right || 0;
            me.bottom += bottom || 0;
            me.height = me.bottom - me.top;
            me.width = me.right - me.left;
            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 ExtUtil.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;
        },
        
        alignTo: function(options) {
            var me = this,
                Region = me.self,
                Offset = ExtUtil.Offset,
                Element = Ext.Element,
                target = parseRegion(options.target),
                targetPlusAnchorOffset,
                rtl = options.rtl,
                overlap = options.overlap,
                align = options.align,
                anchorSize = options.anchorSize,
                offset = options.offset,
                inside = options.inside,
                position = options.position,
                allowXTranslate = options.allowXTranslate,
                allowYTranslate = options.allowYTranslate,
                wasConstrained, result, initialPosition, constrainedPosition;
            if (offset) {
                offset = Offset.fromObject(offset);
                
                if (!(offset instanceof Offset)) {
                    Ext.raise('offset option must be an Ext.util.Offset');
                }
            }
            
            if (anchorSize) {
                anchorSize = Offset.fromObject(anchorSize);
                
                if (!(anchorSize instanceof Offset)) {
                    Ext.raise('anchorSize option must be an Ext.util.Offset');
                }
            }
            
            if (inside && !inside.isRegion) {
                if (Ext.getDom(inside) === document.body) {
                    inside = new Region(0, Element.getDocumentWidth(), Element.getDocumentHeight(), 0);
                } else {
                    inside = Ext.fly(inside).getRegion();
                }
            }
            
            
            
            if (position) {
                if (position.length === 2) {
                    position = new ExtUtil.Point(position[0], position[1]);
                }
                
                result = new Region().copyFrom(me).setPosition(position.x, position.y);
            } else {
                
                align = me.getAlignInfo(align, rtl);
                
                
                if (inside) {
                    if (target.x >= inside.right) {
                        target.setPosition(inside.right - 1, target.y);
                        if (align.position !== 3) {
                            align = me.getAlignInfo('r-l', rtl);
                        }
                    } else if (target.right < inside.x) {
                        target.setPosition(inside.x - target.getWidth() + 1, target.y);
                        if (align.position !== 1) {
                            align = me.getAlignInfo('l-r', rtl);
                        }
                    }
                    if (target.y >= inside.bottom) {
                        target.setPosition(target.x, inside.bottom - 1);
                        if (align.position !== 0) {
                            align = me.getAlignInfo('b-t', rtl);
                        }
                    } else if (target.bottom < inside.y) {
                        target.setPosition(target.x, inside.y - target.getHeight() + 1);
                        if (align.position !== 2) {
                            align = me.getAlignInfo('t-b', rtl);
                        }
                    }
                }
                
                targetPlusAnchorOffset = anchorSize ? addAnchorOffset(target, anchorSize, align.position) : target;
                
                result = Region.from(me).translateBy(me.getAlignToVector(targetPlusAnchorOffset, align));
                
                overlap = !!result.intersect(targetPlusAnchorOffset);
                if (offset && (overlap || !anchorSize)) {
                    result.translateBy(offset);
                }
                
                
                if (anchorSize) {
                    calculateAnchorPosition(target, result, align.position, anchorSize, inside);
                }
            }
            
            if (inside) {
                initialPosition = result.copy();
                
                if (result.left < inside.left) {
                    result.translateBy(inside.left - result.left, 0);
                    wasConstrained = true;
                }
                
                if (result.right > inside.right && result.left > inside.left) {
                    result.translateBy(inside.right - result.right, 0);
                    wasConstrained = true;
                }
                
                if (result.top < inside.top) {
                    result.translateBy(0, inside.top - result.top);
                    wasConstrained = true;
                }
                
                if (result.bottom > inside.bottom && result.top > inside.top) {
                    result.translateBy(0, inside.bottom - result.bottom);
                    wasConstrained = true;
                }
                
                
                if (wasConstrained && !overlap) {
                    
                    result.anchor = null;
                    
                    
                    
                    
                    
                    
                    
                    if (options.axisLock) {
                        if (align.position & 1) {
                            allowYTranslate = false;
                        } else {
                            allowXTranslate = false;
                        }
                    }
                    
                    if (position) {
                        if (result.contains(position)) {
                            position.exclude(result, {
                                inside: inside,
                                centerOnSideChange: false
                            });
                        }
                    } else 
                    {
                        constrainedPosition = result.copy();
                        if (result.intersect(targetPlusAnchorOffset)) {
                            
                            
                            align.position = target.exclude(result, {
                                initialPosition: initialPosition,
                                defaultPosition: align.position,
                                inside: inside,
                                minHeight: options.minHeight,
                                minWidth: options.minWidth,
                                allowX: allowXTranslate,
                                allowY: allowYTranslate,
                                offset: offset,
                                anchorHeight: anchorSize ? anchorSize.y : 0,
                                centerOnSideChange: !!anchorSize
                            });
                        } else if (options.minWidth && result.getWidth() > inside.getWidth()) {
                            result.setPosition(0, result.y);
                            result.setWidth(Math.max(inside.getWidth(), options.minWidth));
                            result.constrainWidth = true;
                        } else if (options.minHeight && result.getHeight() > inside.getHeight()) {
                            result.setPosition(result.x, 0);
                            result.setHeight(Math.max(inside.getHeight(), options.minHeight));
                            result.constrainHeight = true;
                        }
                        result.align = align;
                        if (inside.contains(result)) {
                            
                            
                            if (anchorSize) {
                                calculateAnchorPosition(target, result, align.position, anchorSize, inside);
                            }
                        } else 
                        
                        
                        
                        {
                            result = constrainedPosition;
                        }
                    }
                }
            }
            return result;
        },
        
        exclude: function(other, options) {
            options = options || {};
            var me = this,
                initialPosition = options.initialPosition || other,
                inside = options.inside,
                defaultPosition = options.defaultPosition,
                centerOnSideChange = options.centerOnSideChange,
                minHeight = options.minHeight,
                minWidth = options.minWidth,
                allowX = options.allowX !== false,
                allowY = options.allowY !== false,
                anchorHeight = options.anchorHeight,
                offset = options.offset,
                translations = [],
                testRegion, t, i, sizeConstrainedSolution, leastBadSolution, intersection, result;
            
            if (!offset) {
                offset = zeroOffset;
            }
            
            
            
            if (allowY) {
                translations.push([
                    0,
                    me.top - other.bottom - anchorHeight + offset.y,
                    'b-t',
                    0,
                    Math.abs(me.top - initialPosition.bottom - anchorHeight + offset.y)
                ]);
                translations.push([
                    0,
                    me.bottom - other.top + anchorHeight + offset.y,
                    't-b',
                    2,
                    Math.abs(me.bottom - initialPosition.top + anchorHeight + offset.y)
                ]);
            } else {
                centerOnSideChange = false;
            }
            if (allowX) {
                translations.push([
                    me.left - other.right - anchorHeight + offset.x,
                    0,
                    'r-l',
                    3,
                    Math.abs(me.left - initialPosition.right - anchorHeight + offset.x)
                ]);
                translations.push([
                    me.right - other.left + anchorHeight + offset.x,
                    0,
                    'l-r',
                    1,
                    Math.abs(me.right - initialPosition.left + anchorHeight + offset.x)
                ]);
            } else {
                centerOnSideChange = false;
            }
            
            Ext.Array.sort(translations, function(l, r) {
                var result = l[4] - r[4];
                
                if (!result) {
                    if (l[3] === defaultPosition) {
                        return -1;
                    }
                    if (r[3] === defaultPosition) {
                        return 1;
                    }
                }
                return result;
            });
            
            
            if (inside) {
                for (i = 0; i < translations.length; i++) {
                    t = translations[i];
                    testRegion = ExtUtil.Region.from(other);
                    testRegion.translateBy.apply(testRegion, t);
                    
                    if (inside.contains(testRegion)) {
                        other.copyFrom(testRegion);
                        result = {
                            align: t[2],
                            position: t[3],
                            distance: t[4]
                        };
                        break;
                    }
                    
                    
                    
                    if (minHeight) {
                        checkMinHeight(minHeight, testRegion, me, inside);
                        if (inside.contains(testRegion)) {
                            if (!sizeConstrainedSolution || testRegion.getArea() > sizeConstrainedSolution.region.getArea()) {
                                sizeConstrainedSolution = {
                                    region: testRegion,
                                    align: t[2],
                                    position: t[3],
                                    distance: t[4]
                                };
                            }
                        }
                    }
                    if (minWidth) {
                        checkMinWidth(minWidth, testRegion, me, inside);
                        if (inside.contains(testRegion)) {
                            if (!sizeConstrainedSolution || testRegion.getArea() > sizeConstrainedSolution.region.getArea()) {
                                sizeConstrainedSolution = {
                                    region: testRegion,
                                    align: t[2],
                                    position: t[3],
                                    distance: t[4]
                                };
                            }
                        }
                    }
                    
                    
                    
                    intersection = inside.intersect(testRegion);
                    if (intersection) {
                        intersection = intersection.getArea();
                        if (!leastBadSolution || (intersection && leastBadSolution.area < intersection)) {
                            leastBadSolution = {
                                region: testRegion,
                                align: t[2],
                                position: t[3],
                                distance: t[4],
                                area: intersection
                            };
                        }
                    }
                }
                if (!result) {
                    
                    if (sizeConstrainedSolution) {
                        other.copyFrom(sizeConstrainedSolution.region);
                        result = sizeConstrainedSolution;
                        other.constrainWidth = sizeConstrainedSolution.region.constrainWidth;
                        other.constrainHeight = sizeConstrainedSolution.region.constrainHeight;
                    }
                    
                    else if (leastBadSolution) {
                        other.copyFrom(leastBadSolution.region);
                        result = leastBadSolution;
                    }
                }
                if (result) {
                    
                    
                    if ((result.position & 1) !== (defaultPosition & 1)) {
                        if (result.distance && centerOnSideChange) {
                            t = other.alignTo({
                                align: result.align,
                                target: me,
                                anchorSize: anchorHeight,
                                offset: offset,
                                axisLock: true,
                                inside: inside,
                                minHeight: options.minHeight,
                                minWidth: options.minWidth
                            });
                            if (inside.contains(t)) {
                                other.setPosition(t.x, t.y);
                            }
                        }
                    }
                    return result.position;
                }
            } else 
            {
                
                other.translateBy.apply(other, translations[0]);
                return translations[0][3];
            }
            return defaultPosition;
        },
        getAlignToXY: function(target, align, rtl) {
            var alignVector = this.getAlignToVector(target, align, rtl);
            return [
                this.x + alignVector[0],
                this.y + alignVector[1]
            ];
        },
        getAnchorPoint: function(align, rtl) {
            align = (typeof align === 'string') ? this.getAlignInfo(align + '-tl', rtl) : align;
            return this['getAnchorPoint_' + align.myEdge](align.myOffset);
        },
        getAlignToVector: function(target, align, rtl) {
            align = (typeof align === 'string') ? this.getAlignInfo(align, rtl) : align;
            var myAnchorPoint = this['getAnchorPoint_' + align.myEdge](align.myOffset),
                targetAnchorPoint = target['getAnchorPoint_' + align.otherEdge](align.otherOffset);
            return [
                targetAnchorPoint[0] - myAnchorPoint[0],
                targetAnchorPoint[1] - myAnchorPoint[1]
            ];
        },
        getAnchorPoint_t: function(offset) {
            return [
                this.x + Math.round(this.getWidth() * (offset / 100)),
                this.y
            ];
        },
        getAnchorPoint_b: function(offset) {
            return [
                this.x + Math.round(this.getWidth() * (offset / 100)),
                this.bottom
            ];
        },
        getAnchorPoint_l: function(offset) {
            return [
                this.x,
                this.y + Math.round(this.getHeight() * (offset / 100))
            ];
        },
        getAnchorPoint_r: function(offset) {
            return [
                this.right,
                this.y + Math.round(this.getHeight() * (offset / 100))
            ];
        },
        getAnchorPoint_c: function() {
            return [
                this.x + Math.round(this.getWidth() / 2),
                this.y + Math.round(this.getHeight() / 2)
            ];
        },
        getCenter: function() {
            return [
                this.x + this.width / 2,
                this.y + this.height / 2
            ];
        },
        getHeight: function() {
            return this.bottom - this.y;
        },
        getWidth: function() {
            return this.right - this.x;
        },
        getArea: function() {
            return this.getHeight() * this.getWidth();
        },
        setHeight: function(h) {
            this.height = h;
            this.bottom = this.top + h;
            return this;
        },
        setWidth: function(w) {
            this.width = w;
            this.right = this.left + w;
            return this;
        },
        
        getSize: function() {
            return {
                width: this.right - this.x,
                height: this.bottom - this.y
            };
        },
        setSize: function(w, h) {
            if (h === undefined) {
                h = w;
            }
            this.setWidth(w);
            return this.setHeight(h);
        },
        
        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 me;
        },
        
        toString: function() {
            return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
        },
        
        translateBy: function(x, y) {
            if (x.length) {
                y = x[1];
                x = x[0];
            } else 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);
        },
        
        getOffsetsTo: function(offsetsTo) {
            return {
                x: this.x - offsetsTo.x,
                y: this.y - offsetsTo.y
            };
        },
        
        highlight: function() {
            var highlightEl = Ext.getBody().createChild({
                    style: 'background-color:#52a0db;opacity:0.4;position:absolute;z-index:9999999'
                });
            highlightEl.setBox(this);
            Ext.defer(function() {
                highlightEl.destroy();
            }, 5000);
            return highlightEl;
        }
    };
}, 
function(Region) {
    Region.prototype.getAlignInfo = Region.getAlignInfo;
    Region.EMPTY = new Region(0, 0, 0, 0);
    
    if (Object.freeze) {
        Object.freeze(Region.EMPTY);
    }
});



Ext.define('Ext.util.Point', {
    extend: Ext.util.Region,
    isPoint: true,
    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 + "]";
    },
    
    equals: function(point) {
        return (this.x === point.x && this.y === point.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',
    
    
    
    
    
    
    
    
    
    
    
    
    
    stopped: false,
    
    claimed: false,
    
    defaultPrevented: false,
    isEvent: true,
    
    geckoRelatedTargetEvents: {
        blur: 1,
        dragenter: 1,
        dragleave: 1,
        focus: 1
    },
    statics: {
        resolveTextNode: function(node) {
            return (node && node.nodeType === 3) ? node.parentNode : node;
        },
        
        gestureEvents: {},
        
        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,
            focusin: 1,
            focusenter: 1
        },
        
        blurEvents: {
            blur: 1,
            focusout: 1,
            focusleave: 1
        },
        
        
        
        
        pointerTypeMap: {
            2: 'touch',
            3: 'pen',
            4: 'mouse',
            touch: 'touch',
            pen: 'pen',
            mouse: 'mouse'
        },
        keyEventRe: /^key/,
        keyFlags: {
            CTRL: 'ctrlKey',
            CONTROL: 'ctrlKey',
            ALT: 'altKey',
            SHIFT: 'shiftKey',
            CMD: 'metaKey',
            COMMAND: 'metaKey',
            CMDORCTRL: Ext.isMac ? 'metaKey' : 'ctrlKey',
            COMMANDORCONTROL: Ext.isMac ? 'metaKey' : 'ctrlKey',
            META: 'metaKey'
        },
        modifierGlyphs: {
            ctrlKey: '⌃',
            altKey: '⌥',
            metaKey: Ext.isMac ? '⌘' : '⊞',
            shiftKey: '⇧'
        },
        specialKeyGlyphs: {
            BACKSPACE: '⌫',
            TAB: '⇥',
            ENTER: '⏎',
            RETURN: '⏎',
            SPACE: '␣',
            PAGE_UP: '⇞',
            PAGE_DOWN: '⇟',
            END: '⇲',
            HOME: '⌂',
            LEFT: '←',
            UP: '↑',
            RIGHT: '→',
            DOWN: '↓',
            PRINT_SCREEN: '⎙',
            INSERT: '⎀',
            DELETE: '⌦',
            CONTEXT_MENU: '☰'
        },
        _hyphenRe: /^[a-z]+\-/i,
        
        
        getKeyId: function(keyName) {
            
            if (typeof keyName === 'number') {
                keyName = this.keyCodes[keyName];
            } else {
                keyName = keyName.toUpperCase();
            }
            var me = this,
                delim = me._hyphenRe.test(keyName) ? '-' : '+',
                parts = (keyName === delim) ? [
                    delim
                ] : keyName.split(delim),
                numModifiers = parts.length - 1,
                rawKey = parts[numModifiers],
                result = [],
                eventFlag, i;
            
            if (!Ext.event.Event[rawKey]) {
                Ext.raise('Invalid key name: "' + rawKey + '"');
            }
            
            for (i = 0; i < numModifiers; i++) {
                eventFlag = me.keyFlags[parts[i]];
                
                if (!eventFlag) {
                    Ext.raise('Invalid key modifier: "' + parts[i] + '"');
                }
                
                result[eventFlag] = true;
            }
            if (result.ctrlKey) {
                result.push(me.modifierGlyphs.ctrlKey);
            }
            if (result.altKey) {
                result.push(me.modifierGlyphs.altKey);
            }
            if (result.shiftKey) {
                result.push(me.modifierGlyphs.shiftKey);
            }
            if (result.metaKey) {
                result.push(me.modifierGlyphs.metaKey);
            }
            result.push(this.specialKeyGlyphs[rawKey] || rawKey);
            return result.join('');
        },
        
        globalTabKeyDown: function(e) {
            if (e.keyCode === 9) {
                Ext.event.Event.forwardTab = !e.shiftKey;
            }
        },
        
        globalTabKeyUp: function(e) {
            if (e.keyCode === 9) {
                delete Ext.event.Event.forwardTab;
            }
        }
    },
    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.timeStamp = me.time = Ext.now();
        me.pageX = coordinateOwner.pageX;
        me.pageY = coordinateOwner.pageY;
        me.clientX = coordinateOwner.clientX;
        me.clientY = coordinateOwner.clientY;
        me.target = me.delegatedTarget = resolveTextNode(event.target);
        me.currentTarget = resolveTextNode(event.currentTarget);
        relatedTarget = event.relatedTarget;
        if (relatedTarget) {
            if (Ext.isGecko && me.geckoRelatedTargetEvents[type]) {
                try {
                    me.relatedTarget = resolveTextNode(relatedTarget);
                } catch (e) {
                    me.relatedTarget = null;
                }
            } else {
                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;
        me.buttons = event.buttons;
        
        
        
        
        
        if (me.button === 0 && me.buttons === 0) {
            me.buttons = 1;
        }
        if (self.focusEvents[type] || self.blurEvents[type]) {
            if (self.forwardTab !== undefined) {
                me.forwardTab = self.forwardTab;
            }
            if (self.focusEvents[type]) {
                me.fromElement = event.relatedTarget;
                me.toElement = event.target;
            } else {
                me.fromElement = event.target;
                me.toElement = event.relatedTarget;
            }
        } else if (type !== 'keydown') {
            
            
            
            delete self.forwardTab;
        }
        if (self.mouseEvents[type]) {
            pointerType = 'mouse';
        } else if (self.clickEvents[type]) {
            
            
            
            
            pointerType = self.pointerTypeMap[event.pointerType] || (((Ext.now() - Ext.event.publisher.Dom.lastTouchEndTime) < 1000) ? 'touch' : 'mouse');
        } else if (self.pointerEvents[type]) {
            
            
            pointerType = self.pointerTypeMap[event.pointerType] || 'mouse';
        } else if (self.touchEvents[type]) {
            pointerType = 'touch';
        }
        if (pointerType) {
            me.pointerType = pointerType;
        }
        
        
        me.isMultitouch = event.isPrimary === false || (event.touches && event.touches.length > 1);
    },
    
    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;
    },
    getChar: function() {
        var r = this.which();
        return String.fromCharCode(r);
    },
    
    getCharCode: function() {
        return this.charCode || this.keyCode;
    },
    
    getKey: function() {
        return this.keyCode || this.charCode;
    },
    
    getKeyName: function() {
        return this.type === 'keypress' ? String.fromCharCode(this.getCharCode()) : this.keyCodes[this.keyCode];
    },
    
    key: function() {
        return this.browserEvent.key;
    },
    which: function() {
        var me = this,
            e = me.browserEvent,
            r = e.which;
        if (r == null) {
            if (me.self.keyEventRe.test(e.type)) {
                r = e.charCode || e.keyCode;
            } else if ((r = e.button) !== undefined) {
                
                r = (r & 1) ? 1 : ((r & 4) ? 2 : ((r & 2) ? 3 : 0));
            }
        }
        return r;
    },
    
    getClipboardData: function(type) {
        var clipboardData = this.browserEvent.clipboardData,
            clipIE = Ext.global.clipboardData,
            
            result = null,
            typeIE;
        type = type || 'text/plain';
        if (clipboardData && clipboardData.getData) {
            result = clipboardData.getData(type);
        } else if (clipIE && clipIE.getData) {
            typeIE = this.ieMimeType[type];
            if (typeIE) {
                result = clipIE.getData(typeIE);
            }
        }
        return result;
    },
    
    getPoint: function() {
        var me = this,
            point = me.point,
            xy;
        if (!point) {
            xy = me.getXY();
            point = me.point = new Ext.util.Point(xy[0], xy[1]);
        }
        return point;
    },
    
    getRelatedTarget: function(selector, maxDepth, returnEl) {
        var relatedTarget = this.relatedTarget,
            target = null;
        
        
        
        if (relatedTarget && relatedTarget.nodeType) {
            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 ('deltaX' in event) {
            
            dx = event.deltaX;
            dy = -event.deltaY;
        }
        
        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,
            isKeyPress = me.type === 'keypress';
        
        return ((!isKeyPress || Ext.isGecko) && k >= 33 && k <= 40) || (
        !scrollableOnly && (k === me.RETURN || k === me.TAB || k === me.ESC));
    },
    
    isSpecialKey: function() {
        var me = this,
            k = me.keyCode,
            isGecko = Ext.isGecko,
            isKeyPress = me.type === 'keypress';
        
        return (isGecko && isKeyPress && me.charCode === 0) || (this.isNavKeyPress()) || (k === me.BACKSPACE) || (k === me.ENTER) || (k >= 16 && k <= 20) || (
        (!isKeyPress || isGecko) && k >= 44 && k <= 46);
    },
    
    makeUnpreventable: function() {
        this.browserEvent.preventDefault = Ext.emptyFn;
    },
    
    preventDefault: function() {
        var me = this,
            parentEvent = me.parentEvent;
        me.defaultPrevented = true;
        
        
        if (parentEvent) {
            parentEvent.defaultPrevented = true;
        }
        me.browserEvent.preventDefault();
        return me;
    },
    setCurrentTarget: function(target) {
        this.currentTarget = this.delegatedTarget = target;
    },
    
    stopEvent: function() {
        return this.preventDefault().stopPropagation();
    },
    
    mousedownEvents: {
        mousedown: 1,
        pointerdown: 1,
        touchstart: 1
    },
    
    mouseupEvents: {
        mouseup: 1,
        pointerup: 1,
        touchend: 1
    },
    
    stopPropagation: function() {
        var me = this,
            browserEvent = me.browserEvent,
            parentEvent = me.parentEvent;
        
        
        if (me.mousedownEvents[me.type]) {
            Ext.GlobalEvents.fireMouseDown(me);
        }
        
        
        if (me.mouseupEvents[me.type]) {
            Ext.GlobalEvents.fireMouseUp(me);
        }
        
        
        me.stopped = true;
        
        
        
        
        
        
        
        
        
        
        if (parentEvent && !me.isGesture) {
            parentEvent.stopped = true;
        }
        
        if (!browserEvent.stopPropagation) {
            
            browserEvent.cancelBubble = true;
            return me;
        }
        
        
        
        browserEvent.stopPropagation();
        return me;
    },
    
    claimGesture: function() {
        var me = this,
            parentEvent = me.parentEvent;
        me.claimed = true;
        if (parentEvent && !me.isGesture) {
            parentEvent.claimGesture();
        } else {
            
            
            
            
            me.preventDefault();
        }
        return me;
    },
    
    within: function(el, related, allowEl) {
        var t;
        if (el) {
            t = related ? this.getRelatedTarget() : this.getTarget();
        }
        if (!t || (allowEl === false && t === Ext.getDom(el))) {
            return false;
        }
        return Ext.fly(el).contains(t);
    },
    privates: {
        ieMimeType: {
            "text/plain": 'Text'
        }
    },
    deprecated: {
        '4.0': {
            methods: {
                
                getPageX: 'getX',
                
                getPageY: 'getY'
            }
        }
    }
}, function(Event) {
    var 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,
            
            META: 91,
            
            
            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: 120
        },
        
        keyCodes = {},
        gestureEvents = Event.gestureEvents,
        prototype = Event.prototype,
        i, keyName, keyCode, keys, s, scale;
    if (Ext.isGecko) {
        
        constants.WHEEL_SCALE = 3;
    } else if (Ext.isMac) {
        
        
        
        if (Ext.isSafari && Ext.webKitVersion >= 532) {
            
            
            
            
            
            
            scale = 120;
        } else {
            
            
            scale = 12;
        }
        
        
        
        
        constants.WHEEL_SCALE = 3 * scale;
    }
    Ext.apply(gestureEvents, Event.mouseEvents);
    Ext.apply(gestureEvents, Event.pointerEvents);
    Ext.apply(gestureEvents, Event.touchEvents);
    Ext.apply(Event, constants);
    Ext.apply(prototype, constants);
    
    delete constants.WHEEL_SCALE;
    
    
    
    delete constants.RETURN;
    
    for (keyName in constants) {
        keyCode = constants[keyName];
        keyCodes[keyCode] = keyName;
    }
    Event.keyCodes = prototype.keyCodes = keyCodes;
    
    if (!Ext.isIE9m) {
        document.addEventListener('keydown', Event.globalTabKeyDown, true);
        document.addEventListener('keyup', Event.globalTabKeyUp, true);
    }
    
    prototype.getTrueXY = prototype.getXY;
    if (typeof KeyboardEvent !== 'undefined' && !('key' in KeyboardEvent.prototype)) {
        prototype._keys = keys = {
            3: 'Cancel',
            6: 'Help',
            8: 'Backspace',
            9: 'Tab',
            12: 'Clear',
            13: 'Enter',
            16: 'Shift',
            17: 'Control',
            18: 'Alt',
            19: 'Pause',
            20: 'CapsLock',
            27: 'Escape',
            28: 'Convert',
            29: 'NonConvert',
            30: 'Accept',
            31: 'ModeChange',
            32: ' ',
            33: 'PageUp',
            34: 'PageDown',
            35: 'End',
            36: 'Home',
            37: 'ArrowLeft',
            38: 'ArrowUp',
            39: 'ArrowRight',
            40: 'ArrowDown',
            41: 'Select',
            42: 'Print',
            43: 'Execute',
            44: 'PrintScreen',
            45: 'Insert',
            46: 'Delete',
            48: [
                '0',
                ')'
            ],
            49: [
                '1',
                '!'
            ],
            50: [
                '2',
                '@'
            ],
            51: [
                '3',
                '#'
            ],
            52: [
                '4',
                '$'
            ],
            53: [
                '5',
                '%'
            ],
            54: [
                '6',
                '^'
            ],
            55: [
                '7',
                '&'
            ],
            56: [
                '8',
                '*'
            ],
            57: [
                '9',
                '('
            ],
            91: 'OS',
            93: 'ContextMenu',
            144: 'NumLock',
            145: 'ScrollLock',
            181: 'VolumeMute',
            182: 'VolumeDown',
            183: 'VolumeUp',
            186: [
                ';',
                ':'
            ],
            187: [
                '=',
                '+'
            ],
            188: [
                ',',
                '<'
            ],
            189: [
                '-',
                '_'
            ],
            190: [
                '.',
                '>'
            ],
            191: [
                '/',
                '?'
            ],
            192: [
                '`',
                '~'
            ],
            219: [
                '[',
                '{'
            ],
            220: [
                '\\',
                '|'
            ],
            221: [
                ']',
                '}'
            ],
            222: [
                "'",
                '"'
            ],
            224: 'Meta',
            225: 'AltGraph',
            246: 'Attn',
            247: 'CrSel',
            248: 'ExSel',
            249: 'EraseEof',
            250: 'Play',
            251: 'ZoomOut'
        };
        for (i = 1; i < 25; ++i) {
            keys[i + 111] = 'F' + i;
        }
        
        for (i = 0; i < 26; ++i) {
            
            keys[i] = [
                String.fromCharCode(i + 97),
                String.fromCharCode(i + 65)
            ];
        }
        prototype.key = function() {
            var k = keys[this.browserEvent.which || this.keyCode];
            if (k && typeof k !== 'string') {
                k = k[+this.shiftKey];
            }
            return k;
        };
    }
});


Ext.define('Ext.event.publisher.Dom', {
    extend: Ext.event.publisher.Publisher,
    type: 'dom',
    
    handledDomEvents: [],
    reEnterCount: 0,
    
    
    
    captureEvents: {
        animationstart: 1,
        animationend: 1,
        resize: 1,
        focus: 1,
        blur: 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,
        
        
        
        scroll: 1,
        online: 1,
        offline: 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,
            supportsPassive = Ext.supports.PassiveEventListener;
        me.listenerOptions = supportsPassive ? {
            passive: false
        } : false;
        me.captureOptions = supportsPassive ? {
            passive: false,
            capture: true
        } : true;
        me.bubbleSubscribers = {};
        me.captureSubscribers = {};
        me.directSubscribers = {};
        me.directCaptureSubscribers = {};
        
        
        
        me.delegatedListeners = {};
        me.initHandlers();
        Ext.onInternalReady(me.onReady, me);
        me.callParent();
        me.registerDomEvents();
    },
    registerDomEvents: function() {
        var me = this,
            publishersByEvent = Ext.event.publisher.Publisher.publishersByEvent,
            domEvents = me.handledDomEvents,
            ln = domEvents.length,
            i, eventName;
        for (i = 0; i < ln; i++) {
            eventName = domEvents[i];
            me.handles[eventName] = 1;
            publishersByEvent[eventName] = me;
        }
    },
    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, {
            priority: -10000
        });
    },
    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) {
        var me = this;
        me.delegatedListeners[eventName] = 1;
        me.target.addEventListener(eventName, me.onDelegatedEvent, me.captureEvents[eventName] ? me.captureOptions : me.listenerOptions);
    },
    removeDelegatedListener: function(eventName) {
        var me = this;
        delete me.delegatedListeners[eventName];
        me.target.removeEventListener(eventName, me.onDelegatedEvent, me.captureEvents[eventName] ? me.captureOptions : me.listenerOptions);
    },
    addDirectListener: function(eventName, element, capture) {
        var me = this;
        element.dom.addEventListener(eventName, capture ? me.onDirectCaptureEvent : me.onDirectEvent, capture ? me.captureOptions : me.listenerOptions);
    },
    removeDirectListener: function(eventName, element, capture) {
        var me = this;
        element.dom.removeEventListener(eventName, capture ? me.onDirectCaptureEvent : me.onDirectEvent, capture ? me.captureOptions : me.listenerOptions);
    },
    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(e, targets, claimed) {
        var me = this,
            hasCaptureSubscribers = false,
            hasBubbleSubscribers = false,
            events, type, target, el, i, ln, j, eLn;
        claimed = claimed || false;
        
        
        if (!targets) {
            
            if (e instanceof Array) {
                Ext.raise("Propagation targets must be supplied when publishing an array of events.");
            }
            
            
            target = e.target;
            if (me.captureEvents[e.type]) {
                el = Ext.cache[target.id];
                targets = el ? [
                    el
                ] : [];
            } else {
                targets = me.getPropagatingTargets(target);
            }
        }
        
        
        
        events = Ext.Array.from(e);
        ln = targets.length;
        eLn = events.length;
        for (i = 0; i < eLn; i++) {
            type = events[i].type;
            if (!hasCaptureSubscribers && me.captureSubscribers[type]) {
                hasCaptureSubscribers = true;
            }
            if (!hasBubbleSubscribers && me.bubbleSubscribers[type]) {
                hasBubbleSubscribers = true;
            }
        }
        
        
        
        
        
        
        
        
        if (hasCaptureSubscribers) {
            for (i = ln; i--; ) {
                el = Ext.cache[targets[i].id];
                if (el) {
                    for (j = 0; j < eLn; j++) {
                        e = events[j];
                        me.fire(el, e.type, e, false, true);
                        if (!claimed && e.claimed) {
                            claimed = true;
                            j = me.filterClaimed(events, e);
                            eLn = events.length;
                        }
                        
                        if (e.stopped) {
                            events.splice(j, 1);
                            j--;
                            eLn--;
                        }
                    }
                }
            }
        }
        
        
        if (hasBubbleSubscribers && !e.stopped) {
            for (i = 0; i < ln; i++) {
                el = Ext.cache[targets[i].id];
                if (el) {
                    for (j = 0; j < eLn; j++) {
                        e = events[j];
                        me.fire(el, e.type, e, false, false);
                        if (!claimed && e.claimed && me.filterClaimed) {
                            claimed = true;
                            j = me.filterClaimed(events, e);
                            eLn = events.length;
                        }
                        
                        if (e.stopped) {
                            events.splice(j, 1);
                            j--;
                            eLn--;
                        }
                    }
                }
            }
        }
    },
    
    publishDelegatedDomEvent: function(e) {
        this.publish(e);
    },
    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) {
        var me = this,
            timeStamp;
        e = new Ext.event.Event(e);
        timeStamp = e.time;
        if (!me.isEventBlocked(e)) {
            me.beforeEvent(e);
            Ext.frameStartTime = timeStamp;
            me.reEnterCountAdjusted = false;
            me.reEnterCount++;
            me.publishDelegatedDomEvent(e);
            
            if (!me.reEnterCountAdjusted) {
                me.reEnterCount--;
            }
            me.afterEvent(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, el;
        e = new Ext.event.Event(e);
        timeStamp = e.time;
        if (me.isEventBlocked(e)) {
            return;
        }
        me.beforeEvent(e);
        Ext.frameStartTime = timeStamp;
        el = Ext.cache[currentTarget.id];
        
        
        
        
        if (el) {
            
            
            me.reEnterCountAdjusted = false;
            me.reEnterCount++;
            me.fire(el, e.type, e, true, capture);
            
            if (!me.reEnterCountAdjusted) {
                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.idleEventMask[type]) {
            Ext.fireIdle();
        }
    },
    
    isEventBlocked: function(e) {
        var me = this,
            type = e.type,
            
            
            self = Ext.event.publisher.Dom,
            now = Ext.now();
        
        
        
        
        
        if (Ext.isGecko && e.type === 'click' && e.button === 2) {
            return true;
        }
        
        
        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 GC = Ext.dom['GarbageCollector'],
            eventName;
        for (eventName in this.delegatedListeners) {
            this.removeDelegatedListener(eventName);
        }
        
        
        Ext.Reaper.flush();
        if (GC) {
            GC.collect();
        }
        this.callParent();
    },
    
    reset: function() {
        
        
        var self = Ext.event.publisher.Dom;
        this.reEnterCount = 0;
        
        
        
        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.event.publisher.Gesture', {
    extend: Ext.event.publisher.Dom,
    type: 'gesture',
    isCancelEvent: {
        touchcancel: 1,
        pointercancel: 1,
        MSPointerCancel: 1
    },
    isEndEvent: {
        mouseup: 1,
        touchend: 1,
        pointerup: 1,
        MSPointerUp: 1
    },
    handledEvents: [],
    handledDomEvents: [],
    constructor: function(config) {
        var me = this,
            handledDomEvents = me.handledDomEvents,
            supports = Ext.supports,
            supportsTouchEvents = supports.TouchEvents,
            onTouchStart = me.onTouchStart,
            onTouchMove = me.onTouchMove,
            onTouchEnd = me.onTouchEnd;
        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.activeTouchesMap = {};
        me.activeTouches = [];
        me.changedTouches = [];
        me.recognizers = [];
        me.eventToRecognizer = {};
        me.cancelEvents = [];
        if (supportsTouchEvents) {
            
            me.onTargetTouchMove = me.onTargetTouchMove.bind(me);
            me.onTargetTouchEnd = me.onTargetTouchEnd.bind(me);
        }
        if (supports.PointerEvents) {
            handledDomEvents.push('pointerdown', 'pointermove', 'pointerup', 'pointercancel');
            me.mousePointerType = 'mouse';
        } else if (supports.MSPointerEvents) {
            
            handledDomEvents.push('MSPointerDown', 'MSPointerMove', 'MSPointerUp', 'MSPointerCancel');
            me.mousePointerType = 4;
        } else if (supportsTouchEvents) {
            handledDomEvents.push('touchstart', 'touchmove', 'touchend', 'touchcancel');
        }
        if (!handledDomEvents.length || (supportsTouchEvents && 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,
            eventName, i;
        
        
        recognizer.setOnRecognized(me.onRecognized);
        recognizer.setCallbackScope(me);
        
        
        for (i = 0; i < ln; i++) {
            eventName = handledEvents[i];
            me.handledEvents.push(eventName);
            me.eventToRecognizer[eventName] = recognizer;
        }
        me.registerEvents(handledEvents);
        me.recognizers.push(recognizer);
    },
    onRecognized: function(recognizer, eventName, e, info, isCancel) {
        var me = this,
            touches = e.touches,
            changedTouches = e.changedTouches,
            ln = changedTouches.length,
            events = me.events,
            queueWasEmpty = !events.length,
            cancelEvents = me.cancelEvents,
            targetGroups, targets, i, touch;
        info = info || {};
        
        
        
        
        
        
        info.type = eventName;
        
        
        
        
        
        
        
        
        
        
        info.target = changedTouches[0].target;
        
        
        info.stopped = false;
        info.claimed = false;
        info.isGesture = true;
        e = e.chain(info);
        if (!me.gestureTargets) {
            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.gestureTargets = targets;
        }
        if (isCancel && recognizer.isSingleTouch && (touches.length > 1)) {
            
            
            
            
            e.target = touches[0].target;
            cancelEvents.push(e);
        } else {
            events.push(e);
        }
        if (queueWasEmpty) {
            
            
            
            
            
            me.publishGestures();
        }
    },
    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;
            }
        }
    },
    
    filterClaimed: function(events, claimedEvent) {
        var me = this,
            eventToRecognizer = me.eventToRecognizer,
            claimedEventType = claimedEvent.type,
            claimedRecognizer = eventToRecognizer[claimedEventType],
            claimedEventIndex, recognizer, type, i;
        for (i = events.length; i--; ) {
            type = events[i].type;
            if (type === claimedEventType) {
                claimedEventIndex = i;
            } else {
                recognizer = eventToRecognizer[type];
                
                
                
                
                if (!claimedRecognizer || (recognizer && (recognizer !== claimedRecognizer))) {
                    events.splice(i, 1);
                    if (claimedEventIndex) {
                        claimedEventIndex--;
                    }
                }
            }
        }
        me.claimRecognizer(claimedRecognizer, events[0]);
        return claimedEventIndex;
    },
    
    claimRecognizer: function(claimedRecognizer, e) {
        var me = this,
            recognizers = me.recognizers,
            i, ln, recognizer;
        for (i = 0 , ln = recognizers.length; i < ln; i++) {
            recognizer = recognizers[i];
            
            if (recognizer !== claimedRecognizer) {
                recognizer.isActive = false;
                recognizer.cancel(e);
            }
        }
        if (me.events.length) {
            
            me.publishGestures(true);
        }
    },
    publishGestures: function(claimed) {
        var me = this,
            cancelEvents = me.cancelEvents,
            events = me.events,
            gestureTargets = me.gestureTargets;
        if (cancelEvents.length) {
            me.cancelEvents = [];
            
            
            me.publish(cancelEvents, me.getPropagatingTargets(cancelEvents[0].target), true);
        }
        if (events.length) {
            
            
            
            me.events = [];
            me.gestureTargets = null;
            me.publish(events, gestureTargets || me.getPropagatingTargets(events[0].target), claimed);
        }
    },
    updateTouches: function(e) {
        var me = this,
            browserEvent = e.browserEvent,
            type = e.type,
            
            
            
            
            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 (me.isEndEvent[type] || me.isCancelEvent[type]) {
                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;
    },
    publishDelegatedDomEvent: function(e) {
        var me = this;
        if (!e.button || e.button < 1) {
            
            
            
            
            
            me.events = [
                e
            ];
            
            
            
            
            e.browserEvent.$extHandled = true;
            me.handlers[e.type].call(me, e);
        } else {
            
            me.callParent([
                e
            ]);
        }
    },
    onTouchStart: function(e) {
        var me = this,
            target = e.target,
            touches = e.browserEvent.touches;
        if (e.browserEvent.type === 'touchstart') {
            
            
            
            
            
            
            target.addEventListener('touchmove', me.onTargetTouchMove);
            target.addEventListener('touchend', me.onTargetTouchEnd);
            target.addEventListener('touchcancel', me.onTargetTouchEnd);
        }
        
        
        
        if (touches && touches.length <= me.activeTouches.length) {
            me.removeGhostTouches(touches);
        }
        me.updateTouches(e);
        if (!me.isStarted) {
            
            
            
            if (Ext.enableGarbageCollector) {
                Ext.dom.GarbageCollector.pause();
            }
            
            
            me.isStarted = true;
            me.invokeRecognizers('onStart', e);
        }
        me.invokeRecognizers('onTouchStart', e);
        me.publishGestures();
    },
    onTouchMove: function(e) {
        var me = this,
            mousePointerType = me.mousePointerType,
            isStarted = me.isStarted;
        if (isStarted || (e.pointerType !== 'mouse')) {
            me.updateTouches(e);
        }
        if (isStarted) {
            
            
            
            if (mousePointerType && e.browserEvent.pointerType === mousePointerType && e.buttons === 0) {
                e.type = Ext.dom.Element.prototype.eventMap.touchend;
                e.button = 0;
                me.onTouchEnd(e);
                return;
            }
            if (e.changedTouches.length > 0) {
                me.invokeRecognizers('onTouchMove', e);
            }
        }
        me.publishGestures();
    },
    
    
    
    onTouchEnd: function(e) {
        var me = this,
            isStarted = me.isStarted,
            touchCount;
        if (isStarted || (e.pointerType !== 'mouse')) {
            me.updateTouches(e);
        }
        if (!isStarted) {
            me.publishGestures();
            return;
        }
        touchCount = me.activeTouches.length;
        
        
        
        
        
        try {
            me.invokeRecognizers(me.isCancelEvent[e.type] ? 'onTouchCancel' : 'onTouchEnd', e);
        } finally {
            
            try {
                if (!touchCount) {
                    
                    me.isStarted = false;
                    me.invokeRecognizers('onEnd', e);
                }
            } finally {
                
                try {
                    me.publishGestures();
                } finally {
                    if (!touchCount) {
                        
                        
                        if (Ext.enableGarbageCollector) {
                            Ext.dom.GarbageCollector.resume();
                        }
                    }
                    
                    me.reEnterCountAdjusted = true;
                    me.reEnterCount--;
                }
            }
        }
    },
    onTargetTouchMove: function(e) {
        if (Ext.elevateFunction) {
            
            
            Ext.elevateFunction(this.doTargetTouchMove, this, [
                e
            ]);
        } else {
            this.doTargetTouchMove(e);
        }
    },
    doTargetTouchMove: function(e) {
        var me = this;
        
        
        if (!Ext.getBody().contains(e.target)) {
            me.reEnterCountAdjusted = false;
            me.reEnterCount++;
            this.onTouchMove(new Ext.event.Event(e));
            if (!me.reEnterCountAdjusted) {
                me.reEnterCount--;
            }
        }
    },
    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.reEnterCountAdjusted = false;
            me.reEnterCount++;
            me.onTouchEnd(new Ext.event.Event(e));
            if (!me.reEnterCountAdjusted) {
                me.reEnterCount--;
            }
        }
    },
    
    reset: function() {
        var me = this,
            recognizers = me.recognizers,
            ln = recognizers.length,
            i, recognizer;
        me.activeTouchesMap = {};
        me.activeTouches = [];
        me.changedTouches = [];
        me.isStarted = false;
        me.gestureTargets = null;
        me.events = [];
        me.cancelEvents = [];
        for (i = 0; i < ln; i++) {
            recognizer = recognizers[i];
            recognizer.reset();
            recognizer.isActive = false;
        }
        this.callParent();
    },
    privates: {
        removeGhostTouches: function(touches) {
            var ids = {},
                len = touches.length,
                activeTouches = this.activeTouches,
                map = this.activeTouchesMap,
                i, id, touch;
            
            for (i = 0; i < len; ++i) {
                ids[touches[i].identifier] = true;
            }
            i = activeTouches.length;
            while (i--) {
                touch = activeTouches[i];
                id = touch.identifier;
                if (!touches[id]) {
                    Ext.Array.remove(activeTouches, touch);
                    delete map[id];
                }
            }
        }
    }
}, function(Gesture) {
    var EventProto = Event.prototype,
        stopPropagation = EventProto.stopPropagation;
    if (stopPropagation) {
        EventProto.stopPropagation = function() {
            var me = this,
                publisher = Gesture.instance,
                type = me.type,
                e;
            if (!me.$extHandled && publisher.handles[type]) {
                
                
                e = new Ext.event.Event(me);
                publisher.updateTouches(e);
                publisher.invokeRecognizers('onTouchCancel', e);
                publisher.reset();
                publisher.reEnterCountAdjusted = true;
            }
            stopPropagation.apply(me, arguments);
        };
    }
    Gesture.instance = Ext.$gesturePublisher = new Gesture();
});


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', {
    singleton: true,
    pending: false,
    mode: true,
    
    
    protectedReadQueue: [],
    protectedWriteQueue: [],
    
    readQueue: [],
    writeQueue: [],
    readRequestId: 0,
    writeRequestId: 0,
    timer: null,
    constructor: function() {
        var me = this;
        me.run = me.run.bind(me);
        
        
        
        
        
        me.runProtected = Ext.Function.bind(me.run, me, [
            me.protectedReadQueue,
            me.protectedWriteQueue,
            'runProtected'
        ]);
        me.runProtected.$skipTimerCheck = true;
        
        
        
        
        
        
        
        if (Ext.os.is.iOS) {
            
            me.watch.$skipTimerCheck = true;
            
            me.watchdogTimer = Ext.interval(this.watch, 500, this);
        }
    },
    requestRead: function(fn, scope, args) {
        var request = {
                id: ++this.readRequestId,
                fn: fn,
                scope: scope,
                args: args
            };
        
        if (arguments[3] === true) {
            this.protectedReadQueue.push(request);
            this.request(true, 'runProtected');
        } else {
            
            this.readQueue.push(request);
            this.request(true);
        }
        
        
        return request.id;
    },
    cancelRead: function(id) {
        this.cancelRequest(this.readQueue, id, true);
    },
    requestWrite: function(fn, scope, args) {
        var me = this,
            request = {
                id: ++me.writeRequestId,
                fn: fn,
                scope: scope,
                args: args
            };
        
        if (arguments[3] === true) {
            me.protectedWriteQueue.push(request);
            me.request(false, 'runProtected');
        } else {
            
            me.writeQueue.push(request);
            me.request(false);
        }
        
        
        return request.id;
    },
    cancelWrite: function(id) {
        this.cancelRequest(this.writeQueue, id, false);
    },
    request: function(mode, method) {
        var me = this;
        
        
        var oldMode = me.mode;
        
        if (!me.pending) {
            me.pendingTime = Date.now();
            me.pending = true;
            me.mode = mode;
            if (mode) {
                me.timer = Ext.defer(me[method] || me.run, 1);
            } else {
                me.timer = Ext.raf(me[method] || me.run);
            }
        }
        
        
        if (me.mode === mode && me.timer) {
            if (oldMode) {
                Ext.undefer(me.timer);
            } else {
                Ext.unraf(me.timer);
            }
            if (mode) {
                me.timer = Ext.defer(me[method] || me.run, 1);
            } else {
                me.timer = Ext.raf(me[method] || me.run);
            }
        }
    },
    
    cancelRequest: function(queue, id, mode) {
        for (var i = 0; i < queue.length; i++) {
            if (queue[i].id === id) {
                queue.splice(i, 1);
                break;
            }
        }
        if (!queue.length && this.mode === mode && this.timer) {
            Ext.undefer(this.timer);
        }
    },
    watch: function() {
        if (this.pending && Date.now() - this.pendingTime >= 500) {
            this.run();
        }
    },
    run: function(readQueue, writeQueue, method) {
        var me = this,
            mode = null,
            queue, tasks, task, fn, scope, args, i, len;
        readQueue = readQueue || me.readQueue;
        writeQueue = writeQueue || me.writeQueue;
        me.pending = false;
        me.pending = me.timer = false;
        if (me.mode) {
            queue = readQueue;
            if (writeQueue.length > 0) {
                mode = false;
            }
        } else {
            queue = writeQueue;
            if (readQueue.length > 0) {
                mode = true;
            }
        }
        tasks = queue.slice();
        queue.length = 0;
        for (i = 0 , len = tasks.length; i < len; i++) {
            task = tasks[i];
            fn = task.fn;
            scope = task.scope;
            args = task.args;
            if (scope && (scope.destroying || scope.destroyed)) {
                
                continue;
            }
            if (typeof fn === 'string') {
                fn = scope[fn];
            }
            if (args) {
                fn.apply(scope, args);
            } else {
                fn.call(scope);
            }
        }
        tasks.length = 0;
        if (mode !== null) {
            me.request(mode, method);
        }
    },
    clear: function() {
        var me = this,
            timer = me.timer;
        if (timer) {
            if (me.mode) {
                Ext.undefer(timer);
            } else {
                Ext.unraf(timer);
            }
        }
        me.readQueue.length = me.writeQueue.length = 0;
        me.pending = me.timer = false;
        me.mode = true;
    },
    
    privates: {
        flush: function() {
            var me = this,
                mode = me.mode;
            while (me.readQueue.length || me.writeQueue.length) {
                if (mode) {
                    Ext.undefer(me.timer);
                } else {
                    Ext.unraf(me.timer);
                }
                me.run();
            }
            me.mode = true;
        }
    }
});



Ext.define('Ext.util.sizemonitor.Abstract', {
    mixins: [
        Ext.mixin.Templatable
    ],
    config: {
        element: null,
        callback: Ext.emptyFn,
        scope: null,
        args: []
    },
    width: null,
    height: null,
    contentWidth: null,
    contentHeight: null,
    constructor: function(config) {
        var me = this;
        me.refresh = me.refresh.bind(me);
        me.info = {
            width: 0,
            height: 0,
            contentWidth: 0,
            contentHeight: 0,
            flag: 0
        };
        me.initElement();
        me.initConfig(config);
        me.bindListeners(true);
    },
    bindListeners: Ext.emptyFn,
    applyElement: function(element) {
        if (element) {
            return Ext.get(element);
        }
    },
    updateElement: function(element) {
        element.append(this.detectorsContainer, true);
        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.clientWidth;
    },
    getContentHeight: function() {
        return this.detectorsContainer.clientHeight;
    },
    refreshSize: function() {
        var element = this.getElement();
        if (!element || element.destroyed) {
            return false;
        }
        var me = this,
            size = element.measure(),
            width = size.width,
            height = size.height,
            contentWidth = me.getContentWidth(),
            contentHeight = me.getContentHeight(),
            currentContentWidth = me.contentWidth,
            currentContentHeight = me.contentHeight,
            info = me.info,
            resized = false,
            flag;
        me.width = width;
        me.height = height;
        me.contentWidth = contentWidth;
        me.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;
            me.getCallback().apply(me.getScope(), me.getArgs());
        }
        return resized;
    },
    refresh: function() {
        if (this.destroying || this.destroyed) {
            return;
        }
        this.refreshSize();
        
        
        
        
        
        
        
        
        Ext.TaskQueue.requestWrite('refreshMonitors', this);
    },
    destroy: function() {
        var me = this,
            element = me.getElement();
        me.bindListeners(false);
        if (element && !element.destroyed) {
            element.removeCls(Ext.baseCSSPrefix + 'size-monitored');
        }
        delete me._element;
        
        me.refresh = null;
        me.callParent();
    }
});


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 = this.onScroll.bind(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);
    },
    onScroll: function() {
        if (!this.destroyed) {
            Ext.TaskQueue.requestRead('refresh', this);
        }
    },
    refreshMonitors: function() {
        var expandMonitor = this.expandMonitor,
            shrinkMonitor = this.shrinkMonitor,
            end = 1000000;
        if (expandMonitor && !expandMonitor.destroyed) {
            expandMonitor.scrollLeft = end;
            expandMonitor.scrollTop = end;
        }
        if (shrinkMonitor && !shrinkMonitor.destroyed) {
            shrinkMonitor.scrollLeft = end;
            shrinkMonitor.scrollTop = end;
        }
    },
    destroy: function() {
        
        this.onScroll = null;
        this.callParent();
    }
});


Ext.define('Ext.util.SizeMonitor', {
    
    constructor: function(config) {
        return new Ext.util.sizemonitor.Scroll(config);
    }
});










Ext.define('Ext.event.publisher.ElementSize', {
    extend: Ext.event.publisher.Publisher,
    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];
        }
        if (element.activeRead) {
            Ext.TaskQueue.cancelRead(element.activeRead);
        }
    },
    fireElementResize: function(element, info) {
        delete element.activeRead;
        this.fire(element, 'resize', [
            element,
            info
        ]);
    },
    onElementResize: function(element, info) {
        if (!element.activeRead) {
            element.activeRead = Ext.TaskQueue.requestRead('fireElementResize', this, [
                element,
                info
            ], 
            !!element.$skipResourceCheck);
        }
    },
    
    
    
    
    privates: {
        syncRefresh: function(elements) {
            elements = Ext.Array.from(elements);
            var len = elements.length,
                i = 0,
                el, monitor;
            for (i = 0; i < len; ++i) {
                el = elements[i];
                if (typeof el !== 'string') {
                    el = el.id;
                }
                monitor = this.monitors[el];
                if (monitor) {
                    monitor.forceRefresh();
                }
            }
            
            Ext.TaskQueue.flush();
            
            Ext.Function.fireElevatedHandlers();
        }
    }
}, 
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 = this.onElementPainted.bind(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, true);
        element.addCls(Ext.baseCSSPrefix + 'paint-monitored');
        this.bindListeners(true);
    },
    onElementPainted: function() {},
    destroy: function() {
        var me = this,
            monitorElement = me.monitorElement,
            parentNode = monitorElement.parentNode,
            element = me.getElement();
        me.bindListeners(false);
        delete me.monitorElement;
        if (element && !element.destroyed) {
            element.removeCls(Ext.baseCSSPrefix + 'paint-monitored');
            delete me._element;
        }
        if (parentNode) {
            parentNode.removeChild(monitorElement);
        }
        me.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', {
    constructor: function(config) {
        return new Ext.util.paintmonitor.CssAnimation(config);
    }
});


Ext.define('Ext.event.publisher.ElementPaint', {
    extend: Ext.event.publisher.Publisher,
    type: 'paint',
    handledEvents: [
        'painted'
    ],
    constructor: function() {
        this.monitors = {};
        this.subscribers = {};
        this.callParent(arguments);
    },
    subscribe: function(element) {
        var me = this,
            id = element.id,
            subscribers = me.subscribers;
        if (subscribers[id]) {
            ++subscribers[id];
        } else {
            subscribers[id] = 1;
            me.monitors[id] = new Ext.util.PaintMonitor({
                element: element,
                callback: me.onElementPainted,
                scope: me,
                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];
        }
        if (element.activeRead) {
            Ext.TaskQueue.cancelRead(element.activeRead);
        }
    },
    fireElementPainted: function(element) {
        delete element.activeRead;
        this.fire(element, 'painted', [
            element
        ]);
    },
    onElementPainted: function(element) {
        if (!element.activeRead) {
            element.activeRead = Ext.TaskQueue.requestRead('fireElementPainted', this, [
                element
            ], 
            !!element.$skipResourceCheck);
        }
    }
}, 
function(ElementPaint) {
    ElementPaint.instance = new ElementPaint();
});


Ext.define('Ext.dom.Element', function(Element) {
    var WIN = window,
        DOC = document,
        docEl = DOC.documentElement,
        WIN_TOP = WIN.top,
        EMPTY = [],
        elementIdCounter, windowId, documentId,
        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",
        wordsRe = /\w/g,
        spacesRe = /\s+/,
        classNameSplitRegex = /[\s]+/,
        transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
        endsQuestionRe = /\?$/,
        topRe = /top/i,
        empty = {},
        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'
        },
        selectDir = {
            b: 'backward',
            back: 'backward',
            f: 'forward'
        },
        paddingsTLRB = [
            paddings.l,
            paddings.r,
            paddings.t,
            paddings.b
        ],
        bordersTLRB = [
            borders.l,
            borders.r,
            borders.t,
            borders.b
        ],
        numberRe = /\d+$/,
        unitRe = /\d+(px|r?em|%|vh|vw|vmin|vmax|en|ch|ex|pt|in|cm|mm|pc)$/i,
        defaultUnit = 'px',
        msRe = /^-ms-/,
        camelRe = /(-[a-z])/gi,
        cssRe = /([a-z0-9\-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
        pxRe = /^\d+(?:\.\d*)?px$/i,
        relativeUnitRe = /(%|r?em|auto|vh|vw|vmin|vmax|ch|ex)$/i,
        propertyCache = {},
        ORIGINALDISPLAY = 'originalDisplay',
        camelReplaceFn = function(m, a) {
            return a.charAt(1).toUpperCase();
        },
        clearData = function(node, deep) {
            var childNodes, i, len;
            
            
            if (node.nodeType === 1) {
                node._extData = null;
                if (deep) {
                    childNodes = node.childNodes;
                    for (i = 0 , len = childNodes.length; i < len; ++i) {
                        clearData(childNodes[i], deep);
                    }
                }
            }
        },
        toFloat = function(v) {
            return parseFloat(v) || 0;
        },
        opacityCls = Ext.baseCSSPrefix + 'hidden-opacity',
        visibilityCls = Ext.baseCSSPrefix + 'hidden-visibility',
        displayCls = Ext.baseCSSPrefix + 'hidden-display',
        offsetsCls = Ext.baseCSSPrefix + 'hidden-offsets',
        clipCls = Ext.baseCSSPrefix + 'hidden-clip',
        lastFocusChange = 0,
        lastKeyboardClose = 0,
        editableHasFocus = false,
        isVirtualKeyboardOpen = false,
        inputTypeSelectionSupported = /text|password|search|tel|url/i,
        visFly, scrollFly, caFly, wrapFly, grannyFly, activeElFly;
    
    try {
        elementIdCounter = WIN_TOP.__elementIdCounter__;
    } catch (e) {
        WIN_TOP = WIN;
    }
    WIN_TOP.__elementIdCounter = elementIdCounter = (WIN_TOP.__elementIdCounter__ || 0) + 1;
    windowId = 'ext-window-' + elementIdCounter;
    documentId = 'ext-document-' + elementIdCounter;
    
    if (Object.freeze) {
        Object.freeze(EMPTY);
    }
    
    return {
        alternateClassName: [
            'Ext.Element'
        ],
        mixins: [
            Ext.util.Positionable,
            Ext.mixin.Observable
        ],
        observableType: 'element',
        isElement: true,
        skipGarbageCollection: true,
        $applyConfigs: true,
        identifiablePrefix: 'ext-element-',
        _selectDir: selectDir,
        styleHooks: {
            transform: {
                set: function(dom, value, el) {
                    var prop,
                        result = '';
                    if (typeof value !== 'string') {
                        for (prop in value) {
                            if (result) {
                                result += ' ';
                            }
                            if (prop.indexOf('translate') === 0) {
                                result += prop + '(' + Element.addUnits(value[prop], 'px') + ')';
                            } else {
                                result += prop + '(' + value[prop] + ')';
                            }
                        }
                        value = result;
                    }
                    dom.style.transform = value;
                }
            }
        },
        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.raise("Invalid domNode reference or an id of an existing domNode: " + dom);
                
                return null;
            }
            
            if (Ext.cache[dom.id]) {
                Ext.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.raise('Invalid Element "id": "' + me.id + '"');
            }
            
            
            
            
            me.el = me;
            Ext.cache[id] = me;
            me.longpressListenerCount = 0;
            me.mixins.observable.constructor.call(me);
        },
        inheritableStatics: {
            
            cache: Ext.cache = {},
            
            editableSelector: 'input,textarea,[contenteditable="true"]',
            
            VISIBILITY: 1,
            
            DISPLAY: 2,
            
            OFFSETS: 3,
            
            CLIP: 4,
            
            OPACITY: 5,
            
            minKeyboardHeight: 100,
            unitRe: unitRe,
            
            useDelegatedEvents: true,
            
            validNodeTypes: {
                1: 1,
                
                9: 1
            },
            
            selectableCls: Ext.baseCSSPrefix + 'selectable',
            unselectableCls: Ext.baseCSSPrefix + 'unselectable',
            
            maxRippleDiameter: 75,
            
            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)) {
                    
                    
                    if (!(Ext.isString(size) && size.indexOf('calc') === 0)) {
                        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,
                    classes, element, elementStyle, tag, value, name, i, ln, tmp;
                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;
                for (name in attributes) {
                    if (name !== 'tag') {
                        value = attributes[name];
                        switch (name) {
                            case 'style':
                                if (typeof value === 'string') {
                                    element.setAttribute(name, value);
                                } else {
                                    for (i in value) {
                                        if (value.hasOwnProperty(i)) {
                                            elementStyle[i] = value[i];
                                        }
                                    }
                                };
                                break;
                            case 'className':
                            case 'cls':
                                tmp = value.split(spacesRe);
                                classes = classes ? classes.concat(tmp) : tmp;
                                break;
                            case 'classList':
                                classes = classes ? classes.concat(value) : value;
                                break;
                            case 'text':
                                element.textContent = value;
                                break;
                            case 'html':
                                element.innerHTML = value;
                                break;
                            case 'hidden':
                                if (classes) {
                                    classes.push(displayCls);
                                } else {
                                    classes = [
                                        displayCls
                                    ];
                                };
                                break;
                            case 'children':
                                if (value != null) {
                                    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 (classes) {
                    element.className = classes.join(' ');
                }
                if (domNode) {
                    return element;
                } else {
                    return me.get(element);
                }
            },
            
            fly: function(dom, named) {
                return Ext.fly(dom, named);
            },
            
            fromPoint: (function() {
                
                
                
                var elementFromPointBug;
                if (Ext.isIE || Ext.isEdge) {
                    try {
                        elementFromPointBug = window.self !== window.top;
                    } catch (e) {
                        elementFromPointBug = true;
                    }
                }
                return function(x, y, asDom) {
                    var el = null;
                    el = DOC.elementFromPoint(x, y);
                    if (!el && elementFromPointBug) {
                        el = DOC.elementFromPoint(x, y);
                    }
                    return asDom ? el : Ext.get(el);
                };
            })(),
            
            fromPagePoint: function(x, y, asDom) {
                var scroll = Ext.getDoc().getScroll();
                return Element.fromPoint(x - scroll.left, y - scroll.top, asDom);
            },
            
            get: function(el) {
                var me = this,
                    cache = Ext.cache,
                    nodeType, dom, id, entry, isDoc, isWin, isValidNodeType;
                if (!el) {
                    return null;
                }
                
                function warnDuplicate(id) {
                    Ext.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(asElement) {
                var active = DOC.activeElement;
                
                
                
                
                if (!active || !active.focus) {
                    active = DOC.body;
                }
                return asElement ? Ext.get(active) : active;
            },
            
            getDocumentHeight: function() {
                return Math.max(!Ext.isStrict ? DOC.body.scrollHeight : docEl.scrollHeight, this.getViewportHeight());
            },
            
            getDocumentWidth: function() {
                return Math.max(!Ext.isStrict ? DOC.body.scrollWidth : docEl.scrollWidth, this.getViewportWidth());
            },
            
            getOrientation: function() {
                if (Ext.supports.OrientationChange) {
                    return (WIN.orientation == 0) ? 'portrait' : 'landscape';
                }
                return (WIN.innerHeight > WIN.innerWidth) ? 'portrait' : 'landscape';
            },
            
            getViewportHeight: function() {
                var viewportHeight = Element._viewportHeight;
                
                if (Ext.isIE9m) {
                    return DOC.documentElement.clientHeight;
                }
                
                return (viewportHeight != null) ? viewportHeight : docEl.clientHeight;
            },
            
            getViewportWidth: function() {
                var viewportWidth = Element._viewportWidth;
                
                if (Ext.isIE9m) {
                    return DOC.documentElement.clientWidth;
                }
                
                return (viewportWidth != null) ? viewportWidth : docEl.clientWidth;
            },
            
            getViewportScale: function() {
                
                
                
                
                
                
                var top = WIN_TOP;
                return ((Ext.isiOS || Ext.isAndroid) ? 1 : (top.devicePixelRatio || 
                top.screen.deviceXDPI / top.screen.logicalXDPI)) * 
                this.getViewportTouchScale();
            },
            
            getViewportTouchScale: function(forceRead) {
                var scale = 1,
                    
                    top = WIN_TOP,
                    cachedScale;
                if (!forceRead) {
                    cachedScale = this._viewportTouchScale;
                    if (cachedScale) {
                        return cachedScale;
                    }
                }
                if (Ext.isIE10p || Ext.isEdge || Ext.isiOS) {
                    scale = docEl.offsetWidth / WIN.innerWidth;
                } else if (Ext.isChromeMobile) {
                    scale = top.outerWidth / top.innerWidth;
                }
                return scale;
            },
            
            getViewSize: function() {
                return {
                    width: Element.getViewportWidth(),
                    height: Element.getViewportHeight()
                };
            },
            
            hasUnit: function(size) {
                return !!(size && unitRe.test(size));
            },
            
            isRelativeUnit: function(size) {
                return !size || relativeUnitRe.test(size);
            },
            
            maskIframes: function() {
                var iframes = document.getElementsByTagName('iframe'),
                    fly = new Ext.dom.Fly();
                Ext.each(iframes, function(iframe) {
                    var myMask;
                    myMask = fly.attach(iframe.parentNode).mask();
                    myMask.setStyle('background-color', 'transparent');
                });
            },
            
            normalize: function(prop) {
                
                return propertyCache[prop] || (propertyCache[prop] = prop.replace(msRe, 'ms-').replace(camelRe, camelReplaceFn));
            },
            
            _onWindowFocusChange: function(e) {
                
                
                
                
                if (Ext.fly(e.target).is(Element.editableSelector)) {
                    lastFocusChange = new Date();
                    editableHasFocus = (e.type === 'focusin' || e.type === 'pointerup');
                }
            },
            
            _onWindowResize: function() {
                var documentWidth = docEl.clientWidth,
                    documentHeight = docEl.clientHeight,
                    now = new Date(),
                    threshold = 1000,
                    deltaX, deltaY;
                deltaX = documentWidth - Element._documentWidth;
                deltaY = documentHeight - Element._documentHeight;
                Element._documentWidth = documentWidth;
                Element._documentHeight = documentHeight;
                
                
                
                if (((now - lastFocusChange) < threshold) || ((now - lastKeyboardClose) < threshold)) {
                    
                    
                    
                    
                    if (deltaX === 0 && (editableHasFocus && (deltaY <= -Element.minKeyboardHeight))) {
                        isVirtualKeyboardOpen = true;
                        return;
                    }
                }
                if (isVirtualKeyboardOpen && (deltaX === 0) && (deltaY >= Element.minKeyboardHeight)) {
                    isVirtualKeyboardOpen = false;
                    
                    
                    
                    
                    lastKeyboardClose = new Date();
                }
                if (isVirtualKeyboardOpen) {
                    return;
                }
                
                
                
                Element._viewportWidth = documentWidth;
                Element._viewportHeight = documentHeight;
            },
            
            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);
            },
            
            unmaskIframes: function() {
                var iframes = document.getElementsByTagName('iframe'),
                    fly = new Ext.dom.Fly();
                Ext.each(iframes, function(iframe) {
                    fly.attach(iframe.parentNode).unmask();
                });
            },
            
            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(DOC.body);
                        break;
                    }
                }
                return returnDom ? caFly.dom : Ext.get(caFly);
            }
        },
        
        selectable: function() {
            var me = this;
            
            
            me.dom.unselectable = '';
            me.removeCls(Element.unselectableCls);
            me.addCls(Element.selectableCls);
            return me;
        },
        
        unselectable: function() {
            
            
            
            
            
            
            
            var me = this;
            
            
            
            
            
            if (Ext.isOpera) {
                me.dom.unselectable = 'on';
            }
            
            
            
            
            
            
            
            
            
            me.removeCls(Element.selectableCls);
            me.addCls(Element.unselectableCls);
            return me;
        },
        
        statics: {
            
            
            tabbableSelector: Ext.supports.CSS3NegationSelector ? 'a[href],button,iframe,input,select,textarea,[tabindex]:not([tabindex="-1"]),[contenteditable="true"]' : '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,
                BODY: Ext.isIE ? false : true
            },
            
            naturallyTabbableTags: {
                BUTTON: true,
                IFRAME: true,
                INPUT: true,
                SELECT: true,
                TEXTAREA: true,
                OBJECT: Ext.isIE8m ? true : false
            },
            inputTags: {
                INPUT: true,
                TEXTAREA: true
            },
            tabbableSavedCounterAttribute: 'data-tabindex-counter',
            tabbableSavedValueAttribute: 'data-tabindex-value',
            splitCls: function(cls) {
                if (typeof cls === 'string') {
                    cls = cls.split(spacesRe);
                }
                return cls;
            }
        },
        
        _init: function(E) {
            
            
            E.tabbableSelector += ',[' + E.tabbableSavedCounterAttribute + ']';
        },
        
        addCls: function(names, prefix, suffix) {
            return this.replaceCls(null, names, prefix, suffix);
        },
        
        addClsOnClick: function(className, testFn, scope) {
            var me = this,
                hasTest = Ext.isFunction(testFn);
            me.on("mousedown", function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return false;
                }
                me.addCls(className);
                Ext.getDoc().on({
                    mouseup: function() {
                        
                        if (me.dom) {
                            me.removeCls(className);
                        }
                    },
                    single: true
                });
            });
            return me;
        },
        
        addClsOnFocus: function(className, testFn, scope) {
            var me = this,
                hasTest = Ext.isFunction(testFn);
            me.on("focus", function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return false;
                }
                me.addCls(className);
            });
            me.on("blur", function() {
                
                if (me.dom) {
                    me.removeCls(className);
                }
            });
            return me;
        },
        
        addClsOnOver: function(className, testFn, scope) {
            var me = this,
                hasTest = Ext.isFunction(testFn);
            me.hover(function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return;
                }
                me.addCls(className);
            }, function() {
                me.removeCls(className);
            });
            return me;
        },
        addStyles: function(sides, styles) {
            var totalSize = 0,
                sidesArr = (sides || '').match(wordsRe),
                i,
                len = sidesArr.length,
                side,
                styleSides = [];
            if (len === 1) {
                totalSize = 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);
        },
        
        
        
        animate: function(animation) {
            animation = new Ext.fx.Animation(animation);
            animation.setElement(this);
            this._activeAnimation = animation;
            animation.on({
                animationend: this._onAnimationEnd,
                scope: this
            });
            Ext.Animator.run(animation);
            return animation;
        },
        _onAnimationEnd: function() {
            this._activeAnimation = null;
        },
        getActiveAnimation: function() {
            return this._activeAnimation;
        },
        append: function() {
            return 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(DOC.createDocumentFragment());
                eLen = el.length;
                for (e = 0; e < eLen; e++) {
                    insertEl.appendChild(el[e], returnDom);
                }
                el = Ext.Array.toArray(insertEl.dom.childNodes);
                me.dom.appendChild(insertEl.dom);
                return returnDom ? el : new Ext.dom.CompositeElementLite(el);
            } 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;
            
            if (Ext.supports.Selectors2) {
                return me.selectNode(':scope>' + selector, !!returnDom);
            } else {
                
                
                id = me.id != null ? me.id : Ext.get(me).id;
                return me.selectNode(Ext.makeIdSelector(id) + " > " + selector, !!returnDom);
            }
        },
        
        clone: function(deep, returnDom) {
            var clone = this.dom.cloneNode(deep);
            if (Ext.supports.CloneNodeCopiesExpando) {
                clearData(clone, deep);
            }
            return returnDom ? clone : Ext.get(clone);
        },
        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.destroyed) {
                Ext.Logger.warn("Cannot destroy Element \"" + me.id + "\". Already destroyed.");
                return;
            }
            if (me.resumeFocusEventsTimer) {
                Ext.unasap(me.resumeFocusEventsTimer);
                me.resumeFocusEventsTimer = null;
            }
            if (me.repaintTimer) {
                me.repaintTimer = Ext.undefer(me.repaintTimer);
            }
            if (me.deferFocusTimer) {
                me.deferFocusTimer = Ext.undefer(me.deferFocusTimer);
            }
            if (dom) {
                if (dom === DOC.body) {
                    Ext.raise("Cannot destroy body element.");
                } else if (dom === DOC) {
                    Ext.raise("Cannot destroy document object.");
                } else if (dom === WIN) {
                    Ext.raise("Cannot destroy window object");
                }
            }
            
            if (dom && dom.parentNode) {
                dom.parentNode.removeChild(dom);
            }
            if (me.$ripples) {
                me.destroyAllRipples();
            }
            me.collect();
        },
        detach: function() {
            var dom = this.dom,
                component = this.component;
            if (dom && dom.parentNode && dom.tagName !== 'BODY') {
                
                if (component) {
                    component.revertFocus();
                }
                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;
            }
        },
        
        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;
            return shim;
        },
        
        findParent: function(simpleSelector, limit, returnEl) {
            var me = this,
                target = me.dom,
                topmost = docEl,
                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;
            if (Number(defer)) {
                Ext.defer(me.focus, defer, me, [
                    null,
                    dom
                ]);
            } else {
                Ext.GlobalEvents.fireEvent('beforefocus', dom);
                dom.focus();
            }
            return me;
        },
        
        collect: function() {
            var me = this,
                dom = me.dom,
                shadow = me.shadow,
                shim = me.shim;
            
            
            if (!me.isFly) {
                me.mixins.observable.destroy.call(me);
                delete Ext.cache[me.id];
                me.el = null;
            }
            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)
            };
        },
        
        getBorders: function() {
            var bordersWidth = this.getStyle(bordersTLRB);
            return {
                beforeX: (parseFloat(bordersWidth[borders.l]) || 0),
                afterX: (parseFloat(bordersWidth[borders.r]) || 0),
                beforeY: (parseFloat(bordersWidth[borders.t]) || 0),
                afterY: (parseFloat(bordersWidth[borders.b]) || 0)
            };
        },
        
        getBorderWidth: function(side) {
            return this.addStyles(side, borders);
        },
        
        getClassMap: function(clone) {
            var data = this.getData();
            if (data) {
                data = data.classMap;
                if (clone !== false) {
                    data = Ext.apply({}, data);
                }
            }
            return data;
        },
        
        getData: function(sync) {
            var dom = this.dom,
                data;
            if (dom) {
                data = dom._extData || (dom._extData = {});
                if (sync !== false && !data.isSynchronized) {
                    this.synchronize();
                }
            }
            return data || null;
        },
        getFirstChild: function() {
            return Ext.get(this.dom.firstElementChild);
        },
        getLastChild: function() {
            return Ext.get(this.dom.lastElementChild);
        },
        
        getHeight: function(contentHeight, preciseHeight) {
            var me = this,
                dom = me.dom,
                hidden = me.isStyle('display', 'none'),
                height, floating;
            if (hidden) {
                return 0;
            }
            
            if (dom.nodeName === 'BODY') {
                height = Element.getViewportHeight();
            } else {
                if (preciseHeight) {
                    height = dom.getBoundingClientRect().height;
                } else {
                    height = dom.offsetHeight;
                    
                    if (height == null) {
                        height = dom.getBoundingClientRect().height;
                    }
                }
            }
            
            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 = docEl,
                left, top,
                body = DOC.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, align) {
            align = align || empty;
            var me = this,
                dom = me.dom,
                offsets, clientWidth, clientHeight;
            if (container.isRegion) {
                clientHeight = container.height;
                clientWidth = container.width;
            } else {
                container = Ext.getDom(container);
                clientHeight = container.clientHeight;
                clientWidth = container.clientWidth;
            }
            offsets = me.getOffsetsTo(container);
            return {
                y: me.calcScrollPos(offsets[1] + scrollY, dom.offsetHeight, scrollY, clientHeight, align.y),
                x: me.calcScrollPos(offsets[0] + scrollX, dom.offsetWidth, scrollX, clientWidth, align.x)
            };
        },
        calcScrollPos: function(start, size, viewStart, viewSize, align) {
            var end = start + size,
                viewEnd = viewStart + viewSize,
                force = align && !endsQuestionRe.test(align),
                ret = viewStart;
            if (!force) {
                if (align) {
                    align = align.slice(0, -1);
                }
                if (size > viewSize || start < viewStart) {
                    align = align || 'start';
                    force = true;
                } else if (end > viewEnd) {
                    align = align || 'end';
                    force = true;
                }
            }
            if (force) {
                if (align === 'start') {
                    ret = start;
                } else if (align === 'center') {
                    ret = Math.max(0, start - Math.floor((viewSize / 2)));
                } else if (align === 'end') {
                    ret = Math.max(0, end - viewSize);
                }
            }
            return ret;
        },
        
        getScrollLeft: function() {
            var dom = this.dom;
            if (dom === DOC || dom === DOC.body) {
                return this.getScroll().left;
            } else {
                return dom.scrollLeft;
            }
        },
        
        getScrollTop: function() {
            var dom = this.dom;
            if (dom === DOC || dom === DOC.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);
        },
        getCaretPos: function() {
            var dom = this.dom,
                pos, selection;
            if (inputTypeSelectionSupported.test(dom.type)) {
                pos = dom.selectionStart;
                selection = (typeof pos !== 'number') && this.getTextSelection();
                if (selection) {
                    pos = selection[0];
                }
            } else 
            {
                Ext.raise('Input type of "' + dom.type + '" does not support selectionStart');
            }
            
            return pos;
        },
        setCaretPos: function(pos) {
            this.selectText(pos, pos);
        },
        
        getTextSelection: function() {
            var dom = this.dom;
            if (inputTypeSelectionSupported.test(dom.type)) {
                return [
                    dom.selectionStart,
                    dom.selectionEnd,
                    dom.selectionDirection
                ];
            } else {
                
                Ext.raise('Input type of "' + this.dom.type + '" does not support selectionStart, selectionEnd and selectionDirection');
                
                return [];
            }
        },
        
        
        selectText: function(start, end, direction) {
            var me = this,
                
                range,
                
                dom = me.dom,
                len;
            if (dom && inputTypeSelectionSupported.test(dom.type)) {
                start = start || 0;
                len = dom.value.length;
                if (end === undefined) {
                    end = len;
                }
                direction = selectDir[direction] || direction || 'forward';
                if (dom.setSelectionRange) {
                    dom.setSelectionRange(start, end, direction);
                }
                
                else if (dom.createTextRange) {
                    if (start > end) {
                        start = end;
                    }
                    range = dom.createTextRange();
                    range.moveStart('character', start);
                    range.moveEnd('character', -(len - end));
                    range.select();
                }
            }
            
            
            else if (!inputTypeSelectionSupported.test(dom.type)) {
                Ext.raise('Input type of "' + dom.type + '" does not support setSelectionRange');
            }
            
            return me;
        },
        
        getTop: function(local) {
            return local ? this.getLocalY() : this.getY();
        },
        
        getTouchAction: function() {
            return Ext.dom.TouchAction.get(this.dom);
        },
        
        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,
                body = DOC.body,
                x = 0,
                y = 0,
                bodyRect, rect;
            if (dom !== DOC && dom !== body) {
                
                
                try {
                    bodyRect = body.getBoundingClientRect();
                    rect = dom.getBoundingClientRect();
                    x = rect.left - bodyRect.left;
                    y = rect.top - bodyRect.top;
                } catch (ex) {}
            }
            return [
                round(x),
                round(y)
            ];
        },
        
        getY: function() {
            return this.getXY()[1];
        },
        
        getZIndex: function() {
            return parseInt(this.getStyle('z-index'), 10);
        },
        
        hasCls: function(name) {
            var classMap = this.getClassMap();
            return classMap.hasOwnProperty(name);
        },
        
        hide: function() {
            return this.setVisible(false);
        },
        
        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;
        },
        
        indexOf: function(childEl) {
            var children = this.dom,
                c = childEl && Ext.getDom(childEl);
            children = children && children.childNodes;
            return (c && children) ? Array.prototype.indexOf.call(children, c) : -1;
        },
        
        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(DOC.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 (!child.nodeType) {
                    return false;
                }
                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));
            };
        })(),
        
        isScrollable: function() {
            var dom = this.dom;
            return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
        },
        
        isStyle: function(style, val) {
            return this.getStyle(style) === val;
        },
        
        isVisible: function(deep, mode) {
            var dom = this.dom,
                visible = true,
                end;
            if (!dom) {
                return false;
            }
            mode = mode || 3;
            if (!visFly) {
                visFly = new Ext.dom.Fly();
            }
            for (end = dom.ownerDocument.documentElement; dom !== end; dom = dom.parentNode) {
                if (!dom || dom.nodeType === 11) {
                    
                    visible = false;
                }
                if (visible) {
                    visFly.attach(dom);
                    if (mode & 1) {
                        visible = !visFly.isStyle(DISPLAY, NONE);
                    }
                    if (visible && (mode & 2)) {
                        visible = !visFly.isStyle(VISIBILITY, HIDDEN);
                    }
                }
                if (!visible || !deep) {
                    break;
                }
            }
            return visible;
        },
        
        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;
        },
        
        measure: function(dimension) {
            
            
            
            
            var me = this,
                dom = me.dom,
                includeWidth = dimension !== 'h',
                includeHeight = dimension !== 'w',
                width = 0,
                height = 0,
                addPadding = !Ext.supports.ComputedSizeIncludesPadding,
                style, rect, offsetParent;
            
            if (dom.nodeName === 'BODY') {
                height = includeHeight && Element.getViewportHeight();
                width = includeWidth && Element.getViewportWidth();
            } else {
                offsetParent = dom.offsetParent;
                style = dom.ownerDocument.defaultView.getComputedStyle(dom, null);
                
                addPadding |= style.boxSizing === 'content-box';
                
                if (offsetParent !== null || style.position === 'fixed') {
                    if (includeHeight) {
                        height = toFloat(style.height);
                        if (addPadding) {
                            height += toFloat(style.paddingTop) + toFloat(style.paddingBottom) + toFloat(style.borderTopWidth) + toFloat(style.borderBottomWidth);
                        }
                    }
                    if (includeWidth) {
                        width = toFloat(style.width);
                        if (addPadding) {
                            width += toFloat(style.paddingLeft) + toFloat(style.paddingRight) + toFloat(style.borderLeftWidth) + toFloat(style.borderRightWidth);
                        }
                    }
                }
            }
            
            rect = dimension ? null : {
                width: width,
                height: height
            };
            
            return dimension ? (includeWidth ? width : height) : rect;
        },
        
        measureContent: function(dimension) {
            var me = this,
                includeWidth = dimension !== 'h',
                size = me.measure(dimension),
                
                h = dimension ? size : size.height,
                w = dimension ? size : size.width;
            if (dimension !== 'w') {
                h -= me.getBorderWidth('tb') + me.getPadding('tb');
            }
            if (includeWidth) {
                w -= me.getBorderWidth('lr') + me.getPadding('lr');
            }
            return dimension ? (includeWidth ? w : h) : {
                width: w,
                height: h
            };
        },
        
        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() {
                        Ext.undefer(timer);
                    },
                    destroy: function() {
                        Ext.undefer(timer);
                        if (!me.destroyed) {
                            me.un(listeners);
                        }
                    }
                };
            me.on(listeners);
            return listeners;
        },
        
        next: function(selector, returnDom) {
            return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
        },
        
        parent: function(selector, returnDom) {
            return this.matchNode('parentNode', 'parentNode', selector, returnDom);
        },
        peekData: function() {
            var dom = this.dom;
            return dom && dom._extData || null;
        },
        
        position: function(pos, zIndex, x, y) {
            var me = this;
            if (me.dom.tagName !== 'BODY') {
                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) {
            return this.replaceCls(names, null, prefix, suffix);
        },
        
        repaint: function() {
            var me = this;
            me.addCls(Ext.baseCSSPrefix + 'repaint');
            if (!me.repaintTimer) {
                me.repaintTimer = Ext.defer(function() {
                    me.repaintTimer = null;
                    if (me.dom) {
                        
                        me.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.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(remove, add, prefix, suffix) {
            var me = this,
                added = 0,
                removed = 0,
                rem = remove,
                data = (add || remove) && me.getData(),
                list, map, i, n, name;
            if (data) {
                list = data.classList;
                map = data.classMap;
                add = add ? ((typeof add === 'string') ? add.split(spacesRe) : add) : EMPTY;
                rem = rem ? ((typeof rem === 'string') ? rem.split(spacesRe) : rem) : EMPTY;
                
                prefix = prefix || '';
                if (prefix && prefix[prefix.length - 1] !== '-') {
                    prefix += '-';
                }
                suffix = suffix || '';
                if (suffix && suffix[0] !== '-') {
                    suffix = '-' + suffix;
                }
                for (i = 0 , n = rem.length; i < n; i++) {
                    if (!(name = rem[i])) {
                        
                        
                        continue;
                    }
                    name = prefix + name + suffix;
                    
                    if (spacesRe.test(name)) {
                        Ext.raise('Class names in arrays must not contain spaces');
                    }
                    
                    if (map[name]) {
                        delete map[name];
                        ++removed;
                    }
                }
                for (i = 0 , n = add.length; i < n; i++) {
                    if (!(name = add[i])) {
                        
                        continue;
                    }
                    name = prefix + name + suffix;
                    
                    if (spacesRe.test(name)) {
                        Ext.raise('Class names in arrays must not contain spaces');
                    }
                    
                    if (!map[name]) {
                        map[name] = true;
                        
                        if (!removed) {
                            list.push(name);
                            ++added;
                        }
                    }
                }
                if (removed) {
                    me.setClassMap(map, 
                    true);
                } else if (added) {
                    me.dom.className = list.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 = topRe.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.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;
        },
        
        setClassMap: function(classMap, keep) {
            var data = this.getData(
                false),
                classList;
            if (data) {
                classMap = (keep && classMap) || Ext.apply({}, classMap);
                data.classMap = classMap;
                data.classList = classList = Ext.Object.getKeys(classMap);
                data.isSynchronized = true;
                
                this.dom.className = classList.join(' ');
            }
        },
        
        setCls: function(className) {
            var me = this,
                elementData = me.getData(
                false),
                i, ln, map, classList;
            if (typeof className === 'string') {
                className = className.split(spacesRe);
            }
            elementData.classList = classList = className.slice();
            elementData.classMap = map = {};
            for (i = 0 , ln = classList.length; i < ln; i++) {
                map[classList[i]] = true;
            }
            me.dom.className = classList.join(' ');
        },
        
        setDisplayed: function(value) {
            var me = this;
            if (typeof value === "boolean") {
                value = value ? me._getDisplay() : NONE;
            }
            me.setStyle(DISPLAY, value);
            if (me.shadow || me.shim) {
                me.setUnderlaysVisible(value !== NONE);
            }
            return me;
        },
        
        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;
            }
            return this;
        },
        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 = '';
            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 = '';
            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;
        },
        
        setOpacity: function(opacity) {
            var me = this;
            if (me.dom) {
                me.setStyle('opacity', opacity);
            }
            return me;
        },
        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;
            }
            if (width !== undefined) {
                style.width = Element.addUnits(width);
            }
            if (height !== undefined) {
                style.height = Element.addUnits(height);
            }
            if (me.shadow || me.shim) {
                me.syncUnderlays();
            }
            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;
        },
        
        setTouchAction: function(touchAction) {
            Ext.dom.TouchAction.set(this.dom, touchAction);
        },
        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 && mode !== 4 && mode !== 5) {
                Ext.raise("visibilityMode must be one of the following: " + "Ext.Element.DISPLAY, Ext.Element.VISIBILITY, Ext.Element.OFFSETS, " + "Ext.Element.CLIP, or Element.OPACITY");
            }
            
            this.getData().visibilityMode = mode;
            return this;
        },
        
        setVisible: function(visible) {
            var me = this,
                mode = me.getVisibilityMode(),
                addOrRemove = visible ? 'removeCls' : 'addCls';
            switch (mode) {
                case Element.DISPLAY:
                    me.removeCls([
                        visibilityCls,
                        offsetsCls,
                        clipCls,
                        opacityCls
                    ]);
                    me[addOrRemove](displayCls);
                    break;
                case Element.VISIBILITY:
                    me.removeCls([
                        displayCls,
                        offsetsCls,
                        clipCls,
                        opacityCls
                    ]);
                    me[addOrRemove](visibilityCls);
                    break;
                case Element.OFFSETS:
                    me.removeCls([
                        visibilityCls,
                        displayCls,
                        clipCls,
                        opacityCls
                    ]);
                    me[addOrRemove](offsetsCls);
                    break;
                case Element.CLIP:
                    me.removeCls([
                        visibilityCls,
                        displayCls,
                        offsetsCls,
                        opacityCls
                    ]);
                    me[addOrRemove](clipCls);
                    break;
                case Element.OPACITY:
                    me.removeCls([
                        visibilityCls,
                        displayCls,
                        offsetsCls,
                        clipCls
                    ]);
                    me[addOrRemove](opacityCls);
                    break;
            }
            if (me.shadow || me.shim) {
                me.setUnderlaysVisible(visible);
            }
            if (!visible && me.$ripples) {
                me.destroyAllRipples();
            }
            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 = '';
            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() {
            return this.setVisible(true);
        },
        
        swallowEvent: function(eventName, preventDefault) {
            var me = this,
                e, eLen,
                listeners = {
                    destroyable: true
                },
                fn = function(e) {
                    e.stopPropagation();
                    if (preventDefault) {
                        e.preventDefault();
                    }
                };
            if (Ext.isArray(eventName)) {
                eLen = eventName.length;
                for (e = 0; e < eLen; e++) {
                    listeners[eventName[e]] = fn;
                }
            } else {
                listeners[eventName] = fn;
            }
            return me.on(listeners);
        },
        
        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(
                false);
            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, state) {
            if (state == null) {
                state = !this.hasCls(className);
            }
            return state ? 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) {
                x = Math.round(x);
                y = Math.round(y);
                z = Math.round(z);
                this.dom.style[transformStyleName] = 'translate3d(' + (x || 0) + 'px, ' + (y || 0) + 'px, ' + (z || 0) + 'px)';
            };
        }(),
        
        unwrap: function() {
            var dom = this.dom,
                parentNode = dom.parentNode,
                activeElement = (activeElFly || (activeElFly = new Ext.dom.Fly())).attach(Ext.Element.getActiveElement()),
                grandparentNode, cached, resumeFocus, tabIndex;
            grannyFly = grannyFly || new Ext.dom.Fly();
            cached = Ext.cache[activeElement.dom.id];
            
            
            
            if (cached) {
                activeElement = cached;
            }
            if (this.contains(activeElement)) {
                if (cached) {
                    cached.suspendFocusEvents();
                }
                resumeFocus = true;
            }
            if (parentNode) {
                grandparentNode = parentNode.parentNode;
                
                if (resumeFocus) {
                    tabIndex = grandparentNode.getAttribute('tabIndex');
                    grannyFly.attach(grandparentNode);
                    grannyFly.set({
                        tabIndex: -1
                    });
                    grannyFly.suspendFocusEvents();
                    grannyFly.focus();
                }
                grandparentNode.insertBefore(dom, parentNode);
                grandparentNode.removeChild(parentNode);
            } else {
                grandparentNode = DOC.createDocumentFragment();
                grandparentNode.appendChild(dom);
            }
            if (resumeFocus) {
                if (cached) {
                    cached.focus();
                    cached.resumeFocusEvents();
                } else {
                    activeElement.focus();
                }
                if (grannyFly) {
                    grannyFly.resumeFocusEvents();
                    grannyFly.set({
                        tabIndex: tabIndex
                    });
                }
            }
            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,
                result = Ext.DomHelper.insertBefore(dom, config || {
                    tag: "div"
                }, !returnDom),
                newEl = (wrapFly || (wrapFly = new Ext.dom.Fly())).attach(Ext.getDom(result)),
                
                target = newEl,
                activeElement = (activeElFly || (activeElFly = new Ext.dom.Fly())).attach(Ext.Element.getActiveElement()),
                cached, resumeFocus, tabIndex;
            cached = Ext.cache[activeElement.dom.id];
            
            
            
            if (cached) {
                activeElement = cached;
            }
            if (selector) {
                target = newEl.selectNode(selector, returnDom);
            }
            if (me.contains(activeElement)) {
                if (cached) {
                    cached.suspendFocusEvents();
                }
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                tabIndex = Ext.getDom(newEl).getAttribute('tabIndex');
                newEl.set({
                    tabIndex: -1
                });
                newEl.suspendFocusEvents();
                newEl.focus();
                resumeFocus = true;
            }
            (target.dom || target).appendChild(dom);
            if (resumeFocus) {
                if (cached) {
                    cached.focus();
                    cached.resumeFocusEvents();
                } else {
                    activeElement.focus();
                }
                newEl.resumeFocusEvents();
                
                
                newEl.set({
                    tabIndex: tabIndex
                });
            }
            return result;
        },
        
        isFocusable: function(skipVisibility) {
            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 && (skipVisibility || this.isVisible(true));
            }
            return focusable;
        },
        
        isInputField: function() {
            var dom = this.dom,
                contentEditable = dom.contentEditable;
            
            
            
            
            
            if ((Ext.Element.inputTags[dom.tagName] && dom.type !== 'button') || (contentEditable === '' || contentEditable === 'true')) {
                return true;
            }
            return false;
        },
        
        isTabbable: function(includeHidden) {
            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 && (includeHidden || ((!this.component || this.component.isVisible(true)) && this.isVisible(true)));
            }
            return tabbable;
        },
        ripplingCls: Ext.baseCSSPrefix + 'rippling',
        ripplingTransitionCls: Ext.baseCSSPrefix + 'ripple-transition',
        ripplingUnboundCls: Ext.baseCSSPrefix + 'rippling-unbound',
        rippleBubbleCls: Ext.baseCSSPrefix + 'ripple-bubble',
        rippleContainerCls: Ext.baseCSSPrefix + 'ripple-container',
        rippleWrapperCls: Ext.baseCSSPrefix + 'ripple-wrapper',
        
        noRippleDisplayMap: {
            table: 1,
            'table-row': 1,
            'table-row-group': 1
        },
        
        noRippleTagMap: {
            TABLE: 1,
            TR: 1,
            TBODY: 1
        },
        
        ripple: function(event, options) {
            if (options === true || !options) {
                options = {};
            } else if (Ext.isString(options)) {
                options = {
                    color: options
                };
            }
            var me = this,
                rippleParent = Ext.isString(options.delegate) ? me.down(options.delegate) : me,
                rippleMeasureEl = Ext.isString(options.measureSelector) ? me.down(options.measureSelector) : null,
                color = window.getComputedStyle(rippleParent.dom).color,
                unbound = options.bound === false,
                position = options.position,
                ripplingCls = me.ripplingCls,
                ripplingTransitionCls = me.ripplingTransitionCls,
                ripplingUnboundCls = me.ripplingUnboundCls,
                rippleBubbleCls = me.rippleBubbleCls,
                rippleContainerCls = me.rippleContainerCls,
                rippleWrapperCls = me.rippleWrapperCls,
                offset, width, height, rippleDiameter, center, measureElWidth, measureElHeight, rippleSize, pos, posX, posY, rippleWrapper, rippleContainer, rippleBubble, rippleDestructor, rippleClearFn, rippleDestructionTimer, rippleBox, unboundEl, unboundElData, timeout;
            if (rippleParent) {
                offset = rippleParent.getXY();
                width = rippleParent.getWidth();
                height = rippleParent.getHeight();
                timeout = rippleParent.$rippleClearTimeout;
                if (timeout) {
                    rippleParent.$rippleClearTimeout = Ext.undefer(timeout);
                }
                
                
                if (rippleMeasureEl) {
                    measureElWidth = rippleMeasureEl.getWidth();
                    measureElHeight = rippleMeasureEl.getHeight();
                    rippleDiameter = Math.max(measureElWidth, measureElHeight);
                } else {
                    rippleDiameter = width > height ? width : height;
                }
                
                if (options.diameterLimit === undefined || options.diameterLimit === true) {
                    rippleDiameter = Math.min(rippleDiameter, Element.maxRippleDiameter);
                } else if (options.diameterLimit && options.diameterLimit !== false && options.diameterLimit !== 0) {
                    rippleDiameter = Math.min(rippleDiameter, options.diameterLimit);
                }
                
                center = [
                    offset[0] + width / 2,
                    offset[1] + height / 2
                ];
                if (unbound) {
                    if (options.fit !== false) {
                        
                        
                        rippleSize = rippleDiameter * 2.15;
                        
                        rippleBox = rippleParent.getRegion();
                        rippleBox.setPosition(rippleBox.getCenter()).setSize(rippleSize).translateBy(-rippleSize / 2, -rippleSize / 2);
                        
                        
                        
                        unboundEl = me.up(function(candidate) {
                            var fly = Ext.fly(candidate, 'ripple');
                            return !(candidate.tagName in me.noRippleTagMap) && !(fly.getStyle('display') in me.noRippleDisplayMap) && (fly.getRegion().contains(rippleBox));
                        }) || Ext.getBody();
                    } else {
                        unboundEl = rippleParent;
                    }
                }
                
                
                if (Ext.isString(event)) {
                    options.color = event;
                    event = null;
                } else if (event && !event.isEvent) {
                    event = new Ext.event.Event(event);
                }
                
                if (event && event.isEvent) {
                    
                    if (event.browserEvent.$preventRipple) {
                        return;
                    }
                    position = event.getXY();
                    event.browserEvent.$preventRipple = true;
                }
                
                
                pos = (!unbound && !options.centered && position) || center;
                posX = pos[0] - offset[0] - (rippleDiameter / 2);
                posY = pos[1] - offset[1] - (rippleDiameter / 2);
                
                
                rippleParent.addCls(ripplingTransitionCls);
                if (!unbound) {
                    rippleParent.addCls(ripplingCls);
                    
                    rippleContainer = rippleParent.child('.' + rippleContainerCls);
                } else {
                    
                    
                    unboundElData = unboundEl.getData();
                    rippleWrapper = unboundElData.rippleWrapper;
                    if (!rippleWrapper) {
                        
                        
                        unboundElData.rippleWrapper = rippleWrapper = unboundEl.insertFirst({
                            style: 'position: absolute; top: 0; left: 0',
                            cls: rippleWrapperCls + ' ' + ripplingCls + ' ' + ripplingUnboundCls
                        });
                    }
                }
                if (!rippleContainer) {
                    if (unbound) {
                        
                        
                        rippleContainer = rippleWrapper.append({
                            cls: rippleContainerCls
                        });
                        
                        rippleContainer.setXY(offset);
                    } else {
                        
                        
                        rippleContainer = rippleParent.append({
                            cls: rippleContainerCls
                        });
                    }
                }
                
                rippleBubble = rippleContainer.append({
                    cls: rippleBubbleCls
                });
                if (options.color !== 'default') {
                    rippleBubble.setStyle('backgroundColor', options.color || color);
                }
                rippleBubble.setWidth(rippleDiameter);
                rippleBubble.setHeight(rippleDiameter);
                rippleBubble.setTop(posY);
                rippleBubble.setLeft(posX);
                rippleClearFn = function() {
                    
                    
                    
                    if (!rippleParent.destroyed) {
                        rippleParent.$rippleClearTimeout = Ext.defer(function() {
                            rippleParent.removeCls([
                                ripplingCls,
                                ripplingTransitionCls
                            ]);
                            rippleParent.$rippleClearTimeout = null;
                        }, 50);
                    }
                };
                rippleDestructor = function() {
                    var ripple, timeout;
                    
                    rippleBubble.destroy();
                    
                    if (me.$ripples) {
                        delete me.$ripples[rippleBubble.id];
                    }
                    timeout = rippleParent.$rippleClearTimeout;
                    if (timeout) {
                        rippleParent.$rippleClearTimeout = Ext.undefer(timeout);
                    }
                    if (unbound) {
                        
                        
                        rippleContainer.destroy();
                        
                        ripple = rippleWrapper.child('.' + rippleContainerCls);
                        
                        if (!ripple) {
                            unboundElData.rippleWrapper = null;
                            rippleWrapper.destroy();
                            rippleClearFn();
                        }
                    } else {
                        
                        ripple = rippleContainer.child('.' + rippleBubbleCls);
                        
                        if (!ripple) {
                            rippleContainer.destroy();
                            rippleClearFn();
                        }
                    }
                };
                rippleDestructionTimer = Ext.defer(rippleDestructor, options.destroyTime || 1000, me);
                
                if (!me.$ripples) {
                    me.$ripples = {};
                }
                me.$ripples[rippleBubble.id] = {
                    timerId: rippleDestructionTimer,
                    destructor: rippleDestructor
                };
                rippleBubble.addCls(Ext.baseCSSPrefix + 'ripple');
            }
        },
        destroyAllRipples: function() {
            var me = this,
                rippleEl, ripple;
            for (rippleEl in me.$ripples) {
                ripple = me.$ripples[rippleEl];
                Ext.undefer(ripple.timerId);
                if (ripple.destructor) {
                    ripple.destructor();
                }
            }
            me.$ripples = null;
        },
        privates: {
            
            findTabbableElements: function(options) {
                var skipSelf, skipChildren, excludeRoot, includeSaved, includeHidden,
                    dom = this.dom,
                    cAttr = Ext.Element.tabbableSavedCounterAttribute,
                    selection = [],
                    idx = 0,
                    nodes, node, fly, i, len, tabIndex;
                if (!dom) {
                    return selection;
                }
                if (options) {
                    skipSelf = options.skipSelf;
                    skipChildren = options.skipChildren;
                    excludeRoot = options.excludeRoot;
                    includeSaved = options.includeSaved;
                    includeHidden = options.includeHidden;
                }
                excludeRoot = excludeRoot && Ext.getDom(excludeRoot);
                if (excludeRoot && excludeRoot.contains(dom)) {
                    return selection;
                }
                if (!skipSelf && ((includeSaved && dom.hasAttribute(cAttr)) || this.isTabbable(includeHidden))) {
                    selection[idx++] = dom;
                }
                if (skipChildren) {
                    return selection;
                }
                nodes = dom.querySelectorAll(Ext.Element.tabbableSelector);
                len = nodes.length;
                if (!len) {
                    return selection;
                }
                fly = new Ext.dom.Fly();
                
                
                
                for (i = 0; i < len; i++) {
                    node = nodes[i];
                    
                    
                    
                    
                    
                    
                    tabIndex = +node.getAttribute('tabIndex');
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    if (((includeSaved && node.hasAttribute(cAttr)) || (!(tabIndex < 0) && fly.attach(node).isTabbable(includeHidden))) && !(excludeRoot && (excludeRoot === node || excludeRoot.contains(node)))) {
                        selection[idx++] = node;
                    }
                }
                return selection;
            },
            
            saveTabbableState: function(options) {
                var counterAttr = Ext.Element.tabbableSavedCounterAttribute,
                    savedAttr = Ext.Element.tabbableSavedValueAttribute,
                    counter, nodes, node, i, len;
                
                
                
                
                
                
                if (!options || options.includeSaved == null) {
                    options = Ext.Object.chain(options || null);
                    options.includeSaved = true;
                }
                nodes = this.findTabbableElements(options);
                for (i = 0 , len = nodes.length; i < len; i++) {
                    node = nodes[i];
                    counter = +node.getAttribute(counterAttr);
                    if (counter > 0) {
                        node.setAttribute(counterAttr, ++counter);
                    } else {
                        
                        
                        if (node.hasAttribute('tabIndex')) {
                            node.setAttribute(savedAttr, node.getAttribute('tabIndex'));
                        } else 
                        {
                            node.setAttribute(savedAttr, 'none');
                        }
                        
                        
                        node.setAttribute('tabIndex', '-1');
                        node.setAttribute(counterAttr, '1');
                    }
                }
                return nodes;
            },
            
            restoreTabbableState: function(options) {
                var dom = this.dom,
                    counterAttr = Ext.Element.tabbableSavedCounterAttribute,
                    savedAttr = Ext.Element.tabbableSavedValueAttribute,
                    nodes = [],
                    skipSelf = options && options.skipSelf,
                    skipChildren = options && options.skipChildren,
                    reset = options && options.reset,
                    idx, counter, node, i, len;
                if (!dom) {
                    return this;
                }
                if (!skipChildren) {
                    nodes = Ext.Array.from(dom.querySelectorAll('[' + counterAttr + ']'));
                }
                if (!skipSelf) {
                    nodes.unshift(dom);
                }
                for (i = 0 , len = nodes.length; i < len; i++) {
                    node = nodes[i];
                    if (!node.hasAttribute(counterAttr) || !node.hasAttribute(savedAttr)) {
                        
                        continue;
                    }
                    counter = +node.getAttribute(counterAttr);
                    if (!reset && counter > 1) {
                        node.setAttribute(counterAttr, --counter);
                        
                        continue;
                    }
                    idx = node.getAttribute(savedAttr);
                    
                    if (idx === 'none') {
                        node.removeAttribute('tabIndex');
                    } else {
                        node.setAttribute('tabIndex', idx);
                    }
                    node.removeAttribute(savedAttr);
                    node.removeAttribute(counterAttr);
                }
                return nodes;
            },
            
            setTabIndex: function(tabIndex) {
                var dom = this.dom,
                    savedAttr = Ext.Element.tabbableSavedValueAttribute;
                if (dom.hasAttribute(savedAttr)) {
                    if (tabIndex == null) {
                        
                        dom.setAttribute(savedAttr, 'none');
                        dom.removeAttribute('tabIndex');
                    } else {
                        dom.setAttribute(savedAttr, tabIndex);
                    }
                } else {
                    if (tabIndex == null) {
                        dom.removeAttribute('tabIndex');
                    } else {
                        dom.setAttribute('tabIndex', tabIndex);
                    }
                }
            },
            doAddListener: function(eventName, fn, scope, options, order, caller, manager) {
                var me = this,
                    originalName = eventName,
                    observableDoAddListener, additiveEventName, translatedEventName;
                
                
                
                eventName = Ext.canonicalEventName(eventName);
                
                
                if (!me.blockedEvents[eventName]) {
                    observableDoAddListener = me.mixins.observable.doAddListener;
                    options = options || {};
                    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;
                            if (manager) {
                                options.managedName = originalName;
                            }
                            eventName = translatedEventName;
                        }
                    }
                    if (observableDoAddListener.call(me, eventName, fn, scope, options, order, caller, manager)) {
                        if (me.longpressEvents[eventName] && (++me.longpressListenerCount === 1)) {
                            me.on('MSHoldVisual', 'preventMsHoldVisual', me);
                        }
                    }
                    if (manager && translatedEventName) {
                        delete options.managedName;
                    }
                    
                    
                    
                    
                    delete options.type;
                }
            },
            doRemoveListener: function(eventName, fn, scope) {
                var me = this,
                    observableDoRemoveListener, translatedEventName, additiveEventName, removed;
                
                
                
                eventName = Ext.canonicalEventName(eventName);
                
                
                if (!me.blockedEvents[eventName]) {
                    observableDoRemoveListener = me.mixins.observable.doRemoveListener;
                    
                    
                    additiveEventName = me.additiveEvents[eventName];
                    if (additiveEventName) {
                        
                        
                        
                        
                        eventName = additiveEventName;
                        observableDoRemoveListener.call(me, eventName, fn, scope);
                    }
                    translatedEventName = me.eventMap[eventName];
                    if (translatedEventName) {
                        removed = observableDoRemoveListener.call(me, translatedEventName, fn, scope);
                    }
                    
                    
                    removed = observableDoRemoveListener.call(me, eventName, fn, scope) || removed;
                    if (removed) {
                        if (me.longpressEvents[eventName] && !--me.longpressListenerCount) {
                            me.un('MSHoldVisual', 'preventMsHoldVisual', me);
                        }
                    }
                }
            },
            _initEvent: function(eventName) {
                return (this.events[eventName] = new Ext.dom.ElementEvent(this, eventName));
            },
            _getDisplay: function() {
                var data = this.getData(),
                    display = data[ORIGINALDISPLAY];
                if (display === undefined) {
                    data[ORIGINALDISPLAY] = display = '';
                }
                return display;
            },
            
            _getPublisher: function(eventName, noTranslate) {
                var Publisher = Ext.event.publisher.Publisher,
                    publisher = Publisher.publishersByEvent[eventName],
                    isNative = noTranslate && !Ext.event.Event.gestureEvents[eventName];
                
                
                
                
                
                
                
                
                
                if (isNative || !publisher || (this.dom === window && eventName === 'resize')) {
                    publisher = Publisher.publishers.dom;
                }
                return publisher;
            },
            isFocusSuspended: function() {
                var data = this.peekData();
                return data && data.suspendFocusEvents;
            },
            preventMsHoldVisual: function(e) {
                e.preventDefault();
            },
            suspendFocusEvents: function() {
                if (!this.isFly) {
                    this.suspendEvent('focus', 'blur');
                }
                this.getData().suspendFocusEvents = true;
            },
            resumeFocusEvents: function() {
                function resumeFn() {
                    var data;
                    if (!this.destroyed) {
                        data = this.getData();
                        if (data) {
                            data.suspendFocusEvents = false;
                        }
                        if (!this.isFly) {
                            this.resumeEvent('focus', 'blur');
                        }
                    }
                }
                if (!this.destroyed && this.getData().suspendFocusEvents) {
                    if (Ext.isIE && !this.isFly) {
                        this.resumeFocusEventsTimer = Ext.asap(resumeFn, this);
                    } else {
                        resumeFn.call(this);
                    }
                }
            }
        },
        deprecated: {
            '5.0': {
                methods: {
                    
                    getHTML: 'getHtml',
                    
                    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
                            };
                        }
                    },
                    
                    isTransparent: function(prop) {
                        var value = this.getStyle(prop);
                        return value ? transparentRe.test(value) : false;
                    },
                    
                    purgeAllListeners: 'clearListeners',
                    
                    removeAllListeners: 'clearListeners',
                    
                    setHTML: 'setHtml'
                }
            }
        }
    };
}, function(Element) {
    var DOC = document,
        docEl = DOC.documentElement,
        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;
    prototype._init(Element);
    delete prototype._init;
    
    Ext.id = function(obj, prefix) {
        var el = obj && 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[mouseover] = 'pointerover';
        eventMap[mouseout] = 'pointerout';
        eventMap[mouseenter] = 'pointerenter';
        
        
        
        if (!Ext.isIE11) {
            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[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.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.styleHooks.opacity = {
        name: 'opacity',
        afterSet: function(dom, value, el) {
            var shadow = el.shadow;
            if (shadow) {
                shadow.setOpacity(value);
            }
        }
    };
    
    prototype.getTrueXY = prototype.getXY;
    
    Ext.getViewportHeight = Element.getViewportHeight;
    
    Ext.getViewportWidth = Element.getViewportWidth;
    
    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 typeof el === 'string' ? Ext.getElementById(el) : 'dom' in el ? el.dom : 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);
                Ext._bodyEl.skipGarbageCollection = true;
            }
            return Ext._bodyEl;
        },
        
        getHead: function() {
            if (!Ext._headEl) {
                Ext._headEl = Ext.get(DOC.head || DOC.getElementsByTagName('head')[0]);
                Ext._headEl.skipGarbageCollection = true;
            }
            return Ext._headEl;
        },
        
        getDoc: function() {
            if (!Ext._docEl) {
                Ext._docEl = Ext.get(DOC);
                Ext._docEl.skipGarbageCollection = true;
            }
            return Ext._docEl;
        },
        
        getWin: function() {
            if (!Ext._winEl) {
                Ext._winEl = Ext.get(window);
                Ext._winEl.skipGarbageCollection = true;
            }
            return Ext._winEl;
        },
        
        removeNode: function(node) {
            node = node.dom || node;
            var id = node && node.id,
                el = Ext.cache[id],
                parent;
            if (el) {
                el.destroy();
            } else if (node && (node.nodeType === 3 || 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 === null && 
        
        
        
        
        
        
        ((Ext.isIE8 ? DOC.all[dom.id] : DOC.getElementById(dom.id)) !== dom) && 
        
        !(Ext.detachedBodyEl && Ext.detachedBodyEl.isAncestor(dom))));
    };
    Ext.onInternalReady(function() {
        var bodyCls = [],
            theme;
        
        
        
        Ext.getDoc().on('selectstart', function(ev, dom) {
            var selectableCls = Element.selectableCls,
                unselectableCls = Element.unselectableCls,
                tagName = dom && dom.tagName,
                el = new Ext.dom.Fly();
            tagName = tagName && tagName.toLowerCase();
            
            
            
            if (tagName === 'input' || tagName === 'textarea') {
                return;
            }
            
            while (dom && dom.nodeType === 1 && dom !== DOC.documentElement) {
                el.attach(dom);
                
                if (el.hasCls(selectableCls)) {
                    return;
                }
                
                if (el.hasCls(unselectableCls)) {
                    ev.stopEvent();
                    return;
                }
                dom = dom.parentNode;
            }
        });
        if (Ext.os.is.Android || (Ext.os.is.Windows && Ext.supports.Touch)) {
            
            
            
            
            
            
            var win = Ext.getWin();
            Element._documentWidth = Element._viewportWidth = docEl.clientWidth;
            Element._documentHeight = Element._viewportHeight = docEl.clientHeight;
            win.on({
                
                
                
                
                focusin: '_onWindowFocusChange',
                focusout: '_onWindowFocusChange',
                
                
                
                
                
                
                
                pointerup: '_onWindowFocusChange',
                capture: true,
                delegated: false,
                delay: 1,
                scope: Element
            });
            win.on({
                
                resize: '_onWindowResize',
                priority: 2000,
                scope: Element
            });
        }
        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.isIE10p) {
            bodyCls.push(Ext.baseCSSPrefix + 'ie10p');
        }
        if (Ext.isIE11) {
            bodyCls.push(Ext.baseCSSPrefix + 'ie11');
        }
        if (Ext.isEdge) {
            bodyCls.push(Ext.baseCSSPrefix + 'edge');
        }
        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.isSafari && Ext.browser.version.isLessThan(11)) {
            bodyCls.push(Ext.baseCSSPrefix + 'safari10m');
        }
        if (Ext.isSafari9) {
            bodyCls.push(Ext.baseCSSPrefix + 'safari9');
        }
        if (Ext.isSafari && Ext.browser.version.isLessThan(9)) {
            bodyCls.push(Ext.baseCSSPrefix + 'safari8m');
        }
        if (Ext.isChrome) {
            bodyCls.push(Ext.baseCSSPrefix + 'chrome');
        }
        if (Ext.isMac) {
            bodyCls.push(Ext.baseCSSPrefix + 'mac');
        }
        if (Ext.isWindows) {
            bodyCls.push(Ext.baseCSSPrefix + 'windows');
        }
        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');
        }
        if (Ext.os.deviceType) {
            bodyCls.push(Ext.baseCSSPrefix + Ext.os.deviceType.toLowerCase());
        }
        if (Ext.os.is.BlackBerry) {
            bodyCls.push(Ext.baseCSSPrefix + 'bb');
            if (Ext.browser.userAgent.match(/Kbd/gi)) {
                
                bodyCls.push(Ext.baseCSSPrefix + 'bb-keyboard');
            }
        }
        if (Ext.os.is.iOS && Ext.isSafari) {
            bodyCls.push(Ext.baseCSSPrefix + 'mobile-safari');
        }
        if (Ext.os.is.iOS && Ext.browser.is.WebView && !Ext.browser.is.Standalone) {
            
            bodyCls.push(Ext.baseCSSPrefix + 'ios-native');
        }
        Ext.getBody().addCls(bodyCls);
        theme = Ext.theme;
        if (theme && theme.getDocCls) {
            
            Ext.fly(document.documentElement).addCls(theme.getDocCls());
        }
    }, null, {
        priority: 1500
    });
});


Ext.define('Ext.overrides.dom.Element', {
    override: 'Ext.dom.Element',
    _positionTopLeft: [
        'position',
        'top',
        'left'
    ],
    setX: function(x, animate) {
        return this.setXY([
            x,
            this.getY()
        ], animate);
    },
    setXY: function(xy, animate) {
        var me = this;
        if (!animate) {
            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);
    },
    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
        };
    },
    
    visit: function(options) {
        var me = this,
            dom = me.dom,
            skipSelf = options.skipSelf,
            excludeRoot = options.excludeRoot,
            callback = options.callback || options.fn,
            scope = options.scope || this,
            reverse = options.reverse,
            selector = options.selector,
            whatToShow = options.include || NodeFilter.SHOW_ELEMENT,
            
            
            treeWalker = document.createTreeWalker(dom, whatToShow, null, false),
            result = null,
            node = dom;
        
        if (reverse) {
            for (node = dom.lastElementChild; node.lastElementChild; node = node.lastElementChild){}
            treeWalker.currentNode = node;
        }
        
        for (; result !== false && node; node = reverse ? treeWalker.previousNode() : treeWalker.nextNode()) {
            if (!(excludeRoot && excludeRoot.contains(node))) {
                if (!(node === dom && skipSelf)) {
                    if (!(selector && !node[Ext.supports.matchesSelector](selector))) {
                        result = Ext.callback(callback, scope, [
                            node
                        ]);
                    }
                }
            }
        }
        return result;
    }
});


Ext.define('Ext.GlobalEvents', {
    extend: Ext.mixin.Observable,
    alternateClassName: 'Ext.globalEvents',
    observableType: 'global',
    singleton: true,
    
    resizeBuffer: 100,
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    idleEventMask: {
        mousemove: 1,
        touchmove: 1,
        pointermove: 1,
        MSPointerMove: 1,
        unload: 1
    },
    
    windowListeners: {
        resize: {
            fn: 'fireResize'
        }
    },
    constructor: function() {
        var me = this;
        me.callParent();
        Ext.onInternalReady(function() {
            
            
            me.attachListeners();
        });
    },
    setPressedComponent: function(component, e) {
        var me = this,
            pressedComponent = me.pressedComponent;
        if (pressedComponent && pressedComponent.onRelease) {
            pressedComponent.onRelease(e);
        }
        me.pressedComponent = component;
        if (component) {
            me.pressedScrollStart = Ext.on({
                scrollstart: function() {
                    me.setPressedComponent(null, e);
                },
                destroyable: true
            });
        } else {
            me.pressedScrollStart = Ext.destroy(me.pressedScrollStart);
        }
    },
    attachListeners: function() {
        var me = this,
            win = Ext.getWin(),
            winListeners = me.windowListeners;
        me.onlineState = Ext.isOnline();
        
        me.curHeight = Ext.Element.getViewportHeight();
        me.curWidth = Ext.Element.getViewportWidth();
        win.on({
            scope: me,
            online: 'handleOnlineChange',
            offline: 'handleOnlineChange'
        });
        
        
        
        
        me.fireResize.$skipTimerCheck = true;
        
        
        if (winListeners) {
            winListeners.scope = me;
            
            if (Ext.isModern) {
                winListeners.resize.onFrame = true;
            } else {
                winListeners.resize.buffer = me.resizeBuffer;
            }
            win.on(winListeners);
        }
        Ext.getDoc().on({
            touchstart: 'fireMouseDown',
            mousedown: 'fireMouseDown',
            mouseup: 'fireMouseUp',
            touchend: 'fireMouseUp',
            drop: 'fireMouseUp',
            dragend: 'fireMouseUp',
            scope: me
        });
    },
    fireMouseDown: function(e) {
        this.fireEvent('mousedown', e);
        
        
        
        Ext.ComponentManager.handleDocumentMouseDown(e);
    },
    fireMouseUp: function(e) {
        this.fireEvent('mouseup', e);
        this.setPressedComponent(null, e);
    },
    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;
            if (me.hasListeners.resize) {
                me.fireEvent('resize', w, h);
            }
        }
    },
    handleOnlineChange: function() {
        var online = Ext.isOnline();
        if (online !== this.onlineState) {
            this.onlineState = online;
            this.fireEvent('onlinechange', online);
        }
    }
}, function(GlobalEvents) {
    
    Ext.on = function() {
        return GlobalEvents.addListener.apply(GlobalEvents, arguments);
    };
    
    Ext.un = function() {
        return GlobalEvents.removeListener.apply(GlobalEvents, arguments);
    };
    
    Ext.fireEvent = function() {
        return GlobalEvents.fireEvent.apply(GlobalEvents, arguments);
    };
    
    Ext.fireIdle = function() {
        if (GlobalEvents.hasListeners.idle && !Ext._suppressIdle) {
            GlobalEvents.fireEventArgs('idle');
        }
        Ext._suppressIdle = false;
    };
    Ext._suppressIdle = false;
});


Ext.define('Ext.Glyph', {
    
    isGlyph: true,
    
    
    
    
    constructor: function(glyph) {
        glyph && this.setGlyph(glyph);
    },
    
    setGlyph: function(glyph) {
        var glyphParts;
        this.glyphConfig = glyph;
        if (typeof glyph === 'string') {
            glyphParts = glyph.split('@');
            
            
            
            
            if (isNaN(glyph = isNaN(glyphParts[0]) ? parseInt('0' + glyphParts[0], 16) : parseInt(glyphParts[0], 10)) || !glyph) {
                glyph = glyphParts[0].charCodeAt(0);
            }
            this.fontFamily = glyphParts[1] || Ext._glyphFontFamily;
        } else {
            this.fontFamily = Ext._glyphFontFamily;
        }
        this.codepoint = glyph;
        this.character = Ext.String.fromCodePoint(this.codepoint);
        return this;
    },
    getStyle: function() {
        return {
            'font-family': this.fontFamily
        };
    },
    isEqual: function(other) {
        return other && other.isGlyph && other.codepoint === this.codepoint && other.fontFamily === this.fontFamily;
    },
    statics: (function() {
        var instance;
        return {
            
            fly: function(glyph) {
                return glyph.isGlyph ? glyph : (instance || (instance = new Ext.Glyph())).setGlyph(glyph);
            }
        };
    })()
});


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 || val.isInstance) {
                        
                        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.raise({
                sourceClass: "Ext.JSON",
                sourceMethod: "decode",
                msg: "You're trying to decode an invalid JSON String: " + json
            });
        }
    };
    me.encodeMSDate = encodeMSDate;
    
    if (!Ext.util) {
        Ext.util = {};
    }
    Ext.util.JSON = me;
    
    Ext.encode = me.encode;
    
    Ext.decode = me.decode;
})());







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.getRefOwner(),
            isContainer = me.isContainer,
            parent, inheritedStateInner, getInner, ownerLayout;
        if (!inheritedState || inheritedState.invalid) {
            
            
            
            
            parent = me.getRefOwner();
            ownerLayout = me.ownerLayout;
            if (ownerCt) {
                
                
                
                
                
                getInner = ownerLayout ? ownerLayout === ownerCt.layout : true;
            }
            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;
    },
    
    resolveListenerScope: function(defaultScope, skipThis) {
        var me = this,
            hasSkipThis = (typeof skipThis === 'boolean'),
            namedScope = Ext._namedScopes[defaultScope],
            ret;
        if (!namedScope) {
            
            
            
            
            ret = me.getInheritedConfig('defaultListenerScope', hasSkipThis ? skipThis : true) || defaultScope || me;
        } else if (namedScope.isController) {
            
            
            
            
            ret = me.getInheritedConfig('controller', hasSkipThis ? skipThis : !namedScope.isSelf);
        } else if (namedScope.isOwner) {
            ret = me.getRefOwner();
        } else if (namedScope.isSelf) {
            
            
            ret = me.getInheritedConfig('defaultListenerScope', hasSkipThis && skipThis) || me;
        } else if (namedScope.isThis) {
            
            
            ret = me;
        }
        return ret || null;
    },
    
    resolveSatelliteListenerScope: function(satellite, defaultScope) {
        var me = this,
            namedScope = Ext._namedScopes[defaultScope],
            ret;
        
        
        
        
        
        
        
        if (!namedScope) {
            ret = me.getInheritedConfig('defaultListenerScope') || defaultScope || me;
        } else if (namedScope.isController) {
            ret = me.getInheritedConfig('controller');
        } else if (namedScope.isSelf) {
            ret = me.getInheritedConfig('defaultListenerScope') || satellite;
        } else if (namedScope.isThis) {
            ret = satellite;
        }
        return ret || null;
    },
    
    lookupNameHolder: function(skipThis) {
        return this.getInheritedConfig('nameHolder', skipThis !== false) || null;
    },
    
    lookupReferenceHolder: function(skipThis) {
        return this.getInheritedConfig('referenceHolder', skipThis !== false) || null;
    },
    
    getRefOwner: function() {
        var me = this;
        
        
        
        
        
        return me.ownerCmp || me.ownerCt || me.parent || me.$initParent || me.floatParent;
    },
    
    bubble: function(fn, scope, args) {
        for (var target = this; target; target = target.getRefOwner()) {
            if (fn.apply(scope || target, args || [
                target
            ]) === false) {
                break;
            }
        }
    },
    
    isDescendantOf: function(ancestor) {
        return ancestor ? ancestor.isAncestor(this) : false;
    },
    
    isAncestor: function(possibleDescendant) {
        while (possibleDescendant) {
            if (possibleDescendant.getRefOwner() === this) {
                return true;
            }
            possibleDescendant = possibleDescendant.getRefOwner();
        }
        return false;
    },
    
    
    invalidateInheritedState: function() {
        var inheritedState = this.inheritedState;
        if (inheritedState) {
            
            
            
            inheritedState.invalid = true;
            
            
            
            
            
            delete this.inheritedState;
        }
    },
    privates: {
        _fixName: function() {
            var me = this,
                owner;
            if (me.name) {
                owner = me.lookupNameHolder();
                if (owner && !owner.destroyed) {
                    owner.attachNameRef(me);
                }
            }
        },
        
        _fixReference: function() {
            var me = this,
                refHolder;
            if (me.reference) {
                refHolder = me.lookupReferenceHolder();
                if (refHolder) {
                    refHolder.attachReference(me);
                }
            }
        },
        
        onInheritedAdd: function(parent, instanced) {
            var me = this;
            
            
            
            if (me.inheritedState && instanced) {
                me.invalidateInheritedState();
            }
            if (me.name || me.reference) {
                Ext.ComponentManager.markReferencesDirty();
            }
        },
        
        onInheritedRemove: function(destroying) {
            var me = this;
            if (me.name || me.reference) {
                Ext.ComponentManager.markReferencesDirty();
            }
            if (me.inheritedState && !destroying) {
                me.invalidateInheritedState();
            }
        }
    }
}, 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);
            }
        },
        
        
        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,
    
    nameable: false,
    
    shareableName: false,
    
    reference: null,
    
    validRefRe: /^[a-z_][a-z0-9_]*$/i,
    getReference: function() {
        
        return this.reference;
    },
    
    initInheritedState: function(inheritedState) {
        var me = this,
            reference = me.reference,
            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;
        }
    },
    
    isBound: function(name) {
        var bind = this.getBind();
        return !!(bind && (bind[name || this.defaultBindProperty]));
    },
    
    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,
            state = me.publishedState,
            binds = me.getBind(),
            binding = binds && property && binds[property],
            count = 0,
            name, publishes, vm, path;
        
        if (!(arguments.length === 0 || arguments.length === 2)) {
            Ext.raise('publishState must either be called with no args, or with both name AND value passed');
        }
        
        if (binding && !binding.syncing && !binding.isReadOnly()) {
            
            
            
            
            
            
            if (binding.calls || !(value == null || value === me.getInitialConfig()[property])) {
                binding.setValue(value);
            }
        }
        if (!(publishes = me.getPublishes())) {
            return;
        }
        if (!(vm = me.lookupViewModel())) {
            return;
        }
        
        
        if (!(path = me.viewModelKey)) {
            return;
        }
        state = state || (me.publishedState = {});
        if (property) {
            if (!publishes[property]) {
                return;
            }
            
            
            if (!(value && value.constructor === Object) && !(value instanceof Array)) {
                if (state[property] === value) {
                    return;
                }
            }
            path += '.';
            path += property;
        } else {
            for (name in publishes) {
                ++count;
                state[name] = me.getConfig(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) {
            if (!binds) {
                return binds;
            }
            var me = this,
                viewModel = me.lookupViewModel(),
                twoWayable = me.getTwoWayBindable(),
                getBindTemplateScope = me._getBindTemplateScope,
                b, property, descriptor, destroy;
            me.$hasBinds = true;
            if (!currentBindings || typeof currentBindings === 'string') {
                currentBindings = {};
            }
            
            if (!viewModel) {
                Ext.raise('Cannot use bind config without a viewModel');
            }
            
            if (Ext.isString(binds)) {
                
                if (!me.defaultBindProperty) {
                    Ext.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 && b.isBinding) {
                    b.destroy();
                    b = null;
                    destroy = true;
                }
                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.raise('Cannot bind ' + property + ' on ' + me.$className + ' - missing a ' + b._config.names.set + ' method.');
                    }
                }
                
                if (destroy) {
                    delete currentBindings[property];
                } else {
                    currentBindings[property] = b;
                }
                if (twoWayable && twoWayable[property]) {
                    if (destroy) {
                        me.clearBindableUpdater(property);
                    } else if (!b.isReadOnly()) {
                        me.addBindableUpdater(property);
                    }
                }
            }
            return currentBindings;
        },
        applyController: function(controller) {
            if (controller) {
                controller = Ext.Factory.controller(controller);
                controller.setView(this);
            }
            
            
            this.controller = controller;
            return controller;
        },
        updatePublishes: function(all) {
            var me = this;
            if (me.lookupViewModel()) {
                for (var property in all) {
                    me.addBindableUpdater(property);
                }
            }
            return all;
        },
        
        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) {
                return null;
            }
            if (!viewModel.isViewModel) {
                config = {
                    parent: me.lookupViewModel(true),
                    
                    
                    
                    view: me
                };
                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();
        },
        clearBindableUpdater: function(property) {
            var me = this,
                configs = me.self.$config.configs,
                cfg = configs[property],
                updateName;
            if (cfg && me.hasOwnProperty(updateName = cfg.names.update)) {
                if (me[updateName].$bindableUpdater) {
                    delete me[updateName];
                }
            }
        },
        destroyBindable: function() {
            var me = this,
                viewModel = me.getConfig('viewModel', true),
                session = me.getConfig('session', true),
                controller = me.getController();
            if (viewModel && viewModel.isViewModel) {
                viewModel.destroy();
                me.setViewModel(null);
            }
            if (session && session.isSession) {
                if (session.getAutoDestroy()) {
                    session.destroy();
                }
                me.setSession(null);
            }
            if (controller) {
                me.setController(null);
                controller.destroy();
            }
        },
        
        initBindable: function() {
            var me = this,
                controller = me.controller;
            me.initBindable = Ext.emptyFn;
            me.getBind();
            me.getPublishes();
            
            
            
            
            
            if (!me.viewModel) {
                
                me.getViewModel();
            }
            if (controller) {
                controller.initBindings();
            }
            if (me.reference) {
                
                
                me.publishState();
            }
        },
        
        isSyncing: function(name) {
            var bindings = this.getBind(),
                ret = false,
                binding;
            if (bindings) {
                binding = bindings[name];
                if (binding) {
                    ret = binding.syncing > 0;
                }
            }
            return ret;
        },
        
        makeBindableUpdater: function(cfg) {
            var updateName = cfg.names.update,
                fn = function(newValue, oldValue) {
                    var me = this,
                        updater = me.self.prototype[updateName];
                    if (updater) {
                        updater.call(me, newValue, oldValue);
                    }
                    
                    me.publishState(cfg.name, me[cfg.getInternalName(me)]);
                };
            fn.$bindableUpdater = true;
            return fn;
        },
        notifyIf: function(skipThis) {
            var vm = this.lookupViewModel(skipThis);
            if (vm) {
                vm.notify();
            }
        },
        onBindNotify: function(value, oldValue, binding) {
            binding.syncing = (binding.syncing + 1) || 1;
            this[binding._config.names.set](value);
            --binding.syncing;
        },
        removeBindings: function() {
            var me = this,
                bindings, key, binding;
            if (me.$hasBinds) {
                bindings = me.getBind();
                if (bindings && typeof bindings !== 'string') {
                    for (key in bindings) {
                        binding = bindings[key];
                        binding.destroy();
                        binding._config = binding.getTemplateScope = null;
                    }
                }
            }
            me.setBind(null);
        },
        
        updateSession: function(session) {
            var state = this.getInherited();
            if (session) {
                state.session = session;
            } else {
                delete state.session;
            }
        },
        
        updateViewModel: function(viewModel, oldViewModel) {
            var me = this,
                state = me.getInherited(),
                controller = me.getController();
            if (viewModel) {
                me.hasVM = true;
                state.viewModel = viewModel;
                viewModel.setView(me);
                if (controller) {
                    controller.initViewModel(viewModel);
                }
            } else {
                delete state.viewModel;
            }
            
            
            me.viewModel = viewModel;
        }
    }
});



Ext.define('Ext.mixin.ComponentDelegation', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'componentDelegation'
    },
    privates: {
        
        addDelegatedListener: function(eventName, fn, scope, options, order, caller, manager) {
            var me = this,
                delegatedEvents, event, priority;
            eventName = Ext.canonicalEventName(eventName);
            
            
            
            
            order = order || options.order;
            if (order) {
                priority = (options && options.priority);
                if (!priority) {
                    
                    
                    options = options ? Ext.Object.chain(options) : {};
                    options.priority = me.$orderToPriority[order];
                }
            }
            
            if (options.target) {
                Ext.raise("Cannot add '" + eventName + "' listener to component: '" + me.id + "' - 'delegate' and 'target' event options are incompatible.");
            }
            
            
            
            
            delegatedEvents = me.$delegatedEvents || (me.$delegatedEvents = {});
            event = delegatedEvents[eventName] || (delegatedEvents[eventName] = new Ext.util.Event(me, eventName));
            if (event.addListener(fn, scope, options, caller, manager)) {
                me.$hasDelegatedListeners._incr_(eventName);
            }
        },
        
        clearDelegatedListeners: function() {
            var me = this,
                delegatedEvents = me.$delegatedEvents,
                eventName, event, listenerCount;
            if (delegatedEvents) {
                for (eventName in delegatedEvents) {
                    event = delegatedEvents[eventName];
                    listenerCount = event.listeners.length;
                    event.clearListeners();
                    me.$hasDelegatedListeners._decr_(eventName, listenerCount);
                    delete delegatedEvents[eventName];
                }
            }
        },
        
        doFireDelegatedEvent: function(eventName, args) {
            var me = this,
                ret = true,
                owner, delegatedEvents, event;
            
            
            
            
            
            
            if (me.$hasDelegatedListeners[eventName]) {
                owner = me.getRefOwner();
                while (owner) {
                    delegatedEvents = owner.$delegatedEvents;
                    if (delegatedEvents) {
                        event = delegatedEvents[eventName];
                        if (event) {
                            ret = event.fireDelegated(me, args);
                            if (ret === false) {
                                break;
                            }
                        }
                    }
                    owner = owner.getRefOwner();
                }
            }
            return ret;
        },
        
        removeDelegatedListener: function(eventName, fn, scope) {
            var me = this,
                delegatedEvents = me.$delegatedEvents,
                event;
            if (delegatedEvents) {
                event = delegatedEvents[eventName];
                if (event && event.removeListener(fn, scope)) {
                    me.$hasDelegatedListeners._decr_(eventName);
                    if (event.listeners.length === 0) {
                        delete delegatedEvents[eventName];
                    }
                }
            }
        },
        destroyComponentDelegation: function() {
            if (this.clearPropertiesOnDestroy) {
                this.$delegatedEvents = null;
            }
        }
    },
    onClassMixedIn: function(T) {
        
        
        
        
        
        
        
        
        function HasListeners() {}
        T.prototype.HasListeners = T.HasListeners = HasListeners;
        HasListeners.prototype = T.hasListeners = new Ext.mixin.ComponentDelegation.HasDelegatedListeners();
    }
}, function(ComponentDelegation) {
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    function HasDelegatedListeners() {}
    ComponentDelegation.HasDelegatedListeners = HasDelegatedListeners;
    HasDelegatedListeners.prototype = ComponentDelegation.prototype.$hasDelegatedListeners = new Ext.mixin.Observable.HasListeners();
});


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));
    },
    
    
    getCmp: function() {
        return this.cmp;
    },
    
    setCmp: function(host) {
        this.cmp = host;
    },
    
    
    
    init: Ext.emptyFn,
    
    destroy: function() {
        this.cmp = this.pluginConfig = null;
        this.callParent();
    },
    onClassExtended: function(cls, data, hooks) {
        var alias = data.alias,
            prototype = cls.prototype;
        
        if (alias && !data.ptype) {
            if (Ext.isArray(alias)) {
                alias = alias[0];
            }
            prototype.ptype = alias.split('plugin.')[1];
        }
    },
    resolveListenerScope: function(defaultScope) {
        var me = this,
            cmp = me.getCmp(),
            scope;
        if (cmp) {
            scope = cmp.resolveSatelliteListenerScope(me, defaultScope);
        }
        
        
        
        
        return scope || me.mixins.observable.resolveListenerScope.call(me, defaultScope);
    },
    statics: {
        decode: function(plugins, typeProp, include) {
            if (plugins) {
                var type = Ext.typeOf(plugins),
                    
                    entry, key, obj, value;
                if (type === 'string') {
                    obj = {};
                    
                    
                    obj[typeProp] = plugins;
                    plugins = [
                        obj
                    ];
                } else if (plugins.isInstance) {
                    plugins = [
                        plugins
                    ];
                } else if (type === 'object') {
                    if (plugins[typeProp]) {
                        plugins = [
                            plugins
                        ];
                    } else {
                        obj = include ? Ext.merge(Ext.clone(include), plugins) : plugins;
                        plugins = [];
                        for (key in obj) {
                            if (!(value = obj[key])) {
                                
                                continue;
                            }
                            entry = {
                                id: key
                            };
                            entry[typeProp] = key;
                            if (key === 'responsive') {
                                entry.weight = -1000;
                            }
                            Ext.apply(entry, value);
                            plugins.push(entry);
                        }
                        plugins.sort(Ext.weightSortFn);
                    }
                }
                
                else if (type !== 'array') {
                    Ext.raise('Invalid value for "plugins" config ("' + type + '"');
                } else 
                {
                    plugins = plugins.slice();
                }
            }
            
            return plugins;
        }
    }
});


Ext.define('Ext.mixin.Pluggable', function(Pluggable) {
    return {
        config: {
            
            plugins: null
        },
        
        addPlugin: function(plugin) {
            var me = this,
                plugins = me.getPlugins();
            if (plugins) {
                plugin = me.createPlugin(plugin);
                plugin.init(me);
                plugins.push(plugin);
            } else {
                me.setPlugins(plugin);
                plugin = me.getPlugins()[0];
            }
            return plugin;
        },
        
        destroyPlugin: function(plugin) {
            return this.removePlugin(plugin, true);
        },
        
        findPlugin: function(type) {
            var plugins = this.getPlugins(),
                n = plugins && plugins.length,
                i, plugin, ret;
            for (i = 0; i < n && !ret; i++) {
                plugin = plugins[i];
                
                
                if (plugin.type === type || plugin.ptype === type) {
                    ret = plugin;
                }
            }
            return ret || null;
        },
        
        getPlugin: function(id) {
            var plugins = this.getPlugins(),
                n = plugins && plugins.length,
                i, plugin, ret;
            for (i = 0; i < n && !ret; i++) {
                plugin = plugins[i];
                
                if (plugin.id === id || plugin.pluginId === id) {
                    ret = plugin;
                }
            }
            return ret || null;
        },
        
        removePlugin: function(plugin, destroy) {
            var plugins = this.getPlugins(),
                i = plugins && plugins.length || 0,
                p;
            while (i-- > 0) {
                p = plugins[i];
                if (p === plugin || p.id === plugin) {
                    plugins.splice(i, 1);
                    if (destroy) {
                        if (p.destroy) {
                            p.destroy();
                        }
                    } else if (p.detachCmp) {
                        p.detachCmp();
                        if (p.setCmp) {
                            p.setCmp(null);
                        }
                    }
                    break;
                }
                p = null;
            }
            return p;
        },
        privates: {
            statics: {
                idSeed: 0
            },
            
            activatePlugin: function(type) {
                var me = this,
                    config = me.initialConfig,
                    plugins = config && config.plugins,
                    ret = null,
                    i, include, p;
                if (plugins) {
                    include = me.config.plugins;
                    include = (include && typeof include === 'object') ? include : null;
                    plugins = Ext.plugin.Abstract.decode(plugins, 'type', include);
                    for (i = plugins.length; i-- > 0; ) {
                        p = plugins[i];
                        if (p === type || p.type === type) {
                            me.initialConfig = config = Ext.apply({}, config);
                            config.plugins = plugins;
                            
                            
                            
                            plugins[i] = ret = me.createPlugin(p);
                            break;
                        }
                    }
                }
                return ret;
            },
            
            applyPlugins: function(plugins, oldPlugins) {
                var me = this,
                    oldCount = oldPlugins && oldPlugins.length || 0,
                    count, i, plugin;
                
                
                if (plugins) {
                    plugins = Ext.plugin.Abstract.decode(plugins, 'type');
                }
                
                
                
                for (i = 0; i < oldCount; ++i) {
                    oldPlugins[i].$dead = true;
                }
                
                
                
                
                
                count = plugins && plugins.length || 0;
                for (i = 0; i < count; ++i) {
                    plugins[i] = me.createPlugin(plugins[i]);
                }
                
                
                
                
                for (i = 0; i < count; ++i) {
                    plugin = plugins[i];
                    if (plugin.$dead) {
                        
                        delete plugin.$dead;
                    } else 
                    {
                        plugin.init(me);
                    }
                }
                
                
                
                for (i = 0; i < oldCount; ++i) {
                    if ((plugin = oldPlugins[i]).$dead) {
                        delete plugin.$dead;
                        Ext.destroy(plugin);
                    }
                }
                return plugins;
            },
            
            createPlugin: function(config) {
                if (typeof config === 'string') {
                    config = {
                        type: config
                    };
                }
                var ret = config;
                if (!config.isInstance) {
                    
                    config.cmp = this;
                    ret = Ext.factory(config, null, null, 'plugin');
                    
                    delete config.cmp;
                }
                if (!ret.id) {
                    ret.id = ++Pluggable.idSeed;
                }
                if (ret.setCmp) {
                    ret.setCmp(this);
                }
                return ret;
            }
        }
    };
});


Ext.define('Ext.mixin.Keyboard', function(Keyboard) {
    return {
        extend: Ext.Mixin,
        mixinConfig: {
            id: 'keyboard'
        },
        config: {
            
            keyMap: {
                $value: null,
                cached: true,
                merge: function(value, baseValue, cls, mixin) {
                    
                    if (value === null) {
                        return value;
                    }
                    
                    
                    
                    
                    
                    var ret = (baseValue && !cls.isInstance) ? Ext.Object.chain(baseValue) : {},
                        key, ucKey, v, vs;
                    for (key in value) {
                        if (key !== 'scope') {
                            ucKey = key.toUpperCase();
                            if (!mixin || ret[ucKey] === undefined) {
                                
                                v = value[key];
                                if (v) {
                                    if (typeof v === 'string' || typeof v === 'function') {
                                        v = {
                                            handler: v
                                        };
                                    } else {
                                        v = Ext.apply({
                                            handler: v.fn
                                        }, 
                                        v);
                                    }
                                    vs = v.scope || value.scope || 'self';
                                    v.scope = (vs === 'controller') ? 'self.controller' : vs;
                                }
                                ret[ucKey] = v;
                            }
                        }
                    }
                    return ret;
                }
            },
            
            keyMapEnabled: null
        },
        
        keyMapTarget: 'el',
        applyKeyMap: function(keyMap, existingKeyMap) {
            var me = this,
                
                
                own = me.hasOwnProperty('config');
            if (own && existingKeyMap && existingKeyMap.$owner !== me) {
                
                
                existingKeyMap = Ext.apply({}, existingKeyMap);
            }
            keyMap = keyMap ? Keyboard.combineKeyMaps(existingKeyMap, keyMap, own && me) : null;
            if (me._keyMapReady) {
                me.setKeyMapListener(keyMap && me.getKeyMapEnabled());
            }
            return keyMap;
        },
        
        initKeyMap: function() {
            var me = this,
                enabled = me.getKeyMapEnabled();
            me._keyMapReady = true;
            if (enabled === null) {
                me.setKeyMapEnabled(true);
            } else {
                me.setKeyMapListener(enabled && me.getKeyMap());
            }
        },
        disableKeyMapGroup: function(group) {
            this.setKeyMapGroupEnabled(group, false);
        },
        enableKeyMapGroup: function(group) {
            this.setKeyMapGroupEnabled(group, true);
        },
        setKeyMapGroupEnabled: function(group, state) {
            var me = this,
                disabledGroups = me.disabledKeyMapGroups || (me.disabledKeyMapGroups = {});
            disabledGroups[group] = !state;
        },
        updateKeyMapEnabled: function(enabled) {
            this.setKeyMapListener(enabled && this._keyMapReady && this.getKeyMap());
        },
        privates: {
            
            _keyMapListenCount: 0,
            
            _keyMapReady: false,
            
            comparePriorities: function(lhs, rhs) {
                return (rhs.priority || 0) - (lhs.priority || 0);
            },
            findKeyMapEntries: function(e) {
                var me = this,
                    disabledGroups = me.disabledKeyMapGroups,
                    keyMap = me.getKeyMap(),
                    entries = keyMap && Keyboard.getKeyName(e),
                    entry, len, i,
                    result = [];
                entries = entries && keyMap[entries];
                if (entries) {
                    
                    if (!entries.sorted) {
                        Ext.Array.sort(entries, me.comparePriorities);
                        entries.sorted = true;
                    }
                    len = entries.length;
                    for (i = 0; i < len; i++) {
                        entry = entries[i];
                        
                        
                        if (!disabledGroups || !disabledGroups[entry.group]) {
                            if (Keyboard.matchEntry(entry, e)) {
                                result.push(entry);
                            }
                        }
                    }
                }
                return result;
            },
            onKeyMapEvent: function(e) {
                var me = this,
                    entries = me.getKeyMapEnabled() ? me.findKeyMapEntries(e) : null,
                    len = entries && entries.length,
                    i, entry, result;
                for (i = 0; i < len && result !== false; i++) {
                    entry = entries[i];
                    result = Ext.callback(entry.handler, entry.scope, [
                        e,
                        this
                    ], 0, this);
                }
                return result;
            },
            setKeyMapListener: function(enabled) {
                var me = this,
                    listener = me._keyMapListener,
                    eventSource;
                if (listener) {
                    
                    
                    listener.destroy();
                    listener = null;
                }
                if (enabled) {
                    
                    ++me._keyMapListenCount;
                    
                    if (enabled) {
                        eventSource = me[me.keyMapTarget];
                        if (typeof eventSource === 'function') {
                            eventSource = eventSource.call(me);
                        }
                        
                        listener = eventSource.on({
                            destroyable: true,
                            scope: me,
                            keydown: 'onKeyMapEvent',
                            keypress: 'onKeyMapEvent'
                        });
                    }
                }
                me._keyMapListener = listener || null;
            },
            statics: {
                _charCodeRe: /^#([\d]+)$/,
                _keySpecRe: /^(?:(?:(\*)[\+\-])|(?:([a-z\+\-]*)[\+\-]))?(?:([a-z0-9_]+|[\+\-]|(?:#?\d+))(?:\:([a-z]+))?)$/i,
                _delimiterRe: /\-|\+/,
                _keyMapEvents: {
                    charCode: 'keypress',
                    keyCode: 'keydown'
                },
                combineKeyMaps: function(existingKeyMap, keyMap, owner) {
                    var defaultScope = keyMap.scope || 'controller',
                        entry, key, mapping, existingMapping;
                    for (key in keyMap) {
                        if (key === 'scope') {
                            
                            continue;
                        }
                        if (!(mapping = keyMap[key])) {
                            
                            if (mapping === undefined) {
                                Ext.raise('keyMap entry "' + key + '" is undefined');
                            }
                            
                            
                            
                            if (!existingKeyMap) {
                                
                                continue;
                            }
                        } else {
                            if (typeof mapping === 'string' || typeof mapping === 'function') {
                                
                                
                                mapping = {
                                    handler: mapping,
                                    scope: defaultScope
                                };
                            } else if (mapping) {
                                mapping = Ext.apply({
                                    handler: mapping.fn,
                                    
                                    scope: defaultScope
                                }, 
                                
                                mapping);
                            }
                            existingKeyMap = existingKeyMap || {};
                        }
                        
                        if (Keyboard.parseEntry(key, entry = mapping || {})) {
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            existingMapping = existingKeyMap[entry.name];
                            if (existingMapping) {
                                if (owner && existingMapping.$owner !== owner) {
                                    existingKeyMap[entry.name] = existingMapping = existingMapping.slice();
                                    existingMapping.$owner = owner;
                                }
                                existingMapping.push(mapping);
                                existingMapping.sorted = false;
                            } else {
                                existingMapping = existingKeyMap[entry.name] = [
                                    mapping
                                ];
                                existingMapping.$owner = owner;
                                existingMapping.sorted = true;
                            }
                        } else 
                        {
                            Ext.raise('Invalid keyMap key specification "' + key + '"');
                        }
                    }
                    
                    if (existingKeyMap && owner) {
                        existingKeyMap.$owner = owner;
                    }
                    return existingKeyMap || null;
                },
                getKeyName: function(event) {
                    var keyCode;
                    if (event.isEvent) {
                        keyCode = event.keyCode || event.charCode;
                        event = event.browserEvent;
                        
                        
                        if (keyCode === 229 && 'code' in event) {
                            if (Ext.String.startsWith(event.code, 'Key')) {
                                return event.key.substr(3);
                            }
                            if (Ext.String.startsWith(event.code, 'Digit')) {
                                return event.key.substr(5);
                            }
                        }
                    } else {
                        keyCode = event;
                    }
                    
                    return Ext.event.Event.keyCodes[keyCode] || String.fromCharCode(keyCode);
                },
                matchEntry: function(entry, e) {
                    var ev = e.browserEvent,
                        code;
                    if (e.type !== entry.event) {
                        return false;
                    }
                    if (!(code = entry.charCode)) {
                        if (entry.keyCode !== e.keyCode || (!entry.ignoreModifiers && !entry.shiftKey !== !ev.shiftKey)) {
                            
                            return false;
                        }
                    } else if (e.getCharCode() !== code) {
                        return false;
                    }
                    
                    
                    
                    return entry.ignoreModifiers || (!entry.ctrlKey === !ev.ctrlKey && !entry.altKey === !ev.altKey && !entry.metaKey === !ev.metaKey && !entry.shiftKey === !ev.shiftKey);
                },
                parseEntry: function(key, entry) {
                    key = key.toUpperCase();
                    var me = this,
                        Event = Ext.event.Event,
                        keyFlags = Event.keyFlags,
                        parts = me._keySpecRe.exec(key),
                        type = 'keyCode',
                        name, code, i, match, n;
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    if (parts) {
                        name = parts[3];
                        if (parts[4]) {
                            entry.group = parts[4];
                        }
                        
                        
                        if (!(entry.ignoreModifiers = !!parts[1]) && parts[2]) {
                            
                            parts = parts[2].split(me._delimiterRe);
                            n = parts.length;
                            for (i = 0; i < n; i++) {
                                
                                if (!keyFlags[parts[i]]) {
                                    return false;
                                }
                                
                                entry[keyFlags[parts[i]]] = true;
                            }
                        }
                        
                        
                        entry.name = name;
                        
                        if (isNaN(code = Event[name])) {
                            
                            if (!(match = me._charCodeRe.exec(name))) {
                                if (name.length === 1) {
                                    code = name.charCodeAt(0);
                                }
                            } else {
                                code = +match[1];
                            }
                            
                            if (code) {
                                type = 'charCode';
                            } else {
                                
                                code = +name;
                            }
                            entry.name = Keyboard.getKeyName(code);
                        }
                        entry.event = entry.event || me._keyMapEvents[type];
                        return !isNaN(code) && (entry[type] = code);
                    }
                }
            }
        }
    };
});




Ext.define('Ext.mixin.Focusable', {
    mixinId: 'focusable',
    $isFocusableEntity: true,
    
    
    focusable: false,
    
    hasFocus: false,
    
    containsFocus: false,
    
    focusCls: Ext.baseCSSPrefix + 'focused',
    
    focusEl: 'el',
    
    
    
    
    
    
    getFocusEl: function() 
    {
        var focusEl = this.focusEl;
        return focusEl && focusEl.dom ? focusEl : null;
    },
    
    getFocusClsEl: function() {
        return this.getFocusEl();
    },
    
    initFocusable: Ext.emptyFn,
    
    initFocusableEvents: function(force) {
        
        
        this.initFocusableElement(force);
    },
    enableFocusable: Ext.emptyFn,
    disableFocusable: function() {
        var me = this;
        
        
        
        if (me.hasFocus) {
            me.revertFocus();
        }
        me.removeFocusCls();
    },
    destroyFocusable: function() {
        var me = this;
        Ext.destroy(me.focusListeners);
        me.focusListeners = me.focusEnterEvent = me.focusTask = null;
        me.focusEl = me.ariaEl = null;
    },
    
    isFocusable: function(deep) {
        var me = this,
            focusEl;
        if (!me.focusable && (!me.isContainer || !deep)) {
            return false;
        }
        focusEl = me.getFocusEl();
        if (focusEl && me.canFocus()) {
            
            
            
            return focusEl && !focusEl.destroyed && focusEl.isFocusable(deep);
        }
        return false;
    },
    
    isDestructing: function() {
        for (var c = this; c; c = c.getRefOwner()) {
            if (c.destroying || c.destroyed) {
                return true;
            }
        }
        return false;
    },
    canFocus: function(skipVisibility, includeFocusTarget) {
        var me = this,
            ownerFC = me.ownerFocusableContainer,
            focusableIfDisabled = ownerFC && ownerFC.allowFocusingDisabledChildren,
            canFocus;
        
        
        
        
        canFocus = !me.destroyed && me.rendered && !me.isDestructing() && (me.isContainer || me.focusable) && (!me.isDisabled() || focusableIfDisabled) && (skipVisibility || me.isVisible(true));
        return canFocus || (includeFocusTarget && !!me.findFocusTarget());
    },
    
    focus: function(selectText) {
        var me = this,
            focusTarget, focusElDom;
        if ((!me.focusable && !me.isContainer) || me.destroyed || me.destroying) {
            return false;
        }
        
        
        if (me.canFocus() && (focusTarget = me.getFocusEl())) {
            
            
            if (focusTarget.$isFocusableEntity) {
                return focusTarget.focus.apply(focusTarget, arguments);
            }
            focusElDom = focusTarget.dom;
            
            if (focusElDom) {
                
                focusTarget.focus();
                if (selectText && (me.selectText || focusElDom.select)) {
                    if (me.selectText) {
                        if (Ext.isArray(selectText)) {
                            me.selectText.apply(me, selectText);
                        } else {
                            me.selectText();
                        }
                    } else {
                        focusElDom.select();
                    }
                }
            }
            
            else if (focusTarget.focus) {
                focusTarget.focus();
            } else {
                return false;
            }
        } else {
            
            
            
            
            focusTarget = me.findFocusTarget();
            if (focusTarget && focusTarget != me) {
                return focusTarget.focus.apply(focusTarget, arguments);
            } else {
                return false;
            }
        }
        return true;
    },
    
    onBlur: function(e) {
        var me = this,
            container = me.ownerFocusableContainer;
        me.hasFocus = false;
        if (me.beforeBlur && !me.beforeBlur.$emptyFn) {
            me.beforeBlur(e);
        }
        if (container) {
            container.beforeFocusableChildBlur(me, e);
        }
        me.removeFocusCls(e);
        if (me.hasListeners.blur) {
            me.fireEvent('blur', me, e);
        }
        if (me.postBlur && !me.postBlur.$emptyFn) {
            me.postBlur(e);
        }
        if (container) {
            container.afterFocusableChildBlur(me, e);
        }
    },
    
    onFocus: function(e) {
        var me = this,
            container = me.ownerFocusableContainer;
        if (me.canFocus()) {
            if (me.beforeFocus && !me.beforeFocus.$emptyFn) {
                me.beforeFocus(e);
            }
            if (container) {
                container.beforeFocusableChildFocus(me, e);
            }
            me.addFocusCls(e);
            if (!me.hasFocus) {
                me.hasFocus = true;
                me.fireEvent('focus', me, e);
            }
            if (me.postFocus && !me.postFocus.$emptyFn) {
                me.postFocus(e);
            }
            if (container) {
                container.afterFocusableChildFocus(me, e);
            }
        }
    },
    
    getTabIndex: function() {
        var me = this,
            el, index;
        if (!me.focusable) {
            return;
        }
        el = me.getFocusEl();
        if (el) {
            
            if (el.$isFocusableEntity) {
                index = el.getTabIndex();
            } else if (el.isElement && el.dom) {
                
                
                
                index = el.dom.getAttribute('tabIndex');
                
                
                
                
                if (index !== null) {
                    index -= 0;
                }
            } else 
            
            
            
            {
                return;
            }
        }
        if (typeof index !== 'number') {
            index = me.tabIndex;
        }
        return index;
    },
    
    setTabIndex: function(newTabIndex, focusEl) {
        var me = this,
            ownerFC = me.ownerFocusableContainer,
            focusableIfDisabled = ownerFC && ownerFC.allowFocusingDisabledChildren,
            el;
        
        if (!me.focusable && !me.forceTabIndex) {
            return;
        }
        me.tabIndex = newTabIndex;
        
        
        if (me.destroying || me.destroyed || (me.isDisabled() && !focusableIfDisabled)) {
            return;
        }
        el = focusEl || me.getFocusEl();
        if (el) {
            
            if (el.$isFocusableEntity) {
                el.setTabIndex(newTabIndex);
            }
            
            
            
            else if (el.isElement && el.dom) {
                
                el.setTabIndex(newTabIndex);
            }
        }
    },
    
    onFocusEnter: function(e) {
        var me = this;
        
        
        
        
        if (me.destroying || me.destroyed) {
            return;
        }
        
        
        me.focusEnterEvent = e;
        me.containsFocus = true;
        if (me.hasListeners.focusenter) {
            me.fireEvent('focusenter', me, e);
        }
    },
    
    onFocusLeave: function(e) {
        var me = this;
        
        if (me.destroying || me.destroyed) {
            return;
        }
        me.focusEnterEvent = null;
        me.containsFocus = false;
        if (me.hasListeners.focusleave) {
            me.fireEvent('focusleave', me, e);
        }
    },
    
    onFocusMove: Ext.emptyFn,
    privates: {
        
        
        
        
        
        
        
        
        
        forceTabIndex: false,
        
        revertFocus: function() {
            var me = this,
                focusEvent = me.focusEnterEvent,
                activeElement = Ext.Element.getActiveElement(),
                focusTarget, fromComponent, reverted;
            
            
            
            
            if (focusEvent && !me.preventRefocus && me.el.contains(activeElement)) {
                fromComponent = focusEvent.fromComponent;
                
                if (fromComponent && (fromComponent.destroyed || fromComponent.isDestructing())) {
                    focusTarget = document.body;
                } else 
                
                
                
                
                {
                    focusTarget = focusEvent.relatedTarget;
                }
                
                if (focusTarget === document.body) {
                    fromComponent = me.findFocusTarget();
                    if (fromComponent) {
                        focusTarget = fromComponent.getFocusEl();
                    }
                }
                if (focusTarget && focusTarget.$isFocusableEntity) {
                    if (!focusTarget.destroyed && focusTarget.isFocusable()) {
                        focusTarget.focus();
                    }
                }
                
                else if (Ext.getDoc().contains(focusTarget) && Ext.fly(focusTarget).isFocusable()) {
                    fromComponent = Ext.Component.from(focusTarget);
                    
                    if (fromComponent) {
                        fromComponent.revertFocusTo(focusTarget);
                    } else {
                        focusTarget.focus();
                    }
                }
                
                
                else if (focusEvent.fromComponent && focusEvent.fromComponent.focus) {
                    reverted = focusEvent.fromComponent.focus();
                    
                    
                    
                    
                    if (!reverted) {
                        activeElement.blur();
                    }
                }
            }
        },
        
        revertFocusTo: function(target) {
            target.focus();
        },
        
        findFocusTarget: function() {
            var me = this,
                parentAxis, candidate, len, i, focusTargets, focusIndex;
            if (me.preventRefocus) {
                return null;
            }
            
            
            
            
            for (parentAxis = [] , candidate = me.getRefOwner(); candidate; candidate = candidate.getRefOwner()) {
                if (!candidate.isDisabled()) {
                    parentAxis.unshift(candidate);
                }
            }
            
            
            
            for (i = 0 , len = parentAxis.length; i < len; i++) {
                candidate = parentAxis[i];
                if (candidate.destroying || !candidate.isVisible()) {
                    parentAxis.length = i;
                    break;
                }
            }
            
            for (i = parentAxis.length - 1; i >= 0; i--) {
                candidate = parentAxis[i];
                
                
                
                
                
                
                focusTargets = Ext.ComponentQuery.query(':canfocus()', candidate);
                if (focusTargets.length) {
                    focusIndex = Ext.Array.indexOf(focusTargets, Ext.ComponentManager.getActiveComponent());
                    
                    return focusTargets[focusIndex + 1] || focusTargets[focusIndex - 1] || focusTargets[0];
                }
                
                
                if (candidate.isFocusable && candidate.isFocusable()) {
                    return candidate;
                }
            }
        },
        
        initFocusableElement: function(force) {
            var me = this,
                tabIndex = me.getTabIndex(),
                focusEl = me.getFocusEl();
            if (focusEl && !focusEl.$isFocusableEntity) {
                
                
                
                
                
                if (tabIndex != null && (force || me.canFocus(true))) {
                    me.setTabIndex(tabIndex, focusEl);
                }
                
                
                
                
                if (!focusEl.dom.hasAttribute('data-componentid')) {
                    focusEl.dom.setAttribute('data-componentid', me.id);
                }
            }
        },
        addFocusCls: function(e) {
            var focusCls = this.focusCls,
                el;
            el = this.getFocusClsEl();
            if (focusCls) {
                el = this.getFocusClsEl(e);
                if (el) {
                    el.addCls(focusCls);
                }
            }
        },
        removeFocusCls: function(e) {
            var focusCls = this.focusCls,
                el;
            if (focusCls) {
                el = this.getFocusClsEl(e);
                if (el) {
                    el.removeCls(focusCls);
                }
            }
        },
        
        handleFocusEvent: function(e) {
            var me = this,
                event;
            if (!me.focusable || me.destroying || me.destroyed) {
                return;
            }
            
            
            
            
            
            
            if (me.isFocusing(e)) {
                event = new Ext.event.Event(e.event);
                event.type = 'focus';
                event.relatedTarget = e.fromElement;
                event.target = e.toElement;
                me.onFocus(event);
            }
        },
        
        handleBlurEvent: function(e) {
            var me = this,
                event;
            if (!me.focusable || me.destroying || me.destroyed) {
                return;
            }
            
            
            
            
            if (e.toElement === document.body || me.isBlurring(e)) {
                event = new Ext.event.Event(e.event);
                event.type = 'blur';
                event.target = e.fromElement;
                event.relatedTarget = e.toElement;
                me.onBlur(event);
            }
        },
        
        isFocusing: function(e) {
            var focusEl = this.getFocusEl();
            if (focusEl) {
                if (focusEl.isFocusing) {
                    return focusEl.isFocusing(e);
                } else {
                    
                    
                    
                    return focusEl.dom === document.activeElement && e.toElement === focusEl.dom && e.fromElement !== e.toElement;
                }
            }
            return false;
        },
        
        isBlurring: function(e) {
            var focusEl = this.getFocusEl();
            if (focusEl) {
                if (focusEl.isFocusing) {
                    return focusEl.isBlurring(e);
                } else {
                    
                    
                    
                    return focusEl.dom !== document.activeElement && e.fromElement === focusEl.dom && e.fromElement !== e.toElement;
                }
            }
            return false;
        },
        
        blur: function() {
            var me = this,
                focusEl;
            if (!me.focusable || !me.canFocus()) {
                return;
            }
            focusEl = me.getFocusEl();
            if (focusEl) {
                me.blurring = true;
                focusEl.blur();
                delete me.blurring;
            }
        },
        isTabbable: function() {
            var me = this,
                focusEl;
            if (me.focusable) {
                focusEl = me.getFocusEl();
                if (focusEl && focusEl.isTabbable()) {
                    return focusEl.isTabbable();
                }
            }
            return false;
        },
        disableTabbing: function() {
            var me = this,
                el = me.el,
                focusEl;
            
            
            if (me.destroying || me.destroyed) {
                return;
            }
            
            
            if (el) {
                el.saveTabbableState();
            }
            focusEl = me.getFocusEl();
            if (focusEl) {
                
                if (focusEl.$isFocusableEntity) {
                    focusEl.disableTabbing();
                }
                
                
                
                else if (focusEl.isElement && el && !el.contains(focusEl)) {
                    focusEl.saveTabbableState();
                }
            }
        },
        enableTabbing: function(reset) {
            var me = this,
                el = me.el,
                focusEl;
            
            
            if (me.destroying || me.destroyed) {
                return;
            }
            focusEl = me.getFocusEl();
            if (focusEl) {
                if (focusEl.$isFocusableEntity) {
                    focusEl.enableTabbing();
                } else if (focusEl.isElement && el && !el.contains(focusEl)) {
                    focusEl.restoreTabbableState();
                }
            }
            if (el) {
                el.restoreTabbableState({
                    reset: reset
                });
            }
        }
    }
}, function() {
    var keyboardModeCls = Ext.baseCSSPrefix + 'keyboard-mode',
        keyboardMode = false;
    
    
    Ext.setKeyboardMode = Ext.setKeyboardMode || function(keyboardMode) {
        Ext.keyboardMode = keyboardMode;
        Ext.getBody().toggleCls(keyboardModeCls, keyboardMode);
    };
    Ext.isTouchMode = function() {
        return (Ext.now() - Ext.lastTouchTime) < 500;
    };
    function syncKeyboardMode(e) {
        var type = e.type;
        if (type === 'pointermove') {
            
            
            
            
            keyboardMode = false;
        } else {
            keyboardMode = (type === 'keydown');
            Ext.lastTouchTime = e.pointerType === 'touch' && Ext.now();
            Ext.setKeyboardMode(keyboardMode);
        }
    }
    Ext.onReady(function() {
        Ext.getWin().on({
            pointerdown: syncKeyboardMode,
            pointermove: syncKeyboardMode,
            keydown: syncKeyboardMode,
            capture: true,
            delegated: false
        });
        Ext.on('focus', function() {
            if (keyboardMode !== Ext.getBody().hasCls(keyboardModeCls)) {
                Ext.setKeyboardMode(keyboardMode);
            }
        });
    });
});


Ext.define('Ext.mixin.Accessible', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'accessible'
    },
    
    
    
    config: {
        
        ariaAttributes: {
            $value: null,
            lazy: true
        }
    },
    
    
    
    ariaEl: 'el',
    privates: {
        
        getAriaLabelEl: function(reference) {
            var ids = [],
                refHolder, i, len, cmp, result;
            if (reference) {
                if (Ext.isFunction(reference)) {
                    return reference.call(this);
                } else {
                    if (!Ext.isArray(reference)) {
                        reference = [
                            reference
                        ];
                    }
                    refHolder = this.lookupReferenceHolder();
                    if (refHolder) {
                        for (i = 0 , len = reference.length; i < len; i++) {
                            cmp = refHolder.lookupReference(reference[i]);
                            if (cmp) {
                                ids.push(cmp.ariaEl.id);
                            }
                        }
                    }
                }
            }
            return ids.length ? ids.join(' ') : null;
        }
    }
});


Ext.define('Ext.Widget', {
    extend: Ext.Evented,
    xtype: 'widget',
    alternateClassName: 'Ext.Gadget',
    mixins: [
        Ext.mixin.Inheritable,
        Ext.mixin.Bindable,
        Ext.mixin.ComponentDelegation,
        Ext.mixin.Pluggable,
        Ext.mixin.Keyboard,
        Ext.mixin.Factoryable,
        Ext.mixin.Focusable,
        Ext.mixin.Accessible
    ],
    isWidget: true,
    factoryConfig: {
        creator: null,
        defaultProperty: 'xtype',
        defaultType: 'component',
        typeProperty: 'xtype'
    },
    
    element: {
        reference: 'element'
    },
    observableType: 'component',
    cachedConfig: {
        
        cls: null,
        
        style: null,
        
        border: null,
        
        touchAction: null,
        
        eventHandlers: {
            focus: 'handleFocusEvent',
            blur: 'handleBlurEvent'
        }
    },
    
    name: null,
    config: {
        
        renderTo: null,
        
        ui: null,
        
        userCls: null,
        
        ripple: null,
        
        hideMode: null,
        
        instanceCls: null
    },
    eventedConfig: {
        
        width: null,
        
        height: null,
        
        hidden: null,
        
        disabled: null
    },
    
    template: [],
    
    baseCls: null,
    
    classCls: null,
    
    classClsRoot: true,
    
    classClsList: [],
    clearPropertiesOnDestroy: 'async',
    focusEl: 'element',
    ariaEl: 'element',
    spaceRe: /\s+/,
    
    noBorderCls: Ext.baseCSSPrefix + 'noborder-trbl',
    borderedCls: Ext.baseCSSPrefix + 'bordered',
    disabledCls: Ext.baseCSSPrefix + 'disabled',
    heightedCls: Ext.baseCSSPrefix + 'heighted',
    widthedCls: Ext.baseCSSPrefix + 'widthed',
    constructor: function(config) {
        var me = this,
            baseCls = me.baseCls,
            renderTo = config && config.renderTo,
            controller;
        me.$iid = ++Ext.$nextIid;
        if (baseCls == null || baseCls === true) {
            me.baseCls = me.classCls || Ext.baseCSSPrefix + me.xtype;
        }
        
        if (config && ('baseCls' in config)) {
            Ext.raise('baseCls cannot be used as an instance config. It must be specified at class definition time.');
        }
        
        
        
        
        if ((config && config.controller) || me.config.controller) {
            me.referenceHolder = true;
        }
        me.initId(config);
        me.initElement();
        if (renderTo) {
            config = Ext.apply({}, config);
            delete config.renderTo;
        }
        me.mixins.observable.constructor.call(me, config);
        
        if (me.focusable) {
            me.initFocusableEvents(true);
        }
        if (renderTo) {
            me.setRenderTo(renderTo);
        }
        me.syncUiCls();
        Ext.ComponentManager.register(me);
        controller = me.getController();
        if (controller) {
            controller.init(me);
        }
    },
    afterCachedConfig: function() {
        
        
        
        
        
        
        
        var me = this,
            prototype = me.self.prototype,
            referenceList = me.referenceList,
            renderElement = me.renderElement,
            renderTemplate, element, i, ln, reference, elements;
        
        
        prototype.renderTemplate = renderTemplate = document.createDocumentFragment();
        renderTemplate.appendChild(renderElement.clone(true, 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');
        }
    },
    applyHidden: function(hidden) {
        return !!hidden;
    },
    applyDisabled: function(disabled) {
        return !!disabled;
    },
    updateDisabled: function(disabled) {
        var me = this,
            container = me.ownerFocusableContainer;
        if (container) {
            if (disabled) {
                if (!container.beforeFocusableChildDisable.$nullFn) {
                    container.beforeFocusableChildDisable(me);
                }
            } else {
                if (!container.beforeFocusableChildEnable.$nullFn) {
                    container.beforeFocusableChildEnable(me);
                }
            }
        }
        me.element.toggleCls(me.disabledCls, disabled);
        if (me.focusable) {
            if (disabled) {
                me.disableFocusable();
            } else {
                me.enableFocusable();
            }
        }
        if (container) {
            if (disabled) {
                if (!container.onFocusableChildDisable.$nullFn) {
                    container.onFocusableChildDisable(me);
                }
            } else {
                if (!container.onFocusableChildEnable.$nullFn) {
                    container.onFocusableChildEnable(me);
                }
            }
        }
    },
    
    disable: function() {
        this.setDisabled(true);
    },
    
    enable: function() {
        this.setDisabled(false);
    },
    
    isDisabled: function() {
        return this.getDisabled();
    },
    
    isEnabled: function() {
        return !this.getDisabled();
    },
    applyTouchAction: function(touchAction, oldTouchAction) {
        if (oldTouchAction != null) {
            touchAction = Ext.merge({}, oldTouchAction, touchAction);
        }
        return touchAction;
    },
    applyWidth: function(width) {
        return this.filterLengthValue(width);
    },
    applyHeight: function(height) {
        return this.filterLengthValue(height);
    },
    updateBorder: function(border) {
        var me = this;
        
        border = border || border === null;
        me.toggleCls(me.noBorderCls, !border);
        me.toggleCls(me.borderedCls, !!border);
    },
    clearListeners: function() {
        var me = this;
        me.mixins.observable.clearListeners.call(me);
        me.mixins.componentDelegation.clearDelegatedListeners.call(me);
    },
    
    destroy: function() {
        var me = this;
        
        me.isDestroying = me.destroying = true;
        me.doDestroy();
        
        
        me.clearListeners();
        me.isDestroying = me.destroying = false;
        
        
        me.mixins.componentDelegation.destroyComponentDelegation.call(me);
        me.callParent();
    },
    
    doDestroy: function() {
        var me = this,
            referenceList = me.referenceList,
            container = me.ownerFocusableContainer,
            i, ln, reference;
        
        me.ownerCmp = null;
        if (container && !container.onFocusableChildDestroy.$nullFn) {
            container.onFocusableChildDestroy(me);
        }
        
        for (i = 0 , ln = referenceList.length; i < ln; i++) {
            reference = referenceList[i];
            if (me.hasOwnProperty(reference)) {
                me[reference].destroy();
                me[reference] = null;
            }
        }
        me.destroyBindable();
        Ext.ComponentManager.unregister(me);
    },
    doFireEvent: function(eventName, args, bubbles) {
        var me = this,
            ret;
        ret = me.mixins.observable.doFireEvent.call(me, eventName, args, bubbles);
        
        if (ret !== false && !me.destroyed) {
            ret = me.mixins.componentDelegation.doFireDelegatedEvent.call(me, eventName, args);
        }
        return ret;
    },
    getBubbleTarget: function() {
        return this.getRefOwner();
    },
    
    getElementConfig: function() {
        var me = this,
            el = me.element;
        if (!('children' in el)) {
            el = Ext.apply({
                children: me.getTemplate()
            }, el);
        }
        return el;
    },
    
    getSize: function() {
        return this.el.getSize();
    },
    getTemplate: function() {
        return Ext.clone(this.template);
    },
    
    getClassCls: function() {
        var proto = this.self.prototype,
            prototype = proto,
            classes, classCls, i, ln;
        while (prototype) {
            classCls = prototype.hasOwnProperty('classCls') ? prototype.classCls : null;
            if (classCls) {
                if (classCls instanceof Array) {
                    for (i = 0 , ln = classCls.length; i < ln; i++) {
                        (classes || (classes = [])).push(classCls[i]);
                    }
                } else {
                    (classes || (classes = [])).push(classCls);
                }
            }
            if (prototype.classClsRoot && prototype.hasOwnProperty('classClsRoot')) {
                break;
            }
            prototype = prototype.superclass;
        }
        if (classes) {
            proto.classClsList = classes;
        }
        return classes;
    },
    hide: function() {
        this.setHidden(true);
    },
    
    initElement: function() {
        var me = this,
            prototype = me.self.prototype,
            id = me.getId(),
            
            
            
            referenceList = me.referenceList = me.referenceList = [],
            isFirstInstance = !prototype.hasOwnProperty('renderTemplate'),
            
            uiReferences = prototype.hasOwnProperty('uiReferences') ? prototype.uiReferences : (prototype.uiReferences = {
                element: ''
            }),
            renderTemplate, renderElement, renderConfig, element, referenceNodes, i, ln, referenceNode, reference, classCls, uiCls, baseCls, referenceElement;
        if (isFirstInstance) {
            
            
            renderTemplate = document.createDocumentFragment();
            renderConfig = me.processElementConfig.call(prototype);
            renderElement = Ext.Element.create(renderConfig, true);
            renderTemplate.appendChild(renderElement);
            
            
            
            referenceNodes = renderTemplate.querySelectorAll('[uiCls]');
            for (i = 0 , ln = referenceNodes.length; i < ln; i++) {
                referenceNode = referenceNodes[i];
                reference = referenceNode.getAttribute('reference');
                uiCls = referenceNode.getAttribute('uiCls');
                
                if (!reference) {
                    Ext.raise('Cannot render element with uiCls="' + uiCls + '". uiCls is only allowed on elements that have a reference name.');
                }
                
                uiReferences[reference] = uiCls;
                
                
                referenceNode.removeAttribute('uiCls');
            }
        } else {
            
            
            
            
            renderTemplate = me.renderTemplate.cloneNode(true);
            renderElement = renderTemplate.firstChild;
        }
        referenceNodes = renderTemplate.querySelectorAll('[reference]');
        for (i = 0 , ln = referenceNodes.length; i < ln; i++) {
            referenceNode = referenceNodes[i];
            reference = referenceNode.getAttribute('reference');
            if (!isFirstInstance) {
                
                
                
                
                
                
                
                referenceNode.removeAttribute('reference');
            }
            if (reference === 'element') {
                
                if (element) {
                    
                    Ext.raise("Duplicate 'element' reference detected in '" + me.$className + "' template.");
                }
                
                referenceNode.id = id;
                
                
                element = me.el = me.addElementReference(reference, referenceNode);
                
                element.dom.setAttribute('data-componentid', id);
                if (isFirstInstance) {
                    classCls = me.getClassCls();
                    if (classCls) {
                        element.addCls(classCls);
                    }
                    baseCls = me.baseCls;
                    if (baseCls && (baseCls !== me.classCls)) {
                        element.addCls(baseCls);
                    }
                }
            } else {
                uiCls = uiReferences[reference];
                if (uiCls && isFirstInstance) {
                    
                    
                    
                    
                    
                    
                    
                    
                    referenceElement = me.addElementReference(reference, referenceNode);
                    me.initUiReference(reference, uiCls, false);
                } else {
                    me.addElementReferenceOnDemand(reference, referenceNode);
                }
            }
            
            if (reference === me.focusEl) {
                me.addElementReference('focusEl', referenceNode);
            }
            if (reference === me.ariaEl) {
                me.addElementReferenceOnDemand('ariaEl', referenceNode);
            }
            referenceList.push(reference);
        }
        
        if (!element) {
            Ext.raise("No 'element' reference found in '" + me.$className + "' template.");
        }
        
        if (renderElement === element.dom) {
            me.renderElement = element;
        } else {
            me.addElementReferenceOnDemand('renderElement', renderElement);
        }
        renderElement.setAttribute(me.dataXid, me.$iid);
    },
    dataXid: 'data-' + Ext.baseCSSPrefix.substr(0, Ext.baseCSSPrefix.length - 1) + 'id',
    
    is: function(selector) {
        return Ext.ComponentQuery.is(this, selector);
    },
    
    isHidden: function(deep) {
        var hidden = !!this.getHidden(),
            owner;
        if (!hidden && deep) {
            owner = this.getRefOwner();
            while (owner && owner !== deep && !hidden) {
                hidden = !!owner.getHidden();
                owner = owner.getRefOwner();
            }
        }
        return hidden;
    },
    
    isVisible: function(deep) {
        return this.rendered && !this.destroyed && !this.isHidden(deep);
    },
    
    isXType: function(xtype, shallow) {
        return shallow ? (Ext.Array.indexOf(this.xtypes, xtype) !== -1) : !!this.xtypesMap[xtype];
    },
    
    lookupTpl: function(name) {
        return Ext.XTemplate.getTpl(this, name);
    },
    owns: function(element) {
        var result = false,
            cmp;
        if (element.isEvent) {
            element = element.target;
        } else if (element.isElement) {
            element = element.dom;
        }
        cmp = Ext.Component.from(element);
        if (cmp) {
            result = (cmp === this) || (!!cmp.up(this));
        }
        return result;
    },
    render: function(container, insertBeforeElement) {
        if (container && container.isWidget) {
            container = container.el;
        }
        var dom = this.renderElement.dom,
            containerDom = Ext.getDom(container),
            insertBeforeChildDom;
        if (Ext.isNumber(insertBeforeChildDom)) {
            insertBeforeElement = containerDom.childNodes[insertBeforeElement];
        }
        insertBeforeChildDom = Ext.getDom(insertBeforeElement);
        if (containerDom) {
            if (insertBeforeChildDom) {
                containerDom.insertBefore(dom, insertBeforeChildDom);
            } else {
                containerDom.appendChild(dom);
            }
            
            
            
            this.setRendered(Ext.getBody().contains(dom), true);
        }
    },
    
    toggleCls: function(className, state) {
        this.element.toggleCls(className, state);
        return this;
    },
    resolveListenerScope: function(defaultScope, skipThis) {
        
        return this.mixins.inheritable.resolveListenerScope.call(this, defaultScope, skipThis);
    },
    
    setSize: function(width, height) {
        
        if (width && typeof width === 'object') {
            return this.setSize(width.width, width.height);
        }
        if (width !== undefined) {
            this.setWidth(width);
        }
        if (height !== undefined) {
            this.setHeight(height);
        }
    },
    show: function() {
        this.setHidden(false);
    },
    
    addCls: function(cls, prefix, suffix) {
        if (!this.destroyed) {
            this.el.replaceCls(null, cls, prefix, suffix);
        }
    },
    applyCls: function(cls) {
        return cls && Ext.dom.Element.splitCls(cls);
    },
    applyUi: function(ui) {
        return this.parseUi(ui, true);
    },
    
    removeCls: function(cls, prefix, suffix) {
        if (!this.destroyed) {
            this.el.replaceCls(cls, null, prefix, suffix);
        }
    },
    
    replaceCls: function(oldCls, newCls, prefix, suffix) {
        if (!this.destroyed) {
            this.el.replaceCls(oldCls, newCls, prefix, suffix);
        }
    },
    
    hasCls: function(className) {
        return this.el.hasCls(className);
    },
    
    updateCls: function(newCls, oldCls) {
        this.element.replaceCls(oldCls, newCls);
    },
    updateHidden: function(hidden) {
        var me = this,
            element = me.renderElement,
            container = me.ownerFocusableContainer;
        
        
        if (container) {
            if (hidden) {
                if (!container.beforeFocusableChildHide.$nullFn) {
                    container.beforeFocusableChildHide(me);
                }
            } else {
                if (!container.beforeFocusableChildShow.$nullFn) {
                    container.beforeFocusableChildShow(me);
                }
            }
        }
        
        else if (hidden) {
            
            
            me.revertFocus();
        }
        if (element && !element.destroyed) {
            if (hidden) {
                element.hide();
            } else {
                element.show();
            }
        }
        if (me.focusableContainer && me.activateFocusableContainer) {
            me.activateFocusableContainer(!hidden);
        }
        if (container) {
            if (hidden) {
                if (!container.onFocusableChildHide.$nullFn) {
                    container.onFocusableChildHide(me);
                }
            } else {
                if (!container.onFocusableChildShow.$nullFn) {
                    container.onFocusableChildShow(me);
                }
            }
        }
    },
    updateRipple: function(ripple) {
        var me = this,
            el = me.el;
        if (el) {
            el.un('touchstart', 'onRippleStart', me);
            el.un('touchend', 'onRippleStart', me);
            el.destroyAllRipples();
            if (ripple.release) {
                el.on('touchend', 'onRippleStart', me);
            } else {
                el.on('touchstart', 'onRippleStart', me);
            }
        }
    },
    shouldRipple: function(e) {
        var me = this,
            disabled = me.getDisabled && me.getDisabled(),
            el = me.el,
            ripple = !disabled && me.getRipple(),
            target;
        if (ripple && e) {
            target = e.getTarget(me.noRippleSelector);
            if (target) {
                if ((el.dom === target) || el.contains(target)) {
                    ripple = null;
                }
            }
        }
        return ripple;
    },
    onRippleStart: function(e) {
        var ripple = this.shouldRipple(e);
        if (e.button === 0 && ripple) {
            this.el.ripple(e, ripple);
        }
    },
    
    applyStyle: function(style, oldStyle) {
        
        
        
        
        
        
        if (oldStyle && style === oldStyle && Ext.isObject(oldStyle)) {
            style = Ext.apply({}, style);
        }
        this.element.applyStyles(style);
        return null;
    },
    
    getStyle: function() {
        Ext.Error.raise("'style' is a write-only config.  To query element styles use the Ext.dom.Element API.");
    },
    
    updateRenderTo: function(newContainer) {
        this.render(newContainer);
    },
    updateTouchAction: function(touchAction) {
        var name, childEl, value, hasRootActions;
        for (name in touchAction) {
            childEl = this[name];
            value = touchAction[name];
            if (childEl && childEl.isElement) {
                childEl.setTouchAction(value);
            } else {
                hasRootActions = true;
            }
        }
        if (hasRootActions) {
            this.el.setTouchAction(touchAction);
        }
    },
    updateUi: function() {
        if (!this.isConfiguring) {
            this.syncUiCls();
        }
    },
    
    updateWidth: function(width) {
        var el = this.el;
        el.setWidth(width);
        el.toggleCls(this.widthedCls, width != null && width !== 'auto');
    },
    
    updateHeight: function(height) {
        var el = this.el;
        el.setHeight(height);
        el.toggleCls(this.heightedCls, height != null && height !== 'auto');
    },
    
    isWidthed: function() {
        var width = this.getWidth();
        return width != null && width !== 'auto';
    },
    
    isHeighted: function() {
        var height = this.getHeight();
        return height != null && height !== 'auto';
    },
    
    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()) {
                if (result.destroyed) {
                    return null;
                }
                steps++;
                if (selector.isComponent || selector.isWidget) {
                    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;
    },
    updateLayout: Ext.emptyFn,
    
    updateInstanceCls: function(instanceCls, oldInstanceCls) {
        var me = this,
            el = me.el,
            classClsList = me.classClsList,
            Array = Ext.Array,
            uiReferences = me.uiReferences,
            referenceName, referenceElement, i, ln, cls, uiCls;
        if (oldInstanceCls) {
            el.removeCls(oldInstanceCls);
            oldInstanceCls = Array.from(oldInstanceCls);
            for (i = 0 , ln = oldInstanceCls.length; i < ln; i++) {
                cls = oldInstanceCls[i];
                Array.remove(classClsList, cls);
                for (referenceName in uiReferences) {
                    referenceElement = me[referenceName];
                    uiCls = uiReferences[referenceName];
                    referenceElement.removeCls(cls + '-' + uiCls);
                }
            }
        }
        if (instanceCls) {
            el.addCls(instanceCls);
            instanceCls = Array.from(instanceCls);
            
            me.classClsList = classClsList.concat(instanceCls);
            for (i = 0 , ln = instanceCls.length; i < ln; i++) {
                cls = instanceCls[i];
                for (referenceName in uiReferences) {
                    referenceElement = me[referenceName];
                    uiCls = uiReferences[referenceName];
                    referenceElement.addCls(cls + '-' + uiCls);
                }
            }
        }
        if (!me.isConfiguring) {
            me.syncUiCls();
        }
    },
    
    getBaseCls: function() {
        return this.baseCls;
    },
    
    setBaseCls: function() {
        Ext.raise('baseCls cannot be reconfigured. It must be specified at class definition time.');
    },
    onClassExtended: function(Class, members) {
        if (members.config && members.config.baseCls) {
            Ext.raise('baseCls must be declared directly on the class body. Please move it outside of the config block.');
        }
    },
    
    
    privates: {
        _hideModes: {
            clip: 'CLIP',
            display: 'DISPLAY',
            offsets: 'OFFSETS',
            opacity: 'OPACITY',
            visibility: 'VISIBILITY'
        },
        noRippleSelector: '.' + Ext.baseCSSPrefix + 'no-ripple',
        
        addElementReferenceOnDemand: function(name, domNode) {
            if (this._elementListeners[name]) {
                
                
                
                this.addElementReference(name, domNode);
            } else {
                
                
                Ext.Object.defineProperty(this, name, {
                    get: function() {
                        if (this.destroyed) {
                            return;
                        }
                        
                        
                        
                        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);
                
                
                
                
                
                
                
                
                
                
                for (eventName in listeners) {
                    listener = listeners[eventName];
                    if (typeof listener === 'object') {
                        listener.scope = me;
                    }
                }
                
                
                
                
                
                
                
                
                listeners.scope = me;
                
                
                
                
                
                referenceEl.on(listeners);
            }
            return referenceEl;
        },
        detachFromBody: function() {
            
            Ext.getDetachedBody().appendChild(this.element, true);
            this.isDetached = true;
        },
        reattachToBody: function() {
            var detachedBody;
            if (this.isDetached) {
                detachedBody = Ext.getDetachedBody();
                if (detachedBody.contains(this.element)) {
                    Ext.getBody().appendChild(this.element, true);
                }
            }
            
            this.isDetached = false;
        },
        
        doAddListener: function(name, fn, scope, options, order, caller, manager) {
            var me = this,
                elementName = options && options.element,
                delegate = options && options.delegate,
                listeners, eventOptions, option;
            if (elementName) {
                
                if (Ext.Array.indexOf(me.referenceList, elementName) === -1) {
                    Ext.Logger.error("Adding event listener with an invalid element reference of '" + elementName + "' for this component. Available values are: '" + me.referenceList.join("', '") + "'", me);
                }
                
                listeners = {};
                listeners[name] = fn;
                if (scope) {
                    listeners.scope = scope;
                }
                eventOptions = Ext.Element.prototype.$eventOptions;
                for (option in options) {
                    if (eventOptions[option]) {
                        listeners[option] = options[option];
                    }
                }
                me.mon(me[elementName], listeners);
                return;
            } else if (delegate) {
                me.mixins.componentDelegation.addDelegatedListener.call(me, name, fn, scope, options, order, caller, manager);
                return;
            }
            me.callParent([
                name,
                fn,
                scope,
                options,
                order,
                caller,
                manager
            ]);
        },
        doRemoveListener: function(eventName, fn, scope) {
            var me = this;
            me.mixins.observable.doRemoveListener.call(me, eventName, fn, scope);
            me.mixins.componentDelegation.removeDelegatedListener.call(me, eventName, fn, scope);
        },
        filterLengthValue: function(value) {
            if (!value && value !== 0) {
                return null;
            }
            return value;
        },
        
        initElementListeners: function(elementConfig) {
            var prototype = this,
                superPrototype = prototype.self.superclass,
                superElementListeners = superPrototype._elementListeners,
                reference = elementConfig.reference,
                children = elementConfig.children,
                elementListeners, listeners, superListeners, ln, i;
            if (prototype.hasOwnProperty('_elementListeners')) {
                elementListeners = prototype._elementListeners;
            } else {
                elementListeners = prototype._elementListeners = (superElementListeners ? Ext.Object.chain(superElementListeners) : {});
            }
            if (reference) {
                listeners = elementConfig.listeners;
                if (listeners) {
                    if (superElementListeners) {
                        superListeners = superElementListeners[reference];
                        if (superListeners) {
                            listeners = Ext.Object.chain(superListeners);
                            Ext.apply(listeners, elementConfig.listeners);
                        }
                    }
                    elementListeners[reference] = listeners;
                    
                    
                    
                    elementConfig.listeners = null;
                }
            }
            if (children) {
                for (i = 0 , ln = children.length; i < ln; i++) {
                    prototype.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();
            }
        },
        measure: function(dimension) {
            return this.element.measure(dimension);
        },
        
        processElementConfig: function() {
            var prototype = this,
                superPrototype = prototype.self.superclass,
                elementConfig;
            if (prototype.hasOwnProperty('_elementConfig')) {
                elementConfig = prototype._elementConfig;
            } else {
                
                
                elementConfig = prototype._elementConfig = prototype.getElementConfig();
                if (superPrototype.isWidget) {
                    
                    
                    prototype.processElementConfig.call(superPrototype);
                }
                
                
                
                
                prototype.initElementListeners(elementConfig);
            }
            return elementConfig;
        },
        parseUi: function(ui, asString) {
            ui = Ext.String.splitWords(ui);
            if (asString) {
                ui = ui.join(' ');
            }
            return ui;
        },
        addUi: function(ui) {
            this.setUi(this.doAddUi(ui, this.getUi()));
        },
        doAddUi: function(ui, oldUi) {
            var me = this,
                newUi = null,
                i, u, len;
            if (ui) {
                ui = me.parseUi(ui);
                len = ui.length;
                oldUi = me.parseUi(oldUi);
                for (i = 0; i < len; i++) {
                    u = ui[i];
                    if (Ext.Array.indexOf(oldUi, u) === -1) {
                        oldUi.push(u);
                    }
                }
                newUi = oldUi.join(' ');
            }
            return newUi;
        },
        removeUi: function(ui) {
            this.setUi(this.doRemoveUi(ui, this.getUi()));
        },
        doRemoveUi: function(ui, oldUi) {
            var me = this,
                newUi = null,
                i, u, index, len;
            if (ui) {
                ui = me.parseUi(ui);
                len = ui.length;
                oldUi = me.parseUi(oldUi);
                for (i = 0; i < len; i++) {
                    u = ui[i];
                    index = Ext.Array.indexOf(oldUi, u);
                    if (index !== -1) {
                        oldUi.splice(index, 1);
                    }
                }
                newUi = oldUi.join(' ');
            }
            return newUi;
        },
        
        initUiReference: function(referenceName, uiCls, isInstance) {
            var me = this,
                referenceElement = me[referenceName],
                baseCls = me.baseCls,
                classClsList = me.classClsList,
                cls = [],
                i, n;
            isInstance = (isInstance !== false);
            if (isInstance) {
                
                if (!me.hasOwnProperty('uiReferences')) {
                    me.uiReferences = Ext.clone(me.uiReferences);
                }
                me.uiReferences[referenceName] = uiCls;
            }
            uiCls = '-' + uiCls;
            if (baseCls && (baseCls !== me.classCls)) {
                cls.push(baseCls + uiCls);
            }
            if (classClsList) {
                for (i = 0 , n = classClsList.length; i < n; i++) {
                    cls.push(classClsList[i] + uiCls);
                }
            }
            referenceElement.addCls(cls);
            if (isInstance && !me.isConfiguring) {
                me.syncUiCls();
            }
        },
        syncUiCls: function(refs) {
            var me = this,
                ui = me.getUi(),
                currentUiCls = me.currentUiCls || (me.currentUiCls = {}),
                baseCls = me.baseCls,
                uiReferences = refs || me.uiReferences,
                classClsList = me.classClsList,
                classClsListLen = classClsList ? classClsList.length : 0,
                uiCls, uiLen, refName, refEl, cls, suffix, uiSuffix, i, j;
            if (ui) {
                ui = me.parseUi(ui);
                uiLen = ui.length;
            }
            for (refName in uiReferences) {
                refEl = me[refName];
                uiCls = [];
                if (refEl) {
                    cls = currentUiCls[refName];
                    if (cls) {
                        refEl.removeCls(cls);
                    }
                    if (ui) {
                        suffix = uiReferences[refName];
                        suffix = suffix ? ('-' + suffix) : '';
                        for (i = 0; i < uiLen; i++) {
                            uiSuffix = '-' + ui[i] + suffix;
                            if (baseCls && (baseCls !== me.classCls)) {
                                uiCls.push(baseCls + uiSuffix);
                            }
                            if (classClsList) {
                                for (j = 0; j < classClsListLen; j++) {
                                    uiCls.push(classClsList[j] + uiSuffix);
                                }
                            }
                        }
                        refEl.addCls(uiCls);
                        currentUiCls[refName] = uiCls;
                    }
                }
            }
        },
        applyHideMode: function(mode) {
            return mode || 'display';
        },
        updateHideMode: function(mode) {
            var me = this,
                el = me.el,
                shouldToggle = me.getHidden();
            
            if (!me._hideModes[mode]) {
                Ext.raise('Invalid hideMode: "' + mode + '" (must be one of: "' + Object.keys(me._hideModes).join('", "') + '")');
            }
            
            if (shouldToggle) {
                el.show();
            }
            me.renderElement.setVisibilityMode(Ext.Element[me._hideModes[mode]]);
            if (shouldToggle) {
                el.hide();
            }
        },
        updateUserCls: function(newCls, oldCls) {
            this.element.replaceCls(oldCls, newCls);
        }
    }
}, function(Widget) {
    var prototype = Widget.prototype;
    
    
    (prototype.$elementEventOptions = Ext.Object.chain(Ext.Element.prototype.$eventOptions)).element = 1;
    (prototype.$eventOptions = Ext.Object.chain(prototype.$eventOptions)).delegate = 1;
    
    Ext.updateWidget = Ext.Factory.widget.update;
});


Ext.define('Ext.mixin.Traversable', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'traversable'
    },
    setParent: function(parent) {
        this.parent = parent;
        return this;
    },
    
    hasParent: function() {
        return Boolean(this.getParent());
    },
    
    is: function() {
        return true;
    },
    
    getParent: function() {
        return this.parent || this.$initParent;
    },
    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;
    },
    
    previousNode: function(selector, includeSelf) {
        var node = this,
            parent = node.getRefOwner(),
            result, it, i, sibling;
        
        if (includeSelf && node.is(selector)) {
            return node;
        }
        if (parent) {
            for (it = parent.items.items , i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) {
                sibling = it[i];
                if (sibling.query) {
                    result = sibling.query(selector);
                    result = result[result.length - 1];
                    if (result) {
                        return result;
                    }
                }
                if (!selector || sibling.is(selector)) {
                    return sibling;
                }
            }
            return parent.previousNode(selector, true);
        }
        return null;
    },
    
    previousSibling: function(selector) {
        var parent = this.getRefOwner(),
            it, idx, sibling;
        if (parent) {
            it = parent.items;
            idx = it.indexOf(this);
            if (idx !== -1) {
                if (selector) {
                    for (--idx; idx >= 0; idx--) {
                        if ((sibling = it.getAt(idx)).is(selector)) {
                            return sibling;
                        }
                    }
                } else {
                    if (idx) {
                        return it.getAt(--idx);
                    }
                }
            }
        }
        return null;
    },
    
    nextNode: function(selector, includeSelf) {
        var node = this,
            parent = node.getRefOwner(),
            result, it, len, i, sibling;
        
        if (includeSelf && node.is(selector)) {
            return node;
        }
        if (parent) {
            for (it = parent.items.items , i = Ext.Array.indexOf(it, node) + 1 , len = it.length; i < len; i++) {
                sibling = it[i];
                if (!selector || sibling.is(selector)) {
                    return sibling;
                }
                if (sibling.down) {
                    result = sibling.down(selector);
                    if (result) {
                        return result;
                    }
                }
            }
            return parent.nextNode(selector);
        }
        return null;
    },
    
    nextSibling: function(selector) {
        var parent = this.getRefOwner(),
            it, last, idx, sibling;
        if (parent) {
            it = parent.items;
            idx = it.indexOf(this) + 1;
            if (idx) {
                if (selector) {
                    for (last = it.getCount(); idx < last; idx++) {
                        if ((sibling = it.getAt(idx)).is(selector)) {
                            return sibling;
                        }
                    }
                } else {
                    if (idx < it.getCount()) {
                        return it.getAt(idx);
                    }
                }
            }
        }
        return null;
    }
});


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.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.util.translatable.Abstract', {
    extend: Ext.Evented,
    mixins: [
        Ext.mixin.Factoryable
    ],
    factoryConfig: {
        type: 'translatable',
        defaultType: 'csstransform'
    },
    config: {
        easing: null,
        easingX: {
            duration: 300
        },
        easingY: {
            duration: 300
        }
    },
    
    
    
    
    x: 0,
    
    y: 0,
    activeEasingX: null,
    activeEasingY: null,
    isAnimating: false,
    isTranslatable: true,
    constructor: function(config) {
        this.callParent([
            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: function(x, y) {
        if (this.hasListeners.translate) {
            this.fireEvent('translate', this, x, y);
        }
    },
    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) {
        var me = this;
        me.activeEasingX = easingX;
        me.activeEasingY = easingY;
        me.isAnimating = true;
        if (me.ownerCmp) {
            me.ownerCmp.isTranslating = true;
        }
        me.lastX = null;
        me.lastY = null;
        Ext.AnimationQueue.start(me.doAnimationFrame, me);
        me.fireEvent('animationstart', me, me.x, me.y);
        return me;
    },
    translateAnimated: function(x, y, animation) {
        var me = this,
            now, easing, easingX, easingY;
        if (!Ext.isObject(animation)) {
            animation = {};
        }
        if (me.isAnimating) {
            me.stopAnimation();
        }
        
        me.callback = animation.callback;
        me.callbackScope = animation.scope;
        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;
        if (me.ownerCmp) {
            me.ownerCmp.isTranslating = 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);
    },
    resolveListenerScope: function() {
        var ownerCmp = this.ownerCmp,
            a = arguments;
        if (ownerCmp) {
            return ownerCmp.resolveListenerScope.apply(ownerCmp, a);
        }
        return this.callParent(a);
    },
    destroy: function() {
        var me = this;
        me.destroying = true;
        if (me.isAnimating) {
            me.stopAnimation();
        }
        me.callParent();
        me.destroying = false;
        me.destroyed = true;
    }
});


Ext.define('Ext.overrides.Widget', {
    override: 'Ext.Widget',
    mixins: [
        Ext.mixin.Traversable
    ],
    statics: {
        
        floatInset: 8,
        onModalMaskTap: function(e) {
            var top = this.topModal;
            if (top && top.onModalMaskTap && top.onModalMaskTap(e)) {
                this.topModal = null;
            }
        },
        range: document.createRange()
    },
    config: {
        
        flex: {
            evented: true,
            $value: null
        },
        
        
        itemId: undefined,
        
        floated: null,
        
        relative: null,
        
        x: null,
        
        y: null,
        
        shadow: null,
        
        shim: null,
        
        alwaysOnTop: null,
        
        toFrontOnShow: true,
        
        
        translatable: {
            lazy: true,
            $value: null
        },
        
        constrainAlign: null,
        
        alignSelf: null
    },
    
    inheritUi: false,
    
    floatedCls: Ext.baseCSSPrefix + 'floated',
    
    floatedSelector: '.' + Ext.baseCSSPrefix + 'floated',
    
    shadowCls: Ext.baseCSSPrefix + 'shadow',
    
    noShadowCls: Ext.baseCSSPrefix + 'no-shadow',
    
    floatWrapCls: Ext.baseCSSPrefix + 'float-wrap',
    
    shimCls: Ext.baseCSSPrefix + 'shim',
    rootCls: Ext.baseCSSPrefix + 'root',
    
    
    
    isInner: true,
    clearPropertiesOnDestroy: 'async',
    beforeHide: Ext.emptyFn,
    afterHide: function() {
        var me = this,
            parent = me.getParent();
        if (parent && parent.afterItemHide) {
            parent.afterItemHide(me);
        }
        if (me.getFloated()) {
            me.syncShim();
        }
    },
    beforeShow: Ext.emptyFn,
    afterShow: function() {
        var me = this,
            parent = me.getParent();
        if (parent && parent.afterItemShow) {
            parent.afterItemShow(me);
        }
    },
    applyItemId: function(itemId) {
        return itemId || this.getId();
    },
    doDestroy: function() {
        var me = this,
            parent = me.getParent(),
            fw = me.floatWrap;
        if (parent && parent.remove) {
            parent.remove(me, false);
        }
        me.setShim(false);
        Ext.destroy(me.getTranslatable());
        if (fw) {
            me.un('resize', 'syncFloatWrap', me);
            fw.destroy();
            me.floatWrap = null;
        }
        me.removeBindings();
        me.callParent();
    },
    isInnerItem: function() {
        return this.isInner;
    },
    isCentered: function() {
        return false;
    },
    isDocked: function() {
        return Boolean(this.getDocked());
    },
    isPositioned: function() {
        return false;
    },
    getDocked: function() {
        return this._docked;
    },
    
    isHidden: function(deep) {
        var me = this,
            hidden;
        
        
        
        
        
        if (me.isConfiguring) {
            hidden = me.getConfig('hidden', true);
            
            if (me.getConfig('floated', true)) {
                return hidden !== false;
            } else 
            {
                return !!hidden;
            }
        } else {
            return this.callParent([
                deep
            ]);
        }
        return me.callParent([
            deep
        ]);
    },
    
    onAdded: function(parent, instanced) {
        var me = this,
            currentParent = me.parent;
        if (currentParent && currentParent !== parent) {
            currentParent.remove(me, false);
        }
        me.parent = parent;
        me.onInheritedAdd(parent, instanced);
        
        me.isDetached = false;
        
        if (me.getFloated()) {
            me.findFloatParent();
        }
        if (me.inheritUi) {
            me.doInheritUi();
        }
    },
    onRemoved: function(destroying) {
        var me = this;
        if (me.inheritUi && !destroying) {
            me.doUninheritUi();
        }
        me.onInheritedRemove(destroying);
        me.parent = null;
    },
    setIsInner: function(isInner) {
        var parent;
        if (isInner !== this.isInner) {
            this.isInner = isInner;
            parent = this.initialized && this.getParent();
            if (parent) {
                parent.onItemInnerStateChange(this, isInner);
            }
        }
    },
    refreshInnerState: function() {
        this.setIsInner(!this.getFloated() && !this.isCentered() && !this.isPositioned() && !this.isDocked());
    },
    
    toFront: function(fromMousedown) {
        
        if (!this.getFloated()) {
            Ext.raise('Cannot use toFront on a non-floated component');
        }
        
        var me = this,
            floatParent = me.getFloatParent();
        if (!me.hasListeners.beforetofront || me.fireEvent('beforetofront', me) !== false) {
            me.syncAlwaysOnTop(fromMousedown);
            
            
            
            if (floatParent && floatParent.getFloated()) {
                floatParent.toFront(fromMousedown);
            }
            if (me.hasListeners.tofront) {
                me.fireEvent('tofront', me);
            }
        }
        return me;
    },
    applyTranslatable: function(config, translatable) {
        return Ext.Factory.translatable.update(translatable, config, this, 'createTranslatable');
    },
    createTranslatable: function(config) {
        var me = this,
            listeners = config.listeners;
        config = Ext.apply({
            type: 'cssposition',
            ownerCmp: me,
            element: me.renderElement
        }, config);
        
        
        if (listeners && listeners.scope === 'this') {
            config.listeners = listeners = Ext.apply({}, listeners);
            listeners.scope = me;
        }
        return config;
    },
    ensureTranslatable: function() {
        var me = this,
            translatable = me.getTranslatable();
        if (!translatable) {
            me.setTranslatable(true);
            translatable = me.getTranslatable();
        }
        return translatable;
    },
    translate: function() {
        var translatable = this.ensureTranslatable();
        translatable.translate.apply(translatable, arguments);
    },
    
    getAlignmentInfo: function(component, alignment) {
        var me = this,
            alignToBox = component.isRegion ? component : (component.isWidget ? component.renderElement : Ext.fly(component)).getBox(),
            element = me.renderElement,
            box = element.getBox(),
            stats = {
                alignToBox: alignToBox,
                alignment: alignment,
                top: alignToBox.top,
                left: alignToBox.left,
                
                alignToWidth: alignToBox.width || 0,
                alignToHeight: alignToBox.height || 0,
                width: box.width,
                height: box.height,
                
                
                anchor: !!(me.getAnchor && me.getAnchor())
            },
            currentAlignmentInfo = me.getCurrentAlignmentInfo(),
            isAligned = true;
        if (!Ext.isEmpty(currentAlignmentInfo)) {
            Ext.Object.each(stats, function(key, value) {
                if (!Ext.isObject(value) && currentAlignmentInfo[key] !== value) {
                    isAligned = false;
                    return false;
                }
                return true;
            });
        } else {
            isAligned = false;
        }
        return {
            isAligned: isAligned,
            stats: stats
        };
    },
    
    getCurrentAlignmentInfo: function() {
        return this.$currentAlignmentInfo;
    },
    
    setCurrentAlignmentInfo: function(alignmentInfo) {
        this.$currentAlignmentInfo = Ext.isEmpty(alignmentInfo) ? null : Ext.merge({}, alignmentInfo.stats ? alignmentInfo.stats : alignmentInfo);
    },
    
    alignTo: function(component, alignment, options) {
        var me = this,
            alignmentInfo = me.getAlignmentInfo(component, alignment),
            config = me.initialConfig,
            positioned = !me.getFloated(),
            setX = positioned ? me.setLeft : me.setX,
            setY = positioned ? me.setTop : me.setY,
            oldHeight, resultRegion;
        if (alignmentInfo.isAligned) {
            return;
        }
        if ('unconstrainedWidth' in me) {
            me.setWidth(me.unconstrainedWidth);
        }
        if ('unconstrainedHeight' in me) {
            me.setHeight(me.unconstrainedHeight);
        }
        resultRegion = me.getAlignRegion(component, alignment, options);
        setX.call(me, resultRegion.x);
        setY.call(me, resultRegion.y);
        if (resultRegion.constrainWidth) {
            me.unconstrainedWidth = config.width || me.self.prototype.width;
            
            oldHeight = me.el.getHeight();
            me.setWidth(alignmentInfo.stats.width = resultRegion.getWidth());
            
            
            if (resultRegion.align.position === 0) {
                setY.call(me, resultRegion.y + (oldHeight - me.el.getHeight()));
            }
        }
        if (resultRegion.constrainHeight) {
            me.unconstrainedHeight = config.height || me.self.prototype.height;
            me.setHeight(alignmentInfo.stats.height = resultRegion.getHeight());
        }
        
        
        me.alignToArgs = [
            component,
            alignment,
            options
        ];
        me.setCurrentAlignmentInfo(alignmentInfo);
    },
    
    realign: function(component, alignment, options) {
        var args = this.alignToArgs;
        if (this.isVisible()) {
            this.alignTo.call(this, component || args[0], alignment || args[1], options || args[2]);
        }
    },
    
    getAlignRegion: function(component, alignment, options) {
        var me = this,
            alignmentInfo = me.getAlignmentInfo(component, alignment),
            constrainModifier, inside;
        if (alignmentInfo.isAligned) {
            return;
        }
        var alignToBox = alignmentInfo.stats.alignToBox,
            constrainBox = me.getConstrainAlignRegion(),
            height = alignmentInfo.stats.height,
            width = alignmentInfo.stats.width;
        if (constrainBox && (!alignment || alignment === 'auto')) {
            if (constrainBox.bottom - alignToBox.bottom < height) {
                if (alignToBox.top - constrainBox.top < height) {
                    if (alignToBox.left - constrainBox.left < width) {
                        alignment = 'l-r?';
                    } else {
                        alignment = 'r-l?';
                    }
                } else {
                    alignment = 'b-t?';
                }
            } else {
                alignment = 't-b?';
            }
        }
        constrainModifier = alignment[alignment.length - 1];
        
        if (constrainModifier === '!') {
            inside = component.isRegion ? component : (component.isWidget ? component.renderElement : Ext.fly(component)).getBox();
            alignment = alignment.substr(0, alignment.length - 1);
        } else 
        {
            inside = constrainBox;
            if (constrainModifier === '?') {
                alignment = alignment.substr(0, alignment.length - 1);
            }
        }
        return me.el.getRegion().alignTo(Ext.apply({
            target: Ext.util.Region.from(alignmentInfo.stats.alignToBox),
            align: alignment,
            inside: inside,
            minWidth: me.getMinWidth && me.getMinWidth(),
            minHeight: me.getMinHeight && me.getMinHeight()
        }, options));
    },
    
    render: function(container, insertBeforeElement) {
        if (this.getFloated()) {
            Ext.raise('floated: true components cannot be rendered. They render themselves on first show');
            return;
        }
        this.callParent([
            container,
            insertBeforeElement
        ]);
    },
    
    
    afterRender: Ext.emptyFn,
    
    onRender: Ext.emptyFn,
    
    applyFloated: function(floated) {
        return Boolean(floated);
    },
    updateFloated: function(floated, oldFloated) {
        var me = this,
            fw = me.floatWrap,
            modal, sibling;
        if (floated) {
            me.refreshInnerState = Ext.emptyFn;
            if (me.isPositioned()) {
                me.resetPositioned();
            }
            if (me.isDocked()) {
                me.setDocked(false);
            }
            delete me.refreshInnerState;
        } else {
            
            if (fw) {
                fw.dom.removeChild(me.el.dom);
                me.un('resize', 'syncFloatWrap', me);
                fw.destroy();
                me.floatWrap = null;
                me.setRendered(false);
            }
        }
        
        
        
        
        me.refreshInnerState();
        me.el.toggleCls(me.floatedCls, floated);
        
        
        
        
        
        
        if (me.hasOwnProperty('_modal')) {
            modal = me.getModal && me.getModal();
            if (modal) {
                me.setModal(false);
                if (floated) {
                    
                    
                    
                    Ext.destroy(modal);
                    if (me.isVisible()) {
                        me.showModalMask();
                    }
                } else {
                    
                    
                    
                    sibling = me.getModalSibling();
                    if (sibling) {
                        sibling.showModalMask();
                    } else {
                        me.hideModalMask();
                    }
                }
                me.setModal(true);
            }
            if (me.getHideOnMaskTap && me.getHideOnMaskTap()) {
                me.setHideOnMaskTap(false);
                me.setHideOnMaskTap(true);
            }
        }
        
        
        
        me.syncFloatedState(floated, oldFloated, me.rendered && oldFloated === false);
    },
    applyUi: function(ui) {
        var me = this,
            inheritedUi = me._inheritedUi;
        ui = me.callParent([
            ui
        ]);
        if (inheritedUi) {
            
            ui = me.doAddUi(inheritedUi, ui);
        }
        return ui;
    },
    updateUi: function(ui, oldUi) {
        var me = this,
            item, refItems, i, n;
        me.callParent([
            ui,
            oldUi
        ]);
        if (me.$inheritUiCount) {
            refItems = me.getRefItems();
            for (i = 0 , n = refItems.length; i < n; i++) {
                item = refItems[i];
                if (item.inheritUi) {
                    item.doUninheritUi();
                    item.doInheritUi();
                }
            }
        }
    },
    updateHidden: function(hidden, oldHidden) {
        var globals = Ext.GlobalEvents,
            event = hidden ? 'hide' : 'show';
        this.callParent([
            hidden,
            oldHidden
        ]);
        if (!this.isConfiguring && globals.hasListeners[event]) {
            globals.fireEvent(event, this);
        }
    },
    updateAlignSelf: function(align) {
        this.el.setStyle({
            'align-self': align
        });
    },
    privates: {
        
        setRendered: function(rendered, root) {
            var me = this,
                afterRenderQueue, item, items, ln, i;
            if (!rendered && me.rendered && me.getFloated()) {
                
                
                
                me.floatWrap.dom.parentNode.removeChild(me.floatWrap.dom);
            }
            me.rendered = rendered;
            if (!me.destroying && !me.destroyed) {
                if (rendered && me.onRender) {
                    me.initBindable();
                    me.initKeyMap();
                    if (!me.onRender.$nullFn) {
                        me.onRender();
                    }
                    me.onRender = null;
                    
                    
                    
                    
                    if (!me.afterRender.$nullFn) {
                        (Ext._afterRenderQueue || (Ext._afterRenderQueue = [])).push(me);
                    }
                }
                
                items = me.getRefItems && me.getRefItems();
                ln = items && items.length;
                for (i = 0; i < ln; i++) {
                    item = items[i];
                    
                    
                    if (item.setRendered) {
                        me.setChildRendered(rendered, item);
                    }
                }
                if (me.focusableContainer && me.initFocusableContainer) {
                    me.initFocusableContainer();
                }
                if (root) {
                    afterRenderQueue = Ext._afterRenderQueue;
                    if (afterRenderQueue) {
                        while (afterRenderQueue.length) {
                            item = afterRenderQueue.pop();
                            if (!item.destroyed) {
                                item.afterRender();
                            }
                        }
                    }
                }
            }
        },
        setChildRendered: function(rendered, item) {
            
            
            
            if (!rendered || !item.getFloated()) {
                item.setRendered(rendered);
            }
        },
        hideFromModal: function() {
            this.hide();
        },
        
        owns: function(element) {
            var result = false,
                cmp;
            if (element.isEvent) {
                element = element.target;
            } else if (element.isElement) {
                element = element.dom;
            }
            cmp = Ext.Component.from(element);
            if (cmp) {
                result = (cmp === this) || (!!cmp.up(this));
            }
            return result;
        },
        
        doInheritUi: function() {
            var me = this,
                owner, ownerUi;
            if (me.inheritUi) {
                me._ownUi = me.getUi();
                owner = me.getRefOwner();
                ownerUi = owner.getUi();
                if (ownerUi) {
                    me.addUi(ownerUi);
                    me._inheritedUi = ownerUi;
                }
                owner.$inheritUiCount = (owner.$inheritUiCount || 0) + 1;
            }
        },
        
        doUninheritUi: function() {
            var me = this,
                inheritUi = me.inheritUi,
                ownUi, owner;
            if (inheritUi) {
                owner = me.getRefOwner();
                ownUi = me._ownUi;
                me._ownUi = null;
                me._inheritedUi = null;
                me.setUi(ownUi || null);
                if (owner.$inheritUiCount) {
                    --owner.$inheritUiCount;
                }
            }
        },
        getBubbleTarget: function() {
            return this.getParent();
        },
        getConstrainAlignRegion: function() {
            var me = this,
                isFloated = me.getFloated(),
                constrainAlign = me.getConstrainAlign(),
                parent, isViewport, docInsets;
            if (constrainAlign !== false) {
                if (typeof constrainAlign === 'string') {
                    constrainAlign = parent = me.up(constrainAlign);
                }
                if (!constrainAlign) {
                    
                    
                    parent = isFloated ? me.floatParentNode.getData().component : me.getParent();
                }
                
                
                if (parent) {
                    
                    if (parent.getFloated() && !parent.getRelative()) {
                        constrainAlign = Ext.getBody();
                        isViewport = true;
                    } else {
                        constrainAlign = parent.getRenderTarget ? parent.getRenderTarget() : (parent.bodyElement || parent.element);
                        isViewport = parent.isViewport;
                    }
                }
                
                else if (!constrainAlign) {
                    if (isFloated) {
                        isViewport = true;
                        constrainAlign = Ext.getBody();
                    } else {
                        constrainAlign = me.element.parent();
                    }
                }
                if (!constrainAlign.isRegion) {
                    constrainAlign = Ext.fly(constrainAlign).getConstrainRegion();
                    
                    if (isViewport) {
                        
                        docInsets = Math.round(Ext.Widget.floatInset);
                        constrainAlign.adjust(docInsets, -docInsets, -docInsets, docInsets);
                    }
                }
                return constrainAlign;
            }
        },
        
        getFloatParent: function() {
            var result = this.floatParentNode.getData().component;
            return result && result.getFloated() ? result : null;
        },
        syncFloatedState: function(floated, oldFloated, rendered) {
            var me = this,
                isHidden = me.isHidden();
            if (floated) {
                if (rendered) {
                    if (me.isCentered()) {
                        
                        me.getWidth();
                        me.getHeight();
                        me.center();
                    } else {
                        me.syncXYPosition();
                    }
                    if (!isHidden) {
                        me.showModalMask();
                    }
                } else 
                {
                    if (!isHidden) {
                        me.findFloatParent();
                    } else {
                        
                        
                        
                        
                        
                        
                        
                        me.setHidden(true);
                    }
                }
            } else 
            
            {
                me.translate(0, 0, 0);
            }
        },
        
        findFloatParent: function(needsShow) {
            var me = this,
                parent = me.getRefOwner();
            
            while (parent && !parent.getFloated()) {
                parent = parent.getRefOwner();
            }
            
            
            
            
            if (!parent) {
                me.floatParentNode = Ext.getFloatRoot();
            } else 
            {
                me.floatParentNode = parent.getFloatWrap();
            }
            me.insertFloatedDom(needsShow);
        },
        
        getFloatWrap: function() {
            var me = this,
                fw = me.floatWrap;
            if (!fw) {
                me.floatWrap = fw = Ext.get(Ext.DomHelper.createDom({
                    cls: me.floatWrapCls,
                    id: me.id + '-floatWrap',
                    "data-componentId": me.id
                }));
                me.on('resize', 'syncFloatWrap', me);
                
                
                
                fw.getData().component = me;
                if (me.isContainer && (!me.isWidthed() || !me.isHeighted())) {
                    me.setAutoSize(true);
                }
                
                
                fw.dom.appendChild(me.element.dom);
                
                fw.getData().alwaysOnTop = me.element.getData().alwaysOnTop;
                me.syncFloatWrap();
            }
            return fw;
        },
        
        applyRenderTo: function(renderTo) {
            if (renderTo && this.getFloated()) {
                Ext.raise('floated: true components cannot be rendered. They render themselves on first show');
                return;
            }
            return renderTo;
        },
        
        
        syncFloatWrap: function() {
            var me = this,
                floatWrap = me.floatWrap,
                mySize = me.el.getSize(),
                mask;
            
            
            if (me.getRelative()) {
                floatWrap.setSize(mySize);
            } else {
                mask = floatWrap.getData().modalMask;
                if (mask) {
                    mask.setSize(mySize);
                }
            }
        },
        
        insertFloatedDom: function(needsShow) {
            var me = this,
                fw = me.getFloatWrap(),
                floatParentNode = me.floatParentNode;
            if (fw.dom.parentNode !== floatParentNode.dom) {
                floatParentNode.dom.appendChild(me.getFloatWrap().dom);
                if (needsShow) {
                    
                    
                    
                    me.setVisibility(true);
                    me._hidden = false;
                }
                me.setRendered(true, true);
                me.syncXYPosition();
            }
        },
        applyShim: function(shim) {
            
            if (shim && !this.getFloated()) {
                Ext.raise('Cannot use setShim on a non-floated component');
            }
            
            if (shim) {
                
                return Ext.getBody().createChild(Ext.apply({
                    cls: this.shimCls
                }, shim));
            } else {
                Ext.destroy(this.shim);
                return null;
            }
        },
        updateShim: function(shim, oldShim) {
            var me = this;
            if (shim) {
                me.syncShim();
                if (!oldShim) {
                    me.on('resize', 'syncShim', me);
                }
            } else if (oldShim) {
                me.un('resize', 'syncShim', me);
            }
        },
        hideModalMask: function() {
            var me = this,
                floatRoot = Ext.getFloatRoot(),
                floatParentNode = me.floatParentNode,
                data, mask;
            
            if (floatParentNode) {
                data = floatParentNode.getData();
                
                if (floatParentNode !== floatRoot && !data.component.getRelative()) {
                    data = floatRoot.getData();
                }
                mask = data.modalMask;
                if (mask && mask.dom.parentNode) {
                    mask = mask.dom;
                    Ext.getDetachedBody().appendChild(mask);
                }
            }
        },
        showModalMask: function() {
            var me = this,
                Widget = Ext.Widget,
                floatRoot = Ext.getFloatRoot(),
                positionEl = me.getFloatWrap(),
                parent = me.getParent(),
                floatParentNode = me.floatParentNode,
                data = floatParentNode.getData(),
                mask;
            if (me.getFloated() && me.getModal && me.getModal()) {
                
                
                
                if (floatParentNode !== floatRoot && !data.component.getRelative()) {
                    data = floatRoot.getData();
                }
                
                mask = data.modalMask;
                if (mask) {
                    
                    
                    if (positionEl.dom.parentElement === floatParentNode.dom) {
                        floatParentNode.dom.insertBefore(mask.dom, positionEl.dom);
                    } else {
                        floatParentNode.dom.appendChild(mask.dom);
                    }
                } else {
                    mask = data.modalMask = floatParentNode.createChild({
                        cls: 'x-mask'
                    }, positionEl);
                    mask.on({
                        tap: Widget.onModalMaskTap,
                        scope: Widget
                    });
                    
                    
                    
                    if (Ext.isiOS && floatParentNode === floatRoot) {
                        mask.on({
                            touchmove: function(e) {
                                e.preventDefault();
                            }
                        });
                    }
                }
                Widget.topModal = me;
                
                
                if (parent && parent.getFloated() && !parent.getRelative()) {
                    parent.syncXYPosition();
                }
            }
        },
        syncShim: function() {
            var me = this,
                shim = me.getShim();
            if (shim) {
                if (me.isVisible(true)) {
                    shim.show();
                    me.getFloatWrap().dom.insertBefore(shim.dom, me.el.dom);
                    shim.setSize(me.getSize());
                } else {
                    shim.hide();
                }
            }
        },
        updateAlwaysOnTop: function(alwaysOnTop) {
            var me = this;
            me.getFloatWrap().getData().alwaysOnTop = Number(alwaysOnTop);
            if (!me.floatParentNode) {
                me.findFloatParent();
            }
            if (!me.isConfiguring) {
                me.syncAlwaysOnTop();
            }
        },
        
        syncAlwaysOnTop: function(fromMousedown) {
            var me = this,
                positionEl = me.getFloatWrap().dom,
                parentEl = me.floatParentNode,
                nodes = parentEl.dom.childNodes,
                len = nodes.length,
                alwaysOnTop = Number(me.getAlwaysOnTop()),
                range = me.statics().range,
                maskCls = Ext.Mask.prototype.baseCls,
                i, startIdx, nodeEl, refNode, isTopModal, currentAlwaysOnTop;
            
            
            
            startIdx = parentEl === Ext.floatRoot ? 0 : 1;
            
            
            isTopModal = me.getModal() && positionEl.previousSibling && Ext.fly(positionEl.previousSibling).hasCls(maskCls);
            if (positionEl.nextSibling) {
                for (i = len - 1; i >= startIdx; i--) {
                    nodeEl = Ext.fly(nodes[i]);
                    
                    if (!nodeEl.hasCls(me.shimCls) && !nodeEl.hasCls(maskCls) && nodes[i] !== positionEl) {
                        
                        
                        if (alwaysOnTop >= (Ext.get(nodes[i]).getData().alwaysOnTop || 0)) {
                            refNode = nodes[i].nextSibling;
                            break;
                        }
                    }
                }
                
                if (refNode === positionEl) {
                    return;
                }
                
                if (i < startIdx) {
                    refNode = nodes[0];
                }
                
                
                if (me.containsFocus || fromMousedown) {
                    
                    range.setStartAfter(positionEl);
                    range.setEndAfter(refNode || nodes[len - 1]);
                    
                    parentEl.dom.insertBefore(range.extractContents(), positionEl);
                } else {
                    parentEl.dom.insertBefore(positionEl, refNode);
                }
            } else if (positionEl.previousSibling) {
                for (i = len - 2; i >= startIdx; i--) {
                    
                    
                    if (!Ext.fly(nodes[i]).is('.' + me.shimCls) && nodes[i] !== positionEl) {
                        
                        
                        currentAlwaysOnTop = (Ext.get(nodes[i]).getData().alwaysOnTop);
                        if (alwaysOnTop < currentAlwaysOnTop) {
                            refNode = nodes[i];
                            parentEl.dom.insertBefore(refNode, null);
                            if (nodes[i - 1] && Ext.fly(nodes[i - 1]).hasCls(maskCls)) {
                                parentEl.dom.insertBefore(nodes[i - 1], refNode);
                                i -= 1;
                            }
                            alwaysOnTop = currentAlwaysOnTop;
                        }
                    }
                }
            }
            
            if (isTopModal) {
                me.showModalMask();
            }
            me.syncShim();
            if (refNode && !Ext.fly(refNode).hasCls(maskCls)) {
                Ext.Component.from(refNode).syncShim();
            } else {
                return true;
            }
        },
        updateRelative: function() {
            this.syncXYPosition();
        },
        updateShadow: function(shadow) {
            this.el.toggleCls(this.shadowCls, shadow);
            this.el.toggleCls(this.noShadowCls, shadow === false);
        },
        updateX: function() {
            
            if (!this.getFloated()) {
                Ext.raise('Cannot use setX on a non-floated component');
            }
            
            if (!this.$updatingXY) {
                
                this.syncXYPosition();
            }
        },
        updateY: function() {
            
            if (!this.getFloated()) {
                Ext.raise('Cannot use setY on a non-floated component');
            }
            
            if (!this.$updatingXY) {
                this.syncXYPosition();
            }
        },
        
        syncXYPosition: function(animation) {
            var me = this,
                floatWrap = me.getFloatWrap(),
                maskAnim = animation,
                mask,
                x = me.getX() || 0,
                y = me.getY() || 0;
            
            
            if (me.getRelative()) {
                floatWrap.translate(x, y, animation);
                floatWrap.setWidth(me.el.getWidth());
                floatWrap.setHeight(me.el.getHeight());
                me.translate(0, 0);
                mask = floatWrap.getData().modalMask;
                if (mask) {
                    mask.translate(0, 0);
                }
            } else 
            {
                me.translate(x, y, animation);
                mask = floatWrap.getData().modalMask;
                if (mask) {
                    if (maskAnim) {
                        maskAnim = Ext.apply({}, maskAnim);
                        delete maskAnim.callback;
                    }
                    mask.translate(x, y, maskAnim);
                }
            }
            me.syncShim();
        }
    }
}, function(Widget) {
    this.borrow(Ext.util.Positionable, [
        'clipTo',
        'clearClip'
    ]);
    
    
    Widget.createAlias({
        prev: 'previousSibling',
        next: 'nextSibling'
    });
    
    Ext.getFloatRoot = function() {
        var fp = Ext.floatRoot,
            viewport = Ext['Viewport'],
            
            prototype = Widget.prototype,
            range = Widget.range;
        if (fp) {
            
            if (fp.el.dom.nextSibling) {
                
                
                if (fp.el.contains(document.activeElement)) {
                    range.setStartBefore(fp.el.dom.nextSibling);
                    range.setEndAfter(fp.el.dom.parentNode.lastChild);
                    fp.el.dom.parentNode.insertBefore(range.extractContents(), fp.el.dom);
                } else {
                    fp.el.dom.parentNode.appendChild(fp.dom);
                }
            }
        } else {
            fp = Ext.getBody().createChild({
                cls: prototype.floatWrapCls + ' ' + prototype.rootCls,
                id: 'ext-global-floatWrap',
                "data-sticky": true
            });
            
            fp.$skipResourceCheck = true;
            
            Ext.floatRoot = fp;
        }
        return fp;
    };
});


Ext.define('Ext.util.Format', function() {
    var me;
    
    return {
        singleton: true,
        
        defaultDateFormat: 'm/d/Y',
        
        thousandSeparator: ',',
        
        decimalSeparator: '.',
        
        currencyPrecision: 2,
        
        currencySign: '$',
        
        currencySpacer: '',
        
        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;
        },
        
        
        nbsp: function(value, strict) {
            strict = strict !== false;
            if (strict ? value === '' || value == null : !value) {
                value = '\xa0';
            }
            return value;
        },
        
        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(value) {
            return me.currency(value, '$', 2);
        },
        
        currency: function(value, currencySign, decimals, end, currencySpacer) {
            var negativeSign = '',
                format = ",0",
                i = 0;
            value = value - 0;
            if (value < 0) {
                value = -value;
                negativeSign = '-';
            }
            decimals = Ext.isDefined(decimals) ? decimals : me.currencyPrecision;
            format += (decimals > 0 ? '.' : '');
            for (; i < decimals; i++) {
                format += '0';
            }
            value = me.number(value, format);
            if (currencySpacer == null) {
                currencySpacer = me.currencySpacer;
            }
            if ((end || me.currencyAtEnd) === true) {
                return Ext.String.format("{0}{1}{2}{3}", negativeSign, value, currencySpacer, currencySign || me.currencySign);
            } else {
                return Ext.String.format("{0}{1}{2}{3}", negativeSign, currencySign || me.currencySign, currencySpacer, value);
            }
        },
        
        date: function(value, format) {
            if (!value) {
                return "";
            }
            if (!Ext.isDate(value)) {
                value = new Date(Date.parse(value));
            }
            return Ext.Date.dateFormat(value, 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;
        },
        
        lessThanElse: function(value, threshold, below, above, equal) {
            var v = Ext.Number.from(value, 0),
                t = Ext.Number.from(threshold, 0),
                missing = !Ext.isDefined(equal);
            return v < t ? below : (v > t ? above : (missing ? above : equal));
        },
        
        sign: function(value, negative, positive, zero) {
            if (zero === undefined) {
                zero = positive;
            }
            return me.lessThanElse(value, 0, negative, positive, zero);
        },
        
        stripTags: function(value) {
            return !value ? value : String(value).replace(me.stripTagsRe, "");
        },
        
        stripScripts: function(value) {
            return !value ? value : String(value).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.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;
        },
        repeat: function(value, text, sep) {
            return Ext.String.repeat(text, value, sep);
        },
        
        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(value, singular, plural) {
            return value + ' ' + (value === 1 ? singular : (plural ? plural : singular + '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
            };
        },
        
        resource: function(url, prefix) {
            prefix = prefix || '';
            return Ext.resolveResource(prefix + url);
        },
        
        uri: function(value) {
            return encodeURI(value);
        },
        
        uriCmp: function(value) {
            return encodeURIComponent(value);
        },
        wordBreakRe: /[\W\s]+/,
        
        word: function(value, index, sep) {
            var re = sep ? (typeof sep === 'string' ? new RegExp(sep) : sep) : me.wordBreakRe,
                parts = (value || '').split(re);
            return parts[index || 0] || '';
        }
    };
});


Ext.define('Ext.Template', {
    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 = [],
            length = args.length,
            i, 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.String.formatEncode = function() {
        return Ext.String.htmlEncode(Ext.String.format.apply(this, arguments));
    };
});


Ext.define('Ext.util.XTemplateParser', {
    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;
        me.definitions = me.body = me.switches = 0;
        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,
    isXTemplate: true,
    
    emptyObj: {},
    
    
    
    fn: null,
    
    strict: false,
    apply: function(values, parent, xindex, xcount) {
        var buffer = this.applyOut(values, [], parent, xindex, xcount);
        
        
        
        return buffer.length === 1 ? buffer[0] : buffer.join('');
    },
    applyOut: function(values, out, parent, xindex, xcount) {
        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);
        }
        
        xindex = xindex || 1;
        
        xcount = xcount || 1;
        if (me.strict) {
            me.fn(out, values, parent || me.emptyObj, xindex, xcount);
        } else {
            try {
                me.fn(out, values, parent || me.emptyObj, xindex, xcount);
            } catch (e) {
                
                Ext.log.warn('XTemplate evaluation exception: ' + e.message);
            }
        }
        
        return out;
    },
    
    compile: function() {
        return this;
    },
    statics: {
        get: function(config, source, defaultTpl) {
            var ret = config;
            if (config == null) {
                if (source && defaultTpl) {
                    ret = this.getTpl(source, defaultTpl);
                }
            } else if ((config || config === '') && !config.isTemplate) {
                ret = new this(config);
            }
            return ret;
        },
        
        getTpl: function(instance, name) {
            var tpl = instance[name],
                
                owner;
            if (tpl) {
                
                if (!tpl.isTemplate) {
                    
                    tpl = Ext.XTemplate.get(tpl);
                }
                if (!tpl.owner) {
                    
                    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.util.translatable.Dom', {
    extend: Ext.util.translatable.Abstract,
    alias: 'translatable.dom',
    
    config: {
        element: null
    },
    applyElement: function(element) {
        if (!element) {
            return;
        }
        return Ext.get(element);
    },
    updateElement: function() {
        this.refresh();
    }
});


Ext.define('Ext.util.translatable.CssPosition', {
    extend: Ext.util.translatable.Dom,
    alias: 'translatable.cssposition',
    
    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';
        }
        this.callParent([
            x,
            y
        ]);
    },
    syncPosition: function() {
        var domStyle = this.getElement().dom.style;
        return [
            this.x = parseFloat(domStyle.left),
            this.y = parseFloat(domStyle.top)
        ];
    },
    destroy: function() {
        var domStyle = this.getElement().dom.style;
        domStyle.left = null;
        domStyle.top = null;
        this.callParent();
    }
});


Ext.define('Ext.util.translatable.CssTransform', {
    extend: Ext.util.translatable.Dom,
    alias: 'translatable.csstransform',
    
    isCssTransform: true,
    posRegex: /(\d+)px[^\d]*(\d+)px/,
    doTranslate: function(x, y) {
        var me = this,
            element = me.getElement();
        if (!me.destroyed && !element.destroyed) {
            element.translate(x, y);
        }
        me.callParent([
            x,
            y
        ]);
    },
    syncPosition: function() {
        var pos = this.posRegex.exec(this.getElement().dom.style.tranform);
        if (pos) {
            this.x = parseFloat(pos[1]);
            this.y = parseFloat(pos[2]);
        }
        return [
            this.x,
            this.y
        ];
    },
    destroy: function() {
        var element = this.getElement();
        if (element && !element.destroyed) {
            element.dom.style.webkitTransform = null;
        }
        this.callParent();
    }
});


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(stylesheet) {
            var styleEl = (typeof stylesheet === 'string') ? doc.getElementById(stylesheet) : stylesheet.ownerNode;
            if (styleEl) {
                styleEl.parentNode.removeChild(styleEl);
            }
        },
        
        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.translatable.ScrollPosition', {
    extend: Ext.util.translatable.Dom,
    alias: 'translatable.scrollposition',
    
    constructor: function(config) {
        if (config && config.element) {
            this.x = config.element.getScrollLeft();
            this.y = config.element.getScrollTop();
        }
        this.callParent([
            config
        ]);
    },
    translateAnimated: function() {
        var element = this.getElement();
        this.x = element.getScrollLeft();
        this.y = element.getScrollTop();
        this.callParent(arguments);
    },
    doTranslate: function(x, y) {
        var element = this.getElement();
        element.setScrollLeft(Math.round(x));
        element.setScrollTop(Math.round(y));
    },
    getPosition: function() {
        var me = this,
            position = me.position,
            element = me.getElement();
        position.x = element.getScrollLeft();
        position.y = element.getScrollTop();
        return position;
    }
});


Ext.define('Ext.scroll.Scroller', {
    extend: Ext.Evented,
    alias: 'scroller.scroller',
    mixins: [
        Ext.mixin.Factoryable,
        Ext.mixin.Bufferable
    ],
    factoryConfig: {
        defaultType: 'scroller'
    },
    bufferableMethods: {
        onDomScrollEnd: 100
    },
    isScroller: true,
    
    
    
    
    config: {
        
        direction: undefined,
        
        
        element: undefined,
        
        scrollbars: null,
        
        snapSelector: null,
        
        snapOffset: null,
        
        msSnapInterval: null,
        
        x: true,
        
        y: true,
        
        scrollElement: null,
        
        size: null,
        spacerXY: null,
        
        touchAction: null
    },
    snappableCls: Ext.baseCSSPrefix + 'scroller-snappable',
    elementCls: Ext.baseCSSPrefix + 'scroller',
    spacerCls: Ext.baseCSSPrefix + 'scroller-spacer',
    noScrollbarsCls: Ext.baseCSSPrefix + 'no-scrollbars',
    statics: {
        
        create: function(config, type) {
            return Ext.Factory.scroller(config, type);
        },
        
        getScrollingElement: function() {
            var doc = document,
                standard = this.$standardScrollElement,
                el = doc.scrollingElement,
                iframe, frameDoc;
            
            
            
            if (el) {
                return el;
            }
            
            
            if (standard === undefined) {
                iframe = document.createElement('iframe');
                iframe.style.height = '1px';
                document.body.appendChild(iframe);
                frameDoc = iframe.contentWindow.document;
                frameDoc.write('<!DOCTYPE html><div style="height:9999em">x</div>');
                frameDoc.close();
                standard = frameDoc.documentElement.scrollHeight > frameDoc.body.scrollHeight;
                iframe.parentNode.removeChild(iframe);
                this.$standardScrollElement = standard;
            }
            return standard ? doc.documentElement : doc.body;
        },
        
        initViewportScroller: function() {
            var scroller = Ext.getViewportScroller();
            if (!scroller.getElement()) {
                
                
                
                scroller.setElement(Ext.getBody());
            }
        }
    },
    constructor: function(config) {
        var me = this;
        me.position = {
            x: 0,
            y: 0
        };
        me.callParent([
            config
        ]);
    },
    destroy: function() {
        var me = this,
            partners = me._partners,
            key;
        Ext.undefer(me.restoreTimer);
        
        me.setX(Ext.emptyString);
        me.setY(Ext.emptyString);
        if (me._spacer) {
            me._spacer.destroy();
        }
        if (me.scrollListener) {
            me.scrollListener.destroy();
        }
        if (partners) {
            for (key in partners) {
                me.removePartner(partners[key].scroller);
            }
        }
        
        
        me.setElement(null);
        me._partners = me.component = null;
        if (me.translatable) {
            me.translatable.destroy();
            me.translatable = null;
        }
        me.removeSnapStylesheet();
        me.callParent();
    },
    
    addPartner: function(partner, axis) {
        var me = this,
            partners = me._partners || (me._partners = {}),
            otherPartners = partner._partners || (partner._partners = {});
        
        axis = me.axisConfigs[axis || 'both'];
        partners[partner.getId()] = {
            scroller: partner,
            axes: axis
        };
        otherPartners[me.getId()] = {
            scroller: me,
            axes: axis
        };
    },
    applyElement: function(element, oldElement) {
        var me = this;
        if (oldElement && me.scrollListener) {
            me.scrollListener.destroy();
        }
        if (element) {
            
            if (typeof element === 'string' && !Ext.get(element)) {
                Ext.raise("Cannot create Ext.scroll.Scroller instance. " + "Element with id '" + element + "' not found.");
            }
            
            element = Ext.get(element);
        }
        return element;
    },
    applySize: function(size, oldSize) {
        var x, y;
        if (size === null || typeof size === 'number') {
            x = y = size;
        } else if (size) {
            x = size.x;
            y = size.y;
        }
        if (x === null) {
            x = 0;
        } else if (x === undefined) {
            x = (oldSize ? oldSize.x : 0);
        }
        if (y === null) {
            y = 0;
        } else if (y === undefined) {
            y = (oldSize ? oldSize.y : 0);
        }
        if (!oldSize || x !== oldSize.x || y !== oldSize.y) {
            return {
                x: x,
                y: y
            };
        }
    },
    
    getClientSize: function() {
        var dom = this.getElement().dom;
        return {
            x: dom.clientWidth,
            y: dom.clientHeight
        };
    },
    
    getScrollbarSize: function() {
        var me = this,
            width = 0,
            height = 0,
            element = me.getElement(),
            dom, x, y, hasXScroll, hasYScroll, scrollbarSize;
        if (element && !element.destroyed) {
            x = me.getX();
            y = me.getY();
            dom = element.dom;
            if (x || y) {
                scrollbarSize = Ext.getScrollbarSize();
            }
            if (x === 'scroll') {
                hasXScroll = true;
            } else if (x) {
                hasXScroll = dom.scrollWidth > dom.clientWidth;
            }
            if (y === 'scroll') {
                hasYScroll = true;
            } else if (y) {
                hasYScroll = dom.scrollHeight > dom.clientHeight;
            }
            if (hasXScroll) {
                height = scrollbarSize.height;
            }
            if (hasYScroll) {
                width = scrollbarSize.width;
            }
        }
        return {
            width: width,
            height: height
        };
    },
    
    getPosition: function() {
        var me = this;
        if (me.positionDirty) {
            me.updateDomScrollPosition();
        }
        return me.position;
    },
    
    getSize: function() {
        var element = this.getElement(),
            size, dom;
        if (element && !element.destroyed) {
            dom = element.dom;
            size = {
                x: dom.scrollWidth,
                y: dom.scrollHeight
            };
        } else {
            size = {
                x: 0,
                y: 0
            };
        }
        return size;
    },
    
    getMaxPosition: function() {
        var element = this.getElement(),
            x = 0,
            y = 0,
            dom;
        if (element && !element.destroyed) {
            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.destroyed) {
            dom = element.dom;
            if (me.getX()) {
                x = dom.scrollWidth - dom.clientWidth;
            }
            if (me.getY()) {
                y = dom.scrollHeight - dom.clientHeight;
            }
        }
        return {
            x: x,
            y: y
        };
    },
    
    refresh: function() {
        
        
        this.positionDirty = true;
        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);
    },
    
    ensureVisible: function(el, options) {
        var me = this,
            position = me.getPosition(),
            highlight, newPosition, ret;
        
        if (el) {
            if (el && el.element && !el.isElement) {
                options = el;
                el = options.element;
            }
            options = options || {};
            highlight = options.highlight;
            newPosition = me.getEnsureVisibleXY(el, options);
            
            if (newPosition.y !== position.y || newPosition.x !== position.x) {
                if (highlight) {
                    me.on({
                        scrollend: 'doHighlight',
                        scope: me,
                        single: true,
                        args: [
                            el,
                            highlight
                        ]
                    });
                }
                ret = me.doScrollTo(newPosition.x, newPosition.y, options.animation);
            } else {
                
                if (highlight) {
                    me.doHighlight(el, highlight);
                }
                
                ret = Ext.Deferred.getCachedResolved();
            }
        } else {
            
            ret = Ext.Deferred.getCachedRejected();
        }
        return ret;
    },
    
    scrollIntoView: function(el, hscroll, animate, highlight) {
        return this.ensureVisible(el, {
            animation: animate,
            highlight: highlight,
            x: hscroll
        });
    },
    
    isInView: function(el) {
        return this.doIsInView(el);
    },
    
    scrollTo: function(x, y, animation) {
        var maxPosition;
        if (x) {
            if (x.length) {
                
                animation = y;
                y = x[1];
                x = x[0];
            } else if (typeof x !== 'number') {
                
                animation = 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;
            }
        }
        return this.doScrollTo(x, y, animation);
    },
    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);
        }
    },
    updateScrollbars: function(scrollbars, oldScrollbars) {
        this.syncScrollbarCls();
    },
    updateSize: function(size) {
        var me = this,
            element = me.getElement(),
            x = size.x,
            y = size.y,
            spacer;
        if (element) {
            me.positionDirty = true;
            spacer = me.getSpacer();
            
            
            
            
            if (!x && !y) {
                spacer.hide();
            } else {
                
                if (x > 0) {
                    x -= 1;
                }
                if (y > 0) {
                    y -= 1;
                }
                me.setSpacerXY({
                    x: x,
                    y: y
                });
                spacer.show();
            }
        }
    },
    updateMsSnapInterval: function() {
        this.initMsSnapInterval();
    },
    updateSnapSelector: function() {
        this.initSnap();
    },
    updateSnapOffset: function() {
        this.initSnap();
    },
    updateTouchAction: function(touchAction) {
        var element = this.getElement();
        if (element) {
            element.setTouchAction(touchAction);
        }
    },
    updateElement: function(element, oldElement) {
        var me = this,
            touchAction = me.getTouchAction(),
            scrollListener = me.scrollListener,
            elementCls = me.elementCls,
            eventSource, scrollEl;
        
        if (scrollListener) {
            scrollListener.destroy();
            me.scrollListener = null;
            me.setScrollElement(null);
        }
        if (oldElement && !oldElement.destroyed) {
            
            oldElement.setStyle('overflow', 'hidden');
            oldElement.removeCls(elementCls);
        }
        if (element) {
            if (element.dom === document.documentElement || element.dom === document.body) {
                
                
                eventSource = Ext.getWin();
                scrollEl = Ext.scroll.Scroller.getScrollingElement();
            } else {
                scrollEl = eventSource = element;
            }
            me.setScrollElement(Ext.get(scrollEl));
            me.scrollListener = eventSource.on({
                scroll: me.onDomScroll,
                scope: me,
                destroyable: true
            });
            if (touchAction) {
                element.setTouchAction(touchAction);
            }
            me.initXStyle();
            me.initYStyle();
            element.addCls(elementCls);
            me.initSnap();
            me.initMsSnapInterval();
            me.syncScrollbarCls();
        }
    },
    updateX: function(x) {
        this.initXStyle();
    },
    updateY: function(y) {
        this.initYStyle();
    },
    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: {
        axisConfigs: {
            x: {
                x: true
            },
            y: {
                y: true
            },
            both: {
                x: true,
                y: true
            }
        },
        
        getEnsureVisibleXY: function(el, options) {
            var position = this.getPosition(),
                viewport = this.component ? this.component.getScrollableClientRegion() : this.getElement(),
                newPosition, align;
            if (el && el.element && !el.isElement) {
                options = el;
                el = options.element;
            }
            options = options || {};
            align = options.align;
            if (align) {
                if (Ext.isString(align)) {
                    align = {
                        x: options.x === false ? null : align,
                        y: options.y === false ? null : align
                    };
                } else if (Ext.isObject(align)) {
                    if (align.x && options.x === false) {
                        align.x = null;
                    }
                    if (align.y && options.y === false) {
                        align.y = null;
                    }
                }
            }
            newPosition = Ext.fly(el).getScrollIntoViewXY(viewport, position.x, position.y, align);
            newPosition.x = options.x === false ? position.x : newPosition.x;
            newPosition.y = options.y === false ? position.y : newPosition.y;
            return newPosition;
        },
        getSpacer: function() {
            var me = this,
                spacer = me._spacer,
                element;
            
            
            
            
            
            if (!spacer) {
                element = me.getElement();
                spacer = me._spacer = element.createChild({
                    cls: me.spacerCls,
                    role: 'presentation'
                }, element.dom.firstChild);
                spacer.setVisibilityMode(2);
                
                spacer.hide();
                
                
                element.position();
            }
            return spacer;
        },
        applySpacerXY: function(pos, oldPos) {
            
            if (oldPos && pos.x === oldPos.x && pos.y === oldPos.y) {
                pos = undefined;
            }
            return pos;
        },
        updateSpacerXY: function(pos) {
            var me = this,
                spacer = me.getSpacer(),
                sStyle = spacer.dom.style,
                scrollHeight = pos.y,
                shortfall;
            sStyle.marginTop = '';
            me.translateSpacer(pos.x, me.constrainScrollRange(scrollHeight));
            
            
            sStyle.lineHeight = Number(!parseInt(sStyle.lineHeight, 10)) + 'px';
            
            if (scrollHeight > 1000000) {
                shortfall = scrollHeight - me.getElement().dom.scrollHeight;
                if (shortfall > 0) {
                    sStyle.marginTop = Math.min(shortfall, me.maxSpacerMargin || 0) + 'px';
                }
            }
        },
        
        translateSpacer: function(x, y) {
            this.getSpacer().translate(x, y);
        },
        doIsInView: function(el, skipCheck) {
            var me = this,
                c = me.component,
                result = {
                    x: false,
                    y: false
                },
                elRegion,
                myEl = me.getElement(),
                myElRegion;
            if (el && (skipCheck || (myEl.contains(el) || (c && c.owns(el))))) {
                myElRegion = myEl.getRegion();
                elRegion = Ext.fly(el).getRegion();
                result.x = elRegion.right > myElRegion.left && elRegion.left < myElRegion.right;
                result.y = elRegion.bottom > myElRegion.top && elRegion.top < myElRegion.bottom;
            }
            return result;
        },
        
        
        
        contains: function(component) {
            var el = this.getElement(),
                owner = component;
            while (owner && owner !== Ext.Viewport) {
                if (el.contains(owner.el)) {
                    return true;
                }
                owner = owner.getRefOwner();
            }
            return false;
        },
        constrainScrollRange: function(scrollRange) {
            
            
            if (scrollRange < 1000000) {
                return scrollRange;
            }
            if (!this.maxSpacerTranslate) {
                
                
                
                
                
                
                
                var maxScrollHeight = Math.pow(2, 32),
                    tooHigh = maxScrollHeight,
                    tooLow = 500,
                    scrollTest = Ext.getBody().createChild({
                        style: {
                            position: 'absolute',
                            left: '-10000px',
                            top: '0',
                            width: '500px',
                            height: '500px'
                        },
                        cn: {
                            cls: this.spacerCls
                        }
                    }, null, true),
                    stretcher = Ext.get(scrollTest.firstChild),
                    sStyle = stretcher.dom.style;
                stretcher.translate(0, maxScrollHeight - 1);
                sStyle.lineHeight = Number(!parseInt(sStyle.lineHeight, 10)) + 'px';
                
                while (tooHigh !== tooLow + 1) {
                    stretcher.translate(0, (maxScrollHeight = tooLow + Math.floor((tooHigh - tooLow) / 2)));
                    
                    
                    sStyle.lineHeight = Number(!parseInt(sStyle.lineHeight, 10)) + 'px';
                    if (scrollTest.scrollHeight < maxScrollHeight) {
                        tooHigh = maxScrollHeight;
                    } else {
                        tooLow = maxScrollHeight;
                    }
                }
                stretcher.translate(0, Ext.scroll.Scroller.prototype.maxSpacerTranslate = tooLow);
                
                tooHigh = tooLow * 2;
                while (tooHigh !== tooLow + 1) {
                    stretcher.dom.style.marginTop = ((maxScrollHeight = tooLow + Math.floor((tooHigh - tooLow) / 2))) + 'px';
                    
                    
                    sStyle.lineHeight = Number(!parseInt(sStyle.lineHeight, 10)) + 'px';
                    if (scrollTest.scrollHeight < maxScrollHeight) {
                        tooHigh = maxScrollHeight;
                    } else {
                        tooLow = maxScrollHeight;
                    }
                }
                Ext.fly(scrollTest).destroy();
                Ext.scroll.Scroller.prototype.maxSpacerMargin = tooLow - Ext.scroll.Scroller.prototype.maxSpacerTranslate;
            }
            
            
            return Math.min(scrollRange, this.maxSpacerTranslate);
        },
        
        convertX: function(x) {
            return x;
        },
        
        doHighlight: function(el, highlight) {
            if (highlight !== true) {
                
                Ext.fly(el).highlight(highlight);
            } else {
                Ext.fly(el).highlight();
            }
        },
        doScrollTo: function(x, y, animate) {
            
            
            var me = this,
                element = me.getScrollElement(),
                maxPosition, dom, xInf, yInf, ret, translatable, deferred;
            if (element && !element.destroyed) {
                dom = element.dom;
                xInf = (x === Infinity);
                yInf = (y === Infinity);
                if (xInf || yInf) {
                    maxPosition = me.getMaxPosition();
                    if (xInf) {
                        x = maxPosition.x;
                    }
                    if (yInf) {
                        y = maxPosition.y;
                    }
                }
                if (x !== null) {
                    x = me.convertX(x);
                }
                if (animate) {
                    translatable = me.translatable;
                    if (!translatable) {
                        me.translatable = translatable = new Ext.util.translatable.ScrollPosition({
                            element: element
                        });
                    }
                    deferred = new Ext.Deferred();
                    
                    translatable.on('animationend', function() {
                        
                        if (me.destroyed) {
                            deferred.reject();
                        } else {
                            deferred.resolve();
                        }
                    }, Ext.global, {
                        single: true,
                        onFrame: true
                    });
                    translatable.translate(x, y, animate);
                    ret = deferred.promise;
                } else {
                    if (y != null) {
                        dom.scrollTop = y;
                    }
                    if (x != null) {
                        dom.scrollLeft = x;
                    }
                    ret = Ext.Deferred.getCachedResolved();
                }
                
                me.positionDirty = true;
            } else {
                ret = Ext.Deferred.getCachedRejected();
            }
            return ret;
        },
        fireScrollStart: function(x, y, xDelta, yDelta) {
            var me = this,
                component = me.component;
            me.invokePartners('onPartnerScrollStart', x, y, xDelta, yDelta);
            me.startX = x - xDelta;
            me.startY = y - yDelta;
            if (me.hasListeners.scrollstart) {
                me.fireEvent('scrollstart', me, x, y);
            }
            if (component && component.onScrollStart) {
                component.onScrollStart(x, y);
            }
            Ext.GlobalEvents.fireEvent('scrollstart', me, x, y);
        },
        fireScroll: function(x, y, xDelta, yDelta) {
            var me = this,
                component = me.component;
            me.invokePartners('onPartnerScroll', x, y, xDelta, yDelta);
            if (me.hasListeners.scroll) {
                me.fireEvent('scroll', me, x, y, xDelta, yDelta);
            }
            if (component && component.onScrollMove) {
                component.onScrollMove(x, y);
            }
            Ext.GlobalEvents.fireEvent('scroll', me, x, y, xDelta, yDelta);
        },
        fireScrollEnd: function(x, y, xDelta, yDelta) {
            var me = this,
                component = me.component,
                dx = x - me.startX,
                dy = y - me.startY;
            me.startX = me.startY = null;
            me.invokePartners('onPartnerScrollEnd', x, y, xDelta, yDelta);
            if (me.hasListeners.scrollend) {
                me.fireEvent('scrollend', me, x, y, dx, dy);
            }
            if (component && component.onScrollEnd) {
                component.onScrollEnd(x, y);
            }
            Ext.GlobalEvents.fireEvent('scrollend', me, x, y, dx, dy);
        },
        
        getElementScroll: function(element) {
            return element.getScroll();
        },
        initSnap: function() {
            var me = this,
                snapOffset = me.getSnapOffset(),
                snapSelector = me.getSnapSelector(),
                element = me.getElement(),
                offsetX, offsetY, snapCoordinate;
            if (element && snapSelector) {
                element.addCls(me.snappableCls);
                me.removeSnapStylesheet();
                if (snapOffset) {
                    offsetX = snapOffset.x || 0;
                    offsetY = snapOffset.y || 0;
                    if (offsetX) {
                        offsetX = -offsetX + 'px';
                    }
                    if (offsetY) {
                        offsetY = -offsetY + 'px';
                    }
                }
                snapCoordinate = offsetX + ' ' + offsetY + ';';
                me.snapStylesheet = Ext.util.CSS.createStyleSheet('#' + element.id + ' ' + snapSelector + '{-webkit-scroll-snap-coordinate:' + snapCoordinate + 'scroll-snap-coordinate:' + snapCoordinate + '}');
            }
        },
        initMsSnapInterval: function() {
            var element = this.getElement(),
                interval, x, y, style;
            if (element) {
                interval = this.getMsSnapInterval();
                if (interval) {
                    x = interval.x;
                    y = interval.y;
                    style = element.dom.style;
                    if (x) {
                        style['-ms-scroll-snap-points-x'] = 'snapInterval(0px, ' + x + 'px)';
                    }
                    if (y) {
                        style['-ms-scroll-snap-points-y'] = 'snapInterval(0px, ' + y + 'px)';
                    }
                }
            }
        },
        initXStyle: function() {
            var element = this.getElement(),
                x = this.getX();
            
            if (element && element.dom) {
                if (!x) {
                    x = 'hidden';
                } else if (x === true) {
                    x = 'auto';
                }
                element.setStyle('overflow-x', x);
            }
        },
        initYStyle: function() {
            var element = this.getElement(),
                y = this.getY();
            
            if (element && element.dom) {
                if (!y) {
                    y = 'hidden';
                } else if (y === true) {
                    y = 'auto';
                }
                element.setStyle('overflow-y', y);
            }
        },
        invokePartners: function(method, x, y, xDelta, yDelta) {
            var me = this,
                partners = me._partners,
                partner, id, axes;
            if (!me.suspendSync) {
                me.invokingPartners = true;
                for (id in partners) {
                    axes = partners[id].axes;
                    partner = partners[id].scroller;
                    
                    if (!partner.invokingPartners && (xDelta && axes.x || yDelta && axes.y)) {
                        partner[method](me, axes.x ? x : null, axes.y ? y : null, xDelta, yDelta);
                    }
                }
                me.invokingPartners = false;
            }
        },
        suspendPartnerSync: function() {
            this.suspendSync = (this.suspendSync || 0) + 1;
        },
        resumePartnerSync: function(syncNow) {
            var me = this,
                position;
            if (me.suspendSync) {
                me.suspendSync--;
            }
            if (!me.suspendSync && syncNow) {
                position = me.getPosition();
                me.invokePartners('onPartnerScroll', position.x, position.y);
                me.invokePartners('onPartnerScrollEnd', position.x, position.y);
            }
        },
        readPosition: function(position) {
            var me = this,
                element = me.getScrollElement(),
                elScroll;
            position = position || {};
            if (element && !element.destroyed) {
                elScroll = me.getElementScroll(element);
                position.x = elScroll.left;
                position.y = elScroll.top;
            }
            return position;
        },
        updateDomScrollPosition: function(silent) {
            var me = this,
                position = me.position,
                oldX = position.x,
                oldY = position.y,
                x, y, xDelta, yDelta;
            me.readPosition(position);
            x = position.x;
            y = position.y;
            me.positionDirty = false;
            if (!silent) {
                xDelta = x - oldX;
                yDelta = y - oldY;
                
                
                
                if (xDelta || yDelta) {
                    if (!me.isScrolling) {
                        me.isScrolling = Ext.isScrolling = true;
                        me.fireScrollStart(x, y, xDelta, yDelta);
                    }
                    me.fireScroll(x, y, xDelta, yDelta);
                    me.onDomScrollEnd(x, y, xDelta, yDelta);
                }
            }
            return position;
        },
        
        syncWithPartners: function() {
            var me = this,
                partners = me._partners,
                id, partner, position;
            me.suspendPartnerSync();
            for (id in partners) {
                partner = partners[id].scroller;
                position = partner.getPosition();
                me.onPartnerScroll(partner, position.x, position.y);
            }
            me.resumePartnerSync();
        },
        syncScrollbarCls: function() {
            var element = this.getElement();
            if (element) {
                element.toggleCls(this.noScrollbarsCls, this.getScrollbars() === false);
            }
        },
        onDomScroll: function() {
            var hasTimer = !!this.restoreTimer;
            this.updateDomScrollPosition(hasTimer);
            if (hasTimer) {
                Ext.undefer(this.onDomScrollEnd.timer);
                return;
            }
        },
        doOnDomScrollEnd: function(x, y, xDelta, yDelta) {
            var me = this;
            
            if (me.destroying || me.destroyed) {
                return;
            }
            me.isScrolling = Ext.isScrolling = false;
            
            if (x === undefined) {
                return;
            }
            me.trackingScrollLeft = x;
            me.trackingScrollTop = y;
            me.fireScrollEnd(x, y, xDelta, yDelta);
        },
        onPartnerScrollStart: function(partner, x, y, xDelta, yDelta) {
            
            this.isScrolling = true;
            this.fireScrollStart(x, y, xDelta, yDelta);
        },
        onPartnerScroll: function(partner, x, y, xDelta, yDelta) {
            this.doScrollTo(x, y, false);
            
            
            
            
            this.updateDomScrollPosition(true);
            
            this.fireScroll(x, y, xDelta, yDelta);
        },
        onPartnerScrollEnd: function(partner, x, y, xDelta, yDelta) {
            
            this.cancelOnDomScrollEnd();
            
            
            
            this.doOnDomScrollEnd(x, y, xDelta, yDelta);
        },
        removeSnapStylesheet: function() {
            var stylesheet = this.snapStylesheet;
            if (stylesheet) {
                Ext.util.CSS.removeStyleSheet(stylesheet);
                this.snapStylesheet = null;
            }
        },
        restoreState: function() {
            var me = this,
                el = me.getScrollElement();
            if (el) {
                
                
                if (me.trackingScrollTop !== undefined) {
                    
                    
                    
                    
                    
                    
                    if (!me.restoreTimer) {
                        me.restoreTimer = Ext.defer(function() {
                            me.restoreTimer = null;
                        }, 50);
                    }
                    me.doScrollTo(me.trackingScrollLeft, me.trackingScrollTop, false);
                }
            }
        }
    }
}, 

function(Scroller) {
    
    Ext.getViewportScroller = function() {
        
        
        
        
        var scroller = Scroller.viewport;
        if (!scroller) {
            Scroller.viewport = scroller = new Scroller();
            Scroller.initViewportScroller();
        }
        return scroller;
    };
    
    Ext.setViewportScroller = function(scroller) {
        if (Scroller.viewport !== scroller) {
            Ext.destroy(Scroller.viewport);
            if (scroller && !scroller.isScroller) {
                scroller = new Scroller(scroller);
            }
            Scroller.viewport = scroller;
        }
    };
    Ext.onReady(function() {
        
        
        
        
        
        
        Scroller.initViewportScrollerTimer = Ext.defer(Scroller.initViewportScroller, 100);
    });
});


Ext.define('Ext.Component', {
    extend: Ext.Widget,
    
    alternateClassName: [
        'Ext.lib.Component',
        'Ext.Gadget'
    ],
    
    xtype: 'component',
    cachedConfig: {
        
        margin: null,
        
        padding: null,
        
        tabIndex: null
    },
    eventedConfig: {
        
        left: null,
        
        top: null,
        
        right: null,
        
        bottom: null,
        
        minWidth: null,
        
        minHeight: null,
        
        maxWidth: null,
        
        maxHeight: null,
        
        scrollable: null,
        
        docked: null,
        
        centered: {
            lazy: true,
            $value: null
        }
    },
    config: {
        
        displayed: null,
        
        html: null,
        
        
        draggable: null,
        
        zIndex: null,
        
        tpl: null,
        
        enterAnimation: null,
        
        exitAnimation: null,
        
        showAnimation: null,
        
        hideAnimation: null,
        
        tplWriteMode: 'overwrite',
        
        data: null,
        
        contentEl: null,
        
        record: null,
        
        
        tooltip: null,
        
        axisLock: null,
        
        
        modal: {
            lazy: true,
            $value: null
        },
        
        hideOnMaskTap: null,
        
        weight: null,
        
        userSelectable: null
    },
    
    modelValidation: null,
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    defaultBindProperty: 'html',
    
    isComponent: true,
    
    positioned: false,
    
    rendered: false,
    
    activeAnimation: null,
    
    dockPositions: {
        top: true,
        right: true,
        bottom: true,
        left: true
    },
    bodyElement: null,
    
    classCls: Ext.baseCSSPrefix + 'component',
    
    floatingCls: Ext.baseCSSPrefix + 'floating',
    hiddenCls: Ext.baseCSSPrefix + 'hidden',
    _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
        },
        'false': {
            x: false,
            y: false
        }
    },
    statics: {
        
        from: function(el, limit, selector) {
            return Ext.ComponentManager.from(el, limit, selector);
        },
        
        fromElement: function(el, limit, selector) {
            return Ext.ComponentManager.from(el, limit, selector);
        }
    },
    initialConfig: null,
    $initParent: null,
    
    userSelectableClsMap: {
        "true": Ext.baseCSSPrefix + 'user-selectable-auto',
        "false": Ext.baseCSSPrefix + 'user-selectable-none',
        all: Ext.baseCSSPrefix + 'user-selectable-all',
        auto: Ext.baseCSSPrefix + 'user-selectable-auto',
        text: Ext.baseCSSPrefix + 'user-selectable-text',
        none: Ext.baseCSSPrefix + 'user-selectable-none'
    },
    
    constructor: function(config) {
        var me = this,
            VP = Ext['Viewport'],
            
            
            renderTo = config && config.renderTo,
            hasListeners, hasResize, el;
        me.lastSize = {};
        me.onInitializedListeners = [];
        if (config) {
            me.initialConfig = config;
            
            
            
            me.$initParent = config.$initParent;
            
            
            
            
            if (me.activatePlugin('responsive')) {
                config = me.initialConfig;
            }
        }
        if (renderTo) {
            config = Ext.apply({}, config);
            delete config.renderTo;
        }
        me.callParent([
            config
        ]);
        el = me.el;
        
        
        
        if (!me.getRefOwner()) {
            el.addCls(me.rootCls);
        }
        me.refreshPositioned = me.doRefreshPositioned;
        if (me.refreshPositionedOnInitialized) {
            me.refreshPositioned();
        }
        me.initialize();
        me.triggerInitialized();
        if (me.isCentered()) {
            
            me.center();
        }
        hasListeners = me.hasListeners;
        hasResize = hasListeners.resize;
        
        
        
        if (me.hasListeners.painted) {
            el.on('painted', 'handleElementPainted', me);
        }
        if ((hasResize || me.onResize) && !me.isViewport) {
            if (!hasResize) {
                
                
                
                hasListeners._incr_('resize');
            }
            el.on({
                scope: me,
                resize: 'handleElementResize',
                priority: 1000
            });
        }
        
        if (me.fullscreen && VP) {
            VP.setItemFullScreen(me);
        }
        me.fireEvent('initialize', me);
        if (renderTo) {
            me.setRenderTo(renderTo);
        }
    },
    beforeInitConfig: function(config) {
        this.beforeInitialize.apply(this, arguments);
    },
    
    beforeInitialize: Ext.emptyFn,
    
    initialize: Ext.emptyFn,
    
    center: function() {
        var me = this,
            parent = me.getParent(),
            parentBox, translateXY, xy, size;
        
        
        
        if ((!parent || parent.rendered) && me.shouldRecenter()) {
            translateXY = !!parent;
            parent = parent ? parent.bodyElement : Ext.getBody();
            parentBox = parent.getConstrainRegion();
            size = me.measure();
            xy = [
                (parentBox.getWidth() - size.width) / 2,
                (parentBox.getHeight() - size.height) / 2
            ];
            me.needsCenter = false;
            me._centering = true;
            if (me.getFloated()) {
                if (translateXY) {
                    xy = parent.reverseTranslateXY(xy);
                }
                
                me.setXY(xy);
            } else {
                me.setLeft(xy[0]);
                me.setTop(xy[1]);
            }
            me._centering = false;
        } else {
            me.needsCenter = true;
        }
        return me;
    },
    shouldRecenter: function() {
        
        
        return this.rendered && !this.$isShowing && (this.el.isVisible() || this.el.getVisibilityMode() !== Ext.Element.DISPLAY);
    },
    
    getModalSibling: function() {
        var me = this,
            floatRoot = Ext.getFloatRoot().dom,
            parentWrap, parentContainer, childNodes, c, i;
        
        for (parentWrap = me.floatParentNode && me.floatParentNode.dom; parentWrap; parentWrap = (parentWrap === floatRoot || parentContainer.getRelative()) ? null : parentWrap.parentNode) {
            parentContainer = Ext.fly(parentWrap).getData().component;
            childNodes = parentWrap.childNodes;
            
            for (i = childNodes ? childNodes.length : 0; i-- > 0; ) {
                c = Ext.fly(childNodes[i]);
                if (c.hasCls(me.floatWrapCls)) {
                    c = c.getData().component;
                    if (c && c !== me && c.isVisible() && c.getModal()) {
                        return c;
                    }
                }
            }
        }
        return null;
    },
    
    
    
    
    triggerInitialized: function() {
        var listeners = this.onInitializedListeners,
            ln = listeners.length,
            listener, fn, scope, args, i;
        if (!this.initialized) {
            this.initialized = true;
            if (ln > 0) {
                for (i = 0; i < ln; i++) {
                    listener = listeners[i];
                    fn = listener.fn;
                    scope = listener.scope;
                    args = listener.args;
                    if (typeof fn == 'string') {
                        scope[fn].apply(scope, args);
                    } else {
                        fn.apply(scope, args);
                    }
                }
                listeners.length = 0;
            }
        }
    },
    
    onInitialized: function(fn, scope, args) {
        var listeners = this.onInitializedListeners;
        if (!scope) {
            scope = this;
        }
        if (this.initialized) {
            if (typeof fn == 'string') {
                scope[fn].apply(scope, args);
            } else {
                fn.apply(scope, args);
            }
        } else {
            listeners.push({
                fn: fn,
                scope: scope,
                args: args
            });
        }
    },
    initElement: function() {
        var me = this;
        me.callParent();
        if (!me.bodyElement) {
            me.bodyElement = me.element;
        }
        
        me.innerElement = me.innerElement || me.bodyElement;
    },
    
    initInheritedState: function(inheritedState) {
        var me = this;
        
        
        
        
        if (me.modelValidation !== null) {
            inheritedState.modelValidation = me.modelValidation;
        }
        me.callParent([
            inheritedState
        ]);
    },
    applyScrollable: function(scrollable, oldScrollable) {
        var me = this;
        if ((typeof scrollable === 'boolean') || (typeof scrollable === 'string')) {
            
            if (!me._scrollableCfg[scrollable]) {
                Ext.raise("'" + scrollable + "' is not a valid value for 'scrollable'");
            }
            
            scrollable = me._scrollableCfg[scrollable];
        }
        return Ext.Factory.scroller.update(oldScrollable, scrollable, this, 'createScrollable');
    },
    applyHidden: function(hidden) {
        if (!hidden && this.isConfiguring && this.getFloated()) {
            this.preprocessShow();
        }
        return !!hidden;
    },
    createScrollable: function(defaults) {
        return Ext.apply({
            component: this,
            element: this.getScrollerTarget()
        }, defaults);
    },
    getScrollerTarget: function() {
        return this.bodyElement;
    },
    
    getScrollableClientRegion: function() {
        return this.getScrollerTarget().getClientRegion();
    },
    updatePadding: function(padding) {
        this.bodyElement.setPadding(padding);
    },
    updateMargin: function(margin) {
        this.element.setMargin(margin);
    },
    updateWeight: function(weight, oldWeight) {
        var me = this,
            owner = !me.isConfiguring && me.getRefOwner();
        
        me.weight = weight;
        
        if (owner && owner.onItemWeightChange) {
            owner.onItemWeightChange(me, weight, oldWeight);
        }
    },
    applyContentEl: function(contentEl) {
        if (contentEl) {
            return Ext.get(contentEl);
        }
    },
    updateContentEl: function(newContentEl, oldContentEl) {
        if (oldContentEl) {
            oldContentEl.hide();
            Ext.getBody().append(oldContentEl);
        }
        if (newContentEl) {
            this.setHtml(newContentEl.dom);
            newContentEl.show();
        }
    },
    createTranslatable: function(config) {
        var me = this,
            ret = me.callParent([
                config
            ]);
        if (config && !config.type && me.getFloated()) {
            ret.type = 'csstransform';
        }
        return ret;
    },
    
    isCentered: function() {
        return Boolean(this.getCentered());
    },
    isPositioned: function() {
        return this.positioned;
    },
    isDocked: function() {
        return Boolean(this.getDocked());
    },
    applyTop: function(top) {
        return this.filterLengthValue(top);
    },
    applyRight: function(right) {
        return this.filterLengthValue(right);
    },
    applyBottom: function(bottom) {
        return this.filterLengthValue(bottom);
    },
    applyLeft: function(left) {
        return this.filterLengthValue(left);
    },
    applyMinWidth: function(width) {
        return this.filterLengthValue(width);
    },
    applyMinHeight: function(height) {
        return this.filterLengthValue(height);
    },
    applyMaxWidth: function(width) {
        return this.filterLengthValue(width);
    },
    applyMaxHeight: function(height) {
        return this.filterLengthValue(height);
    },
    updateTop: function(top) {
        if (!this.$updatingXY) {
            this.element.setTop(top);
        }
        this.refreshPositioned();
    },
    updateRight: function(right) {
        if (!this.$updatingXY) {
            this.element.setRight(right);
        }
        this.refreshPositioned();
    },
    updateBottom: function(bottom) {
        if (!this.$updatingXY) {
            this.element.setBottom(bottom);
        }
        this.refreshPositioned();
    },
    updateLeft: function(left) {
        if (!this.$updatingXY) {
            this.element.setLeft(left);
        }
        this.refreshPositioned();
    },
    
    onResize: null,
    updateMinWidth: function(width) {
        this.element.setMinWidth(width);
    },
    updateMinHeight: function(height) {
        this.element.setMinHeight(height);
    },
    updateMaxWidth: function(width) {
        this.element.setMaxWidth(width);
    },
    updateMaxHeight: function(height) {
        this.element.setMaxHeight(height);
    },
    
    applyCentered: function(centered) {
        var me = this,
            doCenter = me.getLeft() === null && me.getRight() === null && me.getTop() === null && me.getBottom() === null;
        
        if (doCenter) {
            return !!centered;
        }
    },
    updateCentered: function(centered) {
        var me = this,
            resizeParent;
        if (me.getFloated()) {
            if (centered) {
                me.center();
                if (!me.centerResizeListener && !me.needsCenter) {
                    resizeParent = me.floatParentNode;
                    resizeParent = (resizeParent === Ext.floatRoot) ? Ext : resizeParent;
                    me.centerResizeListener = resizeParent.on({
                        resize: 'center',
                        scope: me,
                        destroyable: true
                    });
                }
            } else {
                me.centerResizeListener = Ext.destroy(me.centerResizeListener);
            }
        } else {
            me.el.toggleCls(me.floatingCls, centered);
            if (centered) {
                me.refreshInnerState = Ext.emptyFn;
                if (me.isContainer && (!me.isWidthed() || !me.isHeighted())) {
                    me.setAutoSize(true);
                }
                if (me.isPositioned()) {
                    me.resetPositioned();
                }
                if (me.isDocked()) {
                    me.setDocked(false);
                }
                me.setIsInner(false);
                delete me.refreshInnerState;
            } else {
                me.refreshInnerState();
            }
        }
    },
    applyDocked: function(docked) {
        var me = this;
        if (!docked) {
            return null;
        }
        
        if (!/^(top|right|bottom|left)$/.test(docked)) {
            Ext.Logger.error("Invalid docking position of '" + docked.position + "', must be either 'top', 'right', 'bottom', " + "'left' or `null` (for no docking)", me);
            return;
        }
        
        me.refreshInnerState = Ext.emptyFn;
        if (me.isPositioned()) {
            me.resetPositioned();
        }
        if (me.isCentered()) {
            me.setCentered(false);
        }
        me.setIsInner(false);
        delete me.refreshInnerState;
        return docked;
    },
    getDisplayed: function() {
        return !this.getHidden();
    },
    setDisplayed: function(displayed) {
        var me = this,
            hidden = me.getHidden() !== false;
        if (displayed === hidden) {
            me._displayed = displayed;
            me.updateDisplayed(displayed, !displayed);
        }
        return me;
    },
    updateDisplayed: function(displayed) {
        this[displayed ? 'show' : 'hide']();
    },
    updateDocked: function(docked, oldDocked) {
        var me = this;
        if (!me.isConfiguring) {
            me.fireEvent('afterdockedchange', me, docked, oldDocked);
            if (!docked) {
                me.refreshInnerState();
            }
        }
    },
    updateUserSelectable: function(newSelectable, oldSelectable) {
        var me = this,
            map = me.userSelectableClsMap,
            el = me.el,
            name, childEl;
        if (typeof oldSelectable === 'boolean' || typeof oldSelectable === 'string') {
            el.removeCls(map[oldSelectable]);
        } else {
            for (name in oldSelectable) {
                childEl = me[name];
                
                if (!childEl || !childEl.isElement) {
                    Ext.raise('Element not found: "' + name + '"');
                }
                
                childEl.removeCls(map[oldSelectable[name]]);
            }
        }
        if (typeof newSelectable === 'boolean' || typeof newSelectable === 'string') {
            el.addCls(map[newSelectable]);
        } else {
            for (name in newSelectable) {
                childEl = me[name];
                
                if (!childEl || !childEl.isElement) {
                    Ext.raise('Element not found: "' + name + '"');
                }
                
                childEl.addCls(map[newSelectable[name]]);
            }
        }
    },
    
    resetPositioned: function() {
        var me = this;
        me.setTop(null);
        me.setRight(null);
        me.setBottom(null);
        me.setLeft(null);
    },
    refreshPositioned: function() {
        this.refreshPositionedOnInitialized = true;
    },
    doRefreshPositioned: function() {
        var me = this,
            
            
            positioned = !me.getConfig('floated', false, true) && (me.getTop() !== null || me.getBottom() !== null || me.getRight() !== null || me.getLeft() !== null);
        if (positioned !== this.positioned) {
            me.positioned = positioned;
            if (positioned) {
                me.refreshInnerState = Ext.emptyFn;
                if (me.isContainer && (!me.isWidthed() || !me.isHeighted())) {
                    me.setAutoSize(true);
                }
                if (me.isCentered()) {
                    me.setCentered(false);
                }
                if (me.isDocked()) {
                    me.setDocked(false);
                }
                me.setIsInner(false);
                delete me.refreshInnerState;
            }
            me.element.toggleCls(me.floatingCls, positioned);
            if (me.initialized) {
                me.fireEvent('floatingchange', me, positioned);
                me.fireEvent('positionedchange', me, positioned);
            }
            if (!positioned) {
                me.refreshInnerState();
            }
        }
    },
    applyZIndex: function(zIndex) {
        if (!zIndex && zIndex !== 0) {
            zIndex = null;
        }
        if (zIndex !== null) {
            zIndex = Number(zIndex);
            if (isNaN(zIndex)) {
                zIndex = null;
            }
        }
        return zIndex;
    },
    updateZIndex: function(zIndex) {
        var element = this.element,
            modal = !this.getFloated() && this.getModal(),
            domStyle;
        if (element && !element.destroyed) {
            domStyle = element.dom.style;
            if (zIndex !== null) {
                domStyle.setProperty('z-index', zIndex, 'important');
            } else {
                domStyle.removeProperty('z-index');
            }
        }
        if (modal && !modal.destroyed) {
            modal.setZIndex(zIndex - 1);
        }
    },
    getInnerHtmlElement: function() {
        var me = this,
            innerHtmlElement = me.innerHtmlElement;
        if (!innerHtmlElement || !innerHtmlElement.dom || !innerHtmlElement.dom.parentNode) {
            me.innerHtmlElement = innerHtmlElement = Ext.Element.create({
                cls: Ext.baseCSSPrefix + 'innerhtml'
            });
            me.getRenderTarget().appendChild(innerHtmlElement);
        }
        return innerHtmlElement;
    },
    updateHtml: function(html) {
        if (!this.destroyed) {
            var innerHtmlElement = this.getInnerHtmlElement();
            if (Ext.isElement(html)) {
                innerHtmlElement.setHtml('');
                innerHtmlElement.append(html);
            } else {
                innerHtmlElement.setHtml(html);
            }
        }
    },
    updateHidden: function(hidden, oldHidden) {
        var me = this,
            element = me.renderElement,
            modal = me.getModal(),
            name;
        if (me.rendered) {
            if (modal && !modal.destroyed) {
                if (me.getFloated()) {
                    if (hidden) {
                        
                        
                        modal = me.getModalSibling();
                        if (modal) {
                            modal.showModalMask();
                        } else {
                            me.hideModalMask();
                        }
                    } else {
                        me.showModalMask();
                    }
                } else {
                    if (modal !== true) {
                        modal.setZIndex(me.getZIndex() - 1);
                        if (modal.getHidden() !== hidden) {
                            modal.setHidden(hidden);
                        }
                    }
                }
            }
            if (!me.destroying && element && !element.destroyed) {
                element.toggleCls(me.hiddenCls, hidden);
            }
            me.callParent([
                hidden,
                oldHidden
            ]);
        } else {
            element.toggleCls(me.hiddenCls, hidden);
            me.callParent([
                hidden,
                oldHidden
            ]);
        }
        
        if (!me.isConfiguring && !me.destroying) {
            name = hidden ? 'hide' : 'show';
            if (me.hasListeners[name]) {
                me.fireEvent(name, me);
            }
            me[hidden ? 'afterHide' : 'afterShow'](me);
        }
    },
    
    hide: function(animation) {
        var me = this,
            activeAnim = me.activeAnimation,
            modal;
        if (me.isVisible()) {
            
            if (me.hasListeners.beforehide && me.fireEvent('beforehide', me) === false) {
                return;
            }
            if (me.beforeHide() === false) {
                return;
            }
            me.viewportResizeListener = Ext.destroy(me.viewportResizeListener);
            me.setCurrentAlignmentInfo(null);
            if (activeAnim) {
                activeAnim.on({
                    animationend: function() {
                        me.hide(animation);
                    },
                    single: true
                });
                return me;
            }
            if (!me.getHidden()) {
                
                if (animation === undefined || (animation && !Ext.isObject(animation))) {
                    animation = me.getHideAnimation();
                }
                if (animation) {
                    me.on({
                        beforehiddenchange: 'onBeforeHiddenChange',
                        scope: me,
                        single: true,
                        args: [
                            animation
                        ]
                    });
                }
                me.setHidden(true);
            }
            
            
            
            
            if (!me.getFloated()) {
                modal = me.getModal();
                if (modal && modal !== true && !modal.destroyed) {
                    modal.setHidden(true);
                }
            }
        } else {
            me.setHidden(true);
        }
        return me;
    },
    
    preprocessShow: function(component, alignment, options) {
        var me = this,
            hideMode = me.getHideMode(),
            hidden;
        
        
        
        
        
        if (me.isContainer) {
            me.getItems();
        }
        me.getHtml();
        if (me.getFloated()) {
            
            hidden = (!hideMode || hideMode === 'display') && me.getHidden();
            
            
            if (me.rendered) {
                if (hidden) {
                    me.setVisibility(true);
                    me._hidden = false;
                }
            } else 
            
            
            {
                hidden = hidden !== false;
                me.findFloatParent(hidden);
            }
            
            
            if (component) {
                me.notifyIf();
                me.alignTo(component, alignment, options);
            } else {
                if (me.isCentered()) {
                    me.notifyIf();
                    
                    me.updateCentered(true);
                } else {
                    me.syncXYPosition();
                }
            }
            if (me.getModal()) {
                me.showModalMask();
            }
            if (me.getToFrontOnShow()) {
                me.toFront();
            } else {
                me.syncAlwaysOnTop();
            }
            if (hidden) {
                me.setVisibility(false);
                me._hidden = true;
            }
        }
    },
    
    showBy: function(component, alignment, options) {
        var me = this,
            offset;
        if (Ext.isArray(options)) {
            offset = options;
            options = {
                offset: offset
            };
        }
        
        if (me.isVisible()) {
            me.alignTo(component, alignment, options);
        } else {
            
            
            
            if (!me.getFloated()) {
                if (!me.getParent()) {
                    me.setFloated(true);
                } else {
                    me.positioned = true;
                }
            }
            
            
            me.alignToArgs = [
                component,
                alignment,
                options
            ];
            me.show({
                
                
                
                animation: true,
                alignment: {
                    component: component,
                    alignment: alignment,
                    options: options
                }
            });
        }
    },
    
    show: function(animation, options) {
        var me = this,
            hidden = me.getHidden(),
            floated = me.getFloated(),
            alignment, modal;
        
        
        if (animation && typeof animation === 'object' && 'animation' in animation) {
            options = animation;
            animation = options.animation;
        } else 
        {
            options = Ext.apply({
                animation: animation
            }, options);
        }
        
        if (me.hasListeners.beforeshow && me.fireEvent('beforeshow', me, options) === false) {
            return false;
        }
        
        if (me.beforeShow(options) === false) {
            return;
        }
        
        
        alignment = options.alignment || {};
        me.preprocessShow(alignment.component, alignment.alignment, alignment.options);
        if (me.activeAnimation) {
            
            
            if (!me.$isShowing) {
                me.activeAnimation.on({
                    animationend: function() {
                        if (!me.destroying && !me.destroyed) {
                            me.show(animation, options);
                        }
                    },
                    single: true
                });
            }
            return me;
        }
        
        
        
        if (hidden || hidden === null) {
            
            if (animation === undefined || (animation && !Ext.isObject(animation))) {
                animation = me.getShowAnimation();
            }
            if (animation && !me.isConfiguring) {
                me.on({
                    beforehiddenchange: 'onBeforeHiddenChange',
                    scope: me,
                    single: true,
                    args: [
                        animation
                    ]
                });
            }
            me.setHidden(false);
        }
        
        
        
        
        if (!floated) {
            modal = me.getModal();
            if (modal && modal.setHidden) {
                modal.setHidden(false);
            }
        }
        return me;
    },
    onAnimationStart: function(hidden, animation, data) {
        var me = this,
            element = me.element,
            fromTransform = data.from.transform,
            toTransform = data.to.transform;
        
        if (!hidden) {
            me.renderElement.show();
            element.removeCls(me.hiddenCls);
            if (me.needsCenter) {
                me.center();
            }
        }
        me.$isShowing = true;
        
        
        if (me.getFloated() && fromTransform && toTransform && !(fromTransform.translateX | toTransform.translateX | fromTransform.translateY | toTransform.translateY)) {
            fromTransform.translateX = toTransform.translateX = null;
            fromTransform.translateY = toTransform.translateY = null;
        }
    },
    onBeforeHiddenChange: function(animation, component, hidden, oldHidden, controller) {
        var me = this;
        if (animation && (!hidden || (hidden && me.isPainted()))) {
            if (!animation.isAnimation) {
                animation = hidden ? me.createHideAnimation(animation) : me.createShowAnimation(animation);
                animation = Ext.Factory.animation(animation);
            }
            me.activeAnimation = animation;
            animation.on({
                animationstart: 'onAnimationStart',
                scope: me,
                single: true,
                args: [
                    hidden
                ]
            });
            if (!Ext.isEmpty(hidden)) {
                animation.setOnEnd(function() {
                    me.activeAnimation = null;
                    me.$isShowing = false;
                    
                    
                    
                    if (!me.destroyed) {
                        controller.resume();
                        
                        if (me.destroying || me.destroyed) {
                            return;
                        }
                        if (me.getFloated()) {
                            me.syncXYPosition();
                        }
                    }
                });
                
                if (!hidden) {
                    me._hidden = false;
                }
                controller.pause();
            }
            Ext.Animator.run(animation);
        }
    },
    
    setVisibility: function(isVisible) {
        this.renderElement.setVisible(isVisible);
    },
    
    isRendered: function() {
        return this.rendered;
    },
    
    isPainted: function() {
        return this.renderElement.isPainted();
    },
    
    applyTpl: function(tpl) {
        return Ext.XTemplate.get(tpl);
    },
    updateTpl: function(tpl) {
        if (!this.isConfiguring) {
            if (tpl) {
                this.doUpdateTpl();
            } else {
                this.getInnerHtmlElement().setHtml('');
            }
        }
    },
    applyData: function(data) {
        if (Ext.isObject(data)) {
            return Ext.apply({}, data);
        } else if (!data) {
            data = {};
        }
        return data;
    },
    
    updateData: function(newData) {
        var me = this;
        if (newData) {
            me.doUpdateTpl(newData);
            
            if (!me.isConfiguring) {
                me.fireEvent('updatedata', me, newData);
            }
        }
    },
    applyRecord: function(config) {
        if (config && Ext.isObject(config) && config.isModel) {
            return config;
        }
        return null;
    },
    updateRecord: function(newRecord, oldRecord) {
        var me = this;
        if (oldRecord) {
            oldRecord.unjoin(me);
        }
        if (!newRecord) {
            me.updateData('');
        } else {
            newRecord.join(me);
            me.doUpdateTpl(newRecord.getData(true));
        }
    },
    
    afterEdit: function() {
        this.updateRecord(this.getRecord());
    },
    
    afterErase: function() {
        this.setRecord(null);
    },
    
    getXTypes: function() {
        return this.xtypesChain.join('/');
    },
    
    applyDraggable: function(draggable, existing) {
        if (existing) {
            if (draggable) {
                existing.setConfig(draggable);
            } else {
                existing.destroy();
            }
        } else if (draggable) {
            draggable = this.createDraggable(draggable);
            draggable = new Ext.drag.Source(draggable);
            if (this.initDragConstraints) {
                draggable.on('initdragconstraints', 'initDragConstraints', this);
            }
        }
        return draggable;
    },
    createDraggable: function(draggable) {
        var me = this,
            listeners = draggable.listeners;
        draggable = Ext.apply({
            autoDestroy: false,
            component: me,
            ownerCmp: me,
            local: true
        }, draggable);
        
        
        if (listeners && listeners.scope === 'this') {
            draggable.listeners = listeners = Ext.apply({}, listeners);
            listeners.scope = me;
        }
        return draggable;
    },
    updateDraggable: function(dragger, existing) {
        if (existing) {
            if (dragger) {
                existing.setConfig(dragger);
            } else {
                existing.destroy();
            }
        }
    },
    onModalMaskTap: function(e) {
        if (this.getHideOnMaskTap()) {
            this.hide();
        }
    },
    translateAxis: function(axis, value, animation) {
        var x, y;
        if (axis === 'x') {
            x = value;
        } else {
            y = value;
        }
        return this.translate(x, y, animation);
    },
    
    alignTo: function(component, alignment, options) {
        var me = this;
        
        if (!me.viewportResizeListener) {
            me.viewportResizeListener = Ext.on({
                resize: 'onViewportResize',
                scope: me,
                destroyable: true
            });
        }
        me.aligning = true;
        me.callParent([
            component,
            alignment,
            Ext.apply({
                axisLock: me.getAxisLock()
            }, options)
        ]);
        me.aligning = false;
    },
    onViewportResize: function() {
        if (this.isVisible()) {
            this.realign();
        }
    },
    
    showAt: function(x, y) 
    {
        var me = this;
        if (me.getFloated() || me.isPositioned()) {
            if (arguments.length === 1) {
                if (x.x) {
                    y = x.y;
                    x = x.x;
                } else {
                    y = x[1];
                    x = x[0];
                }
            }
            me.show();
            if (me.isPositioned()) {
                me.setLeft(x);
                me.setTop(y);
            } else {
                me.setX(x);
                me.setY(y);
            }
        }
        return me;
    },
    onAdded: function(parent, instanced) {
        var me = this,
            modal;
        me.callParent([
            parent,
            instanced
        ]);
        if (!me.getFloated()) {
            modal = me.getModal();
            if (modal) {
                parent.insertBefore(modal, me);
                modal.setZIndex(me.getZIndex() - 1);
            }
        }
        me.el.removeCls(me.rootCls);
    },
    onRemoved: function(destroying) {
        if (!destroying) {
            this.el.addCls(this.rootCls);
        }
        this.callParent([
            destroying
        ]);
    },
    applyTooltip: function(tooltip) {
        if (tooltip) {
            if (tooltip.isInstance) {
                tooltip.setTarget(this);
                return tooltip;
            } else if (typeof tooltip === 'string') {
                tooltip = {
                    html: tooltip
                };
            } else {
                tooltip = Ext.merge({}, tooltip);
            }
            
            
            
            if (tooltip.autoCreate || tooltip.autoHide === false) {
                delete tooltip.autoCreate;
                tooltip.target = this;
                tooltip.xtype = tooltip.xtype || 'tooltip';
                tooltip = Ext.create(tooltip);
            } else {
                delete tooltip.xtype;
            }
        }
        return tooltip;
    },
    updateTooltip: function(tooltip, oldTooltip) {
        var el = this.el,
            data, manager, target, tip;
        if (oldTooltip) {
            if (oldTooltip.isInstance) {
                Ext.destroy(oldTooltip);
            } else {
                target = Ext.fly(oldTooltip.target);
                data = target && target.peekData();
                if (data) {
                    delete data.qtip;
                }
            }
        }
        if (tooltip && !tooltip.isInstance) {
            el.getData().qtip = tooltip;
            manager = Ext['tip'];
            manager = manager && manager.Manager;
            manager = manager && manager.instance;
            if (manager) {
                tip = manager.tip;
                if (tip.currentTarget.dom === el.dom) {
                    manager.onBeforeShow(tip);
                }
            }
        }
    },
    applyModal: function(modal, currentModal) {
        if (this.getFloated()) {
            return !!modal;
        }
        var isVisible = this.isVisible();
        if (modal === false) {
            modal = true;
            isVisible = false;
        }
        currentModal = Ext.factory(modal, Ext['Mask'], typeof currentModal === 'boolean' ? null : currentModal);
        if (currentModal) {
            currentModal.setVisibility(isVisible);
            currentModal.on('tap', 'onModalMaskTap', this);
        }
        return currentModal;
    },
    updateModal: function(modal) {
        var me = this,
            parent = me.getParent(),
            positionEl = (me.floatWrap || me.element).dom,
            topModal;
        if (me.getFloated()) {
            if (modal) {
                if (me.isVisible() && !positionEl.nextSibling) {
                    me.showModalMask();
                }
            } else {
                topModal = me.getModalSibling();
                if (topModal) {
                    topModal.showModalMask();
                } else {
                    
                    
                    me.hideModalMask();
                }
            }
        } else {
            if (parent) {
                if (modal) {
                    parent.insertBefore(modal, me);
                    modal.setZIndex(me.getZIndex() - 1);
                } else {
                    parent.remove(modal);
                }
            }
        }
    },
    applyHideAnimation: function(hideAnimation, oldHideAnimation) {
        return Ext.Factory.animation.update(oldHideAnimation, hideAnimation, this, 'createHideAnimation');
    },
    createHideAnimation: function(defaults) {
        return Ext.apply({
            type: 'fadeOut',
            element: this.element
        }, defaults);
    },
    applyShowAnimation: function(showAnimation, oldShowAnimation) {
        return Ext.Factory.animation.update(oldShowAnimation, showAnimation, this, 'createShowAnimation');
    },
    createShowAnimation: function(defaults) {
        return Ext.apply({
            type: 'fadeIn',
            element: this.element
        }, defaults);
    },
    
    doDestroy: function() {
        var me = this,
            sibling;
        
        
        if (me.isVisible()) {
            me.revertFocus();
            if (me.getModal() && me.getFloated()) {
                
                
                sibling = me.getModalSibling();
                if (sibling) {
                    sibling.showModalMask();
                } else {
                    me.hideModalMask();
                }
            }
        }
        if (me.hasListeners.destroy) {
            me.fireEvent('destroy', me);
        }
        me.destroyMembers('modal', 'innerHtmlElement', 'scrollerElement', 'scrollable', 
        'showAnimation', 
        'hideAnimation', 'centerResizeListener', 'visibleListener');
        me.setPlugins(null);
        me.setRecord(null);
        me.setTooltip(null);
        me.callParent();
    },
    privates: {
        
        
        
        preciseWidth: false,
        clearWhenVisible: function(fn) {
            var me = this,
                pending = me.pendingVisible;
            if (pending) {
                delete pending[fn];
                if (Ext.Object.isEmpty(pending)) {
                    me.pendingVisible = null;
                    me.visibleListener = Ext.destroy(me.visibleListener);
                }
            }
        },
        convertToLocalXY: function(xy) {
            var me = this,
                pageXY = me.element.getXY(),
                x = 0,
                y = 0;
            if (me.isPositioned()) {
                x = me.getLeft() || 0;
                y = me.getTop() || 0;
            } else {
                x = me.getX() || 0;
                y = me.getY() || 0;
            }
            return [
                xy[0] - pageXY[0] + x,
                xy[1] - pageXY[1] + y
            ];
        },
        doAddListener: function(name, fn, scope, options, order, caller, manager) {
            var me = this,
                el = me.element;
            
            
            
            
            if (me.initialized) {
                if (name === 'painted') {
                    if (!me.hasListeners.painted) {
                        el.on('painted', 'handleElementPainted', me);
                    }
                } else if (name === 'resize' && !me.isViewport) {
                    if (!me.hasListeners.resize) {
                        el.on({
                            scope: me,
                            resize: 'handleElementResize',
                            priority: 1000
                        });
                    }
                }
            }
            return me.callParent([
                name,
                fn,
                scope,
                options,
                order,
                caller,
                manager
            ]);
        },
        doRemoveListener: function(name, fn, scope) {
            var me = this,
                el = me.element,
                ret = me.callParent([
                    name,
                    fn,
                    scope
                ]);
            
            
            if (ret && me.initialized) {
                if (name === 'painted') {
                    if (!me.hasListeners.painted) {
                        el.un('painted', 'handleElementPainted', me);
                    }
                } else if (name === 'resize' && !me.isViewport) {
                    if (!me.hasListeners.resize) {
                        el.un('resize', 'handleElementResize', me);
                    }
                }
            }
            return ret;
        },
        doUpdateTpl: function(data) {
            var me = this,
                tpl = me.getTpl(),
                writeMode;
            if (tpl) {
                writeMode = me.getTplWriteMode();
                data = data || me.getData() || {};
                tpl[writeMode](me.getInnerHtmlElement(), data);
            }
        },
        
        getRenderTarget: function() {
            return this.bodyElement;
        },
        handleElementPainted: function(el) {
            this.fireEvent('painted', this, el);
        },
        handleElementResize: function(el, info) {
            var me = this,
                ceil = Math.ceil,
                lastSize = me.lastSize,
                oldWidth = lastSize.width || null,
                oldHeight = lastSize.height || null,
                w, h;
            
            if (info.flag) {
                w = info.width;
                h = info.height;
                if (!me.preciseWidth) {
                    w = ceil(w);
                    h = ceil(h);
                }
                lastSize.width = w;
                lastSize.height = h;
                if (me.onResize) {
                    me.onResize(w, h, oldWidth, oldHeight, info);
                }
                if (me.isCentered()) {
                    me.center();
                }
                me.fireEvent('resize', this, w, h, oldWidth, oldHeight, info);
            }
        },
        handleGlobalShow: function(c) {
            var me = this;
            if (me.isVisible(true) && (c === me || me.isDescendantOf(c))) {
                me.runWhenVisible();
            }
        },
        runWhenVisible: function() {
            var me = this,
                pending = me.pendingVisible,
                key;
            me.pendingVisible = null;
            me.visibleListener = Ext.destroy(me.visibleListener);
            for (key in pending) {
                me[key].apply(me, pending[key]);
            }
        },
        
        whenVisible: function(fn, args) {
            args = args || Ext.emptyArray;
            var me = this,
                listener = me.visibleListener,
                pending = me.pendingVisible,
                visible = me.isVisible(true);
            if (!visible && !listener) {
                me.visibleListener = Ext.on({
                    scope: me,
                    show: 'handleGlobalShow',
                    destroyable: true
                });
            }
            if (visible) {
                
                
                
                if (pending) {
                    pending[fn] = args;
                    me.runWhenVisible();
                } else {
                    me[fn].apply(me, args);
                }
            } else {
                if (!pending) {
                    me.pendingVisible = pending = {};
                }
                pending[fn] = args;
            }
            return visible;
        },
        
        setXY: function(x, y, animation) {
            var me = this,
                floated = me.getFloated();
            if (typeof x !== 'number') {
                animation = y;
                y = x[1];
                x = x[0];
            }
            me.$updatingXY = true;
            if (floated) {
                if (x != null) {
                    me.setX(x);
                }
                if (y != null) {
                    me.setY(y);
                }
            } else {
                if (x != null) {
                    me.setLeft(x);
                }
                if (y != null) {
                    me.setTop(y);
                }
            }
            me.$updatingXY = false;
            if (floated) {
                me.syncXYPosition(animation);
            } else {
                me.translate(x, y, animation);
            }
        },
        syncXYPosition: function(animation) {
            var me = this;
            
            
            if (!me.aligning) {
                me.viewportResizeListener = Ext.destroy(me.viewportResizeListener);
            }
            me.callParent([
                animation
            ]);
        }
    },
    deprecated: {
        "6.2.0": {
            methods: {
                
                resetFloating: 'resetPositioned'
            }
        },
        '6.5': {
            configs: {
                styleHtmlCls: null,
                styleHtmlContent: null
            }
        }
    }
}, function(Cls) {
    
    if (!document.querySelector('meta[name=viewport]')) {
        Ext.log.warn('Ext JS requires a viewport meta tag in order to function correctly on mobile devices.  Please add the following tag to the <head> of your html page: \n <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=10, user-scalable=yes">');
    }
});



Ext.define('Ext.ProgressBase', {
    mixinId: 'progressbase',
    config: {
        
        value: 0,
        
        textTpl: null
    },
    applyTextTpl: function(textTpl) {
        if (!textTpl.isTemplate) {
            textTpl = new Ext.XTemplate(textTpl);
        }
        return textTpl;
    },
    applyValue: function(value) {
        return value || 0;
    }
});


Ext.define('Ext.Progress', {
    extend: Ext.Gadget,
    xtype: [
        'progress',
        'progressbarwidget'
    ],
    alternateClassName: 'Ext.ProgressBarWidget',
    mixins: [
        Ext.ProgressBase
    ],
    config: {
        
        text: null,
        
        animate: false
    },
    cachedConfig: {
        textCls: Ext.baseCSSPrefix + 'progress-text',
        cls: null
    },
    baseCls: Ext.baseCSSPrefix + 'progress',
    template: [
        {
            reference: 'backgroundEl'
        },
        {
            reference: 'barEl',
            cls: Ext.baseCSSPrefix + 'progress-bar',
            children: [
                {
                    reference: 'textEl'
                }
            ]
        }
    ],
    defaultBindProperty: 'value',
    updateCls: function(cls, oldCls) {
        var el = this.element;
        if (oldCls) {
            el.removeCls(oldCls);
        }
        if (cls) {
            el.addCls(cls);
        }
    },
    updateUi: function(ui, oldUi) {
        var element = this.element,
            barEl = this.barEl,
            baseCls = this.baseCls + '-';
        this.callParent([
            ui,
            oldUi
        ]);
        if (oldUi) {
            element.removeCls(baseCls + oldUi);
            barEl.removeCls(baseCls + 'bar-' + oldUi);
        }
        element.addCls(baseCls + ui);
        barEl.addCls(baseCls + 'bar-' + ui);
    },
    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: Math.round(value * 100)
            }));
        }
        if (!me.isConfiguring && me.getAnimate()) {
            me.stopBarAnimation();
            me.startBarAnimation(Ext.apply({
                from: {
                    width: (oldValue * 100) + '%'
                },
                to: {
                    width: (value * 100) + '%'
                }
            }, me.animate));
        } else {
            me.barEl.setStyle('width', (value * 100) + '%');
        }
    },
    updateText: function(text) {
        this.backgroundEl.setHtml(text);
        this.textEl.setHtml(text);
    },
    doDestroy: function() {
        this.stopBarAnimation();
        this.callParent();
    },
    privates: {
        startBarAnimation: Ext.privateFn,
        stopBarAnimation: Ext.privateFn
    }
});


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,
    mixins: [
        Ext.mixin.Factoryable
    ],
    factoryConfig: {
        type: 'animation'
    },
    isAnimation: true,
    config: {
        name: '',
        element: null,
        
        before: null,
        from: {},
        to: {},
        after: null,
        states: {},
        duration: 300,
        
        easing: 'linear',
        iteration: 1,
        direction: 'normal',
        delay: 0,
        onBeforeStart: null,
        callback: null,
        onEnd: null,
        onBeforeEnd: null,
        scope: null,
        reverse: null,
        preserveEndState: false,
        replacePrevious: true
    },
    STATE_FROM: '0%',
    STATE_TO: '100%',
    DIRECTION_UP: 'up',
    DIRECTION_TOP: 'top',
    DIRECTION_DOWN: 'down',
    DIRECTION_BOTTOM: 'bottom',
    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;
    },
    updateCallback: function(callback) {
        if (callback) {
            this.setOnEnd(callback);
        }
    },
    end: function() {
        
        
        this.stop();
    },
    stop: function() {
        this.fireEvent('stop', this);
    },
    destroy: function() {
        
        this.destroying = true;
        this.stop();
        this.callParent();
        this.destroying = false;
        this.destroyed = true;
    },
    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 me = this,
            states = me.getStates(),
            statesData = {},
            before = me.getBefore(),
            after = me.getAfter(),
            from = states[me.STATE_FROM],
            to = states[me.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;
            }
        }
        return {
            before: before ? before.getData() : {},
            after: after ? after.getData() : {},
            states: statesData,
            from: fromData,
            to: toData,
            duration: me.getDuration(),
            iteration: me.getIteration(),
            direction: me.getDirection(),
            easing: me.getEasing(),
            delay: me.getDelay(),
            onEnd: me.getOnEnd(),
            onBeforeEnd: me.getOnBeforeEnd(),
            onBeforeStart: me.getOnBeforeStart(),
            scope: me.getScope(),
            preserveEndState: me.getPreserveEndState(),
            replacePrevious: me.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',
        top: 'down',
        down: 'up',
        bottom: '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,
            offsetPct, fromX, fromY, toX, toY;
        if (typeof offset === 'string') {
            offsetPct = true;
            offset = parseFloat(offset);
        }
        if (reverse) {
            direction = this.reverseDirectionMap[direction];
        }
        switch (direction) {
            case this.DIRECTION_UP:
            case this.DIRECTION_TOP:
                if (offsetPct) {
                    offset = box.height * offset / 100;
                };
                if (out) {
                    translateY = containerBox.top - box.top - box.height - offset;
                } else {
                    translateY = containerBox.bottom - box.bottom + box.height + offset;
                };
                break;
            case this.DIRECTION_DOWN:
            case this.DIRECTION_BOTTOM:
                if (offsetPct) {
                    offset = box.height * offset / 100;
                };
                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 (offsetPct) {
                    offset = box.width * offset / 100;
                };
                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 (offsetPct) {
                    offset = box.width * offset / 100;
                };
                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 me = this,
            from = me.getFrom(),
            to = me.getTo(),
            direction = me.getDirection(),
            out = me.getOut(),
            half = me.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:
            case this.DIRECTION_TOP:
                if (out) {
                    toRotateX = rotate;
                } else {
                    fromRotateX = -rotate;
                };
                break;
            case this.DIRECTION_DOWN:
            case this.DIRECTION_BOTTOM:
                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();
    }
});


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', {
    
    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) {
            defaultClass = Ext.ClassManager.getByAlias('animation.' + type);
            
            if (!defaultClass) {
                Ext.Logger.error("Invalid animation type of: '" + type + "'");
            }
        }
        
        return Ext.factory(config, defaultClass);
    }
});


Ext.define('Ext.overrides.Progress', {
    override: 'Ext.Progress',
    initialize: function() {
        this.callParent();
        this.on('painted', 'onPainted', this);
    },
    onPainted: function() {
        this.syncWidth();
    },
    onResize: function(width) {
        this.syncWidth(width);
    },
    syncWidth: function(width) {
        var me = this;
        if (width == null) {
            width = me.element.getWidth();
        }
        me.backgroundEl.setWidth(width);
        me.textEl.setWidth(width);
    },
    privates: {
        startBarAnimation: function(o) {
            var me = this;
            me.barAnim = new Ext.fx.Animation(Ext.apply(o, {
                element: me.barEl,
                preserveEndState: true,
                callback: function() {
                    delete me.barAnim;
                }
            }));
            Ext.Animator.run(me.barAnim);
        },
        stopBarAnimation: function() {
            var barAnim = this.barAnim;
            if (barAnim) {
                barAnim.destroy();
            }
            this.barAnim = null;
        }
    }
});


Ext.define('Ext.app.EventDomain', {
    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.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.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 me = this,
                ret;
            ret = doFireEvent.apply(me, arguments);
            
            
            if (ret !== false && !me.destroyed && !me.isSuspended(ev)) {
                ret = domain.dispatch(me, 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;
        this.callParent();
    }
});


Ext.define('Ext.app.domain.Component', {
    extend: Ext.app.EventDomain,
    singleton: true,
    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([
            target,
            ev,
            args
        ]);
    },
    match: function(target, selector) {
        return target.is(selector);
    }
});


Ext.define('Ext.app.EventBus', {
    singleton: true,
    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,
    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.route.Action', {
    config: {
        
        actions: null,
        
        befores: null,
        
        urlParams: []
    },
    
    
    started: false,
    
    stopped: false,
    constructor: function(config) {
        var me = this;
        me.deferred = new Ext.Deferred();
        me.resume = me.resume.bind(me);
        me.stop = me.stop.bind(me);
        me.initConfig(config);
        me.callParent([
            config
        ]);
    },
    applyActions: function(actions) {
        if (actions) {
            actions = Ext.Array.from(actions);
        }
        return actions;
    },
    applyBefores: function(befores) {
        if (befores) {
            befores = Ext.Array.from(befores);
        }
        return befores;
    },
    destroy: function() {
        this.deferred = null;
        this.setBefores(null).setActions(null).setUrlParams(null);
        this.callParent();
    },
    
    resume: function() {
        return this.next();
    },
    
    stop: function() {
        this.stopped = true;
        return this.done();
    },
    
    next: function() {
        var me = this,
            actions = me.getActions(),
            befores = me.getBefores(),
            urlParams = me.getUrlParams().slice(),
            config, ret;
        if (me.stopped || (befores ? !befores.length : true) && (actions ? !actions.length : true)) {
            me.done();
        } else {
            if (befores && befores.length) {
                config = befores.shift();
                urlParams.push(me);
                ret = Ext.callback(config.fn, config.scope, urlParams);
                if (ret && ret.then) {
                    ret.then(function(arg) {
                        me.resume(arg);
                    }, function(arg) {
                        me.stop(arg);
                    });
                }
            } else if (actions && actions.length) {
                config = actions.shift();
                Ext.callback(config.fn, config.scope, urlParams);
                me.next();
            } else {
                
                me.next();
            }
        }
        return me;
    },
    
    run: function() {
        var deferred = this.deferred;
        if (!this.started) {
            this.next();
            this.started = true;
        }
        return deferred.promise;
    },
    
    done: function() {
        var deferred = this.deferred;
        if (this.stopped) {
            deferred.reject();
        } else {
            deferred.resolve();
        }
        this.destroy();
        return this;
    },
    
    before: function(first, fn, scope) {
        if (!Ext.isBoolean(first)) {
            scope = fn;
            fn = first;
            first = false;
        }
        var befores = this.getBefores(),
            config = {
                fn: fn,
                scope: scope
            };
        
        if (this.destroyed) {
            Ext.raise('This action has has already resolved and therefore will never execute this function.');
            return;
        }
        
        if (befores) {
            if (first) {
                befores.unshift(config);
            } else {
                befores.push(config);
            }
        } else {
            this.setBefores(config);
        }
        return this;
    },
    
    action: function(first, fn, scope) {
        if (!Ext.isBoolean(first)) {
            scope = fn;
            fn = first;
            first = false;
        }
        var actions = this.getActions(),
            config = {
                fn: fn,
                scope: scope
            };
        
        if (this.destroyed) {
            Ext.raise('This action has has already resolved and therefore will never execute this function.');
            return;
        }
        
        if (actions) {
            if (first) {
                actions.unshift(config);
            } else {
                actions.push(config);
            }
        } else {
            this.setActions(config);
        }
        return this;
    },
    
    then: function(resolve, reject) {
        
        if (this.destroyed) {
            Ext.raise('This action has has already resolved and therefore will never execute either function.');
            return;
        }
        
        return this.deferred.then(resolve, reject);
    }
});


Ext.define('Ext.route.Route', {
    
    config: {
        
        name: null,
        
        url: null,
        
        allowInactive: false,
        
        caseInsensitive: false,
        
        handlers: []
    },
    
    
    defaultMatcher: '([%a-zA-Z0-9\\-\\_\\s,]+)',
    
    
    paramMatchingRegex: /:([0-9A-Za-z\_]*)/g,
    
    
    isRoute: true,
    constructor: function(config) {
        var me = this,
            url;
        this.initConfig(config);
        Ext.apply(me, config, {
            conditions: {}
        });
        url = me.getUrl();
        me.paramsInMatchString = url.match(me.paramMatchingRegex) || [];
        me.matcherRegex = me.createMatcherRegex(url);
    },
    
    recognize: function(url) {
        var me = this,
            recognized = me.recognizes(url),
            matches, urlParams;
        if (url === me.lastToken) {
            
            return true;
        }
        if (recognized) {
            matches = me.matchesFor(url);
            urlParams = url.match(me.matcherRegex);
            urlParams.shift();
            return Ext.applyIf(matches, {
                historyUrl: url,
                urlParams: urlParams
            });
        }
        return false;
    },
    
    recognizes: function(url) {
        return this.matcherRegex.test(url);
    },
    
    execute: function(token, argConfig) {
        var me = this,
            allowInactive = me.getAllowInactive(),
            handlers = me.getHandlers(),
            queue = Ext.route.Router.getQueueRoutes(),
            length = handlers.length,
            befores = [],
            actions = [],
            urlParams = (argConfig && argConfig.urlParams) || [],
            i, handler, scope, action, promises;
        me.lastToken = token;
        if (!queue) {
            promises = [];
        }
        return new Ext.Promise(function(resolve, reject) {
            for (i = 0; i < length; i++) {
                handler = handlers[i];
                scope = handler.scope;
                if (!allowInactive && scope.isActive && !scope.isActive()) {
                    
                    continue;
                }
                if (queue) {
                    if (handler.before) {
                        befores.push({
                            fn: handler.before,
                            scope: scope
                        });
                    }
                    if (handler.action) {
                        actions.push({
                            fn: handler.action,
                            scope: scope
                        });
                    }
                } else {
                    action = {
                        urlParams: urlParams
                    };
                    if (handler.before) {
                        action.befores = {
                            fn: handler.before,
                            scope: scope
                        };
                    }
                    if (handler.action) {
                        action.actions = {
                            fn: handler.action,
                            scope: scope
                        };
                    }
                    action = new Ext.route.Action(action);
                    if (Ext.fireEvent('beforeroute', action, me) === false) {
                        action.destroy();
                    } else {
                        promises.push(action.run());
                    }
                }
            }
            if (queue) {
                action = new Ext.route.Action({
                    actions: actions,
                    befores: befores,
                    urlParams: urlParams
                });
                if (Ext.fireEvent('beforeroute', action, me) === false) {
                    action.destroy();
                    reject();
                } else {
                    action.run().then(resolve, reject);
                }
            } else {
                Ext.Promise.all(promises).then(resolve, reject);
            }
        });
    },
    
    matchesFor: function(url) {
        var params = {},
            keys = this.paramsInMatchString,
            values = url.match(this.matcherRegex),
            length = keys.length,
            i;
        
        values.shift();
        for (i = 0; i < length; i++) {
            params[keys[i].replace(':', '')] = values[i];
        }
        return params;
    },
    
    createMatcherRegex: function(url) {
        
        
        var paramsInMatchString = this.paramsInMatchString,
            conditions = this.conditions,
            defaultMatcher = this.defaultMatcher,
            length = paramsInMatchString.length,
            modifiers = this.getCaseInsensitive() ? 'i' : '',
            i, params, matcher;
        if (url === '*') {
            
            url = url.replace('*', '\\*');
        } else {
            for (i = 0; i < length; i++) {
                params = paramsInMatchString[i];
                matcher = conditions[params] || defaultMatcher;
                url = url.replace(new RegExp(params), matcher);
            }
        }
        
        return new RegExp('^' + url + '$', modifiers);
    },
    
    addHandler: function(handler) {
        var handlers = this.getHandlers();
        handlers.push(handler);
        return this;
    },
    
    removeHandler: function(scope, config) {
        var handlers = this.getHandlers(),
            length = handlers.length,
            newHandlers = [],
            i, handler;
        for (i = 0; i < length; i++) {
            handler = handlers[i];
            if (handler.scope === scope) {
                if (!config || (Ext.isDefined(config.action) ? handler.action === config.action : true && Ext.isDefined(config.before) ? handler.before === config.before : true)) {
                    
                    continue;
                }
            }
            newHandlers.push(handler);
        }
        this.setHandlers(newHandlers);
        return this;
    }
});


Ext.define('Ext.util.Observable', {
    extend: Ext.mixin.Observable,
    
    
    $applyConfigs: true
}, function(Observable) {
    var Super = Ext.mixin.Observable;
    
    Observable.releaseCapture = Super.releaseCapture;
    
    Observable.capture = Super.capture;
    
    Observable.captureArgs = Super.captureArgs;
    
    Observable.observe = Observable.observeClass = Super.observe;
});


Ext.define('Ext.util.History', {
    singleton: true,
    alternateClassName: 'Ext.History',
    mixins: {
        observable: Ext.util.Observable
    },
    
    useTopWindow: false,
    
    
    
    
    hashRe: /^(#?!?)/,
    constructor: function() {
        var me = this;
        me.ready = false;
        me.currentToken = null;
        me.mixins.observable.constructor.call(me);
    },
    
    getHash: function() {
        return this.win.location.hash.replace(this.hashRe, '');
    },
    
    setHash: function(hash, replace) {
        var me = this,
            hashRe = me.hashRe,
            loc = me.win.location;
        
        hash = hash.replace(hashRe, me.hashbang ? '#!' : '#');
        try {
            if (replace) {
                loc.replace(hash);
            } else {
                loc.hash = hash;
            }
            
            me.currentToken = hash.replace(hashRe, '');
        } catch (e) {}
    },
    
    
    handleStateChange: function(token) {
        
        token = token.replace(this.hashRe, '');
        this.fireEvent('change', this.currentToken = token);
    },
    
    startUp: function() {
        var me = this;
        me.currentToken = me.getHash();
        Ext.get(me.win).on('hashchange', me.onHashChange, 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.onInternalReady(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;
    },
    
    replace: function(token, preventDuplicates) {
        var me = this,
            set = false;
        if (preventDuplicates === false || me.getToken() !== token) {
            this.setHash(token, true);
            set = true;
        }
        return set;
    },
    
    back: function() {
        this.win.history.go(-1);
    },
    
    forward: function() {
        this.win.history.go(1);
    },
    
    getToken: function() {
        return this.ready ? this.currentToken : this.getHash();
    }
});


Ext.define('Ext.route.Router', {
    singleton: true,
    
    
    config: {
        
        hashbang: null,
        
        multipleToken: '|',
        
        queueRoutes: true
    },
    
    
    constructor: function() {
        var History = Ext.util.History;
        if (!History.ready) {
            History.init();
        }
        History.on('change', this.onStateChange, this);
        this.initConfig();
        this.clear();
    },
    updateHashbang: function(hashbang) {
        Ext.util.History.hashbang = hashbang;
    },
    
    onStateChange: function(token) {
        var me = this,
            tokens = token.split(me.getMultipleToken()),
            queue, i, length;
        if (me.isSuspended) {
            queue = me.suspendedQueue;
            i = 0;
            length = tokens.length;
            if (queue) {
                for (; i < length; i++) {
                    token = tokens[i];
                    
                    if (!Ext.Array.contains(queue, token)) {
                        queue.push(token);
                    }
                }
            }
        } else {
            me.handleBefore(tokens);
        }
    },
    
    handleBefore: function(tokens) {
        var me = this,
            action = new Ext.route.Action();
        if (Ext.fireEvent('beforeroutes', action, tokens) === false) {
            action.destroy();
        } else {
            action.run().then(me.handleBeforeRoute.bind(me, tokens), Ext.emptyFn);
        }
    },
    
    handleBeforeRoute: function(tokens) {
        var me = this,
            beforeRoute = me.getByName('*');
        if (beforeRoute) {
            beforeRoute.execute().then(me.doRun.bind(me, tokens), Ext.emptyFn);
        } else {
            
            me.doRun(tokens);
        }
    },
    
    doRun: function(tokens) {
        var me = this,
            app = me.application,
            routes = me.routes,
            i = 0,
            length = tokens.length,
            matched = {},
            unmatched = [],
            token, found, name, route, recognize;
        for (; i < length; i++) {
            token = tokens[i];
            found = false;
            for (name in routes) {
                route = routes[name];
                recognize = route.recognize(token);
                if (recognize) {
                    found = true;
                    if (recognize !== true) {
                        
                        
                        
                        route.execute(token, recognize).then(null, Ext.bind(me.onRouteRejection, me, [
                            route
                        ]));
                    }
                    Ext.Array.remove(unmatched, route);
                    if (!matched[name]) {
                        matched[name] = 1;
                    }
                } else if (!matched[name]) {
                    unmatched.push(route);
                }
            }
            if (!found) {
                if (app) {
                    
                    app.fireEvent('unmatchedroute', token);
                }
                Ext.fireEvent('unmatchedroute', token);
            }
        }
        i = 0;
        length = unmatched.length;
        for (; i < length; i++) {
            route = unmatched[i];
            
            
            
            route.lastToken = null;
        }
    },
    
    onRouteRejection: function(route) {
        Ext.fireEvent('routereject', route);
    },
    
    connect: function(url, config, instance) {
        var routes = this.routes,
            name = config.name || url,
            route, name;
        if (url[0] === '!') {
            
            if (!Ext.util.History.hashbang) {
                Ext.log({
                    level: 'error',
                    msg: 'Route found with "!" ("' + url + '"). Should use new hashbang functionality instead. ' + 'Please see the router guide for more: https://docs.sencha.com/extjs/' + Ext.getVersion().version + '/guides/application_architecture/router.html'
                });
            }
            
            url = url.substr(1);
            this.setHashbang(true);
        }
        if (Ext.isString(config)) {
            config = {
                action: config,
                scope: instance
            };
        } else {
            config.scope = instance;
        }
        route = routes[name];
        if (!route) {
            route = routes[name] = new Ext.route.Route({
                conditions: config.conditions || {},
                name: name,
                url: url
            });
        }
        route.addHandler(config);
    },
    
    disconnect: function(instance, config) {
        var routes = this.routes,
            route, name;
        if (config) {
            route = this.getByName(config.name || config.url);
            if (route) {
                route.removeHandler(instance, config);
            }
        } else {
            for (name in routes) {
                route = routes[name];
                route.removeHandler(instance);
            }
        }
    },
    
    recognize: function(url) {
        var routes = this.routes,
            matches = [],
            name, arr, i, length, route, urlParams;
        for (name in routes) {
            arr = routes[name];
            length = arr && arr.length;
            if (length) {
                i = 0;
                for (; i < length; i++) {
                    route = arr[i];
                    urlParams = route.recognize(url);
                    if (urlParams) {
                        matches.push({
                            route: route,
                            urlParams: urlParams
                        });
                    }
                }
            }
        }
        return matches.length ? matches : false;
    },
    
    draw: function(fn) {
        fn.call(this, this);
    },
    
    clear: function() {
        this.routes = {};
    },
    
    clearLastTokens: function(token) {
        var routes = this.routes,
            name, route;
        for (name in routes) {
            route = routes[name];
            if (!token || route.recognize(token)) {
                route.lastToken = null;
            }
        }
    },
    
    getByName: function(name) {
        var routes = this.routes;
        if (routes) {
            return routes[name];
        }
    },
    
    suspend: function(trackTokens) {
        this.isSuspended = true;
        if (!this.suspendedQueue && trackTokens !== false) {
            this.suspendedQueue = [];
        }
    },
    
    resume: function(discardQueue) {
        var me = this,
            queue = me.suspendedQueue,
            token;
        if (me.isSuspended) {
            me.isSuspended = false;
            me.suspendedQueue = null;
            if (!discardQueue && queue) {
                token = queue.join(me.getMultipleToken());
                me.onStateChange(token);
            }
        }
    }
});


Ext.define('Ext.route.Mixin', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'routerable',
        before: {
            destroy: 'destroyRouterable'
        }
    },
    config: {
        
        routes: null
    },
    destroyRouterable: function() {
        Ext.route.Router.disconnect(this);
    },
    updateRoutes: function(routes, oldRoutes) {
        var me = this,
            Router = Ext.route.Router,
            url, config, method;
        if (oldRoutes) {
            for (url in oldRoutes) {
                config = oldRoutes[url];
                if (Ext.isString(config)) {
                    config = {
                        action: config,
                        name: url
                    };
                }
                Router.disconnect(me, config);
            }
        }
        if (routes) {
            for (url in routes) {
                config = routes[url];
                if (Ext.isString(config)) {
                    config = {
                        action: config
                    };
                }
                method = config.action;
                
                Router.connect(url, config, me);
            }
        }
    },
    
    redirectTo: function(hash, opt) {
        var me = this,
            currentHash = Ext.util.History.getToken(),
            Router = Ext.route.Router,
            delimiter = Router.getMultipleToken(),
            tokens = currentHash ? currentHash.split(delimiter) : [],
            length = tokens.length,
            force, i, name, obj, route, token, match;
        if (hash === -1) {
            return Ext.util.History.back();
        } else if (hash === 1) {
            return Ext.util.History.forward();
        } else if (hash.isModel) {
            hash = hash.toUrl();
        } else if (Ext.isObject(hash)) {
            
            for (name in hash) {
                obj = hash[name];
                if (!Ext.isObject(obj)) {
                    obj = {
                        token: obj
                    };
                }
                if (length) {
                    route = Router.getByName(name);
                    if (route) {
                        match = false;
                        for (i = 0; i < length; i++) {
                            token = tokens[i];
                            if (route.matcherRegex.test(token)) {
                                match = true;
                                if (obj.token) {
                                    
                                    if (obj.fn && obj.fn.call(this, token, tokens, obj) === false) {
                                        
                                        
                                        continue;
                                    }
                                    tokens[i] = obj.token;
                                    if (obj.force) {
                                        
                                        route.lastToken = null;
                                    }
                                } else {
                                    
                                    tokens.splice(i, 1);
                                    i--;
                                    length--;
                                    
                                    route.lastToken = null;
                                }
                            }
                        }
                        if (obj && obj.token && !match) {
                            
                            tokens.push(obj.token);
                        }
                    }
                } else if (obj && obj.token) {
                    
                    tokens.push(obj.token);
                }
            }
            hash = tokens.join(delimiter);
        }
        if (opt === true) {
            
            force = opt;
            opt = null;
        } else if (opt) {
            force = opt.force;
        }
        length = tokens.length;
        if (force && length) {
            for (i = 0; i < length; i++) {
                token = tokens[i];
                Router.clearLastTokens(token);
            }
        }
        if (currentHash === hash) {
            if (force) {
                
                Router.onStateChange(hash);
            }
            
            return false;
        }
        if (opt && opt.replace) {
            Ext.util.History.replace(hash);
        } else {
            Ext.util.History.add(hash);
        }
        return true;
    },
    privates: {
        afterClassMixedIn: function(targetClass) {
            var proto = targetClass.prototype,
                routes = proto.routes;
            if (routes) {
                delete proto.routes;
                targetClass.getConfigurator().add({
                    routes: routes
                });
            }
        }
    }
});


Ext.define('Ext.app.BaseController', {
    mixins: [
        Ext.mixin.Observable,
        Ext.route.Mixin
    ],
    isController: true,
    config: {
        
        id: undefined,
        
        control: null,
        
        listen: 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);
    },
    updateId: function(id) {
        this.id = id;
    },
    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.getId();
        if (control) {
            this.control(control);
        }
    },
    
    updateListen: function(listen) {
        this.getId();
        if (listen) {
            this.listen(listen);
        }
    },
    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;
        if (bus) {
            bus.unlisten(me);
            me.eventbus = null;
        }
        me.callParent();
    }
});


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;
        },
        
        setupPaths: function(appName, appFolder, paths) {
            var manifestPaths = Ext.manifest,
                ns;
            
            if (appName && appFolder !== null) {
                manifestPaths = manifestPaths && manifestPaths.paths;
                
                
                
                
                
                if (!manifestPaths || appFolder !== undefined) {
                    Ext.Loader.setPath(appName, (appFolder === undefined) ? 'app' : appFolder);
                }
            }
            if (paths) {
                for (ns in paths) {
                    if (paths.hasOwnProperty(ns)) {
                        Ext.Loader.setPath(ns, paths[ns]);
                    }
                }
            }
        }
    });
    
    Ext.getNamespace = Ext.app.getNamespace;
});


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,
    
    generation: 0,
    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;
            };
        },
        
        isEqual: function(filter1, filter2) {
            if (filter1.getProperty() !== filter2.getProperty()) {
                return false;
            }
            if (filter1.getOperator() !== filter2.getOperator()) {
                return false;
            }
            if (filter1.getValue() === filter2.getValue()) {
                return true;
            } else if (Ext.isArray(filter1) && Ext.isArray(filter2) && Ext.Array.equals(filter1, filter2)) {
                return true;
            }
            return false;
        },
        
        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,
        notin: 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;
            
            
            me.generatedFilterFn = true;
        }
        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(),
            serialized;
        delete result.id;
        delete result.serializer;
        if (serializer) {
            serialized = serializer.call(this, result);
            if (serialized) {
                result = serialized;
            }
        }
        return result;
    },
    updateDisabled: function() {
        
        this.generation++;
    },
    updateOperator: function() {
        
        this.onConfigMutation();
    },
    updateConvert: function() {
        
        this.onConfigMutation();
    },
    updateProperty: function() {
        
        this.onConfigMutation();
    },
    updateAnyMatch: function() {
        
        this.onConfigMutation();
    },
    updateExactMatch: function() {
        
        this.onConfigMutation();
    },
    updateCaseSensitive: function() {
        
        this.onConfigMutation();
    },
    updateValue: function(value) {
        
        this.onConfigMutation();
        if (this.getDisableOnEmpty()) {
            this.setDisabled(Ext.isEmpty(value));
        }
    },
    updateFilterFn: function(filterFn) {
        delete this.generatedFilterFn;
    },
    onConfigMutation: function() {
        
        this.generation++;
        if (this.generatedFilterFn) {
            this._filterFn = null;
        }
    },
    updateDisableOnEmpty: function(disableOnEmpty) {
        
        
        if (disableOnEmpty) {
            this.setDisabled(Ext.isEmpty(this.getValue()));
        }
    },
    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));
            },
            notin: 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;
            },
            "/=": function(candidate) {
                var me = this,
                    v = me._filterValue;
                candidate = me.getCandidateValue(candidate, v);
                
                if (v !== me.lastRegExpSource) {
                    me.lastRegExpSource = v;
                    try {
                        me.regex = new RegExp(v, 'i');
                    } catch (e) {
                        me.regex = null;
                    }
                }
                return me.regex ? me.regex.test(candidate) : false;
            }
        });
    
    operatorFns['=='] = operatorFns['='];
    operatorFns.gt = operatorFns['>'];
    operatorFns.ge = operatorFns['>='];
    operatorFns.lt = operatorFns['<'];
    operatorFns.le = operatorFns['<='];
    operatorFns.eq = operatorFns['='];
    operatorFns.ne = operatorFns['!='];
});


Ext.define('Ext.util.AbstractMixedCollection', {
    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);
    },
    destroy: function() {
        var me = this;
        me.items = me.map = me.keys = me.indexMap = null;
        me.callParent();
    },
    
    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(item) {
        return item.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;
    },
    
    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();
    },
    
    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;
    },
    
    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.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() {
        var me = this,
            result = {
                root: me.getRoot(),
                property: me.getProperty(),
                direction: me.getDirection()
            };
        
        
        if (me._id) {
            result.id = me._id;
        }
        return result;
    },
    
    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",
    
    
    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(sorters[i], 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.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() {
        this.initConfig();
        this.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 item.$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) {
        this.callParent([
            mapping
        ]);
        this.fireEvent('sort', this);
    },
    
    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.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.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.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;
        }
    },
    updateProperty: 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, oldCollection) {
        if (collection) {
            collection.addObserver(this);
        }
        if (oldCollection) {
            oldCollection.removeObserver(this);
        }
    },
    clone: function() {
        return new Ext.util.CollectionKey(this.getCurrentConfig());
    },
    destroy: function() {
        this.clear();
        this.getCollection().removeObserver(this);
        this.destroyed = true;
    }
});


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.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
    ],
    
    isCollection: true,
    config: {
        autoFilter: true,
        
        autoSort: true,
        
        autoGroup: true,
        
        decoder: null,
        
        extraKeys: null,
        
        filters: null,
        
        grouper: null,
        
        groups: null,
        
        groupConfig: null,
        
        rootProperty: null,
        
        sorters: null,
        
        multiSortLimit: 3,
        
        defaultSortDirection: 'ASC',
        
        source: null,
        
        trackGroups: true
    },
    
    generation: 0,
    
    indices: null,
    
    indexRebuilds: 0,
    
    updating: 0,
    
    grouped: false,
    
    sorted: false,
    
    filtered: false,
    
    $endUpdatePriority: 1001,
    
    manageSorters: true,
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        var me = this;
        
        me.callParent([
            config
        ]);
        
        
        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) {
            
            
            me.grouped = me.sorted = false;
            me.setSorters(null);
            if (me.manageSorters) {
                sorters.destroy();
            }
        }
        if (groups) {
            groups.destroy();
            me._groups = null;
        }
        me.setSource(null);
        me.observers = me.items = me.map = null;
        me.callParent();
    },
    
    add: function(item) {
        var me = this,
            items = me.decodeItems(arguments, 0),
            ret = items;
        if (items.length) {
            me.splice(me.length, 0, items);
            ret = (items.length === 1) ? items[0] : items;
        }
        return ret;
    },
    
    replaceAll: function() {
        var me = this,
            ret, items;
        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.length = 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;
    },
    
    containsAll: function(items) {
        var all = Ext.isArray(items) ? items : arguments,
            i;
        for (i = all.length; i-- > 0; ) {
            if (!this.contains(all[i])) {
                return false;
            }
        }
        return true;
    },
    
    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.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.splice(index, 0, items);
            ret = (items.length === 1) ? items[0] : items;
        }
        return ret;
    },
    
    itemChanged: function(item, modified, oldKey, meta) {
        var me = this,
            keyChanged = oldKey !== undefined,
            filtered = me.filtered && me.getAutoFilter(),
            filterChanged = false,
            itemMovement = 0,
            items = me.items,
            last = me.length - 1,
            sorted = me.sorted && last > 0 && me.getAutoSort(),
            
            
            
            
            source = me.getSource(),
            toRemove = 0,
            itemFiltered = false,
            wasFiltered = false,
            details, newKey, sortFn, toAdd, index, newIndex;
        
        if (source && !source.updating) {
            me.sourceUpdating = true;
            source.itemChanged(item, modified, oldKey, meta);
            me.sourceUpdating = false;
        } 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 !== -1) {
                    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.generation;
            me.beginUpdate();
            me.notify('beforeitemchange', [
                details
            ]);
            if (keyChanged) {
                me.updateKey(item, oldKey, details);
            }
            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;
            }
            
            me.requestedIndex = index;
            source.splice(i, removeItems, newItems);
            delete me.requestedIndex;
            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 - 1) {
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    --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(), index);
                    }
                }
                if (insertAt === length) {
                    end = insertAt;
                    
                    
                    for (i = addItems.length - 1; i >= 0; --i) {
                        items[end + i] = addItems[i];
                    }
                    
                    
                    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, details) {
        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 (map[oldKey] === item && !(newKey in map)) {
                delete map[oldKey];
                
                
                
                me.updating++;
                me.generation++;
                map[newKey] = item;
                if (indices) {
                    indices[newKey] = indices[oldKey];
                    delete indices[oldKey];
                }
                me.notify('updatekey', [
                    Ext.apply({
                        item: item,
                        newKey: newKey,
                        oldKey: oldKey
                    }, details)
                ]);
                me.updating--;
            } else 
            {
                
                
                
                if (newKey in map && map[newKey] !== item) {
                    
                    
                    Ext.raise('Duplicate newKey "' + newKey + '" for item with oldKey "' + oldKey + '"');
                }
                if (oldKey in map && map[oldKey] !== item) {
                    
                    
                    
                    Ext.raise('Incorrect oldKey "' + oldKey + '" for item with newKey "' + newKey + '"');
                }
            }
        }
    },
    
    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;
        
        
        if (!this.sourceUpdating) {
            this.notify('beforeitemchange', [
                details
            ]);
        }
    },
    
    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: function() {
        delete this.onCollectionUpdateKey;
    },
    
    onCollectionRefresh: function(source) {
        var me = this,
            map = {},
            indices = {},
            items = me.items,
            sourceItems = source.items,
            filterFn = me.getFilterFn(),
            i, item, key, length, newLength;
        
        
        
        if (me.filtered && me.getAutoFilter()) {
            for (i = 0 , newLength = 0 , length = sourceItems.length; i < length; i++) {
                if (filterFn(sourceItems[i])) {
                    items[newLength++] = sourceItems[i];
                }
            }
            items.length = newLength;
        } else {
            items.length = 0;
            items.push.apply(items, sourceItems);
        }
        if (me.sorted) {
            me.sortData(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.generation;
        me.notify('refresh');
    },
    
    onCollectionRemove: function(source, details) {
        this.splice(0, details.items);
    },
    
    
    
    
    
    onCollectionUpdateKey: function(source, details) {
        this.updateKey(details.item, details.oldKey, details);
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    _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 me = this,
            observers = me.observers;
        if (!observers) {
            me.observers = observers = [];
        }
        
        if (Ext.Array.contains(observers, observer)) {
            Ext.Error.raise('Observer already added');
        }
        
        
        if (me.notifying) {
            me.observers = observers = observers.slice(0);
        }
        observers.push(observer);
        if (observers.length > 1) {
            
            
            Ext.Array.sort(observers, me.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 {
                    config = Ext.apply(config, value);
                }
                value = new Ext.util.CollectionKey(config);
            } else {
                value.setCollection(me);
            }
            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) {
            me.notifying = true;
            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);
                }
            }
            me.notifying = false;
        }
        
        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(),
            extraKeys, newKeys, key;
        if (!source) {
            
            
            
            extraKeys = me.getExtraKeys();
            if (extraKeys) {
                newKeys = {};
                for (key in extraKeys) {
                    newKeys[key] = extraKeys[key].clone(me);
                }
            }
            source = new Ext.util.Collection({
                keyFn: me.getKey,
                extraKeys: newKeys,
                rootProperty: me.getRootProperty()
            });
            if (me.length) {
                source.add(me.items);
            }
            me.setSource(source);
            me.autoSource = source;
        } else {
            if (source.destroyed) {
                return;
            }
            if (source.length || me.length) {
                
                me.onCollectionRefresh(source);
            }
        }
        me.notify('filter');
    },
    
    
    applyFilters: function(filters, collection) {
        if (!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.getFilterCount() > 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.raise('Collections with sorters cannot resorted');
            }
            
            sortFn = me.getSortFn();
        }
        me.indices = null;
        me.notify('beforesort', [
            me.getSorters(false)
        ]);
        if (me.length) {
            Ext.Array.sort(me.items, sortFn);
        }
        
        
        me.notify('sort');
    },
    
    sortBy: function(sortFn) {
        return this.sortItems(sortFn);
    },
    
    findInsertionIndex: function(item, items, comparatorFn, index) {
        return Ext.Array.findInsertionIndex(item, items || this.items, comparatorFn || this.getSortFn(), index);
    },
    applySorters: function(sorters, collection) {
        if (!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(),
            sorters = me.getSorters(false),
            sorterFn = sorters ? sorters.getSortFn() : null;
        if (!grouper) {
            return sorterFn;
        }
        return function(lhs, rhs) {
            var ret = grouper.sort(lhs, rhs);
            if (!ret && sorterFn) {
                ret = sorterFn(lhs, rhs);
            }
            return ret;
        };
    },
    updateGrouper: function(grouper) {
        var me = this,
            groups = me.getGroups(),
            sorters = me.getSorters(),
            populate;
        me.onSorterChange();
        me.grouped = !!grouper;
        if (grouper) {
            if (me.getTrackGroups()) {
                if (!groups) {
                    groups = new Ext.util.GroupCollection({
                        itemRoot: me.getRootProperty(),
                        groupConfig: me.getGroupConfig()
                    });
                    groups.$groupable = me;
                    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.destroyed) {
            
            
            
            
            oldSorters.un('endupdate', 'onEndUpdateSorters', me);
        }
        if (newSorters) {
            newSorters.on({
                endupdate: 'onEndUpdateSorters',
                scope: me,
                priority: me.$endUpdatePriority
            });
            if (me.manageSorters) {
                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 && me.getAutoGroup()) || (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);
        }
    },
    
    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 = [],
            sortFn = me.getSortFn(),
            
            addItems, end, i, newItem, oldItem, newIndex;
        me.items = items;
        
        
        
        
        
        
        
        
        
        
        
        
        
        for (newIndex = 0; newIndex < newLength; newIndex = end) {
            newItem = newItems[newIndex];
            
            for (; oldIndex < oldLength; ++oldIndex) {
                
                
                
                
                if (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 (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;
    },
    updateAutoGroup: function(autoGroup) {
        var groups = this.getGroups();
        if (groups) {
            groups.setAutoGroup(autoGroup);
        }
        
        
        this.onEndUpdateSorters(this._sorters);
    },
    updateGroups: function(newGroups, oldGroups) {
        if (oldGroups) {
            this.removeObserver(oldGroups);
        }
        if (newGroups) {
            this.addObserver(newGroups);
        }
    },
    updateSource: function(newSource, oldSource) {
        var auto = this.autoSource;
        if (oldSource) {
            if (!oldSource.destroyed) {
                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.data.Range', {
    isDataRange: true,
    
    begin: 0,
    
    buffer: 0,
    
    end: 0,
    
    length: 0,
    
    
    store: null,
    constructor: function(config) {
        var me = this,
            activeRanges, store;
        Ext.apply(me, config);
        store = me.store;
        if (!(activeRanges = store.activeRanges)) {
            store.activeRanges = activeRanges = [];
        }
        activeRanges.push(me);
        me.refresh();
        if ('begin' in config) {
            me.begin = me.end = 0;
            
            me.goto(config.begin, config.end);
        }
    },
    destroy: function() {
        var me = this,
            store = me.store,
            activeRanges = store && store.activeRanges;
        Ext.destroy(me.storeListeners);
        if (activeRanges) {
            Ext.Array.remove(activeRanges, me);
        }
        me.callParent();
    },
    "goto": function(begin, end) {
        var me = this,
            buffer = me.buffer,
            task = me.task;
        me.begin = begin;
        me.end = end;
        me.length = end - begin;
        if (buffer > 0) {
            if (!task) {
                me.task = task = new Ext.util.DelayedTask(me.doGoto, me);
            }
            task.delay(buffer);
        } else {
            me.doGoto();
        }
    },
    privates: {
        lastBegin: 0,
        lastEnd: 0,
        doGoto: Ext.privateFn,
        refresh: function() {
            this.records = this.store.getData().items;
        }
    }
});


Ext.define('Ext.util.ObjectTemplate', {
    isObjectTemplate: true,
    excludeProperties: {},
    valueRe: /^[{][a-z\.]+[}]$/i,
    statics: {
        
        create: function(template, options) {
            
            if (!Ext.isObject(template)) {
                Ext.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 (!template || 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',
    _internalReadOptions: {
        recordsOnly: true,
        asRoot: true
    },
    constructor: function(association, config) {
        var me = this,
            extra = config.extra;
        Ext.apply(me, config);
        if (extra) {
            extra = Ext.apply({}, 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.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);
        }
    },
    $roleFilterId: '$associationRoleFilter',
    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 = {
                
                asynchronousLoad: false,
                model: me.cls,
                role: me,
                session: session,
                associatedEntity: from,
                disableMetaChangeEvent: true,
                pageSize: null,
                remoteFilter: true,
                trackRemoved: !session
            },
            store;
        if (isMany) {
            
            config.filters = [
                {
                    id: me.$roleFilterId,
                    property: me.inverse.field,
                    
                    value: id,
                    exactMatch: true
                }
            ];
        } else if (foreignKeyName) {
            config.filters = [
                {
                    id: me.$roleFilterId,
                    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 (!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, allowInfer) {
        
        
        
        
        var me = this,
            storeName = me.getStoreName(),
            store = inverseRecord[storeName],
            hadStore = store,
            session = inverseRecord.session,
            load = options && options.reload,
            source = inverseRecord.$source,
            isComplete = false,
            phantom = false,
            hadSourceStore, args, i, len, raw, rec, sourceStore, hadRecords, isLoading;
        if (!store) {
            if (session) {
                
                
                
                if (source) {
                    phantom = source.phantom;
                }
                if (!records && source) {
                    sourceStore = source[storeName];
                    if (sourceStore && !sourceStore.isLoading()) {
                        records = [];
                        raw = sourceStore.getData().items;
                        for (i = 0 , len = raw.length; i < len; ++i) {
                            rec = raw[i];
                            records.push(session.getRecord(rec.self, rec.id));
                        }
                        isComplete = !!sourceStore.complete;
                        hadSourceStore = true;
                    }
                }
                if (!hadSourceStore) {
                    
                    hadRecords = !!records;
                    records = me.findRecords(session, inverseRecord, records, allowInfer);
                    if (!hadRecords && (!records || !records.length)) {
                        records = null;
                    }
                    isComplete = phantom || hadRecords;
                }
            } else {
                
                isComplete = !!records;
            }
            
            store = me.createAssociationStore(session, inverseRecord, records, isComplete || inverseRecord.phantom);
            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);
            }
        }
        isLoading = store.isLoading();
        if (load) {
            if (!isLoading) {
                store.load();
            }
        } else if (hadStore && records && !isLoading) {
            store.loadData(records);
        }
        return store;
    },
    
    getAssociatedItem: function(rec) {
        var key = this.isMany ? this.getStoreName() : this.getInstanceName();
        return rec[key] || null;
    },
    onDrop: Ext.emptyFn,
    onIdChanged: 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));
    },
    getOldInstanceName: function() {
        return this.oldInstanceName || (this.oldInstanceName = '$old' + this.getInstanceName());
    },
    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 reader = this.constructReader(fromReader),
            root = reader.getRoot(data);
        if (root) {
            return reader.readRecords(root, readOptions, this._internalReadOptions);
        }
    },
    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;
                rightRecord = null;
            }
        } 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(),
            
            current = leftRecord[instanceName],
            inverse = me.inverse,
            inverseSetter = inverse.setterName,
            
            session = leftRecord.session,
            modified, oldInstanceName;
        if (rightRecord && rightRecord.isEntity) {
            if (current !== rightRecord) {
                oldInstanceName = me.getOldInstanceName();
                leftRecord[oldInstanceName] = current;
                leftRecord[instanceName] = rightRecord;
                if (current && current.isEntity) {
                    current[inverse.getInstanceName()] = undefined;
                }
                if (foreignKey) {
                    leftRecord.set(foreignKey, rightRecord.getId());
                }
                delete leftRecord[oldInstanceName];
                leftRecord.onAssociatedRecordSet(rightRecord, me);
                if (inverseSetter) {
                    
                    
                    
                    rightRecord[inverseSetter](leftRecord);
                }
            }
        } else {
            
            
            
            if (!foreignKey) {
                Ext.raise('No foreignKey specified for "' + me.association.left.role + '" by ' + leftRecord.$className);
            }
            
            modified = (leftRecord.changingKey && !inverse.isMany) || leftRecord.set(foreignKey, rightRecord);
            
            if (modified && current && current.isEntity && !current.isEqual(current.getId(), rightRecord)) {
                
                
                leftRecord[instanceName] = undefined;
                if (!inverse.isMany) {
                    current[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', {
    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;
            }
        },
        onIdChanged: function(rightRecord, oldId, newId) {
            var leftRecord = this.getAssociatedItem(rightRecord),
                fieldName = this.association.getFieldName();
            if (!rightRecord.session && leftRecord && fieldName) {
                leftRecord.set(fieldName, newId);
            }
        },
        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);
                }
                rightRecord.onAssociatedRecordSet(leftRecord, this);
            }
            return ret;
        },
        read: function(rightRecord, node, fromReader, readOptions) {
            var me = this,
                leftRecords = me.callParent([
                    rightRecord,
                    node,
                    fromReader,
                    readOptions
                ]),
                leftRecord;
            if (leftRecords) {
                leftRecord = leftRecords[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 && field) {
                    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 = leftRecord[me.getOldInstanceName()] || 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,
                rightRecords = me.callParent([
                    leftRecord,
                    node,
                    fromReader,
                    readOptions
                ]),
                rightRecord, field, fieldName, session, refs, id, oldId, setKey, data;
            if (rightRecords) {
                rightRecord = rightRecords[0];
                field = me.association.field;
                if (field) {
                    fieldName = field.name;
                }
                session = leftRecord.session;
                data = leftRecord.data;
                if (rightRecord) {
                    if (session) {
                        refs = session.getRefs(rightRecord, this.inverse, true);
                        
                        
                        setKey = (refs && refs[leftRecord.id]) || (data[fieldName] === undefined);
                    } else {
                        setKey = true;
                    }
                    if (setKey) {
                        
                        
                        if (field) {
                            oldId = data[fieldName];
                            id = rightRecord.id;
                            if (oldId !== id) {
                                data[fieldName] = id;
                                if (session) {
                                    session.updateReference(leftRecord, field, id, oldId);
                                }
                            }
                        }
                        rightRecord[me.inverse.getInstanceName()] = leftRecord;
                        leftRecord[me.getInstanceName()] = rightRecord;
                    }
                    
                    delete data[me.role];
                }
            }
        }
    })
});


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();
                    }
                }
            }
        },
        onIdChanged: function(rightRecord, oldId, newId) {
            var fieldName = this.association.getFieldName(),
                store = this.getAssociatedItem(rightRecord),
                leftRecords, i, len, filter;
            if (store) {
                filter = store.getFilters().get(this.$roleFilterId);
                if (filter) {
                    filter.setValue(newId);
                }
                
                
                if (!rightRecord.session && fieldName) {
                    leftRecords = store.getDataSource().items;
                    for (i = 0 , len = leftRecords.length; i < len; ++i) {
                        leftRecords[i].set(fieldName, newId);
                    }
                }
            }
        },
        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, allowInfer) {
            var ret = leftRecords,
                refs = session.getRefs(rightRecord, this, true),
                field = this.association.field,
                fieldName, leftRecord, id, i, len, seen;
            if (field && (refs || allowInfer)) {
                fieldName = field.name;
                ret = [];
                if (leftRecords) {
                    seen = {};
                    
                    
                    
                    
                    for (i = 0 , len = leftRecords.length; i < len; ++i) {
                        leftRecord = leftRecords[i];
                        id = leftRecord.id;
                        if (refs && refs[id]) {
                            ret.push(leftRecord);
                        } else if (allowInfer && leftRecord.data[fieldName] === undefined) {
                            ret.push(leftRecord);
                            leftRecord.data[fieldName] = rightRecord.id;
                            session.updateReference(leftRecord, field, rightRecord.id, undefined);
                        }
                        seen[id] = true;
                    }
                }
                
                if (refs) {
                    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, true);
            }
            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) {
                
                return me.getAssociatedStore(this, options, scope, leftRecords, true);
            };
        },
        createSetter: null,
        
        onAddToMany: function(store, leftRecords) {
            var rightRecord = store.getAssociatedEntity();
            if (this.association.field) {
                this.syncFK(leftRecords, rightRecord, false);
            } else {
                this.setInstances(rightRecord, leftRecords);
            }
        },
        onLoadMany: function(rightRecord, leftRecords, session) {
            this.setInstances(rightRecord, leftRecords, session);
        },
        onRemoveFromMany: function(store, leftRecords) {
            if (this.association.field) {
                this.syncFK(leftRecords, store.getAssociatedEntity(), true);
            } else {
                this.setInstances(null, leftRecords);
            }
        },
        read: function(rightRecord, node, fromReader, readOptions) {
            var me = this,
                
                
                instanceName = me.inverse.getInstanceName(),
                leftRecords = me.callParent([
                    rightRecord,
                    node,
                    fromReader,
                    readOptions
                ]),
                store, len, i;
            if (leftRecords) {
                
                store = rightRecord[me.getterName](null, null, leftRecords);
                
                delete rightRecord.data[me.role];
                leftRecords = store.getData().items;
                for (i = 0 , len = leftRecords.length; i < len; ++i) {
                    leftRecords[i][instanceName] = rightRecord;
                }
            }
        },
        setInstances: function(rightRecord, leftRecords, session) {
            var instanceName = this.inverse.getInstanceName(),
                id = rightRecord ? rightRecord.getId() : null,
                field = this.association.field,
                len = leftRecords.length,
                i, leftRecord, oldId, data, name;
            for (i = 0; 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);
                        }
                    }
                }
            }
        },
        syncFK: function(leftRecords, rightRecord, clearing) {
            
            
            
            
            var foreignKeyName = this.association.getFieldName(),
                inverse = this.inverse,
                setter = inverse.setterName,
                
                instanceName = inverse.getInstanceName(),
                i = leftRecords.length,
                id = rightRecord.getId(),
                different, leftRecord, val;
            while (i-- > 0) {
                leftRecord = leftRecords[i];
                different = !leftRecord.isEqual(id, leftRecord.get(foreignKeyName));
                val = clearing ? null : rightRecord;
                if (different !== clearing) {
                    
                    
                    
                    
                    
                    
                    
                    
                    leftRecord.changingKey = true;
                    leftRecord[setter](val);
                    leftRecord.changingKey = false;
                } else {
                    
                    leftRecord[instanceName] = val;
                }
            }
        }
    }),
    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;
            if (field) {
                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, 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 = joined.length - 1; i >= 0; 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 rightRecords = this.callParent([
                    leftRecord,
                    node,
                    fromReader,
                    readOptions
                ]),
                rightRecord;
            if (rightRecords) {
                rightRecord = rightRecords[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;
        },
        onIdChanged: function(rightRecord, oldId, newId) {
            var store = this.getAssociatedItem(rightRecord);
            if (store) {
                store.getFilters().get(this.$roleFilterId).setValue(newId);
            }
        },
        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) {
                
                return me.getAssociatedStore(this, options, scope, leftRecords, false);
            };
        },
        
        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,
                leftRecords = me.callParent([
                    rightRecord,
                    node,
                    fromReader,
                    readOptions
                ]);
            if (leftRecords) {
                
                rightRecord[me.getterName](null, null, leftRecords);
                
                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
    ],
    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
    ],
    alias: 'schema.default',
    
    aliasPrefix: 'schema.',
    isSchema: true,
    
    type: 'default',
    statics: {
        
        instances: {},
        
        
        clearInstance: function(id) {
            var schema = this.instances[id];
            delete this.instances[id];
            if (schema) {
                schema.clear(true);
                schema.destroy();
            }
        },
        
        
        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.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.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.raise('Ambiguous entity name "' + entity + '". Defined by schema "' + ret.schema.type + '" and "' + name + '"');
                                }
                                ret = match;
                            }
                        }
                    }
                    if (!ret) {
                        Ext.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, dupeCheck) {
        var me = this,
            namer = me.getNamer(),
            entities = me.entities,
            associations = me.associations,
            entityName = entityType.entityName,
            association = descr.association,
            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 (!referenceField || descr.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 (dupeCheck && association in associations) {
            if (dupeCheck(associations[association], association, leftRole, rightRole) !== false) {
                return;
            }
        }
        
        if (association in associations) {
            Ext.raise('Duplicate association: "' + association + '" declared by ' + entityName + (referenceField ? ('.' + referenceField.name) : '') + ' (collides with ' + associations[association].definedBy.entityName + ')');
        }
        if (referenceField && referenceField.definedBy === entities[rightType]) {
            Ext.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,
            left: {
                cls: entityType,
                type: entityName,
                role: leftRole,
                extra: left
            },
            right: {
                cls: entry.cls,
                type: rightType,
                role: rightRole,
                extra: descr
            },
            meta: 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.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.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);
            }
            me.addReference(entityType, referenceField, descr, referenceField.unique);
        },
        addBelongsTo: function(entityType, assoc) {
            this.addKeylessSingle(entityType, assoc, false);
        },
        addHasOne: function(entityType, assoc) {
            this.addKeylessSingle(entityType, assoc, true);
        },
        addKeylessSingle: function(entityType, assoc, unique) {
            var foreignKey, referenceField;
            assoc = Ext.apply({}, this.checkLegacyAssociation(entityType, assoc));
            assoc.type = this.getEntityName(assoc.child || assoc.parent || assoc.type);
            foreignKey = assoc.foreignKey || (assoc.type.toLowerCase() + '_id');
            referenceField = entityType.getField(foreignKey);
            assoc.fromSingle = true;
            if (referenceField) {
                referenceField.$reference = assoc;
                referenceField.unique = true;
                assoc.legacy = true;
                
                Ext.log.warn('Using foreignKey is deprecated, use a keyed association. See Ext.data.field.Field.reference');
            }
            
            this.addReference(entityType, referenceField, assoc, unique);
        },
        addHasMany: function(entityType, assoc) {
            var me = this,
                entities = me.entities,
                pending = me.pending,
                cls, name, referenceField, target, foreignKey, inverseOptions, child, declaredInverse;
            assoc = Ext.apply({}, this.checkLegacyAssociation(entityType, assoc));
            assoc.type = this.getEntityName(assoc.child || assoc.parent || assoc.type);
            name = assoc.type;
            target = entities[name];
            cls = target && target.cls;
            if (cls) {
                name = entityType.entityName;
                foreignKey = assoc.foreignKey || (name.toLowerCase() + '_id');
                delete assoc.foreignKey;
                
                
                declaredInverse = Ext.apply({}, assoc.inverse);
                delete assoc.inverse;
                inverseOptions = Ext.apply({}, assoc);
                delete inverseOptions.type;
                assoc = Ext.apply({
                    type: name,
                    inverse: inverseOptions
                }, declaredInverse);
                child = inverseOptions.child;
                if (child) {
                    delete inverseOptions.child;
                    assoc.parent = name;
                }
                referenceField = cls.getField(foreignKey);
                if (referenceField) {
                    referenceField.$reference = assoc;
                    assoc.legacy = true;
                    
                    Ext.log.warn('Using foreignKey is deprecated, use a keyed association. See Ext.data.field.Field.reference');
                }
                
                
                me.addReference(cls, referenceField, assoc, false, 
                function(association, name, leftRole, rightRole) {
                    
                    var result = !!association.meta.fromSingle && cls === association.left.cls,
                        l, r;
                    if (result) {
                        l = cls.entityName;
                        r = entityType.entityName;
                        Ext.raise('hasMany ("' + r + '") and belongsTo ("' + l + '") should not be used in conjunction to declare a relationship. Use only one.');
                    }
                    return result;
                });
            } else 
            {
                
                if (!pending[name]) {
                    pending[name] = [];
                }
                pending[name].push([
                    entityType,
                    assoc
                ]);
            }
        },
        checkLegacyAssociation: function(entityType, assoc) {
            if (Ext.isString(assoc)) {
                assoc = {
                    type: assoc
                };
            } else {
                assoc = Ext.apply({}, assoc);
            }
            if (assoc.model) {
                assoc.type = assoc.model;
                
                delete assoc.model;
            }
            var name = assoc.associatedName || assoc.name;
            if (name) {
                
                delete assoc.associatedName;
                delete assoc.name;
                assoc.role = name;
            }
            return assoc;
        },
        afterKeylessAssociations: 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.addHasMany.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.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.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
    ],
    factoryConfig: {
        defaultType: 'store',
        type: 'store'
    },
    $configPrefixed: false,
    $configStrict: false,
    config: {
        
        filters: null,
        
        autoDestroy: undefined,
        
        storeId: null,
        
        statefulFilters: false,
        
        sorters: null,
        
        remoteSort: {
            lazy: true,
            $value: false
        },
        
        remoteFilter: {
            lazy: true,
            $value: false
        },
        
        groupField: undefined,
        
        groupDir: 'ASC',
        
        grouper: null,
        
        pageSize: 25,
        
        autoSort: null,
        
        reloadOnClearSorters: false
    },
    
    currentPage: 1,
    
    loading: false,
    
    isStore: true,
    
    updating: 0,
    
    constructor: function(config) {
        var me = this,
            storeId;
        
        me.callParent([
            config
        ]);
        
        
        
        
        
        
        
        
        
        
        
        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);
        }
    },
    
    createActiveRange: function(config) {
        var range = Ext.apply({
                store: this
            }, config);
        return new Ext.data.Range(range);
    },
    
    syncActiveRanges: function() {
        var activeRanges = this.activeRanges,
            len = activeRanges && activeRanges.length,
            i;
        for (i = 0; i < len; i++) {
            activeRanges[i].refresh();
        }
    },
    
    getCount: function() {
        var data = this.getData();
        
        
        return data ? data.getCount() : 0;
    },
    
    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(fieldName, value, startIndex) {
        return this.getData().findIndexBy(function(rec) {
            return rec.isEqual(rec.get(fieldName), value);
        }, this, startIndex);
    },
    
    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;
    },
    
    getFilters: function(autoCreate) {
        var result = this.callParent();
        if (!result && autoCreate !== false) {
            this.setFilters([]);
            result = this.callParent();
        }
        return result;
    },
    applyFilters: function(filters, filtersCollection) {
        var created;
        if (!filtersCollection) {
            filtersCollection = this.createFiltersCollection();
            created = true;
        }
        filtersCollection.add(filters);
        if (created) {
            this.onRemoteFilterSet(filtersCollection, this.getRemoteFilter());
        }
        return filtersCollection;
    },
    
    getSorters: function(autoCreate) {
        var result = this.callParent();
        if (!result && autoCreate !== false) {
            
            this.setSorters([]);
            result = this.callParent();
        }
        return result;
    },
    applySorters: function(sorters, sortersCollection) {
        var created;
        if (!sortersCollection) {
            sortersCollection = this.createSortersCollection();
            created = true;
        }
        sortersCollection.add(sorters);
        if (created) {
            this.onRemoteSortSet(sortersCollection, this.getRemoteSort());
        }
        return sortersCollection;
    },
    
    filter: function(filters, value, suppressEvent) {
        if (Ext.isString(filters)) {
            filters = {
                property: filters,
                value: value
            };
        }
        this.suppressNextFilter = !!suppressEvent;
        this.getFilters().add(filters);
        this.suppressNextFilter = false;
    },
    
    removeFilter: function(toRemove, suppressEvent) {
        var me = this,
            filters = me.getFilters();
        me.suppressNextFilter = !!suppressEvent;
        if (toRemove instanceof Ext.util.Filter) {
            filters.remove(toRemove);
        } else {
            filters.removeByKey(toRemove);
        }
        me.suppressNextFilter = false;
    },
    updateAutoSort: function(autoSort) {
        
        this.getData().setAutoSort(autoSort);
    },
    updateRemoteSort: function(remoteSort) {
        
        
        this.onRemoteSortSet(this.getSorters(false), remoteSort);
    },
    updateRemoteFilter: function(remoteFilter) {
        this.onRemoteFilterSet(this.getFilters(false), remoteFilter);
    },
    
    addFilter: function(filters, suppressEvent) {
        this.suppressNextFilter = !!suppressEvent;
        this.getFilters().add(filters);
        this.suppressNextFilter = false;
    },
    
    filterBy: function(fn, scope) {
        this.getFilters().add({
            filterFn: fn,
            scope: scope || this
        });
    },
    
    clearFilter: function(suppressEvent) {
        var me = this,
            filters = me.getFilters(false);
        if (!filters || filters.getCount() === 0) {
            return;
        }
        me.suppressNextFilter = !!suppressEvent;
        filters.removeAll();
        me.suppressNextFilter = false;
    },
    
    isFiltered: function() {
        return this.getFilters().getCount() > 0;
    },
    
    isSorted: function() {
        var sorters = this.getSorters(false);
        return !!(sorters && sorters.length > 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.hasListeners.beginupdate) {
            
            this.fireEvent('beginupdate');
        }
    },
    
    endUpdate: function() {
        if (this.updating && !--this.updating) {
            if (this.hasListeners.endupdate) {
                this.fireEvent('endupdate');
            }
            this.onEndUpdate();
        }
    },
    
    getState: function() {
        var me = this,
            sorters = [],
            filters = me.getFilters(),
            grouper = me.getGrouper(),
            filterState, hasState, 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,
            stateSorters = state.sorters,
            stateFilters = state.filters,
            stateGrouper = state.grouper;
        if (stateSorters) {
            me.getSorters().replaceAll(stateSorters);
        }
        if (stateFilters) {
            
            me.saveStatefulFilters = true;
            me.getFilters().replaceAll(stateFilters);
        }
        if (stateGrouper) {
            me.setGrouper(stateGrouper);
        }
    },
    
    
    hasPendingLoad: Ext.emptyFn,
    
    isLoaded: Ext.emptyFn,
    
    isLoading: Ext.emptyFn,
    destroy: function() {
        var me = this;
        if (me.hasListeners.beforedestroy) {
            me.fireEvent('beforedestroy', me);
        }
        me.destroying = true;
        if (me.getStoreId()) {
            Ext.data.StoreManager.unregister(me);
        }
        me.doDestroy();
        if (me.hasListeners.destroy) {
            me.fireEvent('destroy', me);
        }
        me.destroying = false;
        
        me.callParent();
    },
    
    doDestroy: Ext.emptyFn,
    
    sort: function(field, direction, mode) {
        var me = this;
        if (arguments.length === 0) {
            if (me.getRemoteSort()) {
                me.load();
            } else {
                me.forceLocalSort();
            }
        } else {
            me.getSorters().addSort(field, direction, mode);
        }
    },
    
    
    onBeforeCollectionSort: function(store, sorters) {
        if (sorters) {
            this.fireEvent('beforesort', this, sorters.getRange());
        }
    },
    onSorterEndUpdate: function() {
        var me = this,
            fireSort = true,
            sorters = me.getSorters(false),
            sorterCount;
        
        
        if (me.settingGroups || !sorters) {
            return;
        }
        sorters = sorters.getRange();
        sorterCount = sorters.length;
        if (me.getRemoteSort()) {
            
            
            
            
            if (sorters.length || me.getReloadOnClearSorters()) {
                
                fireSort = false;
                me.load({
                    callback: function() {
                        me.fireEvent('sort', me, sorters);
                    }
                });
            }
        } else if (sorterCount) {
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        if (fireSort) {
            
            me.fireEvent('sort', me, sorters);
        }
    },
    onFilterEndUpdate: function() {
        var me = this,
            suppressNext = me.suppressNextFilter,
            filters = me.getFilters(false);
        
        if (!filters) {
            return;
        }
        if (me.getRemoteFilter()) {
            
            me.getFilters().each(function(filter) {
                if (filter.getInitialConfig().filterFn) {
                    Ext.raise('Unable to use a filtering function in conjunction with remote filtering.');
                }
            });
            
            me.currentPage = 1;
            if (!suppressNext) {
                me.load();
            }
        } 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,
            sorters = me.getSorters(false),
            change = grouper || (sorters && sorters.length),
            data = me.getData();
        if (grouper && typeof grouper === 'string') {
            grouper = {
                property: grouper,
                direction: direction || me.getGroupDir()
            };
        }
        me.settingGroups = true;
        
        
        
        if (grouper === data.getGrouper()) {
            data.updateGrouper(grouper);
        } else {
            data.setGrouper(grouper);
        }
        delete me.settingGroups;
        if (change) {
            if (me.getRemoteSort()) {
                if (!me.isInitializing) {
                    me.load({
                        scope: me,
                        callback: me.fireGroupChange
                    });
                }
            } else {
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
                me.fireGroupChange();
            }
        } else 
        
        {
            me.fireGroupChange();
        }
    },
    fireGroupChange: function() {
        if (!this.destroyed) {
            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: {
        _metaProperties: {
            count: 'getCount',
            first: 'first',
            last: 'last',
            loading: 'hasPendingLoad',
            totalCount: 'getTotalCount'
        },
        interpret: function(name) {
            var me = this,
                accessor = me._metaProperties[name];
            return accessor && me[accessor]();
        },
        
        loadsSynchronously: Ext.privateFn,
        onRemoteFilterSet: function(filters, remoteFilter) {
            if (filters) {
                filters[remoteFilter ? 'on' : 'un']('endupdate', 'onFilterEndUpdate', this);
            }
        },
        
        
        
        
        
        
        onRemoteSortSet: function(sorters, remoteSort) {
            var me = this,
                data;
            if (sorters) {
                sorters[remoteSort ? 'on' : 'un']('endupdate', 'onSorterEndUpdate', me);
                data = me.getData();
                if (data) {
                    data[remoteSort ? 'un' : 'on']('beforesort', 'onBeforeCollectionSort', me);
                }
            }
        }
    },
    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',
    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, record);
                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,
    idPrefix: 'ext-operation-',
    
    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;
        }
        
        this._internalId = Ext.id(this, this.idPrefix);
    },
    getAction: function() {
        return this.action;
    },
    
    execute: function() {
        var me = this,
            request;
        delete me.error;
        delete me.success;
        me.complete = me.exception = false;
        me.setStarted();
        me.request = request = me.doExecute();
        if (request) {
            request.setOperation(me);
        }
        return request;
    },
    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() {
        var me = this,
            proxy = me.getProxy();
        me.complete = true;
        me.running = false;
        me.triggerCallbacks();
        
        if (proxy && !proxy.destroyed) {
            proxy.completeOperation(me);
        }
    },
    
    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', function() {
    var me;
    return {
        singleton: true,
        constructor: function() {
            me = this;
        },
        
        none: Ext.identityFn,
        
        stripCommasRe: /,/g,
        
        stripTagsRE: /<\/?[^>]+>/gi,
        
        asText: function(s) {
            
            return (s != null) ? String(s).replace(me.stripTagsRE, '') : '\x00';
        },
        
        asUCText: function(s) {
            
            return (s != null) ? String(s).toUpperCase().replace(me.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(me.stripCommasRe, ''));
            return isNaN(val) ? 0 : val;
        },
        
        asInt: function(s) {
            var val = parseInt(String(s).replace(me.stripCommasRe, ''), 10);
            return isNaN(val) ? 0 : val;
        }
    };
});


Ext.define('Ext.data.validator.Validator', {
    mixins: [
        Ext.mixin.Factoryable
    ],
    alias: 'data.validator.base',
    
    isValidator: true,
    factoryConfig: {
        cacheable: true
    },
    
    type: 'base',
    
    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(Validator) {
    Ext.Factory.validator = Ext.Factory.dataValidator;
});


Ext.define('Ext.data.field.Field', {
    mixins: [
        Ext.mixin.Factoryable
    ],
    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,
    
    
    summary: null,
    
    summaryField: '',
    
    
    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 = Ext.Function.toCode(calculate);
                    
                    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) {
            me.calculated = calculated = me.convert.length > 1;
            me.evil = calculated && !depends;
        }
        if (me.persist === null) {
            me.persist = !calculate;
        }
        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;
    },
    constructValidators: function(validators) {
        if (validators) {
            if (!(validators instanceof Array)) {
                validators = [
                    validators
                ];
            }
            var length = validators.length,
                all = this._validators,
                
                i, item, validator, presence;
            for (i = 0; i < length; ++i) {
                item = validators[i];
                if (item.fn) {
                    item = item.fn;
                }
                validator = Ext.Factory.dataValidator(item);
                if (!validator.isPresence) {
                    all.push(validator);
                } else {
                    presence = validator;
                }
            }
            if (presence) {
                this.presence = [
                    presence
                ];
            }
        }
    },
    
    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, record) {
        var me = this,
            validators = me.getValidators(),
            result, presence;
        presence = this.presence;
        if (presence && (value == null || value === '')) {
            result = me.validateGroup(presence, value, separator, errors, record);
            if (result !== true) {
                return result;
            }
        }
        return me.validateGroup(validators, value, separator, errors, record);
    },
    validateGroup: function(validators, value, separator, errors, record) {
        var ret = '',
            validator, length, i, result;
        for (i = 0 , length = validators.length; i < length; ++i) {
            validator = validators[i];
            result = validator.validate(value, record);
            if (result !== true) {
                result = result || this.defaultInvalidMessage;
                if (errors) {
                    if (errors.isMixedCollection) {
                        errors.add(this.name, result);
                    } else if (errors.isCollection) {
                        errors.add(result);
                    } else {
                        errors.push(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;
    },
    
    getSummary: function() {
        var me = this,
            doneSummary = me.doneSummary,
            summary = me.summary;
        if (!doneSummary) {
            me.doneSummary = true;
            if (summary) {
                me.summary = summary = Ext.Factory.dataSummary(summary);
            }
        }
        return summary || null;
    },
    
    getType: function() {
        return 'auto';
    },
    privates: {
        getValidators: function() {
            var me = this,
                validators = me._validators;
            if (!validators) {
                me._validators = validators = [];
                me.constructValidators(me.validators);
                me.constructValidators(me.modelValidators);
                me.constructValidators(me.instanceValidators);
            }
            return validators;
        }
    },
    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, this.useStrict);
        }
        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 == 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,
            id;
        me.initConfig(config);
        id = me.getId();
        if (id) {
            Ext.data.identifier.Generator.all[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 && 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',
    
    isEntity: true,
    
    isModel: true,
    
    validIdRe: null,
    erasing: false,
    loadOperation: null,
    loadCount: 0,
    observableType: 'record',
    
    crudState: 'R',
    
    crudStateWas: null,
    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 = me.data = data || (data = {});
        me.internalId = internalId = modelIdentifier.generate();
        
        var dataId = data[idProperty];
        if (session && !session.isSession) {
            Ext.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.isSummaryModel) {
            if (!(me.id = id = data[idProperty]) && id !== 0) {
                
                if (dataId) {
                    Ext.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;
                me.crudState = 'C';
            }
            if (session) {
                session.add(me);
            }
            
            if (me.phantom) {
                me.crudStateWas = 'C';
            }
        }
        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',
    
    manyToMany: null,
    
    identifier: null,
    
    
    
    
    
    
    
    previousValues: undefined,
    
    
    
    proxy: undefined,
    
    
    schema: 'default',
    
    summary: null,
    
    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)
            };
        }
    },
    
    calculateSummary: function(records) {
        var fields = this.getFields(),
            len = fields.length,
            recLen = records.length,
            i, result, summary, prop, name, field;
        for (i = 0; i < len; ++i) {
            field = fields[i];
            summary = field.getSummary();
            if (summary) {
                result = result || {};
                name = field.name;
                prop = field.summaryField || name;
                result[name] = summary.calculate(records, prop, 'data', 0, recLen);
            }
        }
        if (result) {
            this.set(result, this._commitOptions);
        }
    },
    
    cancelEdit: function() {
        var me = this,
            editMemento = me.editMemento,
            validation = me.validation;
        if (editMemento) {
            me.editing = false;
            
            Ext.apply(me, editMemento);
            me.editMemento = null;
            if (validation && validation.syncGeneration !== me.generation) {
                validation.syncGeneration = 0;
            }
        }
    },
    
    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, options) {
        this.set(this.idProperty, id, options);
    },
    
    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,
        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,
            dirtyRank = 0,
            associations = me.associations,
            currentValue, field, idChanged, key, name, oldId, comparator, dep, dependents, i, numFields, newId, rankedFields, reference, value, values, roleName;
        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.onIdChanged(newId, oldId);
            me.callJoined('onIdChanged', [
                oldId,
                newId
            ]);
            if (associations) {
                for (roleName in associations) {
                    associations[roleName].onIdChanged(me, 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;
        
        if (me.erased) {
            Ext.raise('Cannot reject once a record has been erased.');
        }
        
        if (modified) {
            me.set(modified, me._rejectOptions);
        }
        me.dropped = false;
        me.clearState();
        if (!silent) {
            me.callJoined('afterReject');
        }
    },
    
    commit: function(silent, modifiedFieldNames) {
        var me = this,
            versionProperty = me.versionProperty,
            data = me.data,
            erased;
        me.clearState();
        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(owner) {
        var me = this,
            joined = me.joined;
        
        if (!joined) {
            joined = me.joined = [
                owner
            ];
        } else if (!joined.length) {
            joined[0] = owner;
        } else {
            
            Ext.Array.include(joined, owner);
        }
        if (owner.isStore && !me.store) {
            
            me.store = owner;
        }
    },
    
    unjoin: function(owner) {
        var me = this,
            joined = me.joined,
            
            
            len = joined && joined.length,
            store = me.store,
            i;
        if (owner === me.session) {
            me.session = null;
        } else {
            if (len === 1 && joined[0] === owner) {
                joined.length = 0;
            } else if (len) {
                Ext.Array.remove(joined, owner);
            }
            if (store === owner) {
                store = null;
                if (joined) {
                    for (i = 0 , len = joined.length; i < len; ++i) {
                        owner = joined[i];
                        if (owner.isStore) {
                            store = owner;
                            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() {
        return this.getValidation().isValid();
    },
    
    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();
        me.erasing = false;
        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, options) {
        var me = this,
            associations = me.associations,
            deep, i, item, items, itemData, length, record, role, roleName, opts, clear, associated;
        result = result || {};
        me.$gathering = 1;
        if (options) {
            options = Ext.apply({}, options);
        }
        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;
                    if (options) {
                        associated = options.associated;
                        if (associated === undefined) {
                            options.associated = deep;
                            clear = true;
                        } else if (!deep) {
                            options.associated = false;
                            clear = true;
                        }
                        opts = options;
                    } else {
                        opts = deep ? me._getAssociatedOptions : me._getNotAssociatedOptions;
                    }
                    itemData.push(record.getData(opts));
                    if (clear) {
                        options.associated = associated;
                        clear = false;
                    }
                    delete record.$gathering;
                }
                delete item.$gathering;
            } else {
                opts = options || me._getAssociatedOptions;
                if (options && options.associated === undefined) {
                    opts.associated = true;
                }
                itemData = item.getData(opts);
            }
            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) {
            if (typeof associated === 'object') {
                me.getNestedData(opts, ret);
            } else {
                me.getAssociatedData(ret, opts);
            }
        }
        return ret;
    },
    getNestedData: function(options, result) {
        var me = this,
            associations = me.associations,
            graph = options.associated,
            i, item, items, itemData, length, record, role, roleName, opts;
        result = result || {};
        
        
        
        
        
        
        
        
        
        
        
        
        for (roleName in graph) {
            role = associations[roleName];
            opts = graph[roleName];
            if (opts === true) {
                delete options.associated;
            } else {
                options.associated = opts;
            }
            item = role.getAssociatedItem(me);
            if (item.isStore) {
                items = item.getData().items;
                
                length = items.length;
                itemData = [];
                for (i = 0; i < length; ++i) {
                    record = items[i];
                    itemData.push(record.getData(options));
                }
            } else {
                itemData = item.getData(options);
            }
            result[roleName] = itemData;
        }
        options.associated = graph;
        
        return result;
    },
    
    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.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;
            ++me.loadCount;
            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;
    },
    
    mergeData: function(data) {
        if (!this.dirty) {
            this.set(data, this._commitOptions);
        }
    },
    
    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;
    },
    
    
    statics: {
        
        defaultProxy: 'memory'
    },
    inheritableStatics: {
        
        _associatedReadOptions: {
            recordsOnly: true,
            asRoot: true
        },
        
        loadData: function(data, session) {
            var rec;
            if (data) {
                rec = this.getProxy().getReader().readRecords([
                    data
                ], session ? {
                    recordCreator: session.recordCreator
                } : undefined, this._associatedReadOptions)[0];
            } else {
                rec = new this(data, session);
            }
            return rec;
        },
        
        getSummaryModel: function() {
            var me = this,
                proto = me.prototype,
                summaryModel = me.summaryModel;
            if (!summaryModel) {
                summaryModel = Ext.define(null, {
                    extend: me,
                    fields: proto.summaryFields || [],
                    isSummaryModel: true
                });
                summaryModel.isSummaryModel = true;
                me.summaryModel = proto.summaryModel = summaryModel;
            }
            return summaryModel || null;
        },
        
        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, cleared;
            if (removeFields === true) {
                fields.length = 0;
                me.fieldsMap = fieldsMap = {};
                me.fieldOrdinals = ordinals = {};
                cleared = true;
            } 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;
                }
            }
            
            
            
            if (!cleared) {
                for (i = 0 , len = fields.length; i < len; ++i) {
                    fields[i].rank = null;
                }
            }
            
            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,
                defaultProxy = me.defaultProxy,
                defaults;
            if (!proxy) {
                
                proxy = me.proxyConfig;
                if (!proxy && defaultProxy) {
                    proxy = defaultProxy;
                }
                if (!proxy || !proxy.isProxy) {
                    if (typeof proxy === 'string') {
                        proxy = {
                            type: proxy
                        };
                    }
                    
                    
                    defaults = Ext.merge(me.schema.constructProxy(me), proxy);
                    if (proxy && proxy.type) {
                        proxy = proxy.schema === false ? proxy : defaults;
                    } else {
                        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;
            if (session) {
                rec = session.peekRecord(this, id);
            }
            if (!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
        },
        _getNotAssociatedOptions: {
            associated: false
        },
        _metaProperties: {
            dirty: 'isDirty',
            phantom: 'isPhantom',
            valid: 'isValid'
        },
        
        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,
                state = me.dropped ? 'D' : (me.phantom ? 'C' : (me.dirty ? 'U' : 'R')),
                i, len, fn, item;
            me.crudState = state;
            if (joined || session) {
                if (args) {
                    args.unshift(me);
                } else {
                    args = [
                        me
                    ];
                }
                fn = session && session[funcName];
                if (fn) {
                    fn.apply(session, args);
                }
                if (joined) {
                    for (i = 0 , len = joined.length; i < len; ++i) {
                        item = joined[i];
                        if (item && (fn = item[funcName])) {
                            fn.apply(item, args);
                        }
                    }
                }
            }
            me.crudStateWas = state;
        },
        
        hasPendingLoad: function() {
            return this.isLoading();
        },
        interpret: function(name) {
            var me = this,
                accessor = me._metaProperties[name];
            if (!accessor) {
                accessor = me.associations;
                
                accessor = accessor && accessor[name] && accessor[name].getterName;
            }
            if (accessor) {
                return me[accessor]();
            }
            
            return me.data[name];
        },
        
        isDirty: function() {
            
            return this.dirty;
        },
        
        isPhantom: function() {
            
            return this.phantom;
        },
        
        onAssociatedRecordSet: function(record, role) {
            this.callJoined('afterAssociatedRecordSet', [
                record,
                role
            ]);
        },
        
        onIdChanged: Ext.privateFn,
        
        setSession: function(session) {
            
            if (session) {
                if (this.session) {
                    Ext.raise('This model already belongs to a session.');
                }
                if (!this.id) {
                    Ext.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.isEqual(lhs, rhs);
                }
            }
            
            
            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.raise(cls.$className + " has circular field dependencies: " + topoStack.join(" --> "));
                }
                if (topoStack.length && field.evil) {
                    Ext.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.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, superIdFieldName, 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;
                            superIdFieldName = field.name;
                        }
                    }
                }
                
                delete data.fields;
                if (fieldDefs) {
                    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 = field;
                        }
                    }
                }
                
                
                idField = fieldsMap[idProperty];
                if (!idField) {
                    if (superIdField && superIdField.generated) {
                        ordinal = superIdField.ordinal;
                    } else {
                        ordinal = fields.length;
                    }
                    delete fieldsMap[superIdFieldName];
                    delete fieldOrdinals[superIdFieldName];
                    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[superIdFieldName];
                    delete fieldOrdinals[superIdFieldName];
                    fieldsMap[idProperty] = idDeclared;
                    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;
                }
            },
            
            
            initSummaries: function(data, cls, proto) {
                var summaryDefs = data.summary,
                    superSummaries = proto.summaryFields,
                    summaries, summaryMap, name, summary, len, i, index, field;
                if (superSummaries) {
                    summaries = [];
                    summaryMap = {};
                    for (i = 0 , len = superSummaries.length; i < len; ++i) {
                        summary = superSummaries[i];
                        summaries.push(summary);
                        summaries[summary.name] = i;
                    }
                }
                if (summaryDefs) {
                    delete data.summary;
                    summaries = summaries || [];
                    summaryMap = summaryMap || {};
                    for (name in summaryDefs) {
                        summary = summaryDefs[name];
                        if (typeof summary === 'function') {
                            summary = {
                                summary: summary
                            };
                        }
                        
                        
                        index = summaryMap[name];
                        summary = Ext.apply({
                            name: name
                        }, summary);
                        field = summary.field;
                        if (field) {
                            delete summary.field;
                            summary.summaryField = field;
                        }
                        if (index === undefined) {
                            index = summaries.length;
                            summaryMap[name] = summary;
                        }
                        summaries[index] = summary;
                    }
                }
                if (summaries) {
                    
                    for (i = 0 , len = summaries.length; i < len; ++i) {
                        if (summaries[i].name in proto.fieldsMap) {
                            Ext.raise('Cannot redefine field, use the summary property on the field.');
                        }
                    }
                    
                    
                    proto.summaryFields = summaries;
                }
            },
            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 (validator) {
                            
                            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, o;
                delete data.associations;
                delete data.belongsTo;
                delete data.hasMany;
                delete data.hasOne;
                delete data.manyToMany;
                if (matrices) {
                    schema.addMatrices(cls, matrices);
                }
                if (associations) {
                    associations = Ext.isArray(associations) ? associations : [
                        associations
                    ];
                    for (i = 0 , length = associations.length; i < length; ++i) {
                        assoc = associations[i];
                        o = Ext.apply({}, assoc);
                        delete o.type;
                        switch (assoc.type) {
                            case 'belongsTo':
                                schema.addBelongsTo(cls, o);
                                break;
                            case 'hasMany':
                                schema.addHasMany(cls, o);
                                break;
                            case 'hasOne':
                                schema.addHasOne(cls, o);
                                break;
                            
                            default:
                                Ext.raise('Invalid association type: "' + assoc.type + '"');
                        }
                    }
                }
                
                if (belongsTo) {
                    belongsTo = Ext.isArray(belongsTo) ? belongsTo : [
                        belongsTo
                    ];
                    for (i = 0 , length = belongsTo.length; i < length; ++i) {
                        schema.addBelongsTo(cls, belongsTo[i]);
                    }
                }
                if (hasMany) {
                    hasMany = Ext.isArray(hasMany) ? hasMany : [
                        hasMany
                    ];
                    for (i = 0 , length = hasMany.length; i < length; ++i) {
                        schema.addHasMany(cls, hasMany[i]);
                    }
                }
                if (hasOne) {
                    hasOne = Ext.isArray(hasOne) ? hasOne : [
                        hasOne
                    ];
                    for (i = 0 , length = hasOne.length; i < length; ++i) {
                        schema.addHasOne(cls, hasOne[i]);
                    }
                }
                schema.afterKeylessAssociations(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'
                    ],
                    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];
                    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', 'Ext', code);
                return factory(fields, Ext);
            }
        }
    }
}, 

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.raise('Anonymous entities cannot specify "associations"');
                }
                if (data.belongsTo) {
                    Ext.raise('Anonymous entities cannot specify "belongsTo"');
                }
                if (data.hasMany) {
                    Ext.raise('Anonymous entities cannot specify "hasMany"');
                }
                if (data.hasOne) {
                    Ext.raise('Anonymous entities cannot specify "hasOne"');
                }
                if (data.matrices) {
                    Ext.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);
        if (!data.isSummaryModel) {
            Model.initSummaries(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,
        
        remoteTotal: null,
        
        success: false,
        
        records: null,
        
        message: null,
        
        metadata: null,
        
        groupData: null,
        
        summaryData: null
    },
    
    constructor: function(config) {
        this.initConfig(config);
    },
    getCount: function() {
        var count = this.callParent(),
            records;
        if (!count) {
            records = this.getRecords();
            if (records) {
                count = records.length;
            }
        }
        return count;
    }
});


Ext.define('Ext.data.reader.Reader', {
    alternateClassName: [
        'Ext.data.Reader',
        'Ext.data.DataReader'
    ],
    mixins: [
        Ext.mixin.Observable,
        Ext.mixin.Factoryable
    ],
    alias: 'reader.base',
    factoryConfig: {
        defaultType: null
    },
    config: {
        
        groupRootProperty: '',
        
        implicitIncludes: true,
        
        keepRawData: null,
        
        messageProperty: '',
        
        model: null,
        
        proxy: null,
        
        readRecordsOnFailure: true,
        
        rootProperty: '',
        
        successProperty: 'success',
        
        summaryRootProperty: '',
        
        totalProperty: 'total',
        
        transform: null,
        
        typeProperty: ''
    },
    
    
    
    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();
    },
    forceBuildExtractors: function() {
        if (!this.duringInit) {
            this.buildExtractors(true);
        }
    },
    updateGroupRootProperty: function() {
        this.forceBuildExtractors();
    },
    updateMessageProperty: function() {
        this.forceBuildExtractors();
    },
    applyModel: function(model) {
        return Ext.data.schema.Schema.lookupEntity(model);
    },
    updateSuccessProperty: function() {
        this.forceBuildExtractors();
    },
    updateTotalProperty: function() {
        this.forceBuildExtractors();
    },
    applyTransform: function(transform) {
        if (transform) {
            if (Ext.isFunction(transform)) {
                transform = {
                    fn: transform
                };
            } else if (transform.charAt) {
                
                transform = {
                    fn: this[transform]
                };
            }
            return transform.fn.bind(transform.scope || this);
        }
        return transform;
    },
    
    read: function(response, readOptions) {
        var data, result, responseText;
        if (response) {
            responseText = response.responseText;
            if (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 if (responseText !== '') {
                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, internalReadOptions) {
        var me = this,
            recordsOnly = internalReadOptions && internalReadOptions.recordsOnly,
            asRoot = internalReadOptions && internalReadOptions.asRoot,
            groupData = null,
            summaryData = null,
            success, recordCount, records, root, remoteTotal, total, value, message, transform, meta, summaryOptions;
        
        
        
        meta = me.getMeta ? me.getMeta(data) : data.metaData;
        if (meta) {
            me.onMetaChange(meta);
        }
        transform = me.getTransform();
        if (transform) {
            data = transform(data);
        }
        me.buildExtractors();
        if (me.getKeepRawData()) {
            me.rawData = data;
        }
        if (me.hasListeners.rawdata) {
            me.fireEventArgs('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 (success || me.getReadRecordsOnFailure()) {
            
            
            root = (asRoot || Ext.isArray(data)) ? data : me.getRoot(data);
            if (root) {
                total = root.length;
            }
            if (me.getTotalProperty()) {
                value = parseInt(me.getTotal(data), 10);
                if (!isNaN(value)) {
                    remoteTotal = total = value;
                }
            }
            if (root) {
                records = me.extractData(root, readOptions);
                recordCount = records.length;
            }
            if (me.getGroupRootProperty()) {
                root = me.getGroupRoot(data);
                if (root) {
                    summaryOptions = {
                        includes: false,
                        model: me.getModel().getSummaryModel()
                    };
                    groupData = me.extractData(root, summaryOptions) || null;
                }
            }
            if (me.getSummaryRootProperty()) {
                root = me.getSummaryRoot(data);
                if (root) {
                    summaryOptions = summaryOptions || {
                        includes: false,
                        model: me.getModel().getSummaryModel()
                    };
                    summaryData = me.extractData(root, summaryOptions) || null;
                    
                    if (summaryData) {
                        summaryData = summaryData[0];
                    }
                }
            }
        }
        return recordsOnly ? records : new Ext.data.ResultSet({
            total: total || recordCount,
            remoteTotal: remoteTotal,
            metadata: meta,
            count: recordCount,
            records: records,
            success: success,
            message: message,
            groupData: groupData,
            summaryData: summaryData
        });
    },
    
    extractData: function(root, readOptions) {
        var me = this,
            entityType = readOptions && readOptions.model ? Ext.data.schema.Schema.lookupEntity(readOptions.model) : me.getModel(),
            schema = entityType.schema,
            includes = readOptions && 'includes' in readOptions ? readOptions.includes : schema.hasAssociations(entityType) && me.getImplicitIncludes(),
            fieldExtractorInfo = me.getFieldExtractorInfo(entityType),
            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));
                } else {
                    record = me.extractRecord(node, readOptions, entityType, includes, fieldExtractorInfo);
                }
                
                
                
                
                
                if (record.isModel && record.isNode) {
                    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);
        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(entityType) {
        var extractors = entityType.fieldExtractors,
            type, extractor;
        
        
        if (!extractors) {
            return;
        }
        type = this.$className;
        extractor = extractors[type];
        
        
        if (extractor === undefined) {
            extractors[type] = extractor = this.buildFieldExtractors(entityType);
        }
        return extractor;
    },
    buildFieldExtractors: function(entityType) {
        var fields = entityType.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, self); if (val !== undefined) { data[\'' + name + '\'] = val; }');
                extractors.push(extractor);
                ++cnt;
            }
        }
        if (buffer.length) {
            out = {
                extractors: extractors,
                fn: new Function('raw', 'data', 'extractors', 'self', 'var val;' + buffer.join('\n'))
            };
        }
        return out;
    },
    defaultRecordCreator: function(data, Model) {
        return new Model(data);
    },
    defaultRecordCreatorFromServer: 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, this);
        }
        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);
                }
            }
        }
    },
    
    getData: Ext.identityFn,
    
    getRoot: Ext.identityFn,
    
    getResponseData: function(response) {
        
        Ext.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, successProp, messageProp;
        if (force || !me.hasExtractors) {
            totalProp = me.getTotalProperty();
            successProp = me.getSuccessProperty();
            messageProp = me.getMessageProperty();
            
            if (totalProp) {
                me.getTotal = me.getAccessor(totalProp);
            }
            if (successProp) {
                me.getSuccess = me.getAccessor(successProp);
            }
            if (messageProp) {
                me.getMessage = me.getAccessor(messageProp);
            }
            me.hasExtractors = true;
            return true;
        }
    },
    getAccessor: function(prop) {
        var me = this,
            cache = me.extractorCache,
            ret, key;
        if (typeof prop === 'string') {
            key = me.getAccessorKey(prop);
            if (key) {
                ret = cache.get(key);
            }
            if (!ret) {
                ret = me.createAccessor(prop);
                if (key) {
                    cache.add(key, ret);
                }
            }
        } else {
            ret = me.createAccessor(prop);
        }
        return ret;
    },
    getAccessorKey: function(prop) {
        var className = this.$className;
        return className ? className + prop : '';
    },
    createAccessor: Ext.emptyFn,
    createFieldAccessor: Ext.emptyFn,
    destroy: function() {
        var me = this;
        me.model = me.getTotal = me.getSuccess = me.getMessage = me.rawData = null;
        
        me.onMetaChange = null;
        
        me.transform = null;
        me.callParent();
    },
    privates: {
        copyFrom: function(reader) {
            var me = this;
            reader.buildExtractors();
            me.getTotal = reader.getTotal;
            me.getSuccess = reader.getSuccess;
            me.getMessage = reader.getMessage;
            ++me.duringInit;
            me.setConfig(reader.getConfig());
            --me.duringInit;
            me.hasExtractors = true;
        },
        getGroupRoot: Ext.privateFn,
        getSummaryRoot: Ext.privateFn
    }
}, function(Cls) {
    var proto = Cls.prototype;
    Ext.apply(proto, {
        
        nullResultSet: new Ext.data.ResultSet({
            total: 0,
            count: 0,
            records: [],
            success: true,
            message: ''
        })
    });
    proto.extractorCache = new Ext.util.LruCache();
});


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'
    ],
    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);
        
        this.pendingOperations = {};
    },
    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) {
        
        
        
        
        if (this.isSynchronous) {
            reader = reader || {};
            reader.keepRawData = true;
        }
        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);
            }
        }
    },
    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), null, {
            single: true,
            priority: 1000
        });
        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);
        this.pendingOperations[operation._internalId] = operation;
        return operation;
    },
    completeOperation: function(operation) {
        delete this.pendingOperations[operation._internalId];
    },
    clone: function() {
        return new this.self(this.getInitialConfig());
    },
    destroy: function() {
        var ops = this.pendingOperations,
            opId, op;
        for (opId in ops) {
            op = ops[opId];
            if (op && op.isRunning()) {
                op.abort();
            }
        }
        this.pendingOperations = null;
        this.callParent();
    }
});


Ext.define('Ext.data.proxy.Client', {
    extend: Ext.data.proxy.Proxy,
    alternateClassName: 'Ext.data.ClientProxy',
    
    isSynchronous: true,
    
    clear: function() {
        
        Ext.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: null,
        
        data: {
            $value: null,
            
            
            merge: function(newValue, currentValue, target, mixinClass) {
                return newValue ? Ext.clone(newValue) : newValue;
            }
        },
        
        clearOnRead: null
    },
    
    finishOperation: function(operation) {
        var recs = operation.getRecords(),
            len = recs.length,
            i;
        for (i = 0; i < len; i++) {
            
            recs[i].dropped = !!operation.isDestroyOperation;
            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,
            reader = me.getReader(),
            resultSet = reader.read(me.getData(), {
                recordCreator: reader.defaultRecordCreatorFromServer
            }),
            records = resultSet.getRecords(),
            sorters = operation.getSorters(),
            grouper = operation.getGrouper(),
            filters = operation.getFilters(),
            start = operation.getStart(),
            limit = operation.getLimit(),
            meta;
        
        if (operation.process(resultSet, null, null, false) !== false) {
            
            if (operation.success && me.getClearOnRead()) {
                this.setData(null);
            }
            
            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();
            
            
            if (meta = resultSet.getMetadata()) {
                me.onMetaChange(meta);
            }
        }
    },
    clear: Ext.emptyFn
});


Ext.define('Ext.data.ProxyStore', {
    extend: Ext.data.AbstractStore,
    config: {
        
        
        model: undefined,
        
        
        fields: null,
        
        
        proxy: undefined,
        
        autoLoad: undefined,
        
        autoSync: false,
        
        batchUpdateMode: 'operation',
        
        sortOnLoad: true,
        
        trackRemoved: true,
        
        asynchronousLoad: undefined
    },
    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: 'Ext.data.Model',
    
    
    autoSyncSuspended: 0,
    
    removed: null,
    
    
    
    
    
    constructor: function(config) {
        var me = this;
        
        var configModel = me.model;
        
        me.callParent(arguments);
        if (me.getAsynchronousLoad() === false) {
            me.flushLoad();
        }
        
        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(''));
        }
    },
    
    
    doDestroy: function() {
        var me = this,
            proxy = me.getProxy();
        me.clearLoadTask();
        Ext.destroy(me.getData());
        me.data = null;
        me.setProxy(null);
        if (proxy.autoCreated) {
            proxy.destroy();
        }
        me.setModel(null);
        me.callParent();
    },
    applyAsynchronousLoad: function(asynchronousLoad) {
        
        
        if (asynchronousLoad == null) {
            asynchronousLoad = !this.loadsSynchronously();
        }
        return asynchronousLoad;
    },
    updateAutoLoad: function(autoLoad) {
        
        this.getData();
        if (autoLoad) {
            
            this.load(Ext.isObject(autoLoad) ? autoLoad : undefined);
        }
    },
    
    getTotalCount: function() {
        return this.totalCount || 0;
    },
    applyFields: function(fields) {
        if (fields) {
            this.createImplicitModel(fields);
        }
    },
    applyModel: function(model) {
        if (model) {
            model = Ext.data.schema.Schema.lookupEntity(model);
        } else if (!this.destroying) {
            
            this.getFields();
            model = this.getModel() || this.createImplicitModel();
        }
        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();
                this.useModelProxy = true;
            }
            if (!proxy) {
                proxy = Ext.createByAlias('proxy.memory');
                proxy.autoCreated = true;
            }
        }
        return proxy;
    },
    applyState: function(state) {
        var me = this;
        me.callParent([
            state
        ]);
        
        
        
        
        if (me.getAutoLoad() || me.isLoaded()) {
            me.load();
        }
    },
    updateProxy: function(proxy, oldProxy) {
        this.proxyListeners = Ext.destroy(this.proxyListeners);
    },
    updateTrackRemoved: function(track) {
        this.cleanRemoved();
        this.removed = track ? [] : null;
    },
    
    onMetaChange: function(proxy, meta) {
        this.fireEvent('metachange', this, meta);
    },
    
    create: function(data, options) {
        var me = this,
            Model = me.getModel(),
            instance = new Model(data),
            operation;
        options = Ext.apply({}, options);
        if (!options.records) {
            options.records = [
                instance
            ];
        }
        options.internalScope = me;
        options.internalCallback = me.onProxyWrite;
        operation = me.createOperation('create', options);
        return operation.execute();
    },
    read: function() {
        return this.load.apply(this, arguments);
    },
    update: function(options) {
        var me = this,
            operation;
        options = Ext.apply({}, options);
        if (!options.records) {
            options.records = me.getUpdatedRecords();
        }
        options.internalScope = me;
        options.internalCallback = me.onProxyWrite;
        operation = me.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.cleanRemoved();
        }
    },
    
    
    erase: function(options) {
        var me = this,
            operation;
        options = Ext.apply({}, options);
        if (!options.records) {
            options.records = me.getRemovedRecords();
        }
        options.internalScope = me;
        options.internalCallback = me.onProxyWrite;
        operation = me.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;
        batch.destroy();
        me.fireEvent('datachanged', me);
    },
    
    onBatchException: function(batch, operation) {},
    
    
    
    
    
    
    filterNew: function(item) {
        
        return item.phantom && item.isValid();
    },
    
    getNewRecords: function() {
        return [];
    },
    
    getUpdatedRecords: function() {
        return [];
    },
    
    getModifiedRecords: function() {
        return [].concat(this.getNewRecords(), this.getUpdatedRecords());
    },
    
    filterUpdated: function(item) {
        
        return item.dirty && !item.phantom && item.isValid();
    },
    
    getRemovedRecords: function() {
        var removed = this.getRawRemovedRecords();
        return removed ? Ext.Array.clone(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) {
        var me = this;
        
        if (typeof options === 'function') {
            options = {
                callback: options
            };
        } else {
            
            options = options ? Ext.Object.chain(options) : {};
        }
        me.pendingLoadOptions = options;
        
        
        if (me.getAsynchronousLoad()) {
            if (!me.loadTimer) {
                me.loadTimer = Ext.asap(me.flushLoad, me);
            }
        } else 
        
        {
            me.flushLoad();
        }
        return me;
    },
    
    flushLoad: function() {
        var me = this,
            options = me.pendingLoadOptions,
            operation;
        if (me.destroying || me.destroyed) {
            return;
        }
        
        me.clearLoadTask();
        if (!options) {
            return;
        }
        me.setLoadOptions(options);
        if (me.getRemoteSort() && options.sorters) {
            me.fireEvent('beforesort', me, options.sorters);
        }
        operation = Ext.apply({
            internalScope: me,
            internalCallback: me.onProxyLoad,
            scope: me
        }, options);
        me.lastOptions = operation;
        operation = me.createOperation('read', operation);
        if (me.fireEvent('beforeload', me, operation) !== false) {
            me.onBeforeLoad(operation);
            me.loading = true;
            
            
            if (me.hasListeners.beginload) {
                me.fireEvent('beginload', me, operation);
            }
            operation.execute();
        }
    },
    
    reload: function(options) {
        return this.load(Ext.apply({}, options, this.lastOptions));
    },
    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);
            me.fireEvent('datachanged', me);
        }
    },
    
    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);
            me.fireEvent('datachanged', me);
        }
    },
    afterErase: function(record) {
        this.onErase(record);
    },
    onErase: Ext.emptyFn,
    onUpdate: Ext.emptyFn,
    
    hasPendingLoad: function() {
        return !!this.pendingLoadOptions || 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: {
        
        getRawRemovedRecords: function() {
            return this.removed;
        },
        onExtraParamsChanged: function() {},
        clearLoadTask: function() {
            this.pendingLoadOptions = this.loadTimer = Ext.unasap(this.loadTimer);
        },
        cleanRemoved: function() {
            
            
            
            
            var removed = this.getRawRemovedRecords(),
                len, i;
            if (removed) {
                for (i = 0 , len = removed.length; i < len; ++i) {
                    removed[i].unjoin(this);
                }
                removed.length = 0;
            }
        },
        createOperation: function(type, options) {
            var me = this,
                proxy = me.getProxy(),
                listeners;
            if (!me.proxyListeners) {
                listeners = {
                    scope: me,
                    destroyable: true,
                    beginprocessresponse: me.beginUpdate,
                    endprocessresponse: me.endUpdate
                };
                if (!me.disableMetaChangeEvent) {
                    listeners.metachange = me.onMetaChange;
                }
                me.proxyListeners = proxy.on(listeners);
            }
            return proxy.createOperation(type, options);
        },
        createImplicitModel: function(fields) {
            var me = this,
                modelCfg = {
                    extend: me.implicitModel,
                    statics: {
                        defaultProxy: 'memory'
                    }
                },
                proxy, model;
            if (fields) {
                modelCfg.fields = fields;
            }
            model = Ext.define(null, modelCfg);
            me.setModel(model);
            proxy = me.getProxy();
            if (proxy) {
                model.setProxy(proxy);
            } else {
                me.setProxy(model.getProxy());
            }
        },
        loadsSynchronously: function() {
            return this.getProxy().isSynchronous;
        },
        onBeforeLoad: Ext.privateFn,
        removeFromRemoved: function(record) {
            
            
            
            
            var removed = this.getRawRemovedRecords();
            if (removed) {
                Ext.Array.remove(removed, record);
                record.unjoin(this);
            }
        },
        setLoadOptions: function(options) {
            var me = this,
                filters, sorters;
            if (me.getRemoteFilter()) {
                filters = me.getFilters(false);
                if (filters && filters.getCount()) {
                    options.filters = filters.getRange();
                }
            }
            if (me.getRemoteSort()) {
                sorters = me.getSorters(false);
                if (sorters && sorters.getCount()) {
                    options.sorters = sorters.getRange();
                }
            }
        }
    }
});


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


Ext.define('Ext.data.Group', {
    extend: Ext.util.Group,
    store: null,
    
    getSummaryRecord: function() {
        var me = this,
            summaryRecord = me.summaryRecord,
            store = me.store,
            generation = store.getData().generation,
            M, T;
        if (!summaryRecord) {
            M = store.getModel();
            T = M.getSummaryModel();
            me.summaryRecord = summaryRecord = new T();
        }
        if (!summaryRecord.isRemote && summaryRecord.summaryGeneration !== generation) {
            summaryRecord.calculateSummary(me.items);
            summaryRecord.summaryGeneration = generation;
        }
        return summaryRecord;
    }
});


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(record) {
        return this.insert(this.getCount(), arguments.length === 1 ? record : arguments);
    },
    constructDataCollection: function() {
        var result = new Ext.util.Collection({
                rootProperty: 'data',
                groupConfig: {
                    xclass: 'Ext.data.Group',
                    store: this
                }
            });
        
        
        result.addObserver(this);
        return result;
    },
    
    createModel: function(record) {
        var session = this.getSession(),
            Model;
        if (!record.isModel) {
            Model = this.getModel();
            record = new Model(record, session);
        }
        return record;
    },
    createFiltersCollection: function() {
        return this.getData().getFilters();
    },
    createSortersCollection: function() {
        var sorters = this.getData().getSorters();
        sorters.setSorterConfigure(this.addFieldTransform, this);
        return sorters;
    },
    
    getSummaryRecord: function() {
        var me = this,
            summaryRecord = me.summaryRecord,
            data = me.getData(),
            generation = data.generation,
            T;
        if (!summaryRecord) {
            T = me.getModel().getSummaryModel();
            me.summaryRecord = summaryRecord = new T();
        }
        if (!summaryRecord.isRemote && summaryRecord.summaryGeneration !== generation) {
            summaryRecord.calculateSummary(data.items);
            summaryRecord.summaryGeneration = generation;
        }
        return summaryRecord;
    },
    onCollectionBeginUpdate: function() {
        this.beginUpdate();
    },
    onCollectionEndUpdate: function() {
        this.endUpdate();
    },
    
    
    onCollectionSort: function() {
        this.onSorterEndUpdate();
    },
    
    
    onCollectionFilter: function() {
        this.onFilterEndUpdate();
    },
    notifySorterChange: function() {
        this.getData().onSorterChange();
    },
    forceLocalSort: function() {
        var sorters = this.getSorters();
        
        
        
        sorters.beginUpdate();
        sorters.endUpdate();
    },
    
    contains: function(record) {
        return this.indexOf(record) > -1;
    },
    
    each: function(fn, scope, includeOptions) {
        var data = this.getData(),
            bypassFilters = includeOptions,
            len, record, i;
        if (typeof includeOptions === 'object') {
            bypassFilters = includeOptions.filtered;
        }
        if (bypassFilters && data.filtered) {
            data = data.getSource();
        }
        data = data.items.slice(0);
        
        len = data.length;
        for (i = 0; i < len; ++i) {
            record = data[i];
            if (fn.call(scope || record, record, i, len) === false) {
                break;
            }
        }
    },
    
    collect: function(property, includeOptions, filtered) {
        var me = this,
            allowNull = includeOptions,
            data = me.getData();
        if (typeof includeOptions === 'object') {
            filtered = includeOptions.filtered;
            allowNull = includeOptions.allowNull;
        }
        if (filtered && data.filtered) {
            data = data.getSource();
        }
        return data.collect(property, '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(),
            keyCfg;
        if (data.filtered) {
            if (!data.$hasExtraKeys) {
                keyCfg = this.makeInternalKeyCfg();
                data.setExtraKeys(keyCfg);
                data.$hasExtraKeys = true;
            }
            data = data.getSource();
        }
        if (!data.$hasExtraKeys) {
            data.setExtraKeys(keyCfg || this.makeInternalKeyCfg());
            data.$hasExtraKeys = true;
        }
        return data.byInternalId.get(internalId) || null;
    },
    
    getDataSource: function() {
        var data = this.getData();
        return data.getSource() || data;
    },
    
    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;
    },
    privates: {
        isLast: function(record) {
            return record === this.last();
        },
        makeInternalKeyCfg: function() {
            return {
                byInternalId: {
                    property: 'internalId',
                    rootProperty: ''
                }
            };
        }
    }
});


Ext.define('Ext.data.proxy.Server', {
    extend: Ext.data.proxy.Proxy,
    alias: 'proxy.server',
    alternateClassName: 'Ext.data.ServerProxy',
    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: {}
    },
    primitiveRe: /string|number|boolean/,
    
    
    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) {
        var extraParams = this.getExtraParams();
        extraParams[name] = value;
        this.fireEvent('extraparamschanged', extraParams);
    },
    updateExtraParams: function(newExtraParams, oldExtraParams) {
        this.fireEvent('extraparamschanged', newExtraParams);
    },
    
    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, meta;
        
        
        if (me.destroying || me.destroyed) {
            return;
        }
        
        
        
        
        
        
        
        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() || reader.defaultRecordCreatorFromServer
                });
            }
            operation.process(resultSet, request, response);
            exception = !operation.wasSuccessful();
        } else {
            me.setException(operation, response);
            exception = true;
        }
        
        
        if (me.destroyed) {
            return;
        }
        if (exception) {
            me.fireEvent('exception', me, response, operation);
        } else 
        
        {
            meta = resultSet.getMetadata();
            if (meta) {
                me.onMetaChange(meta);
            }
        }
        
        if (me.destroyed) {
            return;
        }
        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,
            needsEncoding, i, filter, encodedFilter;
        for (i = 0; i < length; i++) {
            filter = filters[i];
            
            
            
            
            filter.getFilterFn();
            if (filter.generatedFilterFn) {
                encodedFilter = filter.serialize();
                needsEncoding |= !this.primitiveRe.test(typeof encodedFilter);
                out.push(encodedFilter);
            }
        }
        
        
        return needsEncoding ? this.applyEncoding(out) : 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();
                
                if (groupDirectionParam === groupParam) {
                    params[groupParam] += ' ' + grouper.getDirection();
                } else {
                    params[groupDirectionParam] = grouper.getDirection();
                }
            } else {
                params[groupParam] = me.encodeSorters([
                    grouper
                ], true);
            }
        }
        if (sortParam && sorters && sorters.length > 0) {
            if (simpleSortMode) {
                
                for (index = (sorters.length > 1 && hasGroups) ? 1 : 0; index < sorters.length; index++) {
                    
                    if (directionParam === sortParam) {
                        params[sortParam] = Ext.Array.push(params[sortParam] || [], sorters[index].getProperty() + ' ' + sorters[index].getDirection());
                    } else {
                        params[sortParam] = Ext.Array.push(params[sortParam] || [], sorters[index].getProperty());
                        params[directionParam] = Ext.Array.push(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.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, callback, scope) {
        
        Ext.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,
    destroy: function() {
        var me = this;
        me.destroying = true;
        
        me.reader = me.writer = Ext.destroy(me.reader, me.writer);
        me.callParent();
        me.destroying = false;
        me.destroyed = true;
    }
});


Ext.define('Ext.data.proxy.Ajax', {
    extend: Ext.data.proxy.Server,
    alias: 'proxy.ajax',
    alternateClassName: [
        'Ext.data.HttpProxy',
        'Ext.data.AjaxProxy'
    ],
    isAjaxProxy: true,
    
    
    
    
    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) {
        return function(options, success, response) {
            var me = this;
            if (request === me.lastRequest) {
                me.lastRequest = null;
            }
            if (!me.destroying && !me.destroyed) {
                me.processResponse(success, operation, request, response);
            }
        };
    },
    destroy: function() {
        this.lastRequest = null;
        this.callParent();
    }
});


Ext.define('Ext.data.reader.Json', {
    extend: Ext.data.reader.Reader,
    alternateClassName: 'Ext.data.JsonReader',
    alias: 'reader.json',
    config: {
        
        record: null,
        
        metaProperty: 'metaData',
        
        useSimpleAccessors: false,
        
        preserveRawData: false
    },
    updateRootProperty: function() {
        this.forceBuildExtractors();
    },
    updateMetaProperty: function() {
        this.forceBuildExtractors();
    },
    
    getResponseData: function(response) {
        var error;
        try {
            return Ext.decode(response.responseText);
        } catch (ex) {
            error = this.createReadError(ex.message);
            Ext.Logger.warn('Unable to parse the JSON returned by the server');
            this.fireEvent('exception', this, response, error);
            return error;
        }
    },
    buildExtractors: function(force) {
        var me = this,
            emptyFn = Ext.emptyFn,
            prop;
        
        if (me.callParent([
            force
        ])) {
            me.getRoot = me.setupExtractor(me.getRootProperty(), Ext.identityFn);
            me.getGroupRoot = me.setupExtractor(me.getGroupRootProperty(), emptyFn);
            me.getSummaryRoot = me.setupExtractor(me.getSummaryRootProperty(), emptyFn);
            me.getMeta = me.setupExtractor(me.getMetaProperty(), emptyFn);
        }
    },
    
    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 simple = this.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, self) {
                    return field.mapping(raw, self);
                };
            } else {
                return me.createAccessor(map);
            }
        }
    },
    getAccessorKey: function(prop) {
        var simple = this.getUseSimpleAccessors() ? 'simple' : '';
        return this.callParent([
            simple + prop
        ]);
    },
    privates: {
        copyFrom: function(reader) {
            this.callParent([
                reader
            ]);
            this.getRoot = reader.getRoot;
        },
        setupExtractor: function(prop, defaultFn) {
            return prop ? this.getAccessor(prop) : defaultFn;
        }
    }
});


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.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.SorterCollection', {
    extend: Ext.util.Collection,
    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.raise('Invalid sort descriptor: ' + property);
                }
                
                sorters = [
                    property
                ];
                mode = direction;
                direction = null;
            }
            
            if (mode && !me._sortModes[mode]) {
                Ext.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();
        }
    },
    clear: function() {
        
        
        this.beginUpdate();
        this.callParent();
        this.endUpdate(this.items);
    },
    
    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.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.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,
    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);
    },
    
    getFilterCount: function() {
        var filters = this.items,
            len = filters.length,
            i;
        for (i = len - 1; i >= 0; i--) {
            if (filters[i].getDisabled()) {
                len--;
            }
        }
        return len;
    },
    
    
    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.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.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,
    isGroupCollection: true,
    config: {
        grouper: null,
        groupConfig: null,
        itemRoot: null
    },
    observerPriority: -100,
    emptyGroupRetainTime: 300000,
    
    constructor: function(config) {
        this.emptyGroups = {};
        this.callParent([
            config
        ]);
        this.on('remove', 'onGroupRemove', this);
    },
    
    getItemGroup: function(item) {
        var key = this.getGrouper().getGroupString(item);
        return this.get(key);
    },
    
    
    onCollectionAdd: function(source, details) {
        if (!this.isConfiguring) {
            this.addItemsToGroups(source, details.items, details.at);
        }
    },
    onCollectionBeforeItemChange: function(source, details) {
        this.changeDetails = details;
    },
    onCollectionBeginUpdate: function() {
        this.beginUpdate();
    },
    onCollectionEndUpdate: function() {
        this.endUpdate();
    },
    onCollectionItemChange: function(source, details) {
        
        
        
        if (!details.indexChanged) {
            this.syncItemGrouping(source, details);
        }
        this.changeDetails = null;
    },
    onCollectionRefresh: function(source) {
        if (source.generation) {
            var me = this,
                itemGroupKeys = me.itemGroupKeys = {},
                groupData = me.createEntries(source, source.items),
                entries = groupData.entries,
                groupKey, i, len, entry, j;
            
            for (i = 0 , len = entries.length; i < len; ++i) {
                entry = entries[i];
                
                entry.group.splice(0, 1.0E99, entry.items);
                
                for (j = 0; j < entry.items.length; j++) {
                    itemGroupKeys[source.getKey(entry.items[j])] = entry.group;
                }
            }
            
            entries = null;
            for (groupKey in me.map) {
                if (!(groupKey in groupData.groups)) {
                    (entries || (entries = [])).push(me.map[groupKey]);
                }
            }
            if (entries) {
                me.remove(entries);
            }
            
            
            me.sortItems();
        }
    },
    onCollectionRemove: function(source, details) {
        var me = this,
            changeDetails = me.changeDetails,
            itemGroupKeys = me.itemGroupKeys || (me.itemGroupKeys = {}),
            entries, entry, group, i, n, j, removeGroups, item;
        if (source.getCount()) {
            if (changeDetails) {
                
                
                item = changeDetails.item || changeDetails.items[0];
                entries = me.createEntries(source, [
                    item
                ]).entries;
                entries[0].group = itemGroupKeys['oldKey' in details ? details.oldKey : source.getKey(item)];
            } else {
                entries = me.createEntries(source, details.items).entries;
            }
            for (i = 0 , n = entries.length; i < n; ++i) {
                group = (entry = entries[i]).group;
                if (group) {
                    group.remove(entry.items);
                }
                
                for (j = 0; j < entry.items.length; j++) {
                    delete itemGroupKeys[source.getKey(entry.items[j])];
                }
                if (group && !group.length) {
                    (removeGroups || (removeGroups = [])).push(group);
                }
            }
        } else 
        {
            me.itemGroupKeys = {};
            removeGroups = me.items;
            for (i = 0 , n = removeGroups.length; i < n; ++i) {
                removeGroups[i].clear();
            }
        }
        if (removeGroups) {
            me.remove(removeGroups);
        }
    },
    
    
    
    onCollectionSort: function(source) {
        
        var me = this,
            sorters = source.getSorters(false),
            items, length, i, group;
        if (sorters) {
            items = me.items;
            length = me.length;
            for (i = 0; i < length; ++i) {
                group = items[i];
                if (group.getSorters() !== sorters) {
                    group.setSorters(sorters);
                }
            }
        }
    },
    onCollectionUpdateKey: function(source, details) {
        if (!details.indexChanged) {
            details.oldIndex = source.indexOf(details.item);
            this.syncItemGrouping(source, details);
        }
    },
    
    
    addItemsToGroups: function(source, items, at, oldIndex) {
        var me = this,
            itemGroupKeys = me.itemGroupKeys || (me.itemGroupKeys = {}),
            entries = me.createEntries(source, items).entries,
            index = -1,
            sourceStartIndex, entry, i, len, j, group, firstIndex, item;
        for (i = 0 , len = entries.length; i < len; ++i) {
            entry = entries[i];
            group = entry.group;
            
            if (oldIndex || oldIndex === 0) {
                item = items[0];
                if (group.getCount() > 0 && source.getSorters().getCount() === 0) {
                    
                    
                    firstIndex = source.indexOf(group.items[0]);
                    if (oldIndex < firstIndex) {
                        index = 0;
                    } else {
                        index = oldIndex - firstIndex;
                    }
                }
                if (index === -1) {
                    group.add(item);
                } else {
                    group.insert(index, item);
                }
            } else {
                if (me.length > 1 && at) {
                    sourceStartIndex = source.indexOf(entries[0].group.getAt(0));
                    at = Math.max(at - sourceStartIndex, 0);
                }
                entry.group.insert(at != null ? at : group.items.length, entry.items);
                
                for (j = 0; j < entry.items.length; j++) {
                    itemGroupKeys[source.getKey(entry.items[j])] = entry.group;
                }
            }
        }
        
        
        me.sortItems();
    },
    createEntries: function(source, items) {
        
        var me = this,
            groups = {},
            entries = [],
            grouper = me.getGrouper(),
            entry, group, groupKey, i, item, len;
        for (i = 0 , len = items.length; i < len; ++i) {
            groupKey = grouper.getGroupString(item = items[i]);
            if (!(entry = groups[groupKey])) {
                group = me.getGroup(source, groupKey);
                entries.push(groups[groupKey] = entry = {
                    group: group,
                    items: []
                });
            }
            
            
            entry.items.push(item);
        }
        return {
            groups: groups,
            entries: entries
        };
    },
    syncItemGrouping: function(source, details) {
        var me = this,
            itemGroupKeys = me.itemGroupKeys || (me.itemGroupKeys = {}),
            item = details.item,
            oldKey, itemKey, oldGroup, group;
        itemKey = source.getKey(item);
        oldKey = 'oldKey' in details ? details.oldKey : itemKey;
        
        oldGroup = itemGroupKeys[oldKey];
        
        group = me.getGroup(source, me.getGrouper().getGroupString(item));
        details.group = group;
        details.oldGroup = oldGroup;
        
        if (!(details.groupChanged = group !== oldGroup)) {
            
            oldGroup.itemChanged(item, details.modified, details.oldKey, details);
        } else {
            
            if (oldGroup) {
                
                oldGroup.updateKey(item, oldKey, itemKey);
                oldGroup.remove(item);
                
                if (!oldGroup.length) {
                    me.remove(oldGroup);
                }
            }
            
            me.addItemsToGroups(source, [
                item
            ], null, details.oldIndex);
        }
        
        delete itemGroupKeys[oldKey];
        itemGroupKeys[itemKey] = group;
    },
    getGroup: function(source, key) {
        var me = this,
            group = me.get(key),
            autoSort = me.getAutoSort();
        if (group) {
            group.setSorters(source.getSorters());
        } else {
            group = me.emptyGroups[key] || Ext.create(Ext.apply({
                xclass: 'Ext.util.Group',
                groupKey: key,
                rootProperty: me.getItemRoot(),
                sorters: source.getSorters()
            }, me.getGroupConfig()));
            group.ejectTime = null;
            me.setAutoSort(false);
            me.add(group);
            me.setAutoSort(autoSort);
        }
        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.$groupable.getAutoGroup());
        me.onSorterChange();
        me.onEndUpdateSorters(me.getSorters());
    },
    destroy: function() {
        var me = this;
        me.$groupable = null;
        
        
        me.destroyGroups(me.items);
        Ext.undefer(me.checkRemoveQueueTimer);
        me.callParent();
    },
    privates: {
        destroyGroups: function(groups) {
            var len = groups.length,
                i;
            for (i = 0; i < len; ++i) {
                groups[i].destroy();
            }
        },
        onGroupRemove: function(collection, info) {
            var me = this,
                groups = info.items,
                emptyGroups = me.emptyGroups,
                len, group, i;
            groups = Ext.Array.from(groups);
            for (i = 0 , len = groups.length; i < len; i++) {
                group = groups[i];
                group.setSorters(null);
                emptyGroups[group.getGroupKey()] = group;
                group.ejectTime = Ext.now();
            }
            
            me.checkRemoveQueue();
        },
        checkRemoveQueue: function() {
            var me = this,
                emptyGroups = me.emptyGroups,
                groupKey, group, reschedule;
            for (groupKey in emptyGroups) {
                group = emptyGroups[groupKey];
                
                if (!group.getCount() && Ext.now() - group.ejectTime > me.emptyGroupRetainTime) {
                    Ext.destroy(group);
                    delete emptyGroups[groupKey];
                } else {
                    reschedule = true;
                }
            }
            
            if (reschedule) {
                Ext.undefer(me.checkRemoveQueueTimer);
                me.checkRemoveQueueTimer = Ext.defer(me.checkRemoveQueue, me.emptyGroupRetainTime, me);
            }
        }
    }
});


Ext.define('Ext.data.Store', {
    extend: Ext.data.ProxyStore,
    alias: 'store.store',
    mixins: [
        Ext.data.LocalStore
    ],
    config: {
        
        data: undefined,
        
        
        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.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([
            config
        ]);
        
        data = me.inlineData;
        if (data) {
            delete me.inlineData;
            me.loadInlineData(data);
        }
    },
    
    
    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();
        if (proxy && proxy.isMemoryProxy) {
            proxy.setData(data);
            
            me.suspendEvents();
            me.read();
            me.resumeEvents();
        } else {
            
            me.removeAll(true);
            
            
            
            me.suspendEvents();
            me.loadData(data);
            me.resumeEvents();
        }
    },
    
    onCollectionAdd: function(collection, info) {
        this.loadCount = this.loadCount || 1;
        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.removed,
            ignoreAdd = me.ignoreCollectionAdd,
            session = me.getSession(),
            replaced = info && info.replaced,
            i, sync, record, replacedItems;
        
        
        
        if (me.activeRanges) {
            me.syncActiveRanges();
        }
        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) {
            
            
            
            
            if (info.replaced) {
                if (lastChunk) {
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                }
            } else {
                me.fireEvent('add', me, records, info.at);
                
                
                
                if (lastChunk) {
                    me.fireEvent('datachanged', me);
                }
            }
        }
        if (replacedItems) {
            me.setMoving(replacedItems, false);
        }
        
        me.needsSync = me.needsSync || sync;
    },
    onCollectionBeforeItemChange: function(collection, info) {
        var record = info.item,
            modifiedFieldNames = info.modified || null,
            type = info.meta;
        
        this.fireEvent('beforeupdate', this, record, type, modifiedFieldNames, info);
    },
    
    onCollectionFilteredItemChange: function() {
        this.onCollectionItemChange.apply(this, arguments);
    },
    onCollectionItemChange: function(collection, info) {
        var me = this,
            record = info.item,
            modifiedFieldNames = info.modified || null,
            type = info.meta;
        if (me.fireChangeEvent(record)) {
            
            
            
            me.onUpdate(record, type, modifiedFieldNames, info);
            me.fireEvent('update', me, record, type, modifiedFieldNames, info);
            me.fireEvent('datachanged', me);
        }
    },
    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);
    },
    afterErase: function(record) {
        this.removeFromRemoved(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.getDataSource(),
            len, i, toRemove, record;
        if (records) {
            if (records.isModel) {
                if (data.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,
            replacement = info.replacement,
            isMove = me.removeIsMove || (replacement && Ext.Array.equals(records, replacement.items)),
            silent = me.removeIsSilent,
            lastChunk = !info.next,
            data = me.getDataSource(),
            i, record;
        if (me.ignoreCollectionRemove) {
            return;
        }
        if (replacement) {
            me.setMoving(replacement.items, true);
        }
        for (i = len - 1; i >= 0; i--) {
            record = records[i];
            
            
            if (!data.contains(record)) {
                
                
                if (removed && !isMove && !record.phantom && !record.erasing) {
                    
                    
                    record.removedFrom = index + i;
                    removed.push(record);
                    
                    
                    me.needsSync = true;
                } else {
                    
                    
                    record.unjoin(me);
                }
            }
        }
        if (!silent) {
            
            
            
            
            
            
            
            
            
            if (!replacement || !replacement.items.length) {
                me.fireEvent('remove', me, records, index, isMove);
                
                
                
                if (lastChunk) {
                    me.fireEvent('datachanged', me);
                }
            }
        }
        if (replacement) {
            me.setMoving(replacement.items, false);
        }
    },
    onFilterEndUpdate: function() {
        var me = this;
        if (me.destroying || me.destroyed) {
            return;
        }
        
        
        
        if (me.activeRanges) {
            me.syncActiveRanges();
        }
        me.callParent(arguments);
        me.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(),
            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);
    },
    
    onProxyLoad: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful();
        if (me.destroyed) {
            return;
        }
        if (resultSet) {
            me.totalCount = resultSet.getTotal();
        }
        if (successful) {
            records = me.processAssociation(records);
            me.loadRecords(records, operation.getAddRecords() ? {
                addRecords: true
            } : undefined);
            me.attachSummaryRecord(resultSet);
        } else {
            me.loading = false;
        }
        if (me.hasListeners.load) {
            me.fireEvent('load', me, records, successful, operation);
        }
        me.callObservers('AfterLoad', [
            records,
            successful,
            operation
        ]);
    },
    onProxyWrite: function(operation) {
        if (operation.wasSuccessful()) {
            this.attachSummaryRecord(operation.getResultSet());
        }
        this.callParent([
            operation
        ]);
    },
    
    filterDataSource: function(fn) {
        var source = this.getDataSource(),
            items = source.items,
            len = items.length,
            ret = [],
            i;
        for (i = 0; i < len; i++) {
            if (fn.call(source, items[i])) {
                ret.push(items[i]);
            }
        }
        return ret;
    },
    getNewRecords: function() {
        return this.filterDataSource(this.filterNew);
    },
    getRejectRecords: function() {
        return this.filterDataSource(this.filterRejects);
    },
    getUpdatedRecords: function() {
        return this.filterDataSource(this.filterUpdated);
    },
    
    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);
        }
        if (!me.isEmptyStore) {
            ++me.loadCount;
            me.complete = true;
        }
        if (me.hasListeners.datachanged) {
            me.fireEvent('datachanged', me);
        }
        if (me.hasListeners.refresh) {
            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) {
        var me = this,
            removed = me.removed,
            data = me.getDataSource(),
            clearRemovedOnLoad = me.getClearRemovedOnLoad(),
            needsUnjoinCheck = removed && isLoad && !clearRemovedOnLoad,
            records, record, i, len;
        
        
        
        
        
        if (data) {
            records = data.items;
            for (i = 0 , len = records.length; i < len; ++i) {
                record = records[i];
                if (needsUnjoinCheck && Ext.Array.contains(removed, record)) {
                    
                    continue;
                }
                record.unjoin(me);
            }
            me.ignoreCollectionRemove = true;
            me.callObservers('BeforeClear');
            data.removeAll();
            me.ignoreCollectionRemove = false;
            me.callObservers('AfterClear');
        }
        if (removed && (!isLoad || clearRemovedOnLoad)) {
            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,
            i = 0;
        Ext.suspendLayouts();
        me.beginUpdate();
        for (; i < len; i++) {
            recs[i].commit();
        }
        me.cleanRemoved();
        me.endUpdate();
        Ext.resumeLayouts(true);
        
        me.fireEvent('commit', me);
    },
    filterNewOnly: function(item) {
        return item.phantom === true;
    },
    filterRejects: function(item) {
        return item.phantom || item.dirty;
    },
    
    rejectChanges: function() {
        var me = this,
            recs = me.getRejectRecords(),
            len = recs.length,
            i, rec, toRemove, sorted, data, currentAutoSort;
        Ext.suspendLayouts();
        me.beginUpdate();
        for (i = 0; i < len; i++) {
            rec = recs[i];
            if (rec.phantom) {
                toRemove = toRemove || [];
                toRemove.push(rec);
            } else {
                rec.reject();
            }
        }
        if (toRemove) {
            me.remove(toRemove);
            for (i = 0 , len = toRemove.length; i < len; ++i) {
                toRemove[i].reject();
            }
        }
        
        recs = me.getRawRemovedRecords();
        if (recs) {
            len = recs.length;
            sorted = !me.getRemoteSort() && me.isSorted();
            if (sorted) {
                
                
                data = me.getData();
                currentAutoSort = data.getAutoSort();
                data.setAutoSort(false);
            }
            for (i = len - 1; i >= 0; i--) {
                rec = recs[i];
                rec.reject();
                if (!sorted) {
                    me.insert(rec.removedFrom || 0, rec);
                }
            }
            if (sorted) {
                
                data.setAutoSort(currentAutoSort);
                me.add(recs);
            }
            
            
            recs.length = 0;
        }
        me.endUpdate();
        Ext.resumeLayouts(true);
        
        me.fireEvent('reject', me);
    },
    doDestroy: function() {
        var me = this,
            task = me.loadTask,
            data = me.getData(),
            source = data.getSource();
        
        me.clearData();
        me.setSession(null);
        me.observers = null;
        if (task) {
            task.cancel();
            me.loadTask = null;
        }
        if (source) {
            source.destroy();
        }
        me.callParent();
    },
    privates: {
        commitOptions: {
            commit: true
        },
        attachSummaryRecord: function(resultSet) {
            if (!resultSet) {
                return;
            }
            var me = this,
                summary = resultSet.getSummaryData(),
                grouper = me.getGrouper(),
                current = me.summaryRecord,
                commitOptions = me.commitOptions,
                groups, len, i, rec, group;
            if (summary) {
                if (current) {
                    current.set(summary.data, commitOptions);
                } else {
                    me.summaryRecord = summary;
                    summary.isRemote = true;
                }
            }
            if (grouper) {
                summary = resultSet.getGroupData();
                if (summary) {
                    groups = me.getGroups();
                    for (i = 0 , len = summary.length; i < len; ++i) {
                        rec = summary[i];
                        group = groups.getItemGroup(rec);
                        if (group) {
                            current = group.summaryRecord;
                            if (current) {
                                current.set(rec.data, commitOptions);
                            } else {
                                group.summaryRecord = rec;
                                rec.isRemote = true;
                            }
                        }
                    }
                }
            }
        },
        
        fetch: function(options) {
            options = Ext.apply({}, options);
            this.setLoadOptions(options);
            var operation = this.createOperation('read', options);
            operation.execute();
        },
        fireChangeEvent: function(record) {
            return this.getDataSource().contains(record);
        },
        onBeforeLoad: function(operation) {
            this.callObservers('BeforeLoad', [
                operation
            ]);
        },
        onRemoteFilterSet: function(filters, remoteFilter) {
            if (filters) {
                this.getData().setFilters(remoteFilter ? null : filters);
            }
            this.callParent([
                filters,
                remoteFilter
            ]);
        },
        onRemoteSortSet: function(sorters, remoteSort) {
            var data = this.getData();
            if (sorters) {
                data.setSorters(remoteSort ? null : sorters);
            }
            data.setAutoGroup(!remoteSort);
            this.callParent([
                sorters,
                remoteSort
            ]);
        },
        
        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;
        },
        setLoadOptions: function(options) {
            
            var me = this,
                pageSize = me.getPageSize(),
                session, grouper;
            if (me.getRemoteSort() && !options.grouper) {
                grouper = me.getGrouper();
                if (grouper) {
                    options.grouper = grouper;
                }
            }
            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;
                me.currentPage = options.page;
            }
            options.addRecords = options.addRecords || false;
            if (!options.recordCreator) {
                session = me.getSession();
                if (session) {
                    options.recordCreator = session.recordCreator;
                }
            }
            me.callParent([
                options
            ]);
        },
        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'
    ],
    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,
    
    
    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, defaultType) {
        
        if (Ext.isArray(store)) {
            var first = store[0],
                data = store,
                arrays, fields, i, len;
            if (Ext.isObject(first)) {
                
                store = {
                    data: data
                };
            } else {
                arrays = Ext.isArray(first);
                fields = [
                    'field1'
                ];
                if (arrays) {
                    
                    for (i = 2 , len = first.length; i <= len; ++i) {
                        fields.push('field' + i);
                    }
                } else {
                    
                    data = [];
                    for (i = 0 , len = store.length; i < len; ++i) {
                        data.push([
                            store[i]
                        ]);
                    }
                }
                return new Ext.data.ArrayStore({
                    data: data,
                    fields: fields,
                    autoDestroy: true,
                    autoCreated: true,
                    expanded: !arrays
                });
            }
        }
        if (Ext.isString(store)) {
            
            return this.get(store);
        } else {
            
            return Ext.Factory.store(store, defaultType);
        }
    },
    
    getKey: function(o) {
        return o.storeId;
    },
    addEmptyStore: function() {
        
        
        
        var emptyStore = this.$emptyStore,
            destoryable = {
                destroy: Ext.emptyFn
            };
        if (!emptyStore) {
            emptyStore = this.$emptyStore = Ext.regStore('ext-empty-store', {
                proxy: 'memory',
                useModelWarning: false
            });
            emptyStore.isEmptyStore = true;
            emptyStore.on = emptyStore.addListener = function() {
                return destoryable;
            };
            emptyStore.un = emptyStore.removeListener = Ext.emptyFn;
            
            emptyStore.add = emptyStore.remove = emptyStore.insert = emptyStore.destroy = emptyStore.loadData = function() {
                Ext.raise('Cannot modify ext-empty-store');
            };
        }
        
        this.add(emptyStore);
    },
    clear: function() {
        this.callParent();
        this.addEmptyStore();
    }
}, function() {
    
    Ext.regStore = function(id, config) {
        var store;
        if (Ext.isObject(id)) {
            config = id;
        } else {
            if (Ext.data.StoreManager.containsKey(id)) {
                return Ext.data.StoreManager.lookup(id);
            }
            config.storeId = id;
        }
        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);
    };
    Ext.data.StoreManager.addEmptyStore();
});


Ext.define('Ext.app.domain.Store', {
    extend: Ext.app.EventDomain,
    singleton: true,
    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.Controller', {
    extend: Ext.app.BaseController,
    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\./,
        profileRegex: /^(.*)\.profile\./,
        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,
                namespaceRe = cls.prototype.isProfile ? Controller.profileRegex : Controller.controllerRegex,
                className, namespace, match;
            
            className = Ext.getClassName(cls);
            namespace = data.$namespace || data.namespace || Ext.app.getNamespace(className) || ((match = namespaceRe.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, profileName) {
            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, profileName);
                
                names[j] = absoluteName = o.absoluteName;
                shortName = o.shortName;
                requires.push(absoluteName);
                getterName = me.getGetterName(shortName, strings.upper);
                if (!cls[getterName]) {
                    cls[getterName] = getter = me.createGetter(strings.getter, name);
                }
                
                else if (getterName === 'getMainView') {
                    Ext.log.warn('Cannot have a view named \'Main\' - getter conflicts with mainView config.');
                }
                
                
                if (getter && kind !== 'controller') {
                    
                    
                    
                    
                    getter['Ext.app.getter'] = true;
                }
            }
        },
        getFullName: function(name, kind, namespace, profileName) {
            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;
                shortName = name.replace(namespace + '.' + kind + '.', '');
            } else {
                
                if (!namespace) {
                    Ext.log.warn("Cannot find namespace for " + kind + " " + name + ", " + "assuming it is fully qualified class name");
                }
                
                if (namespace) {
                    absoluteName = namespace + '.' + kind + '.' + (profileName ? profileName + '.' + name : 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([
            config
        ]);
    },
    
    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;
    },
    applyId: function(id) {
        return id || Ext.app.Controller.getFullName(this.$className, 'controller', this.$namespace).shortName;
    },
    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('destroy', 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, {
                    
                    
                    id: storeId
                });
            }
        }
        return store;
    },
    
    getModel: function(modelName) {
        var name = Ext.app.Controller.getFullName(modelName, 'model', this.$namespace),
            ret = Ext.ClassManager.get(name.absoluteName);
        if (!ret) {
            ret = Ext.data.schema.Schema.lookupEntity(modelName);
        }
        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);
    },
    
    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,
    isApplication: true,
    
    
    
    scope: undefined,
    
    namespaces: [],
    
    paths: null,
    
    
    config: {
        
        name: '',
        
        appProperty: 'app',
        
        
        profiles: [],
        
        currentProfile: null,
        
        
        mainView: {
            $value: null,
            lazy: true
        },
        
        defaultToken: null,
        
        glyphFontFamily: null,
        
        quickTips: true,
        
        router: null
    },
    onClassExtended: function(cls, data, hooks) {
        var Controller = Ext.app.Controller,
            proto = cls.prototype,
            requires = [],
            onBeforeClassCreated, paths, namespace, ns;
        
        
        namespace = data.name || cls.superclass.name;
        if (namespace) {
            data.$namespace = namespace;
            Ext.app.addNamespaces(namespace);
        }
        if (data.namespaces) {
            Ext.app.addNamespaces(data.namespaces);
        }
        if (data['paths processed']) {
            delete data['paths processed'];
        } else {
            Ext.app.setupPaths(namespace, ('appFolder' in data) ? data.appFolder : cls.superclass.appFolder, data.paths);
        }
        
        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);
                
                
                
                
                if (data.__handleRequires) {
                    data.__handleRequires.call(this, requires, Ext.bind(function() {
                        return onBeforeClassCreated.apply(this, args);
                    }, this));
                    return;
                }
                
                Ext.require(requires, function() {
                    return onBeforeClassCreated.apply(this, args);
                });
            };
        }
    },
    getDependencies: Ext.emptyFn,
    
    constructor: function(config) {
        var me = this;
        Ext.route.Router.application = me;
        me.callParent([
            config
        ]);
        
        if (Ext.isEmpty(me.getName())) {
            Ext.raise("[Ext.app.Application] Name property is required");
        }
        
        me.doInit(me);
        Ext.on('appupdate', me.onAppUpdate, me, {
            single: true
        });
        
        Ext.Loader.setConfig({
            enabled: true
        });
        
        this.onProfilesReady();
    },
    applyId: function(id) {
        return id || this.$className;
    },
    updateRouter: function(cfg) {
        if (cfg) {
            Ext.route.Router.setConfig(cfg);
        }
    },
    
    onAppUpdate: Ext.emptyFn,
    onProfilesReady: function() {
        var me = this,
            profiles = me.getProfiles(),
            length = profiles.length,
            current, i, instance;
        for (i = 0; i < length; i++) {
            instance = Ext.create(profiles[i], {
                application: me
            });
            if (instance.isActive() && !current) {
                current = instance;
                me.setCurrentProfile(current);
            }
        }
        if (current) {
            current.init();
        }
        me.initControllers();
        me.onBeforeLaunch();
        me.finishInitControllers();
    },
    doInit: function(app) {
        this.initNamespace(app);
        this.callParent([
            app
        ]);
    },
    initNamespace: function(me) {
        var appProperty = me.getAppProperty(),
            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),
            profile = me.getCurrentProfile(),
            i, ln;
        me.controllers = new Ext.util.MixedCollection();
        for (i = 0 , ln = controllers.length; i < ln; i++) {
            me.getController(controllers[i]);
        }
        
        
        if (profile) {
            controllers = profile.getControllers();
            for (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 || token === defaultToken) {
            Ext.route.Router.onStateChange(token);
        } else if (defaultToken) {
            History.replace(defaultToken);
        }
        
        
        if (Ext.Microloader && Ext.Microloader.appUpdate && Ext.Microloader.appUpdate.updated) {
            Ext.Microloader.fireAppUpdate();
        }
        
        if (!me.cnsTimer) {
            me.cnsTimer = Ext.defer(Ext.ClassManager.clearNamespaceCache, 2000, Ext.ClassManager);
        }
    },
    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 = this.getView(value);
        
        return view.create({
            $initParent: this.viewport
        });
    },
    
    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: className
            });
            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.undefer(me.cnsTimer);
        Ext.un('appupdate', me.onAppUpdate, me);
        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];
        }
        if (Ext.route.Router.application === me) {
            Ext.route.Router.application = null;
        }
        if (Ext.app.Application.instance === me) {
            Ext.app.Application.instance = null;
        }
    },
    updateGlyphFontFamily: function(fontFamily) {
        Ext.setGlyphFontFamily(fontFamily);
    },
    
    applyProfiles: function(profiles) {
        var me = this;
        return Ext.Array.map(profiles, function(profile) {
            return me.getModuleClassName(profile, "profile");
        });
    }
}, function() {
    
    Ext.getApplication = function() {
        return Ext.app.Application.instance;
    };
});








Ext.application = function(config) {
    var createApp = function(App) {
            
            Ext.onReady(function() {
                var Viewport = Ext.viewport;
                Viewport = Viewport && Viewport['Viewport'];
                if (Viewport && Viewport.setup) {
                    Viewport.setup(App.prototype.config.viewport);
                }
                Ext.app.Application.instance = new App();
            });
        };
    if (typeof config === "string") {
        Ext.require(config, function() {
            createApp(Ext.ClassManager.get(config));
        });
    } else {
        config = Ext.apply({
            extend: 'Ext.app.Application'
        }, 
        config);
        
        
        Ext.app.setupPaths(config.name, config.appFolder, config.paths);
        config['paths processed'] = true;
        
        Ext.define(config.name + ".$application", config, function() {
            createApp(this);
        });
    }
};


Ext.define('Ext.util.ItemCollection', {
    extend: Ext.util.MixedCollection,
    alternateClassName: 'Ext.ItemCollection',
    getKey: function(item) {
        return item.getItemId && item.getItemId();
    },
    has: function(item) {
        return this.map.hasOwnProperty(item.getId());
    }
});


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.mixin.Container', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'container'
    },
    
    isContainer: true,
    
    nameHolder: false,
    
    referenceHolder: false,
    
    getNamedItems: function() {
        var CM = Ext.ComponentManager;
        if (CM.referencesDirty) {
            CM.fixReferences();
        }
        return this.nameRefs || null;
    },
    
    getReferences: function() {
        var CM = Ext.ComponentManager;
        if (CM.referencesDirty) {
            CM.fixReferences();
        }
        return this.refs || null;
    },
    
    lookup: function(ref) {
        var refs = this.getReferences();
        return (refs && refs[ref]) || null;
    },
    
    lookupName: function(name) {
        var items = this.getNamedItems();
        return (items && items[name]) || null;
    },
    
    lookupReference: function(ref) {
        return this.lookup(ref);
    },
    privates: {
        
        attachNameRef: function(component) {
            var me = this,
                key = component.name || component._name,
                entry, nameRefs;
            
            if (key && !me.destroying && !me.destroyed) {
                nameRefs = me.nameRefs || (me.nameRefs = {});
                entry = nameRefs[key];
                if (!entry) {
                    entry = component.shareableName ? [
                        component
                    ] : component;
                } else if (!entry.isInstance) {
                    
                    
                    
                    entry.push(component);
                } else 
                {
                    Ext.raise('Duplicate name: "' + key + '" on ' + me.id + ' between ' + entry.id + ' and ' + component.id);
                }
                
                nameRefs[key] = entry;
            }
        },
        
        attachReference: function(component) {
            var me = this,
                key, refs;
            
            if (!me.destroying && !me.destroyed) {
                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;
            }
        },
        containerOnAdded: function(component, instanced) {
            
            
            
            if (instanced) {
                Ext.ComponentManager.markReferencesDirty();
            }
        },
        containerOnRemoved: function(destroying) {
            
            if (!destroying) {
                
                
                
                
                Ext.ComponentManager.markReferencesDirty();
            }
        },
        initContainerInheritedState: function(inheritedState, inheritedStateInner) {
            var me = this,
                controller = me.getController(),
                session = me.getSession(),
                
                
                viewModel = me.getConfig('viewModel', true),
                reference = me.reference,
                referenceHolder = me.referenceHolder;
            if (me.nameHolder) {
                inheritedState.nameHolder = me;
            }
            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 + '.';
            }
        },
        setupReference: function(reference) {
            var len;
            if (reference && reference.charAt(len = reference.length - 1) === '>') {
                this.isParentReference = true;
                reference = reference.substring(0, len);
            }
            
            if (reference && !Ext.validIdRe.test(reference)) {
                Ext.Error.raise('Invalid reference "' + reference + '" for ' + this.getId() + ' - not a valid identifier');
            }
            
            return reference;
        }
    }
});


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) {
            Ext.raise("Legacy multi-argument KeyMap constructor is removed. Use a config object instead.");
        }
        
        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();
    },
    
    addBinding: function(binding) {
        var me = this,
            keyCode = binding.key,
            i, len;
        if (me.processing) {
            me.bindings = me.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.ctrl === item.ctrl && 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, result;
        if (me.enabled) {
            bindings = me.bindings;
            
            event = me.processEvent.apply(me.processEventScope || me, arguments);
            
            if (event) {
                me.lastKeyEvent = event;
                
                if (me.ignoreInputFields && Ext.fly(event.target).isInputField()) {
                    return;
                }
                
                
                if (!event.getKey) {
                    return event;
                }
                me.processing = true;
                for (i = 0 , len = bindings.length; i < len; i++) {
                    result = me.processBinding(bindings[i], event);
                    if (result === false) {
                        me.processing = false;
                        return result;
                    }
                }
                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, result;
            
            if (keyCode.test) {
                if (keyCode.test(String.fromCharCode(event.getCharCode()))) {
                    result = handler.call(scope, key, event);
                    if (result !== true && defaultEventAction) {
                        event[defaultEventAction]();
                    }
                    if (result === false) {
                        return result;
                    }
                }
            }
            
            else if (keyCode.length) {
                for (i = 0 , len = keyCode.length; i < len; i++) {
                    if (key === keyCode[i]) {
                        result = handler.call(scope, key, event);
                        if (result !== true && defaultEventAction) {
                            event[defaultEventAction]();
                        }
                        if (result === false) {
                            return result;
                        }
                        break;
                    }
                }
            }
        }
    },
    
    checkModifiers: function(binding, event) {
        var keys = [
                'shift',
                'ctrl',
                'alt'
            ],
            i, len, val, key;
        for (i = 0 , len = keys.length; 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,
                priority: me.priority
            });
            me.enabled = true;
        }
    },
    
    disable: function() {
        var me = this;
        if (me.enabled) {
            if (!me.target.destroyed) {
                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();
        me.target = null;
        if (removeTarget) {
            target.destroy();
            Ext.raise("Using removeTarget argument in KeyMap destructor is not supported.");
        }
        me.callParent();
    }
});


Ext.define('Ext.util.KeyNav', {
    alternateClassName: 'Ext.KeyNav',
    
    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,
            keymapCfg, map;
        
        if (arguments.length === 2) {
            Ext.raise("2-argument KeyNav constructor is removed. Use a config object instead.");
        }
        
        config = config || {};
        keymapCfg = {
            target: config.target,
            ignoreInputFields: config.ignoreInputFields,
            eventName: me.getKeyEvent('forceKeyDown' in config ? config.forceKeyDown : me.forceKeyDown, config.eventName),
            capture: config.capture
        };
        if (me.map) {
            me.map.destroy();
        }
        
        me.initConfig(config);
        if (config.processEvent) {
            keymapCfg.processEvent = config.processEvent;
            keymapCfg.processEventScope = config.processEventScope || me;
        }
        if (config.priority) {
            keymapCfg.priority = config.priority;
        }
        
        if (config.keyMap) {
            map = me.map = config.keyMap;
        } else 
        {
            map = me.map = new Ext.util.KeyMap(keymapCfg);
            me.destroyKeyMap = true;
        }
        me.addBindings(config);
        map.disable();
        if (!config.disabled) {
            map.enable();
        }
    },
    addBindings: function(bindings) {
        var me = this,
            map = me.map,
            keyCodes = Ext.util.KeyNav.keyOptions,
            Event = Ext.event.Event,
            defaultScope = bindings.scope || me,
            binding, keyName, keyCode;
        for (keyName in bindings) {
            binding = bindings[keyName];
            
            
            
            
            
            
            keyCode = keyName.length === 1 ? keyName.charCodeAt(0) : (keyCodes[keyName] || Event[keyName.toUpperCase()]);
            if (keyCode != null) {
                keyName = keyCode;
            }
            if (binding && (keyName.length === 1 || !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) {
        var me = this;
        if (removeEl) {
            Ext.raise("removeEl argument in KeyNav destructor is not supported anymore.");
        }
        if (me.destroyKeyMap) {
            me.map.destroy(removeEl);
        }
        me.map = null;
        me.callParent();
    },
    
    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;
    },
    isEnabled: function() {
        return !this.disabled;
    },
    
    getKeyEvent: function(forceKeyDown, configuredEventName) {
        if (forceKeyDown || (Ext.supports.SpecialKeyDownRepeat && !configuredEventName)) {
            return 'keydown';
        } else {
            return configuredEventName || this.eventName;
        }
    }
});


Ext.define('Ext.mixin.FocusableContainer', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'focusablecontainer'
    },
    isFocusableContainer: true,
    
    focusableContainer: false,
    
    resetFocusPosition: false,
    
    activeChildTabIndex: 0,
    
    inactiveChildTabIndex: -1,
    
    allowFocusingDisabledChildren: false,
    
    focusableContainerEl: 'el',
    privates: {
        initFocusableContainer: function(clearChildren) {
            var items, i, len;
            
            
            if (this.focusableContainer) {
                clearChildren = clearChildren != null ? clearChildren : true;
                this.doInitFocusableContainer(clearChildren);
            } else 
            
            
            
            
            {
                items = this.getFocusables();
                for (i = 0 , len = items.length; i < len; i++) {
                    items[i].ownerFocusableContainer = null;
                }
            }
        },
        doInitFocusableContainer: function(clearChildren) {
            var me = this,
                el = me.focusableContainerEl,
                child;
            
            
            if (!el.isElement) {
                el = me.focusableContainerEl = me[el];
            }
            if (me.initFocusableContainerKeyNav) {
                me.initFocusableContainerKeyNav(el);
            }
            
            
            
            
            
            if (clearChildren) {
                me.clearFocusables();
                if (!me.isDisabled()) {
                    child = me.findNextFocusableChild({
                        step: 1
                    }) || me.findNextFocusableChild({
                        beforeRender: true
                    });
                    if (child) {
                        me.activateFocusable(child);
                    }
                }
            }
            child = me.findNextFocusableChild({
                firstTabbable: true
            });
            
            
            me.activateFocusableContainer(!!child && !me.isDisabled());
        },
        initFocusableContainerKeyNav: function(el) {
            var me = this;
            if (!me.focusableKeyNav) {
                el = el || me.focusableContainerEl;
                me.focusableKeyNav = new Ext.util.KeyNav({
                    target: el,
                    disabled: true,
                    eventName: 'keydown',
                    
                    processEvent: me.processFocusableContainerKeyEvent,
                    processEventScope: me,
                    scope: me,
                    tab: me.onFocusableContainerTabKey,
                    enter: {
                        handler: me.onFocusableContainerEnterKey,
                        ctrl: false,
                        shift: false,
                        alt: false
                    },
                    space: {
                        handler: me.onFocusableContainerSpaceKey,
                        ctrl: false,
                        shift: false,
                        alt: false
                    },
                    up: {
                        handler: me.onFocusableContainerUpKey,
                        ctrl: false,
                        shift: false,
                        alt: false
                    },
                    down: {
                        handler: me.onFocusableContainerDownKey,
                        ctrl: false,
                        shift: false,
                        alt: false
                    },
                    left: {
                        handler: me.onFocusableContainerLeftKey,
                        ctrl: false,
                        shift: false,
                        alt: false
                    },
                    right: {
                        handler: me.onFocusableContainerRightKey,
                        ctrl: false,
                        shift: false,
                        alt: false
                    }
                });
            }
        },
        destroyFocusableContainer: function() {
            this.focusableKeyNav = Ext.destroy(this.focusableKeyNav);
        },
        activateFocusableContainer: function(enable) {
            var keyNav = this.focusableKeyNav;
            if (keyNav) {
                keyNav.setDisabled(!enable);
            }
        },
        isFocusableContainerActive: function() {
            var keyNav = this.focusableKeyNav;
            return keyNav ? !keyNav.disabled : false;
        },
        
        getFocusables: function() {
            return this.items.items;
        },
        initDefaultFocusable: function() {
            var me = this,
                haveFocusable = false,
                items, item, i, len;
            items = me.getFocusables();
            len = items.length;
            if (!len) {
                return;
            }
            
            
            
            for (i = 0; i < len; i++) {
                item = items[i];
                if (!item.isDisabled() && item.isFocusable()) {
                    haveFocusable = true;
                    
                    
                    break;
                }
            }
            if (!haveFocusable) {
                return;
            }
            item = me.findNextFocusableChild({
                items: items,
                step: true
            });
            if (item) {
                me.activateFocusable(item);
            }
            return item;
        },
        clearFocusables: function(skipFocused) {
            var me = this,
                items = me.getFocusables(),
                len = items.length,
                item, i;
            for (i = 0; i < len; i++) {
                item = items[i];
                if (!item.destroyed && item.focusable && item.isTabbable()) {
                    me.deactivateFocusable(item);
                }
            }
        },
        
        processFocusableContainerKeyEvent: function(e) {
            if (!Ext.fly(e.target).isInputField()) {
                return e;
            }
        },
        activateFocusable: function(child) {
            child.setTabIndex(this.activeChildTabIndex);
        },
        deactivateFocusable: function(child) {
            child.setTabIndex(this.inactiveChildTabIndex);
        },
        onFocusableContainerTabKey: function() {
            return true;
        },
        onFocusableContainerEnterKey: function() {
            return true;
        },
        onFocusableContainerSpaceKey: function() {
            return true;
        },
        onFocusableContainerUpKey: function(e) {
            
            e.preventDefault();
            return this.moveChildFocus(e, false);
        },
        onFocusableContainerDownKey: function(e) {
            
            e.preventDefault();
            return this.moveChildFocus(e, true);
        },
        onFocusableContainerLeftKey: function(e) {
            
            e.preventDefault();
            return this.moveChildFocus(e, false);
        },
        onFocusableContainerRightKey: function(e) {
            
            e.preventDefault();
            return this.moveChildFocus(e, true);
        },
        getFocusableFromEvent: function(e) {
            var child = Ext.Component.from(e);
            
            if (!child) {
                Ext.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: child,
                    step: forward
                });
            if (nextChild) {
                nextChild.focus();
            }
            return nextChild;
        },
        findNextFocusableChild: function(options) {
            
            var beforeRender = options.beforeRender,
                firstTabbable = options.firstTabbable,
                items, item, child, step, idx, i, len, allowDisabled;
            items = options.items || this.getFocusables();
            step = options.step != null ? options.step : 1;
            child = options.child;
            
            allowDisabled = !!this.allowFocusingDisabledChildren;
            
            
            
            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 || (item.isDisabled() && !allowDisabled)) {
                    
                    continue;
                }
                if (firstTabbable) {
                    if (item.isTabbable && item.isTabbable()) {
                        return item;
                    }
                }
                
                
                
                
                
                
                
                
                else if (beforeRender || (item.isFocusable && item.isFocusable())) {
                    return item;
                }
            }
            return null;
        },
        onFocusEnter: function(e) {
            var me = this,
                target = e.toComponent,
                child;
            
            
            
            
            
            if (target === me) {
                child = me.initDefaultFocusable();
                if (child) {
                    child.focus();
                }
            }
            
            me.activateFocusableContainer(true);
        },
        onFocusLeave: function(e) {
            var me = this;
            if (me.resetFocusPosition) {
                me.clearFocusables();
                me.initDefaultFocusable();
            }
        },
        beforeFocusableChildBlur: Ext.privateFn,
        afterFocusableChildBlur: Ext.privateFn,
        beforeFocusableChildFocus: function(child) {
            var me = this;
            if (!me.focusableContainer || me.destroying || me.destroyed) {
                return;
            }
            
            me.clearFocusables();
            me.activateFocusable(child);
        },
        afterFocusableChildFocus: function(child) {
            var me = this;
            if (!me.focusableContainer || me.destroying || me.destroyed) {
                return;
            }
            me.lastFocusedChild = child;
        },
        onFocusableChildAdd: function(child) {
            var me = this;
            if (child.focusable) {
                child.ownerFocusableContainer = me;
            }
        },
        onFocusableChildRemove: function(child) {
            var me = this,
                next;
            child.ownerFocusableContainer = null;
            
            
            if (child === me.lastFocusedChild) {
                me.lastFocusedChild = null;
                next = me.initDefaultFocusable();
                
                
                
                if (child.hasFocus) {
                    next = next || child.findFocusTarget();
                    if (next) {
                        next.focus();
                    }
                }
            }
            child = next || me.findNextFocusableChild({
                step: 1,
                beforeRender: true
            });
            if (!child) {
                me.activateFocusableContainer(false);
            }
        },
        beforeFocusableChildEnable: Ext.privateFn,
        onFocusableChildEnable: function(child) {
            var me = this,
                active;
            if (!me.focusableContainer || me.destroying || me.destroyed) {
                return;
            }
            
            if (me.containsFocus) {
                active = Ext.ComponentManager.getActiveComponent();
                me.clearFocusables();
                me.activateFocusable(active);
            } else if (me.resetFocusPosition || me.lastFocusedChild == null) {
                me.clearFocusables();
                if (child.hasFocus) {
                    me.activateFocusable(child);
                    active = child;
                }
            } else {
                me.deactivateFocusable(child);
                
                
                
                
                
                
                if (child === me.lastFocusedChild) {
                    me.clearFocusables();
                    me.activateFocusable(child);
                }
                active = me.findNextFocusableChild({
                    firstTabbable: true
                });
            }
            if (!active) {
                me.initDefaultFocusable();
            }
            
            me.activateFocusableContainer(true);
        },
        beforeFocusableChildDisable: function(child) {
            var me = this,
                next;
            if (!me.focusableContainer || me.destroying || me.destroyed) {
                return;
            }
            
            
            
            
            
            
            if (child.hasFocus) {
                next = me.findNextFocusableChild({
                    child: child
                }) || child.findFocusTarget();
                
                
                
                
                if (next) {
                    next.focus();
                }
            }
        },
        onFocusableChildDisable: function(child) {
            var me = this,
                next;
            if (!me.focusableContainer || me.destroying || me.destroyed) {
                return;
            }
            
            
            next = me.findNextFocusableChild({
                firstTabbable: true
            });
            
            if (!next) {
                next = me.initDefaultFocusable();
            }
            
            
            
            if (!next) {
                me.activateFocusableContainer(false);
            }
        },
        beforeFocusableChildHide: function(child) {
            return this.beforeFocusableChildDisable(child);
        },
        onFocusableChildHide: function(child) {
            return this.onFocusableChildDisable(child);
        },
        beforeFocusableChildShow: function(child) {
            return this.beforeFocusableChildEnable(child);
        },
        onFocusableChildShow: function(child) {
            return this.onFocusableChildEnable(child);
        },
        
        onFocusableChildMasked: Ext.privateFn,
        onFocusableChildDestroy: Ext.privateFn,
        onFocusableChildUpdate: Ext.privateFn
    },
    deprecated: {
        7: {
            configs: {
                enableFocusableContainer: 'focusableContainer'
            }
        }
    }
});


Ext.define('Ext.Container', {
    extend: Ext.Component,
    alternateClassName: [
        'Ext.lib.Container',
        'Ext.container.Container'
    ],
    xtype: 'container',
    isContainer: true,
    mixins: [
        Ext.mixin.Queryable,
        Ext.mixin.Container,
        Ext.mixin.FocusableContainer
    ],
    
    
    
    
    
    
    eventedConfig: {
        
        activeItem: 0
    },
    config: {
        activeItemIndex: null,
        
        autoSize: null,
        
        
        
        layout: 'auto',
        
        control: null,
        
        defaults: null,
        
        
        items: null,
        
        autoDestroy: true,
        
        defaultType: null,
        
        defaultFocus: {
            $value: null,
            lazy: true
        },
        
        innerCls: null,
        
        
        masked: null
    },
    
    weighted: false,
    
    manageBorders: false,
    classCls: Ext.baseCSSPrefix + 'container',
    managedBordersCls: Ext.baseCSSPrefix + 'managed-borders',
    template: [
        {
            reference: 'bodyElement',
            cls: Ext.baseCSSPrefix + 'body-el',
            uiCls: 'body-el'
        }
    ],
    constructor: function(config) {
        var me = this;
        me._items = me.items = new Ext.util.ItemCollection();
        me.innerItems = [];
        me.getReferences = me.getFirstReferences;
        me.onItemAdd = me.onFirstItemAdd;
        me.callParent(arguments);
        delete me.getReferences;
    },
    initialize: function() {
        var me = this;
        me.reference = me.setupReference(me.reference);
        me.callParent();
        if (me.manageBorders) {
            me.addCls(me.managedBordersCls);
        }
        
        
        
        me.getLayout();
    },
    
    applyMasked: function(masked) {
        var isVisible = true,
            currentMask;
        if (masked === false) {
            masked = true;
            isVisible = false;
        }
        
        currentMask = Ext.factory(masked, Ext['Mask'], this.getMasked());
        if (currentMask) {
            currentMask.setHidden(!isVisible);
            
            
            currentMask.render(this.el);
        }
        return currentMask;
    },
    
    mask: function(mask) {
        this.setMasked(mask || true);
    },
    
    unmask: function() {
        this.setMasked(false);
    },
    initInheritedState: function(inheritedState, inheritedStateInner) {
        this.callParent([
            inheritedState,
            inheritedStateInner
        ]);
        this.initContainerInheritedState(inheritedState, inheritedStateInner);
    },
    onAdded: function(parent, instanced) {
        this.callParent([
            parent,
            instanced
        ]);
        this.containerOnAdded(parent, instanced);
    },
    onRemoved: function(destroying) {
        this.containerOnRemoved(destroying);
        this.callParent([
            destroying
        ]);
    },
    afterItemShow: function(item) {
        var layout;
        if (item.getDocked()) {
            layout = this.getLayout();
            this.items.generation++;
            layout.handleDockedItemBorders();
        }
    },
    afterItemHide: function(item) {
        var layout;
        if (item.getDocked()) {
            layout = this.getLayout();
            this.items.generation++;
            layout.handleDockedItemBorders();
        }
    },
    applyItems: function(items, collection) {
        if (items) {
            var me = this,
                activeItem;
            me.getDefaultType();
            me.getDefaults();
            if (me.initialized && collection.length > 0) {
                me.removeAll();
            }
            
            
            if (me.weighted && !items.isWidget && !items.xtype) {
                items = Ext.convertKeyedItems(items);
            }
            me.add(items);
            
            if (me.initialized) {
                activeItem = me.initialConfig.activeItem || me.config.activeItem || 0;
                me.setActiveItem(activeItem);
            }
        }
    },
    
    applyControl: function(selectors) {
        var selector, key, listener, listeners;
        for (selector in selectors) {
            listeners = selectors[selector];
            for (key in listeners) {
                listener = listeners[key];
                if (Ext.isObject(listener)) {
                    listener.delegate = selector;
                }
            }
            listeners.delegate = selector;
            this.addListener(listeners);
        }
        return selectors;
    },
    updateDisabled: function(disabled) {
        var me = this;
        me.callParent([
            disabled
        ]);
        if (me.focusableContainer) {
            me.getItems();
            if (disabled) {
                me.element.saveTabbableState();
            } else {
                me.element.restoreTabbableState();
            }
            me.activateFocusableContainer(!disabled);
            if (!disabled) {
                me.initDefaultFocusable();
            }
        }
    },
    
    onFirstItemAdd: function(item) {
        var me = this;
        delete me.onItemAdd;
        if (item.isInner && me.innerHtmlElement && !me.getHtml() && !me.getTpl()) {
            me.innerHtmlElement.destroy();
            delete me.innerHtmlElement;
        }
        return me.onItemAdd.apply(me, arguments);
    },
    applyLayout: function(layout, oldLayout) {
        if (typeof layout === 'string') {
            layout = {
                type: layout
            };
        }
        if (oldLayout) {
            if (layout) {
                if (!layout.isLayout) {
                    oldLayout.setConfig(layout);
                } else 
                {
                    Ext.raise('Cannot change layout instances on ' + this.$className);
                }
            }
            
            return oldLayout;
        }
        
        if (!(layout && layout.isLayout)) {
            layout = Ext.Factory.layout(Ext.apply({
                container: this
            }, layout), Ext.layout.Auto);
        }
        this.link('layout', layout);
        return layout;
    },
    updateDefaultType: function(defaultType) {
        
        this.defaultItemClass = Ext.ClassManager.getByAlias('widget.' + defaultType);
        
        if (!this.defaultItemClass) {
            Ext.Logger.error("Invalid defaultType of: '" + defaultType + "', must be a valid component xtype");
        }
    },
    
    
    factoryItem: function(item) {
        
        if (!item) {
            Ext.Logger.error("Invalid item given: " + item + ", must be either the config object to factory a new item, " + "or an existing component instance");
        }
        
        var me = this;
        item = me.applyItemDefaults(item);
        if (!item.isComponent) {
            
            
            if (!me.$hasCachedDefaultItemClass) {
                me.getDefaultType();
                me.$hasCachedDefaultItemClass = true;
            }
            item = Ext.factory(item, me.defaultItemClass);
        }
        return item;
    },
    
    add: function(newItems) {
        var me = this,
            items = me.getItems(),
            weighted = me.weighted,
            addingArray = true,
            addedItems = [],
            doWeightedInsert, i, ln, item, instanced;
        if (!Ext.isArray(newItems)) {
            newItems = [
                newItems
            ];
            addingArray = false;
        }
        
        
        
        
        if (weighted) {
            if (items.length) {
                doWeightedInsert = true;
            } else {
                Ext.Array.sort(newItems, Ext.weightSortFn);
            }
        }
        for (i = 0 , ln = newItems.length; i < ln; i++) {
            item = newItems[i];
            if (item) {
                instanced = item.isWidget;
                if (!instanced) {
                    item.$initParent = me;
                }
                item = me.factoryItem(item);
                
                
                if (doWeightedInsert) {
                    me.doInsert(items.findInsertionIndex(item, Ext.weightSortFn), item, instanced);
                } else {
                    me.doAdd(item, instanced);
                }
                delete item.$initParent;
                if (me.focusableContainer) {
                    me.onFocusableChildAdd(item);
                }
                addedItems.push(item);
            }
            
            else if (item !== null) {
                Ext.raise('Invalid item passed to add');
            }
        }
        
        if ((me.isConfiguring || !me.getActiveItem()) && me.innerItems.length > 0) {
            me.setActiveItem(me.initialConfig.activeItem || 0);
        }
        if (me.rendered && ln && me.focusableContainer) {
            me.initFocusableContainer();
        }
        return addingArray ? addedItems : addedItems[0];
    },
    onItemWeightChange: function(item) {
        var items = this.getItems(),
            oldIndex = items.indexOf(item),
            index;
        items.remove(item);
        index = items.findInsertionIndex(item, Ext.weightSortFn);
        items.insert(index, item);
        this.insertInner(item, index);
        this.onItemMove(item, index, oldIndex);
    },
    
    doAdd: function(item, instanced) {
        var me = this,
            items = me.getItems(),
            index;
        if (!items.has(item)) {
            index = items.length;
            items.add(item);
            if (item.isInnerItem()) {
                me.insertInner(item, index);
            }
            item.onAdded(me, !!instanced);
            if (me.focusableContainer) {
                me.onFocusableChildAdd(item);
            }
            me.onItemAdd(item, index);
        }
    },
    
    remove: function(which, destroy) {
        var me = this,
            component = me.getComponent(which),
            
            activeItem, index, innerItems, item, wasActive;
        if (destroy === undefined) {
            destroy = me.getAutoDestroy();
        }
        if (!component) {
            
            if (!Ext.isArray(which)) {
                Ext.raise('Invalid first argument to Ext.Container#remove() - ', Ext.typeOf(which));
            }
            
            activeItem = me.getActiveItem();
            for (index = 0; index < which.length; ++index) {
                item = me.getComponent(which[index]);
                if (item === activeItem) {
                    wasActive = true;
                } else if (item) {
                    me.remove(item, destroy);
                }
            }
            
            if (wasActive) {
                me.remove(activeItem, destroy);
            }
            return which;
        }
        
        index = me.indexOf(component);
        innerItems = me.getInnerItems();
        if (index !== -1) {
            if (!me.removingAll && innerItems.length > 1 && component === me.getActiveItem()) {
                me.on({
                    activeitemchange: 'doRemove',
                    scope: me,
                    single: true,
                    order: 'after',
                    args: [
                        component,
                        index,
                        destroy
                    ]
                });
                me.doResetActiveItem(innerItems.indexOf(component));
            } else {
                me.doRemove(component, index, destroy);
                if (innerItems.length === 0) {
                    me.setActiveItem(null);
                }
            }
        }
        return component;
    },
    doResetActiveItem: function(innerIndex) {
        if (innerIndex === 0) {
            this.setActiveItem(1);
        } else {
            this.setActiveItem(0);
        }
    },
    doRemove: function(item, index, destroy) {
        var me = this;
        
        
        if (!me.destroying) {
            me.items.remove(item);
            if (item.isInnerItem()) {
                me.removeInner(item);
            }
            me.onItemRemove(item, index, destroy);
        }
        if (!item.destroyed) {
            item.onRemoved(item.destroying || destroy);
        }
        if (me.focusableContainer && !me.destroying && !me.destroyed) {
            me.onFocusableChildRemove(item, destroy);
        }
        if (destroy && !item.destroyed) {
            item.destroy();
        }
    },
    
    removeAll: function(destroy, everything) {
        var me = this,
            destroying = me.destroying,
            items = me.items,
            removed = destroying ? null : [],
            ln = items.length,
            i, item;
        if (typeof destroy !== 'boolean') {
            destroy = this.getAutoDestroy();
        }
        
        
        me.removingAll = true;
        for (i = 0; i < ln; i++) {
            item = items.getAt(i);
            if (item && (everything || item.isInnerItem())) {
                me.doRemove(item, i, destroy);
                
                
                if (!destroying) {
                    i--;
                    ln--;
                }
            }
            if (removed) {
                removed.push(item);
            }
        }
        if (!destroying) {
            me.setActiveItem(null);
        }
        me.removingAll = false;
        return removed;
    },
    
    getAt: function(index) {
        return this.items.getAt(index);
    },
    getInnerAt: function(index) {
        return this.innerItems[index];
    },
    
    removeAt: function(index, destroy) {
        var item = this.getAt(index);
        if (item) {
            this.remove(item, destroy);
        }
        return item;
    },
    
    removeInnerAt: function(index) {
        var item = this.getInnerItems()[index];
        if (item) {
            this.remove(item);
        }
        return item;
    },
    
    has: function(item) {
        return this.getItems().indexOf(item) != -1;
    },
    
    hasInnerItem: function(item) {
        return this.innerItems.indexOf(item) != -1;
    },
    
    indexOf: function(item) {
        return this.getItems().indexOf(item);
    },
    innerIndexOf: function(item) {
        return this.innerItems.indexOf(item);
    },
    
    insertInner: function(item, index) {
        var items = this.getItems().items,
            innerItems = this.innerItems,
            currentInnerIndex = innerItems.indexOf(item),
            newInnerIndex = -1,
            nextSibling;
        if (currentInnerIndex !== -1) {
            innerItems.splice(currentInnerIndex, 1);
        }
        if (typeof index == 'number') {
            do {
                nextSibling = items[++index];
            } while (nextSibling && !nextSibling.isInnerItem());
            if (nextSibling) {
                newInnerIndex = innerItems.indexOf(nextSibling);
                innerItems.splice(newInnerIndex, 0, item);
            }
        }
        if (newInnerIndex === -1) {
            innerItems.push(item);
            newInnerIndex = innerItems.length - 1;
        }
        if (currentInnerIndex !== -1) {
            this.onInnerItemMove(item, newInnerIndex, currentInnerIndex);
        }
        return this;
    },
    onInnerItemMove: Ext.emptyFn,
    
    removeInner: function(item) {
        Ext.Array.remove(this.innerItems, item);
        return this;
    },
    
    insert: function(index, item) {
        var me = this,
            instanced, i;
        
        if (typeof index != 'number') {
            Ext.Logger.error("Invalid index of '" + index + "', must be a valid number");
        }
        
        if (Ext.isArray(item)) {
            for (i = item.length - 1; i >= 0; i--) {
                me.insert(index, item[i]);
            }
            return me;
        }
        instanced = item.isWidget;
        if (!instanced) {
            item.$initParent = me;
        }
        item = me.factoryItem(item);
        me.doInsert(index, item, instanced);
        delete item.$initParent;
        return item;
    },
    
    doInsert: function(index, item, instanced) {
        var me = this,
            items = me.items,
            itemsLength = items.length,
            currentIndex, isInnerItem;
        isInnerItem = item.isInnerItem();
        if (index > itemsLength) {
            index = itemsLength;
        }
        if (items[index - 1] === item) {
            return;
        }
        currentIndex = me.indexOf(item);
        if (currentIndex !== -1) {
            items.removeAt(currentIndex);
        }
        items.insert(index, item);
        if (currentIndex === -1) {
            item.onAdded(me, !!instanced);
        }
        if (isInnerItem) {
            me.insertInner(item, index);
        }
        if (currentIndex !== -1) {
            me.onItemMove(item, index, currentIndex);
        } else {
            me.onItemAdd(item, index);
        }
    },
    
    insertFirst: function(item) {
        return this.insert(0, item);
    },
    
    insertLast: function(item) {
        return this.insert(this.getItems().length, item);
    },
    
    insertBefore: function(item, relativeToItem) {
        var index = this.indexOf(relativeToItem);
        if (index !== -1) {
            this.insert(index, item);
        }
        return this;
    },
    
    insertAfter: function(item, relativeToItem) {
        var index = this.indexOf(relativeToItem);
        if (index !== -1) {
            this.insert(index + 1, item);
        }
        return this;
    },
    
    onItemAdd: function(item, index) {
        var me = this;
        me.doItemLayoutAdd(item, index);
        if (me.initialized) {
            if (item.hasListeners.added) {
                item.fireEvent('added', item, me, index);
            }
            if (me.hasListeners.add) {
                me.fireEvent('add', me, item, index);
            }
        }
    },
    doItemLayoutAdd: function(item, index) {
        var layout = this.getLayout();
        if (this.rendered && !item.rendered) {
            item.fireAction('renderedchange', [
                this,
                item,
                true
            ], 'onItemAdd', layout, {
                args: [
                    item,
                    index
                ]
            });
        } else {
            layout.onItemAdd(item, index);
        }
    },
    
    onItemRemove: function(item, index, destroying) {
        var me = this;
        me.doItemLayoutRemove(item, index, destroying);
        if (item.hasListeners.removed) {
            item.fireEvent('removed', item, me, index);
        }
        if (me.hasListeners.remove) {
            me.fireEvent('remove', me, item, index);
        }
    },
    doItemLayoutRemove: function(item, index, destroying) {
        var layout = this.getLayout();
        if (item.rendered) {
            item.setRendered(false);
            item.fireAction('renderedchange', [
                this,
                item,
                false
            ], 'onItemRemove', layout, {
                args: [
                    item,
                    index,
                    destroying
                ]
            });
        } else {
            layout.onItemRemove(item, index, destroying);
        }
    },
    
    onItemMove: function(item, toIndex, fromIndex) {
        var me = this;
        me.doItemLayoutMove(item, toIndex, fromIndex);
        if (item.hasListeners.moved) {
            item.fireEvent('moved', item, me, toIndex, fromIndex);
        }
        if (me.hasListeners.move) {
            me.fireEvent('move', me, item, toIndex, fromIndex);
        }
    },
    doItemLayoutMove: function(item, toIndex, fromIndex) {
        this.getLayout().onItemMove(item, toIndex, fromIndex);
    },
    onItemInnerStateChange: function(item, isInner) {
        var layout = this.getLayout();
        if (isInner) {
            this.insertInner(item, this.items.indexOf(item));
        } else {
            this.removeInner(item);
        }
        layout.onItemInnerStateChange.apply(layout, arguments);
    },
    onItemFloatedChange: function(item, floated) {
        var layout = this.getLayout();
        layout.onItemFloatedChange(item, floated);
    },
    
    getInnerItems: function() {
        return this.innerItems;
    },
    
    getDockedItems: function() {
        var items = this.getItems().items,
            dockedItems = [],
            ln = items.length,
            item, i;
        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.isDocked()) {
                dockedItems.push(item);
            }
        }
        return dockedItems;
    },
    
    applyActiveItem: function(activeItem, currentActiveItem) {
        var me = this,
            innerItems = me.getInnerItems(),
            initialConfig = me.initialConfig,
            initialActive = initialConfig.activeItem || activeItem;
        
        me.getItems();
        if (me.isConfiguring && !initialConfig.activeItem) {
            activeItem = initialActive;
        }
        
        if (!activeItem && innerItems.length === 0) {
            return 0;
        } else if (typeof activeItem == 'number') {
            activeItem = Math.max(0, Math.min(activeItem, innerItems.length - 1));
            activeItem = innerItems[activeItem];
            if (activeItem) {
                return activeItem;
            } else if (currentActiveItem) {
                return null;
            }
        } else if (activeItem) {
            var item;
            
            if (typeof activeItem == 'string') {
                item = me.child(activeItem);
            } else if (activeItem.isComponent) {
                item = activeItem;
            } else {
                activeItem = Ext.apply({
                    $initParent: me
                }, activeItem);
                item = me.factoryItem(activeItem);
            }
            if (!item) {
                return null;
            }
            me.pendingActiveItem = item;
            
            if (!item.isInnerItem()) {
                Ext.Logger.error("Setting activeItem to be a non-inner item");
            }
            
            if (!me.has(item)) {
                me.add(item);
            }
            delete item.$initParent;
            return item;
        }
    },
    
    animateActiveItem: function(activeItem, animation) {
        var layout = this.getLayout(),
            defaultAnimation;
        if (this.activeItemAnimation) {
            this.activeItemAnimation.destroy();
        }
        this.activeItemAnimation = animation = new Ext.Factory.layoutCardFx(animation);
        if (animation && layout.isCard) {
            animation.setLayout(layout);
            defaultAnimation = layout.getAnimation();
            if (defaultAnimation) {
                defaultAnimation.disable();
            }
            animation.on('animationend', function() {
                if (defaultAnimation) {
                    defaultAnimation.enable();
                }
                animation.destroy();
            }, this);
        }
        return this.setActiveItem(activeItem);
    },
    updateActiveItem: function(newActiveItem, oldActiveItem) {
        delete this.pendingActiveItem;
        if (oldActiveItem && !oldActiveItem.destroyed) {
            oldActiveItem.fireEvent('deactivate', oldActiveItem, this, newActiveItem);
        }
        if (newActiveItem) {
            newActiveItem.fireEvent('activate', newActiveItem, this, oldActiveItem);
        }
        this.setActiveItemIndex(this.innerItems.indexOf(newActiveItem));
    },
    updateActiveItemIndex: function(index) {
        this.setActiveItem(this.innerItems[index]);
    },
    
    getRefItems: function(deep) {
        var items = this.getItems().items,
            result, ln, i, item;
        if (items) {
            
            if (deep) {
                result = [];
                for (i = 0 , ln = items.length; i < ln; i++) {
                    item = items[i];
                    result[result.length] = item;
                    if (item.getRefItems) {
                        result.push.apply(result, item.getRefItems(true));
                    }
                }
            } else 
            {
                result = items.slice();
            }
        }
        
        
        return result || [];
    },
    
    getComponent: function(component) {
        if (typeof component === 'number') {
            return this.getItems().getAt(component);
        }
        if (Ext.isObject(component)) {
            component = component.getItemId();
        }
        return this.getItems().get(component);
    },
    
    getDockedComponent: function(component) {
        if (Ext.isObject(component)) {
            component = component.getItemId();
        }
        var dockedItems = this.getDockedItems(),
            ln = dockedItems.length,
            item, i;
        if (Ext.isNumber(component)) {
            return dockedItems[component];
        }
        for (i = 0; i < ln; i++) {
            item = dockedItems[i];
            if (item.id == component) {
                return item;
            }
        }
        return false;
    },
    doDestroy: function() {
        var me = this;
        if (me.focusableContainer) {
            me.destroyFocusableContainer();
        }
        me.removeAll(true, true);
        Ext.destroy(me.items, me.getMasked());
        me.items = null;
        
        if (me._layout) {
            me._layout = Ext.destroy(me._layout);
        }
        me.callParent();
    },
    
    getFocusEl: function() {
        var delegate = this.findDefaultFocus();
        if (delegate) {
            return delegate.isWidget ? delegate.getFocusEl() : delegate;
        } else if (this.focusable) {
            return this.focusEl;
        }
        
        return undefined;
    },
    
    findDefaultFocus: function() {
        var result = this.getDefaultFocus();
        
        
        
        
        if (result && !result.isWidget) {
            result = this.down(result);
            if (result && !result.canFocus()) {
                return;
            }
        }
        
        return result;
    },
    onFocusEnter: function(e) {
        var me = this;
        me.callParent([
            e
        ]);
        
        
        
        
        if (me.focusableContainer && !me.destroying && !me.destroyed) {
            me.mixins.focusablecontainer.onFocusEnter.call(me, e);
        }
    },
    onFocusLeave: function(e) {
        var me = this;
        me.callParent([
            e
        ]);
        
        if (me.focusableContainer && !me.destroying && !me.destroyed) {
            me.mixins.focusablecontainer.onFocusLeave.call(me, e);
        }
    },
    updateInnerCls: function(innerCls, old) {
        var el = this.getRenderTarget();
        el.replaceCls(old, innerCls);
    },
    updateAutoSize: function(autoSize) {
        var me = this,
            bodySizerElement = me.bodySizerElement;
        if (autoSize === false) {
            if (!bodySizerElement) {
                me.bodySizerElement = me.bodyElement.wrap({
                    cls: Ext.baseCSSPrefix + 'body-sizer-el'
                });
            }
        } else if (bodySizerElement) {
            me.bodyElement.unwrap();
            bodySizerElement.destroy();
            me.bodySizerElement = null;
        }
    },
    updateMaxHeight: function(maxHeight, oldMaxHeight) {
        var me = this,
            height, stashedHeight, maxHeightElement;
        me.callParent([
            maxHeight,
            oldMaxHeight
        ]);
        if (Ext.isIE11 && (maxHeight != null) && (me.getAutoSize() !== false)) {
            me.getMaxHeightElement().setMaxHeight(maxHeight);
            me.addCls(Ext.baseCSSPrefix + 'max-height-wrapped');
        }
    },
    privates: {
        
        getFirstReferences: function() {
            var me = this;
            delete me.getReferences;
            me.getItems();
            
            return me.getReferences.apply(me, arguments);
        },
        
        getPositionedItemTarget: function() {
            return this.getRenderTarget();
        },
        
        applyItemDefaults: function(item) {
            var defaults = this.getDefaults();
            if (defaults && !item.ignoreDefaults) {
                if (item.isComponent) {
                    if (item.isInnerItem() && !this.has(item)) {
                        if (Ext.isFunction(defaults)) {
                            defaults = defaults(item);
                        }
                        item.setConfig(defaults, null, {
                            defaults: true
                        });
                    }
                } else 
                
                {
                    if (Ext.isFunction(defaults)) {
                        defaults = defaults(item);
                    }
                    
                    item = Ext.merge({}, defaults, item);
                }
            }
            return item;
        },
        setChildRendered: function(rendered, item) {
            if (item.isInnerItem()) {
                this.getLayout().renderInnerItem(item);
            } else if (!rendered || !item.getFloated()) {
                
                
                
                item.setRendered(rendered);
            }
        },
        
        getMaxHeightElement: function() {
            var el = this.el,
                maxHeightElement = this.maxHeightElement,
                selector = '.x-dock,.x-panelheader,.x-body-el,.x-body-wrap-el,.x-tab-guard-el',
                childNodes, node, i, ln;
            if (!maxHeightElement) {
                this.maxHeightElement = maxHeightElement = el.insertFirst({
                    cls: Ext.baseCSSPrefix + 'max-height-el'
                });
                childNodes = Ext.Array.clone(el.dom.childNodes);
                for (i = 1 , ln = childNodes.length; i < ln; i++) {
                    node = childNodes[i];
                    if (Ext.fly(node).is(selector)) {
                        maxHeightElement.appendChild(node);
                    }
                }
            }
            return maxHeightElement;
        }
    }
}, function() {
    this.prototype.defaultItemClass = this;
});


Ext.define('Ext.mixin.Hookable', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'hookable'
    },
    bindHook: function(instance, boundMethod, bindingMethod, preventDefault, extraArgs) {
        instance.afterMethod(boundMethod, bindingMethod || boundMethod, this, preventDefault, extraArgs);
        return this;
    },
    unbindHook: function(instance, boundMethod, bindingMethod) {
        instance.removeMethodListener(boundMethod, bindingMethod || boundMethod, this);
        return this;
    }
});


Ext.define('Ext.util.Wrapper', {
    mixins: [
        Ext.mixin.Hookable
    ],
    constructor: function(elementConfig, wrappedElement) {
        var element = this.link('element', Ext.Element.create(elementConfig));
        if (wrappedElement) {
            element.insertBefore(wrappedElement);
            this.wrap(wrappedElement);
        }
    },
    bindSize: function(sizeName) {
        var wrappedElement = this.wrappedElement,
            boundMethodName;
        this.boundSizeName = sizeName;
        this.boundMethodName = boundMethodName = sizeName === 'width' ? 'setWidth' : 'setHeight';
        this.bindHook(wrappedElement, boundMethodName, 'onBoundSizeChange');
        wrappedElement[boundMethodName].call(wrappedElement, wrappedElement.getStyleValue(sizeName));
    },
    onBoundSizeChange: function(size, args) {
        var element = this.element;
        if (typeof size === 'string' && size.substr(-1) === '%') {
            args[0] = '100%';
        } else {
            size = '';
        }
        element[this.boundMethodName].call(element, size);
    },
    wrap: function(wrappedElement) {
        var element = this.element,
            innerDom;
        this.wrappedElement = wrappedElement;
        innerDom = element.dom;
        while (innerDom.firstElementChild !== null) {
            innerDom = innerDom.firstElementChild;
        }
        innerDom.appendChild(wrappedElement.dom);
    },
    destroy: function() {
        var me = this,
            element = me.element,
            dom = element.dom,
            wrappedElement = me.wrappedElement,
            boundMethodName = me.boundMethodName,
            parentNode = dom.parentNode,
            size;
        if (boundMethodName) {
            me.unbindHook(wrappedElement, boundMethodName, 'onBoundSizeChange');
            size = element.getStyle(me.boundSizeName);
            if (size) {
                wrappedElement[boundMethodName].call(wrappedElement, size);
            }
        }
        if (parentNode) {
            if (!wrappedElement.destroyed) {
                parentNode.replaceChild(dom.firstElementChild, dom);
            }
            delete me.wrappedElement;
        }
        me.callParent();
    }
});


Ext.define('Ext.layout.wrapper.BoxDock', {
    config: {
        direction: 'horizontal',
        element: {
            className: Ext.baseCSSPrefix + 'dock'
        },
        innerWrapper: null,
        container: null,
        manageBorders: null
    },
    positionMap: {
        top: 'start',
        left: 'start',
        bottom: 'end',
        right: 'end'
    },
    managedBordersCls: Ext.baseCSSPrefix + 'managed-borders',
    constructor: function(config) {
        this.items = {
            start: [],
            end: []
        };
        this.itemsCount = 0;
        this.initConfig(config);
    },
    addItems: function(items) {
        var i, ln, item;
        for (i = 0 , ln = items.length; i < ln; i++) {
            item = items[i];
            this.addItem(item);
        }
    },
    addItem: function(item) {
        var docked = item.getDocked(),
            position = this.positionMap[docked],
            wrapper = item.$dockWrapper,
            container = this.getContainer(),
            index = container.indexOf(item),
            element = item.element,
            items = this.items,
            sideItems = items[position],
            i, ln, sibling, referenceElement, siblingIndex;
        if (wrapper) {
            wrapper.removeItem(item);
        }
        item.$dockWrapper = this;
        item.addCls(Ext.baseCSSPrefix + 'dock-item');
        item.addCls(Ext.baseCSSPrefix + 'docked-' + docked);
        for (i = 0 , ln = sideItems.length; i < ln; i++) {
            sibling = sideItems[i];
            siblingIndex = container.indexOf(sibling);
            if (siblingIndex > index) {
                referenceElement = sibling.element;
                sideItems.splice(i, 0, item);
                break;
            }
        }
        if (!referenceElement) {
            sideItems.push(item);
            referenceElement = this.getInnerWrapper().getElement();
        }
        this.itemsCount++;
        if (position === 'start') {
            element.insertBefore(referenceElement);
        } else {
            element.insertAfter(referenceElement);
        }
    },
    removeItem: function(item, oldDocked) {
        var me = this,
            position = oldDocked || item.getDocked(),
            items = me.items[me.positionMap[position]];
        Ext.Array.remove(items, item);
        item.element.detach();
        delete item.$dockWrapper;
        item.removeCls(Ext.baseCSSPrefix + 'dock-item');
        item.removeCls(Ext.baseCSSPrefix + 'docked-' + position);
        if (--me.itemsCount === 0) {
            me.destroy();
        }
    },
    getItemsSlice: function(index) {
        var container = this.getContainer(),
            items = this.items,
            slice = [],
            sideItems, i, ln, item;
        for (sideItems = items.start , i = 0 , ln = sideItems.length; i < ln; i++) {
            item = sideItems[i];
            if (container.indexOf(item) > index) {
                slice.push(item);
            }
        }
        for (sideItems = items.end , i = 0 , ln = sideItems.length; i < ln; i++) {
            item = sideItems[i];
            if (container.indexOf(item) > index) {
                slice.push(item);
            }
        }
        return slice;
    },
    applyElement: function(element) {
        return Ext.Element.create(element);
    },
    updateElement: function(element) {
        element.addCls(Ext.baseCSSPrefix + 'dock-' + this.getDirection());
    },
    updateInnerWrapper: function(innerWrapper, oldInnerWrapper) {
        if (oldInnerWrapper) {
            innerWrapper.getElement().replace(oldInnerWrapper.getElement(), false);
            oldInnerWrapper.$outerWrapper = null;
        } else {
            this.getElement().append(innerWrapper.getElement());
        }
        innerWrapper.setManageBorders(this.getManageBorders());
        innerWrapper.$outerWrapper = this;
    },
    updateManageBorders: function(manageBorders) {
        var me = this,
            innerWrapper = me.getInnerWrapper();
        me.getElement().toggleCls(me.managedBordersCls, manageBorders);
        if (innerWrapper) {
            innerWrapper.setManageBorders(manageBorders);
        }
    },
    destroy: function() {
        var me = this,
            innerWrapper = me.getInnerWrapper(),
            outerWrapper = me.$outerWrapper,
            innerWrapperElement;
        if (innerWrapper) {
            if (outerWrapper) {
                outerWrapper.setInnerWrapper(innerWrapper);
            } else {
                innerWrapperElement = innerWrapper.getElement();
                if (!innerWrapperElement.destroyed) {
                    innerWrapperElement.replace(me.getElement());
                }
                delete innerWrapper.$outerWrapper;
            }
        }
        delete me.$outerWrapper;
        me.unlink([
            '_element'
        ]);
        me.callParent();
    }
});


Ext.define('Ext.layout.wrapper.Inner', {
    config: {
        container: null,
        manageBorders: null
    },
    constructor: function(config) {
        this.initConfig(config);
    },
    getElement: function() {
        var container = this.getContainer();
        
        
        
        
        return container.boxScrollerElement || container.bodySizerElement || container.bodyElement;
    },
    setInnerWrapper: Ext.emptyFn,
    getInnerWrapper: Ext.emptyFn
});


Ext.define('Ext.layout.Auto', {
    alias: [
        'layout.default',
        'layout.auto'
    ],
    alternateClassName: 'Ext.layout.Default',
    mixins: [
        Ext.mixin.Observable,
        Ext.mixin.Factoryable
    ],
    factoryConfig: {
        type: 'layout',
        defaultType: 'auto',
        instanceProp: 'isLayout'
    },
    isLayout: true,
    config: {
        
        animation: null,
        
        container: null
    },
    centerCls: Ext.baseCSSPrefix + 'center',
    cls: Ext.baseCSSPrefix + 'layout-auto',
    itemCls: Ext.baseCSSPrefix + 'layout-auto-item',
    spaceRe: /\s+/,
    positionMap: {
        top: 'start',
        left: 'start',
        middle: 'center',
        bottom: 'end',
        right: 'end'
    },
    positionDirectionMap: {
        top: 'vertical',
        bottom: 'vertical',
        left: 'horizontal',
        right: 'horizontal'
    },
    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);
    },
    updateContainer: function(container, oldContainer) {
        var me = this;
        me.dockedItems = [];
        container.getRenderTarget().addCls(me.cls);
        if (container.initialized) {
            me.onContainerInitialized();
        } else {
            container.onInitialized('onContainerInitialized', me);
        }
    },
    onContainerInitialized: function() {
        var me = this;
        me.handleDockedItemBorders();
        me.getContainer().on({
            delegate: '> component',
            beforecenteredchange: 'onItemCenteredChange',
            positionedchange: 'onItemPositionedChange',
            afterdockedchange: 'onAfterItemDockedChange',
            
            scope: me
        });
    },
    onItemAdd: function(item) {
        var me = this,
            container = me.getContainer(),
            floated = item.getFloated();
        if (item.getDocked() != null) {
            me.dockItem(item);
        } else if (item.isCentered()) {
            me.onItemCenteredChange(item, true);
        } else if (item.isPositioned()) {
            me.onItemPositionedChange(item, true);
        } else if (!floated) {
            me.onItemInnerStateChange(item, true);
        }
        if (container.rendered && !floated) {
            if (item.isInnerItem()) {
                me.renderInnerItem(item, true);
            } else {
                item.setRendered(true, true);
            }
        }
    },
    
    onItemInnerStateChange: function(item, isInner, destroying) {
        var itemCls = this.itemCls;
        if (isInner) {
            this.insertInnerItem(item, this.getContainer().innerIndexOf(item));
            item.addCls(itemCls);
        } else {
            this.removeInnerItem(item);
            item.removeCls(itemCls);
        }
    },
    insertInnerItem: function(item, index) {
        var itemDom = item.element.dom,
            container = this.getContainer(),
            renderTarget = container.getRenderTarget(item),
            nextSibling = null;
        if (index !== -1) {
            if (renderTarget === container.getRenderTarget()) {
                nextSibling = container.getInnerAt(index + 1);
                nextSibling = nextSibling ? nextSibling.element.dom : null;
            } else {
                nextSibling = renderTarget.dom.childNodes[index];
            }
        }
        renderTarget.dom.insertBefore(itemDom, nextSibling);
    },
    insertPositionedItem: function(item) {
        var me = this,
            renderTarget = me.getContainer().getPositionedItemTarget(item).dom;
        if (item.getZIndex() === null) {
            item.setZIndex((me.getContainer().indexOf(item) + 1) * 2);
        }
        renderTarget.insertBefore(item.element.dom, renderTarget.firstChild);
        return me;
    },
    removeInnerItem: function(item) {
        item.element.detach();
    },
    removePositionedItem: function(item) {
        item.setZIndex(null);
        item.element.detach();
    },
    onItemRemove: function(item, index, destroying) {
        var me = this;
        if (item.getDocked()) {
            me.undockItem(item);
        } else if (item.isCentered()) {
            me.onItemCenteredChange(item, false);
        } else if (item.isPositioned()) {
            me.onItemPositionedChange(item, false);
        } else if (!item.getFloated()) {
            me.onItemInnerStateChange(item, false, destroying);
        }
    },
    onItemMove: function(item, toIndex, fromIndex) {
        if (item.isCentered() || item.isPositioned()) {
            item.setZIndex((toIndex + 1) * 2);
        } else if (item.isInnerItem()) {
            this.insertInnerItem(item, this.getContainer().innerIndexOf(item));
        } else {
            this.undockItem(item);
            this.dockItem(item);
        }
    },
    onItemCenteredChange: function(item, centered) {
        var wrapperName = '$centerWrapper';
        if (item.getFloated()) {
            item.center();
        } else {
            if (centered) {
                this.insertPositionedItem(item);
                item.link(wrapperName, new Ext.util.Wrapper({
                    className: this.centerCls
                }, item.element));
            } else {
                item.unlink([
                    wrapperName
                ]);
                this.removePositionedItem(item);
            }
        }
    },
    onItemPositionedChange: function(item, positioned) {
        if (positioned) {
            this.insertPositionedItem(item);
        } else {
            this.removePositionedItem(item);
        }
    },
    onAfterItemDockedChange: function(item, docked, oldDocked) {
        
        
        if (item.initialized) {
            if (oldDocked) {
                this.undockItem(item, oldDocked);
            }
            if (docked) {
                this.dockItem(item);
            }
        }
    },
    dockItem: function(item) {
        var me = this,
            BoxDock = Ext.layout.wrapper.BoxDock,
            dockedItems = me.dockedItems,
            ln = dockedItems.length,
            container = me.getContainer(),
            itemIndex = container.indexOf(item),
            positionDirectionMap = me.positionDirectionMap,
            direction = positionDirectionMap[item.getDocked()],
            dockInnerWrapper = me.dockInnerWrapper,
            needsInnerWrapper = !dockInnerWrapper,
            referenceDirection, i, dockedItem, index, previousItem, slice, referenceItem, referenceDocked, referenceWrapper, newWrapper, nestedWrapper, oldInnerWrapper;
        if (needsInnerWrapper) {
            dockInnerWrapper = new Ext.layout.wrapper.Inner({
                container: container
            });
        }
        if (ln === 0) {
            dockedItems.push(item);
            newWrapper = new BoxDock({
                container: container,
                direction: direction,
                manageBorders: container.manageBorders
            });
            newWrapper.getElement().replace(dockInnerWrapper.getElement(), false);
            newWrapper.setInnerWrapper(dockInnerWrapper);
            newWrapper.addItem(item);
        } else {
            for (i = 0; i < ln; i++) {
                dockedItem = dockedItems[i];
                index = container.indexOf(dockedItem);
                if (index > itemIndex) {
                    referenceItem = previousItem || dockedItems[0];
                    dockedItems.splice(i, 0, item);
                    break;
                }
                previousItem = dockedItem;
            }
            if (!referenceItem) {
                referenceItem = dockedItems[ln - 1];
                dockedItems.push(item);
            }
            referenceDocked = referenceItem.getDocked();
            referenceWrapper = referenceItem.$dockWrapper;
            referenceDirection = positionDirectionMap[referenceDocked];
            if (direction === referenceDirection) {
                referenceWrapper.addItem(item);
            } else {
                slice = referenceWrapper.getItemsSlice(itemIndex);
                newWrapper = new BoxDock({
                    container: container,
                    direction: direction
                });
                if (slice.length > 0) {
                    if (slice.length === referenceWrapper.itemsCount) {
                        nestedWrapper = referenceWrapper;
                        newWrapper.getElement().replace(nestedWrapper.getElement(), false);
                        newWrapper.setInnerWrapper(nestedWrapper);
                    } else {
                        nestedWrapper = new BoxDock({
                            container: container,
                            direction: referenceDirection
                        });
                        oldInnerWrapper = referenceWrapper.getInnerWrapper();
                        newWrapper.setInnerWrapper(nestedWrapper);
                        referenceWrapper.setInnerWrapper(newWrapper);
                        nestedWrapper.setInnerWrapper(oldInnerWrapper);
                        nestedWrapper.addItems(slice);
                    }
                } else {
                    oldInnerWrapper = referenceWrapper.getInnerWrapper();
                    referenceWrapper.setInnerWrapper(newWrapper);
                    newWrapper.setInnerWrapper(oldInnerWrapper);
                }
                newWrapper.addItem(item);
            }
        }
        if (newWrapper) {
            me.link('dockOuterWrapper', newWrapper);
        }
        if (needsInnerWrapper) {
            me.link('dockInnerWrapper', dockInnerWrapper);
        }
        if (container.initialized) {
            me.handleDockedItemBorders();
        }
    },
    getDockWrapper: function() {
        var dockedItems = this.dockedItems;
        if (dockedItems.length > 0) {
            return dockedItems[0].$dockWrapper;
        }
        return null;
    },
    undockItem: function(item, oldDocked) {
        var me = this,
            dockedItems = me.dockedItems,
            lastBorderMask, lastBorderCollapse,
            dockWrapper = item.$dockWrapper;
        if (dockWrapper) {
            dockWrapper.removeItem(item, oldDocked);
        }
        if (me.getContainer().initialized) {
            lastBorderMask = item.lastBorderMask;
            lastBorderCollapse = item.lastBorderCollapse;
            if (lastBorderMask) {
                item.lastBorderMask = 0;
                item.removeCls(me.noBorderClassTable[lastBorderMask]);
            }
            if (lastBorderCollapse) {
                item.lastBorderCollapse = 0;
                item.removeCls(me.getBorderCollapseTable()[lastBorderCollapse]);
            }
            me.handleDockedItemBorders();
        }
        Ext.Array.remove(dockedItems, item);
    },
    destroy: function() {
        this.dockedItems = null;
        Ext.destroy(this.getAnimation());
        this.callParent();
    },
    
    noBorderClassTable: [
        0,
        
        Ext.baseCSSPrefix + 'noborder-l',
        
        Ext.baseCSSPrefix + 'noborder-b',
        
        Ext.baseCSSPrefix + 'noborder-bl',
        
        Ext.baseCSSPrefix + 'noborder-r',
        
        Ext.baseCSSPrefix + 'noborder-rl',
        
        Ext.baseCSSPrefix + 'noborder-rb',
        
        Ext.baseCSSPrefix + 'noborder-rbl',
        
        Ext.baseCSSPrefix + 'noborder-t',
        
        Ext.baseCSSPrefix + 'noborder-tl',
        
        Ext.baseCSSPrefix + 'noborder-tb',
        
        Ext.baseCSSPrefix + 'noborder-tbl',
        
        Ext.baseCSSPrefix + 'noborder-tr',
        
        Ext.baseCSSPrefix + 'noborder-trl',
        
        Ext.baseCSSPrefix + 'noborder-trb',
        
        Ext.baseCSSPrefix + 'noborder-trbl'
    ],
    
    
    edgeMasks: {
        top: 8,
        right: 4,
        bottom: 2,
        left: 1
    },
    handleDockedItemBorders: function(force) {
        var me = this,
            edges = 0,
            maskT = 8,
            maskR = 4,
            maskB = 2,
            maskL = 1,
            container = me.getContainer(),
            bodyBorder = container.getBodyBorder && container.getBodyBorder(),
            containerBorder = container.getBorder(),
            collapsed = me.collapsed,
            edgeMasks = me.edgeMasks,
            noBorderCls = me.noBorderClassTable,
            dockedItemsGen = container.items.generation,
            bodyClsEl = container.boxScrollerElement || container.bodyElement,
            b, borderCls, docked, edgesTouched, i, ln, item, dock, lastValue, mask, addCls, removeCls, header;
        if ((!force && (me.initializedBorders === dockedItemsGen)) || !container.manageBorders) {
            return;
        }
        addCls = [];
        removeCls = [];
        borderCls = me.getBorderCollapseTable();
        noBorderCls = me.getBorderClassTable ? me.getBorderClassTable() : noBorderCls;
        me.initializedBorders = dockedItemsGen;
        
        me.collapsed = false;
        docked = container.getDockedItems();
        me.collapsed = collapsed;
        header = container.getHeader && container.getHeader();
        if (header) {
            docked = ([
                header
            ]).concat(docked);
        }
        for (i = 0 , ln = docked.length; i < ln; i++) {
            item = docked[i];
            if (item.getHidden()) {
                
                continue;
            }
            dock = item.isPanelHeader ? item.getPosition() : item.getDocked();
            mask = edgesTouched = 0;
            addCls.length = 0;
            removeCls.length = 0;
            if (dock !== 'bottom') {
                if (edges & maskT) {
                    
                    b = item.border;
                } else {
                    b = containerBorder;
                    if (b !== false) {
                        edgesTouched += maskT;
                    }
                }
                if (b === false) {
                    mask += maskT;
                }
            }
            if (dock !== 'left') {
                if (edges & maskR) {
                    
                    b = item.border;
                } else {
                    b = containerBorder;
                    if (b !== false) {
                        edgesTouched += maskR;
                    }
                }
                if (b === false) {
                    mask += maskR;
                }
            }
            if (dock !== 'top') {
                if (edges & maskB) {
                    
                    b = item.border;
                } else {
                    b = containerBorder;
                    if (b !== false) {
                        edgesTouched += maskB;
                    }
                }
                if (b === false) {
                    mask += maskB;
                }
            }
            if (dock !== 'right') {
                if (edges & maskL) {
                    
                    b = item.border;
                } else {
                    b = containerBorder;
                    if (b !== false) {
                        edgesTouched += maskL;
                    }
                }
                if (b === false) {
                    mask += maskL;
                }
            }
            if ((lastValue = item.lastBorderMask) !== mask) {
                item.lastBorderMask = mask;
                if (lastValue) {
                    removeCls[0] = noBorderCls[lastValue];
                }
                if (mask) {
                    addCls[0] = noBorderCls[mask];
                }
            }
            if ((lastValue = item.lastBorderCollapse) !== edgesTouched) {
                item.lastBorderCollapse = edgesTouched;
                if (lastValue) {
                    removeCls.push.apply(removeCls, borderCls[lastValue]);
                }
                if (edgesTouched) {
                    addCls.push.apply(addCls, borderCls[edgesTouched]);
                }
            }
            if (removeCls.length) {
                item.removeCls(removeCls);
            }
            if (addCls.length) {
                item.addCls(addCls);
            }
            
            
            edges |= edgeMasks[dock];
        }
        
        mask = edgesTouched = 0;
        addCls.length = 0;
        removeCls.length = 0;
        if (edges & maskT) {
            
            b = bodyBorder;
        } else {
            b = containerBorder;
            if (b !== false) {
                edgesTouched += maskT;
            }
        }
        if (b === false) {
            mask += maskT;
        }
        if (edges & maskR) {
            
            b = bodyBorder;
        } else {
            b = containerBorder;
            if (b !== false) {
                edgesTouched += maskR;
            }
        }
        if (b === false) {
            mask += maskR;
        }
        if (edges & maskB) {
            
            b = bodyBorder;
        } else {
            b = containerBorder;
            if (b !== false) {
                edgesTouched += maskB;
            }
        }
        if (b === false) {
            mask += maskB;
        }
        if (edges & maskL) {
            
            b = bodyBorder;
        } else {
            b = containerBorder;
            if (b !== false) {
                edgesTouched += maskL;
            }
        }
        if (b === false) {
            mask += maskL;
        }
        if ((lastValue = me.lastBodyBorderMask) !== mask) {
            me.lastBodyBorderMask = mask;
            if (lastValue) {
                removeCls[0] = noBorderCls[lastValue];
            }
            if (mask) {
                addCls[0] = noBorderCls[mask];
            }
        }
        if ((lastValue = me.lastBodyBorderCollapse) !== edgesTouched) {
            me.lastBodyBorderCollapse = edgesTouched;
            if (lastValue) {
                removeCls.push.apply(removeCls, borderCls[lastValue]);
            }
            if (edgesTouched) {
                addCls.push.apply(addCls, borderCls[edgesTouched]);
            }
        }
        if (removeCls.length) {
            bodyClsEl.removeCls(removeCls);
        }
        if (addCls.length) {
            bodyClsEl.addCls(addCls);
        }
    },
    
    borderCollapseMap: {},
    
    
    getBorderCollapseTable: function() {
        var me = this,
            map = me.borderCollapseMap,
            container = me.getContainer(),
            classCls = container.classCls,
            ui = container.getUi(),
            uiKey = ui || 'default',
            uiList, table, classClsList, baseCls, uiCls, i, ln, j, uiLen;
        map = map[classCls] || (map[classCls] = {});
        table = map[uiKey];
        if (!table) {
            classClsList = container.classClsList;
            map[uiKey] = table = [
                [],
                [],
                [],
                [],
                [],
                [],
                [],
                [],
                [],
                [],
                [],
                [],
                [],
                [],
                [],
                []
            ];
            uiList = [
                0
            ];
            if (ui) {
                uiList = uiList.concat(ui.split(me.spaceRe));
            }
            uiLen = uiList.length;
            for (i = 0 , ln = classClsList.length; i < ln; i++) {
                classCls = classClsList[i];
                for (j = 0; j < uiLen; j++) {
                    ui = uiList[j];
                    uiCls = (ui ? ('-' + ui) : '');
                    baseCls = classCls + uiCls + '-outer-border-';
                    table[1].push(baseCls + 'l');
                    
                    table[2].push(baseCls + 'b');
                    
                    table[3].push(baseCls + 'bl');
                    
                    table[4].push(baseCls + 'r');
                    
                    table[5].push(baseCls + 'rl');
                    
                    table[6].push(baseCls + 'rb');
                    
                    table[7].push(baseCls + 'rbl');
                    
                    table[8].push(baseCls + 't');
                    
                    table[9].push(baseCls + 'tl');
                    
                    table[10].push(baseCls + 'tb');
                    
                    table[11].push(baseCls + 'tbl');
                    
                    table[12].push(baseCls + 'tr');
                    
                    table[13].push(baseCls + 'trl');
                    
                    table[14].push(baseCls + 'trb');
                    
                    table[15].push(baseCls + 'trbl');
                }
            }
        }
        
        return table;
    },
    setConfig: function(name, value, options) {
        var config = name,
            alias = this.alias,
            type = config.type;
        if (name) {
            if (typeof name === 'string') {
                config = {};
                config[name] = value;
            } else {
                options = value;
            }
            if (!type || (alias && alias.indexOf('layout.' + type) > -1)) {
                this.callParent([
                    config,
                    options
                ]);
            } else 
            {
                Ext.raise('Cannot change layout from ' + this.$className + ' to "' + type + '"');
            }
        }
        
        return this;
    },
    privates: {
        renderInnerItem: function(item, asRoot) {
            item.setRendered(true, asRoot);
        }
    }
});


Ext.define('Ext.Indicator', {
    extend: Ext.Component,
    xtype: 'indicator',
    config: {
        
        activeIndex: null,
        
        count: null,
        
        direction: 'horizontal',
        
        tapMode: 'direction'
    },
    
    activeCls: Ext.baseCSSPrefix + 'indicator-active',
    baseCls: Ext.baseCSSPrefix + 'indicator',
    
    itemCls: Ext.baseCSSPrefix + 'indicator-item',
    defaultBindProperty: 'activeIndex',
    twoWayBindable: [
        'activeIndex'
    ],
    
    isIndicator: true,
    
    
    
    
    constructor: function(config) {
        this.indicators = [];
        this.callParent([
            config
        ]);
    },
    initialize: function() {
        this.callParent();
        this.element.on({
            tap: 'onTap',
            scope: this
        });
    },
    doDestroy: function() {
        Ext.destroy(this.indicators);
        this.callParent();
    },
    
    applyActiveIndex: function(index) {
        var indicators = this.indicators,
            max = indicators.length - 1;
        if (index > max) {
            Ext.raise('Cannot set the active index greater than the number of indicators');
        }
        return index;
    },
    
    updateActiveIndex: function(index, oldIndex) {
        var activeCls = this.activeCls,
            baseCls = this.baseCls,
            indicators = this.indicators,
            currentActiveItem = indicators[oldIndex],
            activeItem = indicators[index];
        if (currentActiveItem) {
            currentActiveItem.removeCls(activeCls);
        }
        if (activeItem) {
            activeItem.addCls(activeCls);
        }
    },
    updateCount: function(count) {
        var indicators = this.indicators;
        while (indicators.length < count) {
            this.doAdd();
        }
        while (indicators.length > count) {
            this.doRemove();
        }
    },
    
    applyDirection: function(direction) {
        if (direction !== 'vertical' && direction !== 'horizontal') {
            Ext.raise('Invalid indicator direction provided: ' + direction);
            direction = 'horizontal';
        }
        return direction;
    },
    
    updateDirection: function(newDirection, oldDirection) {
        this.element.replaceCls(oldDirection, newDirection, this.baseCls);
    },
    
    sync: function(count, activeIndex) {
        if (Ext.isNumber(count)) {
            this.setCount(count);
        }
        if (Ext.isNumber(activeIndex)) {
            this.setActiveIndex(activeIndex);
        }
        return this;
    },
    
    add: function() {
        var count = this.getCount();
        return this.setCount(++count);
    },
    
    removeAll: function() {
        return this.setCount(0);
    },
    
    remove: function() {
        var count = this.getCount();
        return this.setCount(--count);
    },
    
    doAdd: function() {
        var indicators = this.indicators;
        indicators.push(this.element.createChild({
            tag: 'span',
            cls: this.itemCls
        }));
        return this;
    },
    
    doRemove: function() {
        var indicators = this.indicators,
            indicator = indicators.pop();
        if (indicator) {
            indicator.destroy();
        }
        return this;
    },
    
    onTap: function(e) {
        var mode = this.getTapMode();
        if (mode === 'item') {
            this.onTapItem(e);
        } else {
            this.onTapDirection(e);
        }
    },
    
    onTapItem: function(e) {
        var me = this,
            item = e.getTarget('.' + me.itemCls, 1, true),
            index;
        if (item) {
            index = me.indicators.indexOf(item);
            if (index !== -1) {
                me.fireEvent('indicatortap', me, index, item);
            }
        } else {
            
            me.onTapDirection(e);
        }
    },
    
    onTapDirection: function(e) {
        var me = this,
            direction = me.getDirection(),
            touch = e.touch,
            box = me.element.getBox(),
            centerX = box.left + (box.width / 2),
            centerY = box.top + (box.height / 2),
            event = (direction === 'horizontal' && touch.pageX >= centerX) || (direction === 'vertical' && touch.pageY >= centerY) ? 'next' : 'previous';
        me.fireEvent(event, me);
    }
});


Ext.define('Ext.layout.card.fx.Abstract', {
    extend: Ext.Evented,
    mixins: [
        Ext.mixin.Factoryable
    ],
    alias: 'layout.card.fx.abstract',
    
    isAnimation: true,
    config: {
        direction: null,
        duration: null,
        reverse: null,
        layout: null
    },
    updateLayout: function(layout) {
        if (layout) {
            this.enable();
        }
    },
    enable: function() {
        var layout = this.getLayout();
        if (layout) {
            layout.on('beforeactiveitemchange', 'onActiveItemChange', this);
        }
    },
    disable: function() {
        var layout = this.getLayout();
        if (this.isAnimating) {
            this.stopAnimation();
        }
        if (layout) {
            layout.un('beforeactiveitemchange', 'onActiveItemChange', this);
        }
    },
    onActiveItemChange: Ext.emptyFn,
    destroy: function() {
        var me = this,
            layout = me.getLayout();
        if (me.isAnimating) {
            me.stopAnimation();
        }
        if (layout) {
            layout.un('beforeactiveitemchange', 'onActiveItemChange', this);
        }
        me.setLayout(null);
        if (me.observableId) {
            me.fireEvent('destroy', this);
        }
        me.callParent();
    }
});


Ext.define('Ext.layout.card.fx.Style', {
    extend: Ext.layout.card.fx.Abstract,
    config: {
        inAnimation: {
            before: {
                visibility: null
            },
            preserveEndState: false,
            replacePrevious: true
        },
        outAnimation: {
            preserveEndState: false,
            replacePrevious: true
        }
    },
    
    isAnimating: false,
    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, controller) {
        var me = this,
            inElement, outElement, inAnimation, outAnimation;
        if (newItem && oldItem && oldItem.isPainted()) {
            inAnimation = me.getInAnimation();
            outAnimation = me.getOutAnimation();
            inElement = newItem.renderElement;
            outElement = oldItem.renderElement;
            inAnimation.setElement(inElement);
            outAnimation.setElement(outElement);
            outAnimation.setOnEnd(function() {
                me.isAnimating = false;
                controller.resume();
            });
            inElement.dom.style.setProperty('visibility', 'hidden', 'important');
            newItem.show();
            cardLayout.renderInnerItem(newItem, true);
            me.isAnimating = true;
            Ext.Animator.run([
                outAnimation,
                inAnimation
            ]);
            controller.pause();
        }
    },
    destroy: function() {
        Ext.destroy(this.getInAnimation(), this.getOutAnimation());
        this.callParent();
    }
});


Ext.define('Ext.layout.card.fx.Cover', {
    extend: Ext.layout.card.fx.Style,
    alias: 'layout.card.fx.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
        }
    }
});


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


Ext.define('Ext.layout.card.fx.Serial', {
    extend: Ext.layout.card.fx.Style,
    duration: 500,
    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.layout.card.fx.Fade', {
    extend: Ext.layout.card.fx.Serial,
    alias: 'layout.card.fx.fade',
    config: {
        reverse: null,
        inAnimation: {
            type: 'fade',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'fade',
            easing: 'ease-out',
            out: true
        }
    }
});


Ext.define('Ext.layout.card.fx.Flip', {
    extend: Ext.layout.card.fx.Serial,
    alias: 'layout.card.fx.flip',
    config: {
        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, 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);
    }
});


Ext.define('Ext.layout.card.fx.Pop', {
    extend: Ext.layout.card.fx.Serial,
    alias: 'layout.card.fx.pop',
    config: {
        inAnimation: {
            type: 'pop',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'pop',
            easing: 'ease-in',
            out: true
        }
    }
});


Ext.define('Ext.layout.card.fx.Reveal', {
    extend: Ext.layout.card.fx.Style,
    alias: 'layout.card.fx.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
        }
    }
});


Ext.define('Ext.layout.card.fx.Scroll', {
    extend: Ext.layout.card.fx.Abstract,
    alias: 'layout.card.fx.scroll',
    config: {
        duration: 150
    },
    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, 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.bodyElement;
            containerWidth = containerElement.getWidth();
            containerHeight = containerElement.getHeight();
            inElement = newItem.renderElement;
            outElement = oldItem.renderElement;
            this.oldItem = oldItem;
            this.newItem = newItem;
            this.containerElement = containerElement;
            this.currentEventController = controller;
            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.layout.card.fx.ScrollCover', {
    extend: Ext.layout.card.fx.Scroll,
    alias: 'layout.card.fx.scrollcover',
    onActiveItemChange: function(cardLayout, inItem, outItem, controller) {
        var containerElement, containerSize, xy, animConfig, inTranslate, outTranslate;
        this.currentEventController = controller;
        this.inItem = inItem;
        if (inItem && outItem) {
            containerElement = this.getLayout().container.bodyElement;
            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() {
        if (!this.destroyed) {
            this.inItem.getTranslatable().getWrapper().dom.style.removeProperty('z-index');
            
            this.currentEventController.resume();
        }
    }
});


Ext.define('Ext.layout.card.fx.ScrollReveal', {
    extend: Ext.layout.card.fx.Scroll,
    alias: 'layout.card.fx.scrollreveal',
    onActiveItemChange: function(cardLayout, inItem, outItem, controller) {
        var containerElement, containerSize, xy, animConfig, outTranslate, inTranslate;
        this.currentEventController = controller;
        this.outItem = outItem;
        this.inItem = inItem;
        if (inItem && outItem) {
            containerElement = this.getLayout().container.bodyElement;
            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() {
        if (!this.destroyed) {
            this.outItem.getTranslatable().getWrapper().dom.style.removeProperty('z-index');
            
            this.currentEventController.resume();
        }
    }
});


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


Ext.define('Ext.layout.Card', {
    extend: Ext.layout.Auto,
    alias: 'layout.card',
    type: 'card',
    isCard: true,
    
    
    config: {
        
        indicator: {
            lazy: true,
            $value: {
                xtype: 'indicator',
                flex: 1
            }
        }
    },
    
    deferRender: true,
    cls: Ext.baseCSSPrefix + 'layout-card',
    itemCls: Ext.baseCSSPrefix + 'layout-card-item',
    
    applyAnimation: function(animation) {
        return animation ? new Ext.Factory.layoutCardFx(animation) : null;
    },
    
    updateAnimation: function(animation, oldAnimation) {
        var me = this,
            direction;
        me.autoDirection = null;
        if (animation && animation.isAnimation) {
            animation.setLayout(me);
            direction = animation.getDirection();
            if (!direction || me.autoDirectionMap[direction]) {
                me.autoDirection = direction || 'horizontal';
                
                animation.setDirection(null);
            }
        }
        if (oldAnimation) {
            oldAnimation.destroy();
        }
    },
    applyIndicator: function(indicator, currentIndicator) {
        return Ext.updateWidget(currentIndicator, indicator, this, 'createIndicator');
    },
    createIndicator: function(indicator) {
        return Ext.apply({
            ownerCmp: this.getContainer()
        }, indicator);
    },
    updateIndicator: function(indicator) {
        if (indicator) {
            var container = this.getContainer(),
                innerItems = container.getInnerItems(),
                activeItem = container.getActiveItem();
            indicator.sync(innerItems.length, innerItems.indexOf(activeItem)).on({
                indicatortap: 'onIndicatorTap',
                next: 'next',
                previous: 'previous',
                scope: this
            });
        }
    },
    onContainerInitialized: function() {
        var me = this,
            container = me.getContainer(),
            firstItem = container.getInnerAt(0),
            activeItem = container.getActiveItem();
        me.callParent();
        if (activeItem) {
            
            
            activeItem.show();
            if (firstItem && firstItem !== activeItem) {
                firstItem.hide();
            }
        }
        container.on('activeitemchange', 'onContainerActiveItemChange', me);
    },
    
    onContainerActiveItemChange: function(container, newItem, oldItem) {
        var me = this,
            innerItems = container.getInnerItems(),
            newIndex = innerItems.indexOf(newItem),
            oldIndex = innerItems.indexOf(oldItem),
            animation = me.getAnimation(),
            autoDirection = me.autoDirection,
            horizontal = autoDirection && autoDirection === 'horizontal',
            direction;
        if (autoDirection && newIndex !== -1 && oldIndex !== -1) {
            if (newIndex < oldIndex) {
                direction = horizontal ? 'right' : 'up';
            } else {
                direction = horizontal ? 'left' : 'down';
            }
            animation.setDirection(direction);
        }
        me.fireEventedAction('activeitemchange', [
            me,
            newItem,
            oldItem
        ], 'doActiveItemChange', me);
    },
    onItemInnerStateChange: function(item, isInner, destroying) {
        this.callParent([
            item,
            isInner,
            destroying
        ]);
        var container = this.getContainer(),
            activeItem = container.getActiveItem();
        if (isInner) {
            if (activeItem !== container.innerIndexOf(item) && activeItem !== item && item !== container.pendingActiveItem) {
                item.hide();
            }
        } else {
            if (!destroying && !item.destroyed && item.destroying !== true) {
                item.show();
            }
        }
    },
    
    doActiveItemChange: function(me, newActiveItem, oldActiveItem) {
        var indicator = me.getConfig('indicator', null, true),
            container, innerItems;
        if (oldActiveItem && !oldActiveItem.destroyed) {
            oldActiveItem.hide();
        }
        if (newActiveItem && !newActiveItem.destroyed) {
            me.showItem(newActiveItem);
            if (indicator) {
                container = this.getContainer();
                innerItems = container.getInnerItems();
                indicator.setActiveIndex(innerItems.indexOf(newActiveItem));
            }
        }
    },
    onItemAdd: function(item, index) {
        var indicator, style;
        this.callParent([
            item,
            index
        ]);
        if (item.isInnerItem()) {
            indicator = this.getConfig('indicator', null, true);
            if (indicator) {
                indicator.add();
            }
            
            
            style = item.element.dom.style;
            style.width = style.height = '';
        }
    },
    onItemRemove: function(item, index, destroying) {
        var indicator, w, h;
        this.callParent([
            item,
            index,
            destroying
        ]);
        if (item.isInnerItem()) {
            indicator = this.getConfig('indicator', null, true);
            if (indicator) {
                indicator.remove();
            }
            
            w = item.getWidth();
            h = item.getHeight();
            item.setWidth(null).setWidth(w);
            item.setHeight(null).setHeight(w);
        }
    },
    
    next: function() {
        var container = this.getContainer(),
            activeItem = container.getActiveItem(),
            innerItems = container.getInnerItems(),
            index = innerItems.indexOf(activeItem);
        activeItem = innerItems[index + 1];
        if (activeItem) {
            container.setActiveItem(activeItem);
        }
    },
    
    previous: function() {
        var container = this.getContainer(),
            activeItem = container.getActiveItem(),
            innerItems = container.getInnerItems(),
            index = innerItems.indexOf(activeItem);
        activeItem = innerItems[index - 1];
        if (activeItem) {
            container.setActiveItem(activeItem);
        }
    },
    onIndicatorTap: function(indicator, index) {
        var container = this.getContainer();
        container.setActiveItem(index);
    },
    destroy: function() {
        Ext.destroy(this.getAnimation(), this.getIndicator());
        this.callParent();
    },
    privates: {
        autoDirectionMap: {
            horizontal: 1,
            vertical: 1
        },
        renderInnerItem: function(item, asRoot) {
            if (!this.deferRender || this.getContainer().getActiveItem() === item) {
                this.callParent([
                    item,
                    asRoot
                ]);
            }
        },
        showItem: function(item) {
            item.show();
            item.setRendered(true, true);
        }
    }
});


Ext.define('Ext.util.InputBlocker', {
    singleton: true,
    blockInputs: function() {
        if (Ext.browser.is.ie) {
            Ext.select('.x-field-text .x-input:not(.x-item-disabled) .x-input-el, .x-field-textarea .x-input:not(.x-item-disabled) .x-input-el, .x-field-search .x-input:not(.x-item-disabled) .x-input-el').each(function(item) {
                if (item.dom.offsetWidth > 0) {
                    item.dom.setAttribute('disabled', true);
                    item.dom.setAttribute('overlayfix', true);
                }
            });
        }
    },
    unblockInputs: function() {
        if (Ext.browser.is.ie) {
            Ext.select('[overlayfix]').each(function(item) {
                item.dom.removeAttribute('disabled');
                item.dom.removeAttribute('overlayfix');
            });
        }
    }
});


Ext.define('Ext.viewport.Default', function() {
    var TOP = 1,
        RIGHT = 2,
        BOTTOM = 4,
        LEFT = 8,
        sideMap = {
            top: TOP,
            right: RIGHT,
            bottom: BOTTOM,
            left: LEFT
        },
        oppositeSide = {
            "1": BOTTOM,
            "2": LEFT,
            "4": TOP,
            "8": RIGHT
        },
        oppositeSideNames = {
            left: 'right',
            right: 'left',
            top: 'bottom',
            bottom: 'top',
            up: 'bottom',
            down: 'top'
        };
    return {
        extend: Ext.Container,
        xtype: 'viewport',
        PORTRAIT: 'portrait',
        LANDSCAPE: 'landscape',
        nameHolder: true,
        
        
        
        config: {
            
            autoMaximize: false,
            
            autoBlurInput: true,
            
            preventZooming: false,
            
            autoRender: true,
            
            layout: 'card',
            
            width: '100%',
            
            height: '100%',
            
            menus: {},
            
            orientation: null,
            
            swipeThreshold: 30
        },
        classCls: Ext.baseCSSPrefix + 'viewport',
        getTemplate: function() {
            var template = this.callParent();
            
            if (!Ext.feature.has.MatchMedia) {
                template.unshift({
                    reference: 'orientationElement',
                    className: Ext.baseCSSPrefix + 'orientation-inspector',
                    children: [
                        {
                            className: Ext.baseCSSPrefix + 'orientation-inspector-landscape'
                        }
                    ]
                });
            }
            return template;
        },
        
        isReady: false,
        isViewport: true,
        isMaximizing: false,
        id: 'ext-viewport',
        isInputRegex: /^(input|textarea|select|a)$/i,
        isInteractiveWebComponentRegEx: /^(audio|video)$/i,
        notScalableRe: /user-scalable=no/,
        focusable: false,
        focusEl: null,
        ariaEl: null,
        allSidesCls: [
            Ext.baseCSSPrefix + 'top',
            Ext.baseCSSPrefix + 'right',
            Ext.baseCSSPrefix + 'bottom',
            Ext.baseCSSPrefix + 'left'
        ],
        sideClsMap: {
            top: Ext.baseCSSPrefix + 'top',
            right: Ext.baseCSSPrefix + 'right',
            bottom: Ext.baseCSSPrefix + 'bottom',
            left: Ext.baseCSSPrefix + 'left'
        },
        hasViewportCls: Ext.baseCSSPrefix + 'has-viewport',
        fixedCls: Ext.baseCSSPrefix + 'fixed-viewport',
        
        fullscreenItemCls: Ext.baseCSSPrefix + 'fullscreen',
        constructor: function(config) {
            var me = this;
            me.doPreventPanning = me.doPreventPanning.bind(me);
            me.doPreventZooming = me.doPreventZooming.bind(me);
            me.maximizeOnEvents = [
                'ready',
                'orientationchange'
            ];
            
            window.devicePixelRatio = window.devicePixelRatio || 1;
            me.callParent([
                config
            ]);
            me.updateSize();
            me.windowOuterHeight = me.getWindowOuterHeight();
            
            
            
            
            Ext.setViewportScroller(me.getScrollable() || Ext.getViewportScroller().setConfig({
                x: false,
                y: false,
                component: me
            }));
            
            Ext.getBody().setStyle('overflow', 'hidden');
            Ext.get(document.documentElement).addCls(me.hasViewportCls);
            me.stretchHeights = me.stretchHeights || {};
            if (Ext.feature.has.OrientationChange) {
                me.addWindowListener('orientationchange', me.onOrientationChange.bind(me));
            }
            if (!Ext.os.is.iOS || !me.isScalable()) {
                Ext.get(document.documentElement).addCls(me.fixedCls);
            }
            
            Ext.GlobalEvents.on('resize', 'onWindowResize', me, {
                priority: 1000
            });
            Ext.onDocumentReady(me.onDomReady, me);
            return me;
        },
        initialize: function() {
            var me = this;
            me.addMeta('apple-mobile-web-app-capable', 'yes');
            me.addMeta('apple-touch-fullscreen', 'yes');
            me.callParent();
        },
        getRefItems: function(deep) {
            var menus = this.getMenus(),
                result = this.callParent([
                    deep
                ]),
                side, menu;
            for (side in menus) {
                menu = menus[side];
                if (menu) {
                    Ext.Array.include(result, menu);
                }
            }
            return result;
        },
        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
                ]);
            }
        },
        onAppLaunch: function() {
            var me = this;
            if (!me.isReady) {
                me.onDomReady();
            }
        },
        onDomReady: function() {
            var me = this;
            if (me.isReady) {
                return;
            }
            me.isReady = true;
            me.updateSize();
            me.onReady();
            me.fireEvent('ready', me);
            Ext.GlobalEvents.fireEvent('viewportready', me);
        },
        onReady: function() {
            if (this.getAutoRender()) {
                this.render();
            }
        },
        render: function() {
            var me = this,
                body = Ext.getBody();
            if (!me.rendered) {
                
                
                me.callParent([
                    body,
                    Ext.floatRoot
                ]);
                me.setOrientation(me.determineOrientation());
                Ext.getBody().addCls(Ext.baseCSSPrefix + me.getOrientation());
            }
        },
        applyAutoMaximize: function(autoMaximize) {
            return Ext.browser.is.WebView ? false : autoMaximize;
        },
        updateAutoMaximize: function(autoMaximize) {
            var me = this;
            if (autoMaximize) {
                me.on('ready', 'doAutoMaximizeOnReady', me, {
                    single: true
                });
                me.on('orientationchange', 'doAutoMaximizeOnOrientationChange', me);
            } else {
                me.un('ready', 'doAutoMaximizeOnReady', me);
                me.un('orientationchange', 'doAutoMaximizeOnOrientationChange', me);
            }
        },
        updatePreventPanning: function(preventPanning) {
            this.toggleWindowListener(preventPanning, 'touchmove', this.doPreventPanning, false);
        },
        updatePreventZooming: function(preventZooming) {
            var touchstart = Ext.feature.has.TouchEvents ? 'touchstart' : 'mousedown';
            this.toggleWindowListener(preventZooming, touchstart, this.doPreventZooming, false);
        },
        doAutoMaximizeOnReady: function() {
            var me = this;
            me.isMaximizing = true;
            me.on('maximize', function() {
                me.isMaximizing = false;
                me.updateSize();
                me.fireEvent('ready', me);
            }, me, {
                single: true
            });
            me.maximize();
        },
        doAutoMaximizeOnOrientationChange: function() {
            var me = this;
            me.isMaximizing = true;
            me.on('maximize', function() {
                me.isMaximizing = false;
                me.updateSize();
            }, me, {
                single: true
            });
            me.maximize();
        },
        doPreventPanning: function(e) {
            var target = e.target,
                touch;
            
            
            
            
            if (this.isInteractiveWebComponentRegEx.test(target.tagName) && e.touches && e.touches.length > 0) {
                touch = e.touches[0];
                if (touch && touch.target && this.isInputRegex.test(touch.target.tagName)) {
                    return;
                }
            }
            if (target && target.nodeType === 1 && !this.isInputRegex.test(target.tagName)) {
                e.preventDefault();
            }
        },
        doPreventZooming: function(e) {
            
            if ('button' in e && e.button !== 0) {
                return;
            }
            var target = e.target,
                inputRe = this.isInputRegex,
                touch;
            if (this.isInteractiveWebComponentRegEx.test(target.tagName) && e.touches && e.touches.length > 0) {
                touch = e.touches[0];
                if (touch && touch.target && inputRe.test(touch.target.tagName)) {
                    return;
                }
            }
            if (target && target.nodeType === 1 && !inputRe.test(target.tagName)) {
                e.preventDefault();
            }
        },
        addWindowListener: function(eventName, fn, capturing) {
            window.addEventListener(eventName, fn, Boolean(capturing));
        },
        removeWindowListener: function(eventName, fn, capturing) {
            window.removeEventListener(eventName, fn, Boolean(capturing));
        },
        supportsOrientation: function() {
            return Ext.feature.has.Orientation;
        },
        supportsMatchMedia: function() {
            return Ext.feature.has.MatchMedia;
        },
        onOrientationChange: function() {
            this.setOrientation(this.determineOrientation());
        },
        determineOrientation: function() {
            var me = this,
                orientationElement = me.orientationElement,
                nativeOrientation, visible;
            
            if (me.supportsOrientation()) {
                nativeOrientation = me.getWindowOrientation();
                
                if (Math.abs(nativeOrientation) === 90 || nativeOrientation === 270) {
                    return me.LANDSCAPE;
                } else {
                    return me.PORTRAIT;
                }
            }
            
            else if (me.supportsMatchMedia()) {
                return window.matchMedia('(orientation : landscape)').matches ? me.LANDSCAPE : me.PORTRAIT;
            }
            
            else if (orientationElement) {
                visible = orientationElement.first().isVisible();
                return visible ? me.LANDSCAPE : me.PORTRAIT;
            }
            return null;
        },
        updateOrientation: function(newValue, oldValue) {
            if (oldValue) {
                this.fireOrientationChangeEvent(newValue, oldValue);
            }
        },
        fireOrientationChangeEvent: function(newOrientation, oldOrientation) {
            var me = this,
                newSize = me.updateSize();
            Ext.getBody().replaceCls(Ext.baseCSSPrefix + oldOrientation, Ext.baseCSSPrefix + newOrientation);
            me.fireEvent('orientationchange', me, newOrientation, newSize.width, newSize.height);
        },
        onWindowResize: function(width, height) {
            var me = this,
                oldWidth = me.lastSize.width,
                oldHeight = me.lastSize.height;
            me.updateSize(width, height);
            
            
            
            me.setOrientation(me.determineOrientation());
            
            if (width != null) {
                me.fireEvent('resize', this, width, height, oldWidth, oldHeight);
            }
        },
        updateSize: function(width, height) {
            var lastSize = this.lastSize;
            lastSize.width = width !== undefined ? width : this.getWindowWidth();
            lastSize.height = height !== undefined ? height : this.getWindowHeight();
            return lastSize;
        },
        waitUntil: function(condition, onSatisfied, onTimeout, delay, timeoutDuration) {
            if (!delay) {
                delay = 50;
            }
            if (!timeoutDuration) {
                timeoutDuration = 2000;
            }
            var scope = this,
                elapse = 0;
            Ext.defer(function repeat() {
                elapse += delay;
                if (condition.call(scope) === true) {
                    if (onSatisfied) {
                        onSatisfied.call(scope);
                    }
                } else {
                    if (elapse >= timeoutDuration) {
                        if (onTimeout) {
                            onTimeout.call(scope);
                        }
                    } else {
                        Ext.defer(repeat, delay);
                    }
                }
            }, delay);
        },
        maximize: function() {
            this.fireMaximizeEvent();
        },
        fireMaximizeEvent: function() {
            this.updateSize();
            this.fireEvent('maximize', this);
        },
        updateHeight: function(height, oldHeight) {
            Ext.getBody().setHeight(height);
            this.callParent([
                height,
                oldHeight
            ]);
        },
        updateWidth: function(width, oldWidth) {
            Ext.getBody().setWidth(width);
            this.callParent([
                width,
                oldWidth
            ]);
        },
        scrollToTop: function() {
            window.scrollTo(0, -1);
        },
        
        getWindowWidth: function() {
            return window.innerWidth;
        },
        
        getWindowHeight: function() {
            return window.innerHeight;
        },
        getWindowOuterHeight: function() {
            return window.outerHeight;
        },
        getWindowOrientation: function() {
            return window.orientation;
        },
        getSize: function() {
            return this.lastSize;
        },
        setItemFullScreen: function(item) {
            item.addCls(this.fullscreenItemCls);
            item.setTop(0);
            item.setRight(0);
            item.setBottom(0);
            item.setLeft(0);
            this.add(item);
        },
        
        setMenu: function(menu, config) {
            config = config || {};
            
            if (config.reveal && config.cover) {
                Ext.raise('[Ext.Viewport] setMenu(): Only one of reveal or cover allowed in config');
            }
            
            var me = this,
                side, menus, oldMenu;
            
            if (Ext.os.is.iOS && !me.hasiOSOrientationFix) {
                me.hasiOSOrientationFix = true;
                me.on('orientationchange', function() {
                    window.scrollTo(0, 0);
                }, me);
            }
            
            if (!menu) {
                Ext.raise("You must specify a menu configuration.");
            }
            
            menus = me.getMenus();
            if (!me.addedSwipeListener) {
                me.attachSwipeListeners();
                me.addedSwipeListener = true;
            }
            
            
            menu = me.configureMenu(menu, config);
            side = menu.getSide();
            
            
            
            
            oldMenu = menus[side];
            if (oldMenu && !oldMenu.destroyed && oldMenu !== menu) {
                me.hideMenu(side);
                oldMenu.setSide(null);
            }
            menus[side] = menu;
            me.setMenus(menus);
            return menu;
        },
        attachSwipeListeners: function() {
            var me = this;
            me.element.on({
                tap: me.onTap,
                swipestart: me.onSwipeStart,
                edgeswipestart: me.onEdgeSwipeStart,
                edgeswipe: me.onEdgeSwipe,
                edgeswipeend: me.onEdgeSwipeEnd,
                scope: me
            });
        },
        configureMenu: function(menu, config) {
            
            
            var isInstanced = menu.isComponent,
                
                
                reveal = isInstanced && !('reveal' in config) ? menu.getReveal() : !!config.reveal,
                cover = (isInstanced && !('cover' in config) ? menu.getCover() : config.cover) !== false && !reveal,
                side = isInstanced && !('side' in config) ? menu.getSide() : config.side,
                wasFloated;
            
            if (!side) {
                Ext.raise("You must specify a side to dock the menu.");
            }
            if (!sideMap[side]) {
                Ext.raise("You must specify a valid side (left, right, top or bottom) to dock the menu.");
            }
            
            
            
            config = {
                hideAnimation: null,
                showAnimation: null,
                hidden: true,
                floated: cover,
                zIndex: cover ? null : 5,
                reveal: reveal,
                cover: cover,
                side: side
            };
            config[oppositeSideNames[side]] = null;
            if (isInstanced) {
                wasFloated = menu.getFloated();
                
                if (config.floated !== wasFloated) {
                    if (menu.rendered) {
                        
                        
                        
                        if (wasFloated) {
                            this.remove(menu, false);
                        } else 
                        
                        {
                            menu.el.dom.parentNode.removeChild(menu.el.dom);
                            menu.setRendered(false);
                        }
                    }
                    
                    menu.setConfig({
                        top: null,
                        right: null,
                        bottom: null,
                        left: null
                    });
                }
                
                
                
                menu.setConfig(config, null, {
                    strict: false
                });
            } else {
                config.xtype = 'actionsheet';
                menu = Ext.create(Ext.apply(config, menu));
            }
            
            
            config = {
                left: 0,
                right: 0,
                top: 0,
                bottom: 0
            };
            config[oppositeSideNames[side]] = null;
            menu.setConfig(config);
            menu.toggleCls(menu.floatingCls, !menu.getFloated());
            menu.removeCls(this.getLayout().itemCls);
            menu.toggleCls(Ext.baseCSSPrefix + 'menu-cover', cover);
            menu.toggleCls(Ext.baseCSSPrefix + 'menu-reveal', reveal);
            menu.replaceCls(this.allSidesCls, this.sideClsMap[side]);
            menu.isViewportMenu = true;
            return menu;
        },
        
        removeMenu: function(side, animation) {
            var me = this,
                menus = me.getMenus() || {},
                menu = menus[side];
            if (menu) {
                me.hideMenu(side, animation);
                menu.removeCls(me.sideClsMap[side]);
            }
            delete menus[side];
            me.setMenus(menus);
        },
        
        showMenu: function(side) {
            var me = this,
                sideValue = sideMap[side],
                menu = me.getMenus()[side],
                viewportAfter = {
                    translateX: 0,
                    translateY: 0
                },
                size;
            if (!menu || !menu.isHidden()) {
                return;
            }
            
            me.beforeMenuAnimate(menu);
            size = menu.element.measure(sideValue & (LEFT | RIGHT) ? 'w' : 'h');
            if (sideValue === LEFT) {
                viewportAfter.translateX = size;
            } else if (sideValue === RIGHT) {
                viewportAfter.translateX = -size;
            } else if (sideValue === TOP) {
                viewportAfter.translateY = size;
            } else if (sideValue === BOTTOM) {
                viewportAfter.translateY = -size;
            }
            
            menu.translate(0, 0, {
                duration: 200
            });
            
            if (!menu.getFloated()) {
                me.translate(viewportAfter.translateX, viewportAfter.translateY, {
                    duration: 200
                });
            }
        },
        
        hideMenu: function(side, animate) {
            var me = this,
                sideValue = sideMap[side],
                menu = me.getMenus()[side],
                after = {
                    translateX: 0,
                    translateY: 0
                },
                size;
            animate = animate !== false;
            if (!menu || menu.isHidden()) {
                return;
            }
            size = menu.element.measure(sideValue & (LEFT | RIGHT) ? 'w' : 'h');
            if (sideValue === LEFT) {
                after.translateX = -size;
            } else if (sideValue === RIGHT) {
                after.translateX = size;
            } else if (sideValue === TOP) {
                after.translateY = -size;
            } else if (sideValue === BOTTOM) {
                after.translateY = size;
            }
            
            if (animate) {
                menu.revertFocus();
                menu.translate(after.translateX, after.translateY, {
                    duration: 200,
                    callback: function() {
                        if (!menu.destroyed) {
                            menu.translate(0, 0);
                            menu.setHidden(true);
                        }
                    }
                });
            } else 
            {
                menu.getTranslatable().stopAnimation();
                menu.setHidden(true);
            }
            
            
            if (!menu.getFloated()) {
                me.translate(0, 0, animate ? {
                    duration: 200
                } : null);
            }
        },
        
        hideAllMenus: function(animation) {
            var menus = this.getMenus(),
                side;
            for (side in menus) {
                this.hideMenu(side, animation);
            }
        },
        
        hideOtherMenus: function(side, animate) {
            var menus = this.getMenus(),
                menu;
            for (menu in menus) {
                if (side !== menu) {
                    this.hideMenu(menu, animate);
                }
            }
        },
        
        toggleMenu: function(side) {
            var menus = this.getMenus(),
                menu;
            if (menus[side]) {
                menu = menus[side];
                menu.setDisplayed(menu.isHidden());
            }
        },
        applyScrollable: function(scrollable) {
            return this.callParent([
                scrollable,
                Ext.getViewportScroller()
            ]);
        },
        doDestroy: function() {
            var me = this,
                docEl = Ext.get(document.documentElement),
                scroller = me._scrollable;
            docEl.removeCls(me.hasViewportCls);
            docEl.removeCls(me.fixedCls);
            
            
            if (scroller) {
                
                scroller.setConfig({
                    x: true,
                    y: true
                });
                me._scrollable = null;
            }
            Ext.un('resize', 'onWindowResize', me);
            me.callParent();
            Ext.Viewport = null;
        },
        privates: {
            addMeta: function(name, content) {
                var meta = document.createElement('meta');
                meta.setAttribute('name', name);
                meta.setAttribute('content', content);
                Ext.getHead().append(meta);
            },
            
            beforeMenuAnimate: function(menu) {
                var me = this,
                    side = menu.getSide(),
                    sideValue = sideMap[side],
                    before = {
                        translateX: 0,
                        translateY: 0
                    },
                    size, modal;
                me.hideOtherMenus(side);
                
                if (menu.getFloated()) {
                    me.add(menu);
                } else {
                    
                    
                    Ext.getBody().insertFirst(menu.element);
                    if (!menu.rendered) {
                        menu.setRendered(true);
                    }
                    modal = menu.getModal();
                    if (modal) {
                        Ext.getBody().insertFirst(modal.element);
                        if (!modal.rendered) {
                            modal.setRendered(true);
                        }
                    }
                    
                    me.translate(0, 0);
                }
                menu.removeCls(me.getLayout().itemCls);
                
                
                menu.show(false, {
                    side: null
                });
                size = menu.element.measure(sideValue & (LEFT | RIGHT) ? 'w' : 'h');
                if (sideValue === LEFT) {
                    before.translateX = -size;
                } else if (sideValue === RIGHT) {
                    before.translateX = size;
                } else if (sideValue === TOP) {
                    before.translateY = -size;
                } else if (sideValue === BOTTOM) {
                    before.translateY = size;
                }
                menu.translate(before.translateX, before.translateY);
            },
            doAddListener: function(eventName, fn, scope, options, order, caller, manager) {
                var me = this;
                if (eventName === 'ready' && me.isReady && !me.isMaximizing) {
                    fn.call(scope);
                    return me;
                }
                me.callParent([
                    eventName,
                    fn,
                    scope,
                    options,
                    order,
                    caller,
                    manager
                ]);
            },
            
            isScalable: function() {
                var me = this,
                    metas = document.querySelectorAll('meta[name="viewport"]'),
                    
                    meta = metas.length && metas[metas.length - 1],
                    scalable = true,
                    content;
                if (meta) {
                    content = meta.getAttribute('content');
                    scalable = !(content && me.notScalableRe.test(content));
                }
                return scalable;
            },
            
            onTap: function(e) {},
            
            
            onSwipeStart: function(e) {
                var side = this.sideForSwipeDirection(e.direction),
                    menu = this.getMenus()[side];
                
                if (menu && !menu.owns(e)) {
                    this.hideMenu(side);
                }
            },
            
            onEdgeSwipeStart: function(e) {
                var me = this,
                    menus = me.getMenus(),
                    menu = menus[oppositeSideNames[e.direction]],
                    menuSide, checkMenu;
                if (!menu || !menu.isHidden()) {
                    return;
                }
                
                e.claimGesture();
                for (menuSide in menus) {
                    checkMenu = menus[menuSide];
                    if (checkMenu.isVisible()) {
                        return;
                    }
                }
                me.$swiping = true;
                
                me.beforeMenuAnimate(menu);
            },
            
            onEdgeSwipe: function(e) {
                var me = this,
                    side = me.sideForDirection(e.direction),
                    menu = me.getMenus()[oppositeSideNames[e.direction]],
                    size, after, viewportAfter, movement, viewportMovement;
                if (!menu || !me.$swiping) {
                    return;
                }
                
                e.claimGesture();
                
                if (e.distance !== me.lastSwipeDistance) {
                    me.reverseSwiping = e.distance < me.lastSwipeDistance;
                }
                me.lastSwipeDistance = e.distance;
                size = menu.element.measure(side & (LEFT | RIGHT) ? 'w' : 'h');
                movement = Math.min(e.distance - size, 0);
                viewportMovement = Math.min(e.distance, size);
                after = {
                    translateX: 0,
                    translateY: 0
                };
                viewportAfter = {
                    translateX: 0,
                    translateY: 0
                };
                if (side === LEFT) {
                    after.translateX = movement;
                    viewportAfter.translateX = viewportMovement;
                } else if (side === RIGHT) {
                    after.translateX = -movement;
                    viewportAfter.translateX = -viewportMovement;
                } else if (side === TOP) {
                    after.translateY = movement;
                    viewportAfter.translateY = viewportMovement;
                } else if (side === BOTTOM) {
                    after.translateY = -movement;
                    viewportAfter.translateY = -viewportMovement;
                }
                menu.translate(after.translateX, after.translateY);
                
                if (!menu.getFloated()) {
                    me.translate(viewportAfter.translateX, viewportAfter.translateY);
                }
            },
            
            onEdgeSwipeEnd: function(e) {
                var me = this,
                    side = me.sideForDirection(e.direction),
                    menu = me.getMenus()[oppositeSideNames[e.direction]],
                    shouldRevert = me.reverseSwiping || (e.distance < me.getSwipeThreshold()),
                    after = {
                        translateX: 0,
                        translateY: 0
                    },
                    viewportAfter = {
                        translateX: 0,
                        translateY: 0
                    },
                    size, velocity, movement, viewportMovement;
                if (!menu) {
                    return;
                }
                size = menu.element.measure(side & (LEFT | RIGHT) ? 'w' : 'h');
                velocity = (e.flick) ? e.flick.velocity : 0;
                
                if (side === RIGHT) {
                    if (velocity.x > 0) {
                        shouldRevert = true;
                    }
                } else if (side === LEFT) {
                    if (velocity.x < 0) {
                        shouldRevert = true;
                    }
                } else if (side === TOP) {
                    if (velocity.y < 0) {
                        shouldRevert = true;
                    }
                } else if (side === BOTTOM) {
                    if (velocity.y > 0) {
                        shouldRevert = true;
                    }
                }
                movement = shouldRevert ? size : 0;
                viewportMovement = shouldRevert ? 0 : -size;
                if (side === LEFT) {
                    after.translateX = -movement;
                    viewportAfter.translateX = -viewportMovement;
                } else if (side === RIGHT) {
                    after.translateX = movement;
                    viewportAfter.translateX = viewportMovement;
                } else if (side === TOP) {
                    after.translateY = -movement;
                    viewportAfter.translateY = -viewportMovement;
                } else if (side === BOTTOM) {
                    after.translateY = movement;
                    viewportAfter.translateY = viewportMovement;
                }
                
                menu.translate(after.translateX, after.translateY, {
                    duration: 200,
                    callback: function() {
                        if (shouldRevert) {
                            menu.setHidden(true);
                        }
                    }
                });
                
                if (!menu.getFloated()) {
                    me.translate(viewportAfter.translateX, viewportAfter.translateY, {
                        duration: 200
                    });
                }
                me.$swiping = false;
            },
            
            sideForDirection: function(direction) {
                if (direction === 'up') {
                    direction = 'top';
                } else if (direction === 'down') {
                    direction = 'bottom';
                }
                return oppositeSide[sideMap[direction]];
            },
            
            sideForSwipeDirection: function(direction) {
                if (direction === 'up') {
                    return 'top';
                } else if (direction === 'down') {
                    return 'bottom';
                }
                return direction;
            },
            toggleWindowListener: function(on, eventName, fn, capturing) {
                if (on) {
                    this.addWindowListener(eventName, fn, capturing);
                } else {
                    this.removeWindowListener(eventName, fn, capturing);
                }
            }
        }
    };
});


Ext.define('Ext.viewport.Ios', {
    extend: Ext.viewport.Default,
    isFullscreen: function() {
        return this.isHomeScreen();
    },
    isHomeScreen: function() {
        return window.navigator.standalone === true;
    },
    constructor: function() {
        this.callParent(arguments);
        
        
        
        document.documentElement.style.overflow = 'hidden';
    },
    maximize: function() {
        if (this.isFullscreen()) {
            return this.callParent();
        }
        var stretchHeights = this.stretchHeights,
            orientation = this.getOrientation(),
            currentHeight = this.getWindowHeight(),
            height = stretchHeights[orientation];
        if (window.scrollY > 0) {
            this.scrollToTop();
            if (!height) {
                stretchHeights[orientation] = height = this.getWindowHeight();
            }
            this.setHeight(height);
            this.fireMaximizeEvent();
        } else {
            if (!height) {
                height = this.getScreenHeight();
            }
            this.setHeight(height);
            this.waitUntil(function() {
                this.scrollToTop();
                return currentHeight !== this.getWindowHeight();
            }, function() {
                if (!stretchHeights[orientation]) {
                    height = stretchHeights[orientation] = this.getWindowHeight();
                    this.setHeight(height);
                }
                this.fireMaximizeEvent();
            }, function() {
                
                Ext.Logger.error("Timeout waiting for window.innerHeight to change", this);
                
                height = stretchHeights[orientation] = this.getWindowHeight();
                this.setHeight(height);
                this.fireMaximizeEvent();
            }, 50, 1000);
        }
    },
    getScreenHeight: function() {
        return window.screen[this.getOrientation() === this.PORTRAIT ? 'height' : 'width'];
    },
    scrollToTop: function() {
        window.scrollTo(0, 0);
    }
}, function() {
    if (!Ext.os.is.iOS) {
        return;
    }
    if (Ext.os.version.lt('3.2')) {
        this.override({
            constructor: function() {
                var stretchHeights = this.stretchHeights = {};
                stretchHeights[this.PORTRAIT] = 416;
                stretchHeights[this.LANDSCAPE] = 268;
                return this.callOverridden(arguments);
            }
        });
    }
    if (Ext.os.version.lt('5')) {
        this.override({
            fieldMaskClsTest: '-field-mask',
            doPreventZooming: function(e) {
                var target = e.target;
                if (target && target.nodeType === 1 && !this.isInputRegex.test(target.tagName) && target.className.indexOf(this.fieldMaskClsTest) == -1) {
                    e.preventDefault();
                }
            }
        });
    }
    if (Ext.os.is.iPad) {
        this.override({
            isFullscreen: function() {
                return true;
            }
        });
    }
    
    
    
    if (Ext.os.version.gtEq('7') && Ext.os.version.lt('8')) {
        
        if (Ext.os.deviceType === 'Tablet' || !Ext.browser.is.Safari || window.navigator.standalone) {
            this.override({
                constructor: function() {
                    var stretchHeights = {},
                        stretchWidths = {},
                        orientation = this.determineOrientation(),
                        screenHeight = window.screen.height,
                        screenWidth = window.screen.width,
                        menuHeight = orientation === this.PORTRAIT ? screenHeight - window.innerHeight : screenWidth - window.innerHeight;
                    stretchHeights[this.PORTRAIT] = screenHeight - menuHeight;
                    stretchHeights[this.LANDSCAPE] = screenWidth - menuHeight;
                    stretchWidths[this.PORTRAIT] = screenWidth;
                    stretchWidths[this.LANDSCAPE] = screenHeight;
                    this.stretchHeights = stretchHeights;
                    this.stretchWidths = stretchWidths;
                    this.callOverridden(arguments);
                    this.on('ready', this.setViewportSizeToAbsolute, this);
                    this.on('orientationchange', this.setViewportSizeToAbsolute, this);
                },
                getWindowHeight: function() {
                    return this.stretchHeights[this.getOrientation()];
                },
                getWindowWidth: function() {
                    return this.stretchWidths[this.getOrientation()];
                },
                setViewportSizeToAbsolute: function() {
                    this.setWidth(this.getWindowWidth());
                    this.setHeight(this.getWindowHeight());
                }
            });
        }
        
        if (Ext.os.deviceType === 'Tablet') {
            this.override({
                constructor: function() {
                    this.callOverridden(arguments);
                    window.addEventListener('scroll', function() {
                        if (window.scrollX !== 0) {
                            window.scrollTo(0, window.scrollY);
                        }
                    }, false);
                },
                setViewportSizeToAbsolute: function() {
                    window.scrollTo(0, 0);
                    this.callOverridden(arguments);
                },
                onElementBlur: function() {
                    this.callOverridden(arguments);
                    if (window.scrollY !== 0) {
                        window.scrollTo(0, 0);
                    }
                }
            });
        }
    }
});


Ext.define('Ext.viewport.Android', {
    extend: Ext.viewport.Default,
    config: {
        translatable: {
            type: 'csstransform'
        }
    },
    
    preventPullRefresh: true,
    constructor: function() {
        var me = this;
        me.callParent(arguments);
        me.on({
            orientationchange: 'hideKeyboardIfNeeded',
            scope: me,
            
            priority: 1001
        });
        
        if (me.preventPullRefresh) {
            Ext.getBody().setStyle({
                overflow: 'hidden'
            });
        }
    },
    getWindowWidth: function() {
        return this.element.getWidth();
    },
    getWindowHeight: function() {
        return this.element.getHeight();
    },
    getDummyInput: function() {
        var input = this.dummyInput,
            focusedElement = this.focusedElement,
            box = Ext.fly(focusedElement).getBox();
        if (!input) {
            this.dummyInput = input = document.createElement('input');
            input.style.position = 'absolute';
            input.style.opacity = '0';
            input.style.pointerEvents = 'none';
            document.body.appendChild(input);
        }
        input.style.left = box.left + 'px';
        input.style.top = box.top + 'px';
        input.style.display = '';
        return input;
    },
    doBlurInput: function(e) {
        var target = e.target,
            focusedElement = this.focusedElement,
            dummy;
        if (focusedElement && !this.isInputRegex.test(target.tagName)) {
            dummy = this.getDummyInput();
            delete this.focusedElement;
            dummy.focus();
            Ext.defer(function() {
                dummy.style.display = 'none';
            }, 100);
        }
    },
    hideKeyboardIfNeeded: function() {
        var focusedElement = this.focusedElement;
        if (focusedElement) {
            delete this.focusedElement;
            if (Ext.os.version.lt('4')) {
                focusedElement.style.display = 'none';
            } else {
                focusedElement.blur();
            }
            Ext.defer(function() {
                focusedElement.style.display = '';
            }, 1000);
        }
    },
    doFireOrientationChangeEvent: function() {
        this.orientationChanging = true;
        this.waitUntil(function() {
            return this.getWindowOuterHeight() !== this.windowOuterHeight;
        }, function() {
            this.windowOuterHeight = this.getWindowOuterHeight();
            this.updateSize();
            this.orientationChanging = false;
        }, function() {
            
            Ext.Logger.error("Timeout waiting for viewport's outerHeight to change before firing orientationchange", this);
        });
        
        return this;
    },
    getActualWindowOuterHeight: function() {
        return Math.round(this.getWindowOuterHeight() / window.devicePixelRatio);
    },
    maximize: function() {
        var stretchHeights = this.stretchHeights,
            orientation = this.orientation,
            height;
        height = stretchHeights[orientation];
        if (!height) {
            stretchHeights[orientation] = height = this.getActualWindowOuterHeight();
        }
        if (!this.addressBarHeight) {
            this.addressBarHeight = height - this.getWindowHeight();
        }
        this.setHeight(height);
        var isHeightMaximized = this.isHeightMaximized.bind(this, height);
        this.scrollToTop();
        this.waitUntil(isHeightMaximized, this.fireMaximizeEvent, this.fireMaximizeEvent);
    },
    isHeightMaximized: function(height) {
        this.scrollToTop();
        return this.getWindowHeight() === height;
    },
    doPreventZooming: function(e) {
        
        if ('button' in e && e.button !== 0) {
            return;
        }
        var target = e.target;
        if (target && target.nodeType === 1 && !this.isInputRegex.test(target.tagName) && !this.focusedElement) {
            e.preventDefault();
        }
    }
}, function() {
    if (!Ext.os.is.Android) {
        return;
    }
    var version = Ext.os.version,
        userAgent = Ext.browser.userAgent,
        
        
        isBuggy = /(htc|desire|incredible|ADR6300)/i.test(userAgent) && version.lt('2.3');
    if (isBuggy) {
        this.override({
            constructor: function(config) {
                if (!config) {
                    config = {};
                }
                config.autoMaximize = false;
                this.watchDogTick = this.watchDogTick.bind(this);
                Ext.interval(this.watchDogTick, 1000);
                return this.callParent([
                    config
                ]);
            },
            watchDogTick: function() {
                this.watchDogLastTick = Ext.Date.now();
            },
            doPreventPanning: function() {
                var now = Ext.Date.now(),
                    lastTick = this.watchDogLastTick,
                    deltaTime = now - lastTick;
                
                if (deltaTime >= 2000) {
                    return;
                }
                return this.callParent(arguments);
            },
            doPreventZooming: function() {
                var now = Ext.Date.now(),
                    lastTick = this.watchDogLastTick,
                    deltaTime = now - lastTick;
                
                if (deltaTime >= 2000) {
                    return;
                }
                return this.callParent(arguments);
            }
        });
    }
    if (version.match('2')) {
        this.override({
            onReady: function() {
                this.addWindowListener('resize', this.onWindowResize.bind(this));
                this.callParent(arguments);
            },
            scrollToTop: function() {
                document.body.scrollTop = 100;
            },
            onWindowResize: function() {
                var oldWidth = this.windowWidth,
                    oldHeight = this.windowHeight,
                    width = this.getWindowWidth(),
                    height = this.getWindowHeight();
                if (this.getAutoMaximize() && !this.isMaximizing && !this.orientationChanging && window.scrollY === 0 && oldWidth === width && height < oldHeight && ((height >= oldHeight - this.addressBarHeight) || !this.focusedElement)) {
                    this.scrollToTop();
                }
            }
        });
    } else if (version.gtEq('3.1')) {
        this.override({
            isHeightMaximized: function(height) {
                this.scrollToTop();
                return this.getWindowHeight() === height - 1;
            }
        });
    } else if (version.match('3')) {
        this.override({
            isHeightMaximized: function() {
                this.scrollToTop();
                return true;
            }
        });
    }
    if (version.gtEq('4')) {
        this.override({
            doBlurInput: Ext.emptyFn
        });
    }
});


Ext.define('Ext.viewport.WindowsPhone', {
    extend: Ext.viewport.Default,
    requires: [],
    alternateClassName: 'Ext.viewport.WP',
    
    
    
    
    
    config: {
        translatable: {
            type: 'csstransform'
        }
    },
    initialize: function() {
        
        var preventSelection = function(e) {
                var srcElement = e.srcElement.nodeName.toUpperCase(),
                    selectableElements = [
                        'INPUT',
                        'TEXTAREA'
                    ];
                if (selectableElements.indexOf(srcElement) == -1) {
                    return false;
                }
            };
        document.body.addEventListener('onselectstart', preventSelection);
        this.addMeta('msapplication-tap-highlight', 'no');
        this.callParent();
    },
    supportsOrientation: function() {
        return false;
    },
    onWindowResize: function(width, height) {
        this.callParent([
            width,
            height
        ]);
        this.waitUntil(function() {
            var oldWidth = this.windowWidth,
                oldHeight = this.windowHeight,
                width = this.getWindowWidth(),
                height = this.getWindowHeight(),
                currentOrientation = this.getOrientation(),
                newOrientation = this.determineOrientation();
            return ((oldWidth !== width && oldHeight !== height) && currentOrientation !== newOrientation);
        }, function() {
            var currentOrientation = this.getOrientation(),
                newOrientation = this.determineOrientation();
            this.fireOrientationChangeEvent(newOrientation, currentOrientation);
        }, Ext.emptyFn, 250);
    }
});


Ext.define('Ext.viewport.Viewport', {
    singleton: true,
    setup: function(config) {
        var osName = Ext.os.name,
            viewportName, viewport;
        switch (osName) {
            case 'Android':
                viewportName = (Ext.browser.name === 'ChromeMobile') ? 'Default' : 'Android';
                break;
            case 'iOS':
                viewportName = 'Ios';
                break;
            case 'Windows':
                viewportName = (Ext.browser.name === 'IE') ? 'WindowsPhone' : 'Default';
                break;
            case 'WindowsPhone':
                viewportName = 'WindowsPhone';
                break;
            default:
                viewportName = 'Default';
                break;
        }
        return Ext.Viewport = viewport = Ext.create('Ext.viewport.' + viewportName, config);
    }
});




Ext.define('Ext.overrides.app.Application', {
    override: 'Ext.app.Application',
    config: {
        
        viewport: null
    },
    
    
    quickTips: false,
    destroy: function() {
        this.setQuickTips(false);
        this.callParent();
    },
    initMainView: function() {
        var me = this,
            viewport = me.viewport = Ext.Viewport,
            mainView;
        me.callParent();
        mainView = me.getMainView();
        
        viewport.onAppLaunch();
        if (mainView) {
            viewport.add(mainView);
        }
    },
    applyQuickTips: function(quickTips) {
        quickTips = quickTips || null;
        if (quickTips) {
            if (quickTips === true) {
                quickTips = {};
            }
            quickTips = new Ext.tip['Manager'](quickTips);
        }
        return quickTips;
    },
    updateQuickTips: function(quickTips, oldQuickTips) {
        if (oldQuickTips) {
            oldQuickTips.destroy();
        }
    }
});


Ext.define('Ext.app.Profile', {
    mixins: [
        Ext.mixin.Observable
    ],
    
    isProfile: true,
    
    
    config: {
        
        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;
    },
    
    init: function() {
        var views = this.getViews(),
            xtype;
        if (views && !(views instanceof Array)) {
            for (xtype in views) {
                Ext.ClassManager.setXType(views[xtype], xtype);
            }
        }
    },
    
    launch: Ext.emptyFn,
    onClassExtended: function(cls, data, hooks) {
        var onBeforeClassCreated = hooks.onBeforeCreated;
        hooks.onBeforeCreated = function(cls, data) {
            var Controller = Ext.app.Controller,
                className = cls.$className,
                requires = [],
                proto = cls.prototype,
                views = data.views,
                name, namespace;
            
            
            name = data.name;
            if (name) {
                delete data.name;
            } else {
                name = className.split('.');
                name = name[name.length - 1];
            }
            cls._name = name;
            cls._namespace = name = (data.namespace || name).toLowerCase();
            delete data.namespace;
            namespace = Controller.resolveNamespace(cls, data);
            Controller.processDependencies(proto, requires, namespace, 'model', data.models, name);
            Controller.processDependencies(proto, requires, namespace, 'store', data.stores, name);
            Controller.processDependencies(proto, requires, namespace, 'controller', data.controllers, name);
            if (views) {
                if (views instanceof Array) {
                    Controller.processDependencies(proto, requires, namespace, 'view', views, name);
                } else {
                    Ext.app.Profile.processViews(className, views, requires);
                }
            }
            Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));
        };
    },
    getName: function() {
        
        
        return this.self._name;
    },
    getNamespace: function() {
        
        
        return this.self._namespace;
    },
    privates: {
        statics: {
            processViews: function(className, views, requires) {
                var body, cls, s, xtype;
                for (xtype in views) {
                    cls = views[xtype];
                    if (typeof cls !== 'string') {
                        s = cls.xclass;
                        
                        if (!s) {
                            Ext.raise('Views must specify an xclass');
                        }
                        
                        body = Ext.apply({
                            extend: s
                        }, cls);
                        delete body.xclass;
                        
                        Ext.define(views[xtype] = className + '$' + xtype, body);
                        cls = s;
                    }
                    requires.push(cls);
                }
            }
        }
    }
});


Ext.define('Ext.app.domain.View', {
    extend: Ext.app.EventDomain,
    isInstance: true,
    constructor: function(controller) {
        this.callParent([
            controller
        ]);
        this.controller = controller;
        this.monitoredClasses = [
            Ext.Widget
        ];
    },
    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,
    alias: 'controller.controller',
    mixins: [
        Ext.mixin.Factoryable
    ],
    isViewController: true,
    
    factoryConfig: {
        
        type: 'controller'
    },
    config: {
        
        bindings: {
            $value: null,
            lazy: true
        },
        closeViewAction: 'destroy'
    },
    view: null,
    constructor: function(config) {
        this.compDomain = new Ext.app.domain.View(this);
        this.callParent([
            config
        ]);
    },
    
    beforeInit: Ext.emptyFn,
    
    init: Ext.emptyFn,
    
    initViewModel: Ext.emptyFn,
    
    destroy: function() {
        var me = this,
            domain = me.compDomain,
            bind, b, key;
        if (me.$hasBinds) {
            bind = me.getBindings();
            for (key in bind) {
                b = bind[key];
                if (b) {
                    b.destroy();
                }
            }
        }
        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
        ]);
    },
    applyId: function(id) {
        if (!id) {
            id = Ext.id(null, 'controller-');
        }
        return id;
    },
    
    getReferences: function() {
        var view = this.view;
        return view && view.getReferences();
    },
    
    getView: function() {
        return this.view;
    },
    
    lookup: function(key) {
        var view = this.view;
        return view && view.lookup(key);
    },
    
    lookupReference: function(key) {
        return this.lookup(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, args) {
        var view = this.view,
            result = false,
            a = arguments;
        if (view) {
            if (view !== args) {
                a = Ext.Array.slice(a);
                a.splice(1, 0, view);
            }
            
            result = view.fireEvent.apply(view, a);
        }
        return result;
    },
    
    applyBindings: function(bindings) {
        if (!bindings) {
            return null;
        }
        var me = this,
            viewModel = me.getViewModel(),
            getBindTemplateScope = me.getBindTemplateScope(),
            b, fn, descriptor;
        me.$hasBinds = true;
        
        if (!viewModel) {
            Ext.raise('Cannot use bind config without a viewModel');
        }
        
        for (fn in bindings) {
            descriptor = bindings[fn];
            b = null;
            if (descriptor) {
                b = viewModel.bind(descriptor, fn, me);
                b.getTemplateScope = getBindTemplateScope;
            }
            bindings[fn] = b;
        }
        return bindings;
    },
    
    privates: {
        view: null,
        
        attachReference: function(component) {
            var view = this.view;
            if (view) {
                view.attachReference(component);
            }
        },
        getBindTemplateScope: function() {
            
            
            return this.scope;
        },
        initBindings: function() {
            
            this.getBindings();
        },
        
        setView: function(view) {
            this.view = view;
            if (!this.beforeInit.$nullFn) {
                this.beforeInit(view);
            }
        }
    }
});


Ext.define('Ext.util.Bag', {
    isBag: true,
    constructor: function() {
        
        this.items = [];
        
        this.map = {};
    },
    
    generation: 0,
    
    length: 0,
    beginUpdate: Ext.emptyFn,
    endUpdate: Ext.emptyFn,
    add: function(item) {
        var me = this,
            items = me.items,
            map = me.map,
            n = 1,
            old, i, idx, id, it, ret, was;
        if (Ext.isArray(item)) {
            old = ret = [];
            n = item.length;
        }
        for (i = 0; i < n; i++) {
            id = me.getKey(it = old ? item[i] : item);
            idx = map[id];
            if (idx === undefined) {
                items.push(it);
                map[id] = me.length++;
                if (old) {
                    old.push(it);
                } else {
                    ret = it;
                }
            } else {
                was = items[idx];
                if (old) {
                    old.push(was);
                } else {
                    ret = was;
                }
                items[idx] = it;
            }
        }
        ++me.generation;
        return ret;
    },
    clear: function() {
        var me = this,
            needsClear = me.generation || me.length,
            ret = needsClear ? me.items : [];
        if (needsClear) {
            me.items = [];
            me.length = 0;
            me.map = {};
            ++me.generation;
        }
        return ret;
    },
    clone: function() {
        var me = this,
            ret = new me.self(),
            len = me.length;
        if (len) {
            Ext.apply(ret.map, me.map);
            ret.items = me.items.slice();
            ret.length = me.length;
        }
        return ret;
    },
    contains: function(item) {
        var ret = false,
            map = this.map,
            key;
        if (item != null) {
            key = this.getKey(item);
            if (key in map) {
                ret = this.items[map[key]] === item;
            }
        }
        return ret;
    },
    containsKey: function(key) {
        return key in this.map;
    },
    destroy: function() {
        this.items = this.map = null;
        this.callParent();
    },
    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;
    },
    getAt: function(index) {
        var out = null;
        if (index < this.length) {
            out = this.items[index];
        }
        return out;
    },
    get: function(key) {
        return this.getByKey(key);
    },
    getByKey: function(key) {
        var map = this.map,
            ret = (key in map) ? this.items[map[key]] : null;
        return ret;
    },
    indexOfKey: function(key) {
        var map = this.map,
            ret = (key in map) ? map[key] : -1;
        return ret;
    },
    last: function() {
        return this.items[this.length - 1];
    },
    updateKey: function(item, oldKey) {
        var me = this,
            map = me.map,
            newKey;
        if (!item || !oldKey) {
            return;
        }
        if ((newKey = me.getKey(item)) !== oldKey) {
            if (me.getAt(map[oldKey]) === item && !(newKey in map)) {
                me.generation++;
                map[newKey] = map[oldKey];
                delete map[oldKey];
            }
        } else 
        {
            
            
            
            if (newKey in map && me.getAt(map[newKey]) !== item) {
                
                
                Ext.raise('Duplicate newKey "' + newKey + '" for item with oldKey "' + oldKey + '"');
            }
            if (oldKey in map && me.getAt(map[oldKey]) !== item) {
                
                
                
                Ext.raise('Incorrect oldKey "' + oldKey + '" for item with newKey "' + newKey + '"');
            }
        }
    },
    
    getCount: function() {
        return this.length;
    },
    getKey: function(item) {
        return item.id || item.getId();
    },
    getRange: function(begin, end) {
        var items = this.items,
            length = items.length,
            range;
        if (!length) {
            range = [];
        } else {
            range = Ext.Number.clipIndices(length, [
                begin,
                end
            ]);
            range = items.slice(range[0], range[1]);
        }
        return range;
    },
    remove: function(item) {
        var me = this,
            map = me.map,
            items = me.items,
            ret = null,
            n = 1,
            changed, old, i, idx, id, last, was;
        if (Ext.isArray(item)) {
            n = item.length;
            old = ret = [];
        }
        if (me.length) {
            for (i = 0; i < n; i++) {
                idx = map[id = me.getKey(old ? item[i] : item)];
                if (idx !== undefined) {
                    delete map[id];
                    was = items[idx];
                    if (old) {
                        old.push(was);
                    } else {
                        ret = was;
                    }
                    last = items.pop();
                    if (idx < --me.length) {
                        items[idx] = last;
                        map[me.getKey(last)] = idx;
                    }
                    changed = true;
                }
            }
            if (changed) {
                ++me.generation;
            }
        }
        return ret;
    },
    removeByKey: function(key) {
        var item = this.getByKey(key);
        if (item) {
            this.remove(item);
        }
        return item || null;
    },
    replace: function(item) {
        this.add(item);
        return item;
    },
    sort: function(fn) {
        var me = this,
            items = me.items,
            n = items.length,
            item;
        if (n) {
            Ext.Array.sort(items, fn);
            me.map = {};
            while (n-- > 0) {
                item = items[n];
                me.map[me.getKey(item)] = n;
            }
            ++me.generation;
        }
    }
});


Ext.define('Ext.util.Scheduler', {
    mixins: [
        Ext.mixin.Observable
    ],
    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
    },
    
    suspendOnNotify: true,
    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.Bag();
    },
    destroy: function() {
        var me = this,
            timer = me.timer;
        if (timer) {
            window.clearTimeout(timer);
            me.timer = null;
        }
        me.items.destroy();
        me.items = me.orderedItems = null;
        me.callParent();
        
        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.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.sort(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.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.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,
            suspend = me.suspendOnNotify,
            busyCounter, i, item, len, queue, firedEvent;
        if (timer) {
            window.clearTimeout(timer);
            me.timer = null;
        }
        
        
        if (!me.firing && me.scheduledCount) {
            if (suspend) {
                Ext.suspendLayouts();
            }
            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 (suspend) {
                Ext.resumeLayouts(true);
            }
        }
        
        
        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.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.raise('Exceeded cycleLimit ' + this.getCycleLimit());
        },
        scheduleItem: function(item) {
            if (!item) {
                Ext.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.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', {
    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
        });
    },
    commit: function() {
        var members = this.members,
            id;
        for (id in members) {
            members[id][2] = 0;
        }
    },
    onStoreLoad: function(store) {
        this.update(store.getData().items, 0);
    },
    update: function(recordsOrIds, state) {
        
        if (!(recordsOrIds instanceof Array)) {
            Ext.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 && !(state === 1 && assoc[2] === 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);
                }
            }
        }
    },
    updateId: 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];
        }
    },
    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', {
    
    constructor: function(matrix, index, role) {
        var me = this;
        
        me.matrix = matrix;
        
        me.index = index;
        
        me.role = role;
        
        me.slices = {};
    },
    commit: function() {
        var slices = this.slices,
            id;
        for (id in slices) {
            slices[id].commit();
        }
    },
    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);
    },
    updateId: function(oldId, newId) {
        var slice = this.get(oldId);
        if (slice) {
            slice.updateId(newId);
        }
    },
    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', {
    
    
    
    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;
    },
    commit: function() {
        this.left.commit();
        this.right.commit();
    },
    update: function(id1, id2, state) {
        return this.left.update(id1, id2, state);
    },
    updateId: function(record, oldId, newId) {
        var Type = record.self,
            left = this.left,
            right = this.right,
            matchSide;
        
        if (Type === left.role.cls) {
            matchSide = left;
        }
        if (Type === right.role.cls) {
            matchSide = right;
        }
        if (matchSide) {
            matchSide.updateId(oldId, newId);
        }
    },
    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, operationType, proxy, batchActions, records, len, i;
        if (map) {
            if (!batch) {
                batch = new Ext.data.Batch();
            }
            for (name in map) {
                bucket = map[name];
                entity = bucket.entity;
                
                proxy = entity.getProxy();
                batchActions = proxy.getBatchActions();
                delete bucket.entity;
                
                for (operationType in bucket) {
                    if (batchActions) {
                        operation = proxy.createOperation(operationType, {
                            records: bucket[operationType]
                        });
                        operation.entityType = entity;
                        batch.add(operation);
                    } else {
                        records = bucket[operationType];
                        for (i = 0 , len = records.length; i < len; ++i) {
                            operation = proxy.createOperation(operationType, {
                                records: [
                                    records[i]
                                ]
                            });
                            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.mixin.Dirty', {
    mixinId: 'dirty',
    
    config: {
        
        dirty: {
            $value: false,
            lazy: true
        }
    },
    dirty: false,
    
    
    _dirtyRecordCount: 0,
    
    ignoreDirty: false,
    
    recordStateIsDirtyState: true,
    
    isDirty: function() {
        
        return this.getDirty();
    },
    applyDirty: function(dirty) {
        return this.ignoreDirty ? false : dirty;
    },
    updateDirty: function(dirty) {
        var me = this;
        
        me.dirty = dirty;
        if (me.fireEvent && !me.isDirtyInitializing) {
            me.fireDirtyChange();
        }
    },
    
    clearRecordStates: function() {
        var me = this,
            counters = me._crudCounters;
        if (counters) {
            counters.C = counters.U = counters.D = 0;
        }
        me._dirtyRecordCount = 0;
        if (me.recordStateIsDirtyState) {
            me.setDirty(false);
        }
    },
    fireDirtyChange: function() {
        var me = this;
        if (!me.ignoreDirty && me.hasListeners.dirtychange) {
            me.fireEvent('dirtychange', me, me.dirty);
        }
    },
    
    trackRecordState: function(record, initial) {
        var me = this,
            counters = me._crudCounters || (me._crudCounters = {
                C: 0,
                R: 0,
                U: 0,
                D: 0
            }),
            dirtyRecordCountWas = me._dirtyRecordCount,
            state = record.crudState,
            stateWas = record.crudStateWas,
            changed, dirtyRecordCount;
        if (initial || state !== stateWas) {
            if (!initial && stateWas) {
                --counters[stateWas];
            }
            if (!(record.phantom && state === 'D')) {
                ++counters[state];
            }
            
            me.checkCounters();
            
            me._dirtyRecordCount = dirtyRecordCount = counters.C + counters.U + counters.D;
            changed = !dirtyRecordCount !== !dirtyRecordCountWas;
            if (changed && me.recordStateIsDirtyState) {
                me.setDirty(dirtyRecordCount > 0);
            }
        }
        return changed;
    },
    
    untrackRecordState: function(record) {
        var me = this,
            counters = me._crudCounters,
            dirtyRecordCountWas = me._dirtyRecordCount,
            state = record.crudState,
            changed, dirtyRecordCount;
        
        if (counters && state !== 'D' && !record.erased) {
            --counters[state];
            
            me.checkCounters();
            
            me._dirtyRecordCount = dirtyRecordCount = counters.C + counters.U + counters.D;
            changed = !dirtyRecordCount !== !dirtyRecordCountWas;
            if (changed && me.recordStateIsDirtyState) {
                me.setDirty(dirtyRecordCount > 0);
            }
        }
        return changed;
    },
    
    checkCounters: function() {
        var counters = this._crudCounters,
            key;
        for (key in counters) {
            if (counters[key] < 0) {
                Ext.raise('Invalid state for ' + key);
            }
        }
    }
});



Ext.define('Ext.data.Session', {
    mixins: [
        Ext.mixin.Dirty,
        Ext.mixin.Observable
    ],
    isSession: true,
    config: {
        
        schema: 'default',
        
        parent: null,
        
        autoDestroy: true,
        crudProperties: {
            create: 'C',
            read: 'R',
            update: 'U',
            drop: 'D'
        }
    },
    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
    },
    statics: {
        nextId: 1
    },
    constructor: function(config) {
        var me = this;
        
        me.data = {};
        
        me.matrices = {};
        me.id = Ext.data.Session.nextId++;
        me.identifierCache = {};
        
        me.recordCreator = me.recordCreator.bind(me);
        me.mixins.observable.constructor.call(me, 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 = null;
                    
                    
                    
                    record.unjoin(me);
                }
            }
        }
        
        me.identifierCache = 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.raise('Record already belongs to an existing session');
        }
        
        if (record.session !== me) {
            me.add(record);
            if (associations) {
                for (roleName in associations) {
                    associations[roleName].adoptAssociated(record, me);
                }
            }
        }
    },
    
    commit: function() {
        var me = this,
            data = me.data,
            matrices = me.matrices,
            dirtyWas = me.getDirty(),
            entityName, entities, id, record;
        me.suspendEvent('dirtychange');
        for (entityName in data) {
            entities = data[entityName];
            for (id in entities) {
                record = entities[id].record;
                if (record) {
                    record.commit();
                }
            }
        }
        for (id in matrices) {
            matrices[id].commit();
        }
        me.clearRecordStates();
        me.resumeEvent('dirtychange');
        if (me.getDirty() !== dirtyWas) {
            me.fireDirtyChange();
        }
    },
    
    createRecord: function(type, data, preventAdd) {
        
        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.raise('A parent session already contains an entry for ' + Model.entityName + ': ' + id);
            }
        }
        
        return new Model(data, preventAdd ? null : 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,
            wasInstance = type.isModel,
            record, Model, parent, parentRec;
        if (wasInstance) {
            wasInstance = type;
            id = type.id;
            type = type.self;
        }
        record = me.peekRecord(type, id);
        if (!record) {
            Model = type.$isClass ? type : me.getSchema().getEntity(type);
            parent = me.getParent();
            if (parent) {
                parentRec = parent.peekRecord(Model, id);
            }
            if (parentRec) {
                if (parentRec.isLoading()) {
                    
                    
                    
                    wasInstance = false;
                } else {
                    record = parentRec.copy(undefined, me);
                    record.$source = parentRec;
                }
            }
            if (!record) {
                if (wasInstance) {
                    record = wasInstance;
                    me.adopt(record);
                } 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.raise('Unable to read association entity: ' + this.getModelIdentifier(entityType, id));
    },
    
    onInvalidEntityCreate: function(entityType, id) {
        Ext.raise('Cannot create, record already not exists: ' + this.getModelIdentifier(entityType, id));
    },
    
    onInvalidEntityDrop: function(entityType, id) {
        Ext.raise('Cannot drop, record does not exist: ' + this.getModelIdentifier(entityType, id));
    },
    
    onInvalidEntityRead: function(entityType, id) {
        Ext.raise('Cannot read, record already not exists: ' + this.getModelIdentifier(entityType, id));
    },
    
    onInvalidEntityUpdate: function(entityType, id, dropped) {
        if (dropped) {
            Ext.raise('Cannot update, record dropped: ' + this.getModelIdentifier(entityType, id));
        } else {
            Ext.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() {
        var me = this,
            parent = me.getParent(),
            visitor;
        if (parent) {
            visitor = new Ext.data.session.ChildChangesVisitor(me);
            me.visitData(visitor);
            parent.update(visitor.result);
            me.commit();
        } else 
        {
            Ext.raise('Cannot commit session, no parent exists');
        }
    },
    
    
    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,
            dirtyWas = me.getDirty(),
            entityName, entityType, entityInfo, i, operation, item, associations, key, role, associationData;
        me.suspendEvent('dirtychange');
        
        me.getSchema().processKeyChecks(true);
        
        for (entityName in data) {
            entityType = schema.getEntity(entityName);
            
            if (!entityType) {
                Ext.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.raise('Invalid association key for ' + entityName + ', "' + key + '"');
                }
                
                associationData = entityInfo[role.role];
                role.processUpdate(me, associationData);
            }
        }
        me.resumeEvent('dirtychange');
        if (me.getDirty() !== dirtyWas) {
            me.fireDirtyChange();
        }
    },
    
    
    afterCommit: function(record) {
        this.trackRecordState(record);
    },
    
    afterDrop: function(record) {
        this.trackRecordState(record);
    },
    
    afterEdit: function(record) {
        this.trackRecordState(record);
    },
    
    afterErase: function(record) {
        this.evict(record);
    },
    
    afterReject: function(record) {
        this.trackRecordState(record);
    },
    privates: {
        
        add: function(record) {
            var me = this,
                id = record.id,
                entry = me.getEntry(record.self, id),
                associations, roleName;
            
            if (entry.record) {
                Ext.raise('Duplicate id ' + record.id + ' for ' + record.entityName);
            }
            
            record.session = me;
            entry.record = record;
            me.trackRecordState(record, true);
            me.registerReferences(record);
            associations = record.associations;
            for (roleName in associations) {
                associations[roleName].checkMembership(me, record);
            }
        },
        
        applySchema: function(schema) {
            return Ext.data.schema.Schema.get(schema);
        },
        
        
        checkModelType: function(name) {
            if (name.$isClass) {
                name = name.entityName;
            }
            if (!name) {
                Ext.raise('Unable to use anonymous models in a Session');
            } else if (!this.getSchema().getEntity(name)) {
                Ext.raise('Unknown entity type ' + name);
            }
        },
        
        
        createEntities: function(entityType, items) {
            var me = this,
                len = items.length,
                i, data, rec, id;
            for (i = 0; i < len; ++i) {
                data = items[i];
                id = entityType.getIdFromData(data);
                rec = me.peekRecord(entityType, id);
                if (!rec) {
                    
                    
                    
                    
                    
                    
                    
                    
                    rec = me.createRecord(entityType, data, true);
                    rec.phantom = true;
                    rec.crudState = 'C';
                    me.add(rec);
                    
                    rec.crudStateWas = 'C';
                } else {
                    me.onInvalidEntityCreate(entityType, id);
                }
            }
        },
        
        dropEntities: function(entityType, ids) {
            var len = ids.length,
                i, rec, id, extractId;
            if (len) {
                
                extractId = Ext.isObject(ids[0]);
            }
            for (i = 0; i < len; ++i) {
                id = ids[i];
                if (extractId) {
                    id = entityType.getIdFromData(id);
                }
                rec = this.peekRecord(entityType, id);
                if (rec) {
                    rec.drop();
                } else {
                    this.onInvalidEntityDrop(entityType, id);
                }
            }
        },
        
        evict: function(record) {
            var me = this,
                entityName = record.entityName,
                entities = me.data[entityName],
                id = record.id;
            if (entities && entities[id]) {
                me.untrackRecordState(record);
                
                
                
                record.unjoin(me);
                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.getId() || entityType.entityName;
                ret = cache[key];
                if (!ret) {
                    if (identifier.clone) {
                        ret = identifier.clone({
                            id: null
                        });
                    } 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,
                matrices = me.matrices,
                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, key;
            
            if (bucket[newId]) {
                Ext.raise('Cannot change ' + entityName + ' id from ' + oldId + ' to ' + newId + ' id already exists');
            }
            
            delete bucket[oldId];
            bucket[newId] = entry;
            for (key in matrices) {
                matrices[key].updateId(record, oldId, newId);
            }
            if (refs) {
                for (roleName in refs) {
                    roleRefs = refs[roleName];
                    role = associations[roleName];
                    association = role.association;
                    if (!association.isManyToMany) {
                        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, records, 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 me = this,
                len = items.length,
                i, data, rec, id;
            for (i = 0; i < len; ++i) {
                data = items[i];
                id = entityType.getIdFromData(data);
                rec = me.peekRecord(entityType, id);
                if (!rec) {
                    rec = me.createRecord(entityType, data, true);
                } else {
                    me.onInvalidEntityRead(entityType, id);
                }
                
                
                rec.phantom = false;
                me.add(rec);
            }
        },
        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);
                record.mergeData(data);
            }
            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.destroyed) {
            scheduler.remove(me);
        }
        me.scheduler = null;
        me.schedule = me.react = Ext.emptyFn;
        me.callParent();
    },
    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.destroyed) {
                    scheduler.unscheduleItem(me);
                }
                me.scheduled = false;
            }
        },
        
        
        sort: function() {}
    }
});


Ext.define('Ext.app.bind.BaseBinding', {
    extend: Ext.util.Schedulable,
    isBinding: true,
    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.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;
        if (owner) {
            owner.onBindDestroy(me);
        }
        me.callParent();
        me.scope = me.callback = me.owner = null;
    },
    isReadOnly: function() {
        return true;
    },
    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 || me.valueChanged(value, previous)) {
                ++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();
                }
            }
        },
        valueChanged: function(value, previous) {
            var ret = true;
            if (previous !== value) {
                if (value && previous && value instanceof Date && previous instanceof Date) {
                    ret = value.getTime() !== previous.getTime();
                }
            } else {
                ret = Ext.isArray(value);
            }
            return ret;
        }
    }
});


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.isAvailable() && !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;
        return stub && stub.getValue();
    },
    
    isAvailable: function() {
        var stub = this.stub;
        return stub && stub.isAvailable();
    },
    
    isLoading: function() {
        var stub = this.stub;
        return stub && stub.isLoading();
    },
    
    isReadOnly: function() {
        var stub = this.stub,
            options = this.options,
            ret = true;
        if (!(options && options.twoWay === false)) {
            if (stub) {
                ret = stub.isReadOnly();
            }
        }
        return ret;
    },
    
    refresh: function() {},
    
    
    setValue: function(value) {
        
        if (this.isReadOnly()) {
            Ext.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;
            return stub && stub.getRawValue();
        },
        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,
    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();
        }
        if (me.scheduled) {
            me.unschedule();
        }
        me.callParent();
    },
    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.isAvailable() ? this.getRawValue() : null;
    },
    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;
    },
    isAvailable: function() {
        return true;
    },
    isLoading: function() {
        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,
    isStub: true,
    dirty: true,
    formula: null,
    validationKey: 'validation',
    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.checkHadValue();
        }
        me.path = path;
    },
    destroy: function() {
        var me = this,
            formula = me.formula,
            storeBinding = me.storeBinding;
        if (formula) {
            formula.destroy();
        }
        if (storeBinding) {
            storeBinding.destroy();
        }
        me.detachBound();
        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, value, this);
                } else {
                    callback.call(scope, field, value, 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,
            bindMappings = me.bindMappings,
            storeMappings = bindMappings.store,
            modelMappings = bindMappings.model,
            ret;
        if (!parentData && !Ext.isString(parentData)) {
            
            
            ret = me.hadValue ? null : undefined;
        } else {
            ret = me.inspectValue(parentData);
            if (!ret) {
                if (parentData.isEntity) {
                    
                    if (modelMappings[name]) {
                        ret = parentData[modelMappings[name]]();
                    } else {
                        ret = parentData.data[name];
                    }
                } else if (parentData.isStore && storeMappings[name]) {
                    ret = parentData[storeMappings[name]]();
                } else {
                    ret = parentData[name];
                    if (ret === undefined && me.hadValue) {
                        ret = null;
                    }
                }
            }
        }
        return ret;
    },
    getDataObject: function() {
        var me = this,
            parentData = me.parent.getDataObject(),
            
            name = me.name,
            ret = parentData ? parentData[name] : null,
            storeMappings = me.bindMappings.store,
            associations;
        if (!ret) {
            if (parentData && parentData.isEntity) {
                
                associations = parentData.associations;
                if (associations && name in associations) {
                    ret = parentData[associations[name].getterName]();
                }
            }
        } else if (parentData.isStore && name in storeMappings) {
            ret = parentData[storeMappings[name]]();
        }
        if (!ret || !(ret.$className || Ext.isObject(ret))) {
            parentData[name] = ret = {};
            
            me.hadValue = 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, ret;
        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;
        replacement.checkHadValue();
        ret = me.callParent([
            replacement
        ]);
        ret.invalidate(true, true);
        return ret;
    },
    isAvailable: function() {
        return this.checkAvailability();
    },
    isLoading: function() {
        return !this.checkAvailability(true);
    },
    invalidate: function(deep, dirtyOnly) {
        var me = this,
            children = me.children,
            name;
        me.dirty = true;
        me.checkHadValue();
        if (!dirtyOnly && me.isAvailable()) {
            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, preventClimb) {
        var me = this,
            parent = me.parent,
            name = me.name,
            formula = me.formula,
            parentData, associations, association, formulaStub, setterName;
        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.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];
                setterName = association.setterName;
                if (setterName) {
                    parentData[setterName](value);
                }
                
                me.invalidate(true);
            } else {
                
                parentData.set(name, value);
            }
        }
        
        
        else if ((value && value.constructor === Object) || !(value === parentData[name] && parentData.hasOwnProperty(name))) {
            
            
            if (preventClimb || !me.setByLink(value)) {
                if (value === undefined) {
                    delete parentData[name];
                } else {
                    parentData[name] = value;
                }
                me.inspectValue(parentData);
                
                
                me.invalidate(true);
            }
        }
    },
    onStoreDataChanged: 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,
            bindMappings = this.bindMappings.model,
            key, i, child, name, ref;
        
        if (children) {
            if (len) {
                
                for (i = 0; i < len; ++i) {
                    name = modifiedFieldNames[i];
                    child = children[name];
                    if (!child) {
                        ref = record.fieldsMap[name];
                        ref = ref && ref.reference;
                        child = ref && children[ref.role];
                    }
                    if (child) {
                        child.invalidate(true);
                    }
                }
            } else {
                
                
                
                for (key in children) {
                    if (!(associations && key in associations)) {
                        children[key].invalidate(true);
                    }
                }
            }
            
            
            for (key in bindMappings) {
                child = children[key];
                if (child) {
                    child.invalidate();
                }
            }
        }
        this.invalidate();
    },
    afterReject: function(record) {
        
        this.afterEdit(record, null);
    },
    afterAssociatedRecordSet: function(record, associated, role) {
        var children = this.children,
            key = role.role;
        if (children && key in children) {
            children[key].invalidate(true);
        }
    },
    setByLink: function(value) {
        var me = this,
            n = 0,
            ret = false,
            i, link, path, stub, root, name;
        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;
        }
        stub = null;
        if (link) {
            root = link.parent;
            name = link.name;
            if (!root.shouldClimb(name)) {
                
                stub = root.insertChild(name);
            } else {
                stub = link.getTargetStub();
            }
        }
        if (stub) {
            
            
            
            if (path) {
                stub = stub.descend(path);
            }
            stub.set(value);
            ret = true;
        }
        return ret;
    },
    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();
                }
            }
        }
        this.callParent();
    },
    privates: {
        bindMappings: {
            store: {
                count: 'getCount',
                first: 'first',
                last: 'last',
                loading: 'hasPendingLoad',
                totalCount: 'getTotalCount'
            },
            model: {
                dirty: 'isDirty',
                phantom: 'isPhantom',
                valid: 'isValid'
            }
        },
        checkAvailability: function(isLoading) {
            var me = this,
                parent = me.parent,
                bindMappings = me.bindMappings,
                name = me.name,
                available = !!(parent && parent.checkAvailability(isLoading)),
                associations, parentValue, value, availableSet, loading;
            if (available) {
                parentValue = me.getParentValue();
                value = me.inspectValue(parentValue);
                
                if (value) {
                    if (isLoading) {
                        available = !value.hasPendingLoad();
                    } else {
                        
                        if (value.isStore) {
                            available = true;
                        } else {
                            
                            
                            available = !value.isLoading() || value.loadCount > 0;
                        }
                    }
                } else {
                    if (parentValue) {
                        if (parentValue.isModel) {
                            if (bindMappings.model[name]) {
                                available = !parent.isLoading();
                                availableSet = true;
                            } else {
                                associations = parentValue.associations;
                                
                                
                                
                                
                                if (!(associations && name in associations)) {
                                    available = true;
                                    availableSet = true;
                                }
                            }
                        } else if (parentValue.isStore && bindMappings.store[name] && name !== 'loading') {
                            available = !parent.isLoading();
                            availableSet = true;
                        }
                    }
                    if (!availableSet) {
                        available = me.hadValue || me.getRawValue() !== undefined;
                    }
                }
            }
            return available;
        },
        checkHadValue: function() {
            if (!this.hadValue) {
                this.hadValue = this.getRawValue() !== undefined;
            }
        },
        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, raw, changed, associatedEntity;
            if (parentData && parentData.isEntity) {
                associations = parentData.associations;
                if (associations && (name in associations)) {
                    boundValue = parentData[associations[name].getterName]();
                } 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 && boundValue.autoLoad !== false && !boundValue.complete && !boundValue.hasPendingLoad()) {
                            boundValue.load();
                        }
                        
                        
                        boundValue.on({
                            scope: me,
                            
                            
                            
                            
                            
                            
                            
                            beginload: 'onStoreDataChanged',
                            load: 'onStoreDataChanged',
                            datachanged: 'onStoreDataChanged',
                            destroy: 'onDestroyBound'
                        });
                    }
                }
                me.boundValue = boundValue;
            }
            return boundValue;
        },
        detachBound: function() {
            var me = this,
                current = me.boundValue;
            if (current && !current.destroyed) {
                if (current.isModel) {
                    current.unjoin(me);
                } else {
                    current.un({
                        scope: me,
                        beginload: 'onStoreDataChanged',
                        load: 'onStoreDataChanged',
                        datachanged: 'onStoreDataChanged',
                        destroy: 'onDestroyBound'
                    });
                }
            }
        },
        onDestroyBound: function() {
            if (!this.owner.destroying) {
                this.set(null);
            }
        },
        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,
            root = this.parent,
            name = this.name,
            rootData, ret;
        if (root.isRootStub && !root.shouldClimb(name)) {
            rootData = root.owner.getData();
            if (!rootData.hasOwnProperty(name)) {
                rootData[name] = ret = {};
            }
        } else {
            ret = binding && binding.getDataObject();
        }
        return ret;
    },
    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;
    },
    isAvailable: function() {
        var binding = this.binding;
        return binding ? binding.isAvailable() : false;
    },
    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,
    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, 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;
    },
    set: function(value, preventClimb) {
        
        if (!value || value.constructor !== Object) {
            Ext.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(),
            stub, v, key, setSelf, created;
        for (key in value) {
            
            if (key.indexOf('.') >= 0) {
                Ext.raise('Value names cannot contain dots');
            }
            
            
            
            v = value[key];
            if (v !== undefined) {
                stub = children[key];
                setSelf = preventClimb || !me.shouldClimb(key);
                if (!stub) {
                    stub = me.createRootChild(key, setSelf);
                    created = true;
                } else if (setSelf && stub.isLinkStub && !stub.getLinkFormulaStub()) {
                    stub = me.insertChild(key);
                }
                if (!created || !data.hasOwnProperty(value)) {
                    owner.invalidateChildLinks(key);
                }
                stub.set(v, setSelf);
            }
            
            
            else if (data.hasOwnProperty(key)) {
                delete data[key];
                stub = children[key];
                if (stub) {
                    if (!stub.isLinkStub && parentVM) {
                        stub = me.createRootChild(key);
                    }
                    owner.invalidateChildLinks(key, true);
                    stub.invalidate(true);
                }
            }
        }
    },
    schedule: Ext.emptyFn,
    unschedule: Ext.emptyFn,
    privates: {
        checkAvailability: function() {
            
            return true;
        },
        insertChild: function(name) {
            return this.createRootChild(name, true);
        },
        invalidateChildLink: function(name, clear) {
            var children = this.children,
                stub = children && children[name];
            if (stub && stub.isLinkStub && !stub.getLinkFormulaStub()) {
                stub = this.createRootChild(name);
                if (clear) {
                    stub.invalidate(true);
                }
                this.owner.invalidateChildLinks(name, clear);
            }
        },
        shouldClimb: function(name) {
            var parent = this.owner.getParent();
            while (parent) {
                if (parent.getData().hasOwnProperty(name)) {
                    return true;
                }
                parent = parent.getParent();
            }
            return false;
        }
    }
});


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;
    },
    isAvailable: function() {
        for (var bindings = this.bindings,
            n = bindings.length; n-- > 0; ) {
            if (bindings[n].isAvailable()) {
                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,
    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_]' + Ext.String.escapeRegex(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,
    fnKeywordArgumentNamesRe: /^function\s*[^\(]*\(\s*([^,\)\s]+)/,
    fnKeywordRe: /^\s*function/,
    replaceParenRe: /[\(\)]/g,
    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.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 = Ext.Function.toCode(formula),
            defaultProp = 'get',
            expressions = {
                $literal: true
            },
            match, getterProp, formulaRe, expr;
        if (this.fnKeywordRe.test(str)) {
            match = this.fnKeywordArgumentNamesRe.exec(str);
            if (match) {
                getterProp = match[1];
            }
        } else {
            match = str.split('=>')[0];
            if (match) {
                match = Ext.String.trim(match.replace(this.replaceParenRe, '')).split(',');
                getterProp = match[0];
            }
        }
        getterProp = getterProp || defaultProp;
        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.util.Fly', {
    inheritableStatics: {
        flyPoolSize: 2,
        
        fly: function() {
            var T = this,
                flyweights = T.flyweights || (T.flyweights = []),
                instance = flyweights.length ? flyweights.pop() : new T();
            instance.reset.apply(instance, arguments);
            return instance;
        }
    },
    
    release: function() {
        var me = this,
            T = me.self,
            flyweights = T.flyweights || (T.flyweights = []);
        me.reset();
        if (flyweights.length < T.flyPoolSize) {
            flyweights.push(me);
        }
    },
    
    reset: Ext.emptyFn
});


Ext.define('Ext.parse.Tokenizer', function(Tokenizer) {
    var flyweights = (Tokenizer.flyweights = []),
        BOOLEAN = {
            literal: true,
            "boolean": true
        },
        ERROR = {
            error: true
        },
        IDENT = {
            ident: true
        },
        LITERAL = {
            literal: true
        },
        NULL = {
            literal: true,
            nil: true
        },
        NUMBER = {
            literal: true,
            number: true
        },
        STRING = {
            literal: true,
            string: true
        };
    return {
        extend: Ext.util.Fly,
        isTokenizer: true,
        statics: {
            BOOLEAN: BOOLEAN,
            ERROR: ERROR,
            IDENT: IDENT,
            LITERAL: LITERAL,
            NULL: NULL,
            NUMBER: NUMBER,
            STRING: STRING
        },
        config: {
            
            keywords: {
                'null': {
                    type: 'literal',
                    is: NULL,
                    value: null
                },
                'false': {
                    type: 'literal',
                    is: BOOLEAN,
                    value: false
                },
                'true': {
                    type: 'literal',
                    is: BOOLEAN,
                    value: true
                }
            },
            
            operators: {
                '+': 'plus',
                '-': 'minus',
                '*': 'multiply',
                '/': 'divide',
                '!': 'bang',
                ',': 'comma',
                ':': 'colon',
                '[': 'arrayOpen',
                ']': 'arrayClose',
                '{': 'curlyOpen',
                '}': 'curlyClose',
                '(': 'parenOpen',
                ')': 'parenClose'
            }
        },
        
        error: null,
        
        index: -1,
        constructor: function(config) {
            this.operators = {};
            this.initConfig(config);
        },
        
        next: function() {
            var token = this.peek();
            this.head = undefined;
            
            return token;
        },
        
        peek: function() {
            var me = this,
                error = me.error,
                token = me.head;
            if (error) {
                return error;
            }
            if (token === undefined) {
                me.head = token = me.advance();
            }
            return token;
        },
        
        release: function() {
            this.reset();
            if (flyweights.length < Tokenizer.flyPoolSize) {
                flyweights.push(this);
            }
        },
        
        reset: function(text, pos, end) {
            var me = this;
            me.error = null;
            me.head = undefined;
            me.index = -1;
            me.text = text || null;
            me.pos = pos || 0;
            me.end = (text && end == null) ? text.length : end;
            return me;
        },
        privates: {
            digitRe: /[0-9]/,
            identFirstRe: /[a-z_$]/i,
            identRe: /[0-9a-z_$]/i,
            spaceRe: /[ \t]/,
            
            end: 0,
            
            head: undefined,
            
            pos: 0,
            
            text: null,
            applyOperators: function(ops) {
                var operators = this.operators,
                    block, c, def, i, len, name, op;
                
                for (op in ops) {
                    block = operators;
                    name = ops[op];
                    len = op.length;
                    for (i = 0; i < len; ++i) {
                        c = op.charAt(i);
                        block = block[c] || (block[c] = {});
                    }
                    if (name) {
                        block.token = def = {
                            type: 'operator',
                            value: op,
                            is: {
                                operator: true
                            }
                        };
                        def.is[name] = true;
                    } else {
                        block.token = null;
                    }
                }
            },
            
            advance: function() {
                var me = this,
                    spaceRe = me.spaceRe,
                    text = me.text,
                    length = me.end,
                    c;
                while (me.pos < length) {
                    c = text.charAt(me.pos);
                    if (spaceRe.test(c)) {
                        ++me.pos;
                        
                        
                        continue;
                    }
                    me.index = me.pos;
                    return me.parse(c);
                }
                return null;
            },
            
            parse: function(c) {
                var me = this,
                    digitRe = me.digitRe,
                    text = me.text,
                    length = me.end,
                    ret;
                
                if (c === '.' && me.pos + 1 < length) {
                    if (digitRe.test(text.charAt(me.pos + 1))) {
                        ret = me.parseNumber();
                    }
                }
                if (!ret && me.operators[c]) {
                    ret = me.parseOperator(c);
                }
                if (!ret) {
                    if (c === '"' || c === "'") {
                        ret = me.parseString();
                    } else if (digitRe.test(c)) {
                        ret = me.parseNumber();
                    } else if (me.identFirstRe.test(c)) {
                        ret = me.parseIdent();
                    } else {
                        ret = me.syntaxError('Unexpected character');
                    }
                }
                return ret;
            },
            
            parseIdent: function() {
                var me = this,
                    identRe = me.identRe,
                    keywords = me.getKeywords(),
                    includeDots = !me.operators['.'],
                    text = me.text,
                    start = me.pos,
                    end = start,
                    length = me.end,
                    prev = 0,
                    c, value;
                while (end < length) {
                    c = text.charAt(end);
                    if (includeDots && c === '.') {
                        if (prev === '.') {
                            return me.syntaxError(end, 'Unexpected dot operator');
                        }
                        ++end;
                    } else if (identRe.test(c)) {
                        ++end;
                    } else {
                        break;
                    }
                    prev = c;
                }
                if (prev === '.') {
                    return me.syntaxError(end - 1, 'Unexpected dot operator');
                }
                value = text.substring(start, me.pos = end);
                return (keywords && keywords[value]) || {
                    type: 'ident',
                    is: IDENT,
                    value: value
                };
            },
            
            parseNumber: function() {
                var me = this,
                    digitRe = me.digitRe,
                    text = me.text,
                    start = me.pos,
                    length = me.end,
                    c, decimal, exp, token;
                while (me.pos < length) {
                    c = text.charAt(me.pos);
                    if (c === '-' || c === '+') {
                        if (me.pos !== start) {
                            return me.syntaxError(start, 'Invalid number');
                        }
                        ++me.pos;
                    } else if (c === '.') {
                        if (decimal) {
                            break;
                        }
                        decimal = true;
                        ++me.pos;
                    } else if (c === 'e' || c === 'E') {
                        if (exp) {
                            break;
                        }
                        decimal = exp = true;
                        
                        c = text.charAt(++me.pos);
                        
                        if (c === '-' || c === '+') {
                            ++me.pos;
                        }
                    }
                    
                    else if (digitRe.test(c)) {
                        ++me.pos;
                    } else {
                        break;
                    }
                }
                token = {
                    type: 'literal',
                    is: NUMBER,
                    
                    
                    value: +text.substring(start, me.pos)
                };
                if (!isFinite(token.value)) {
                    token = me.syntaxError(start, 'Invalid number');
                }
                return token;
            },
            parseOperator: function(c) {
                var me = this,
                    block = me.operators,
                    text = me.text,
                    length = me.end,
                    end = me.pos,
                    match, matchEnd, token;
                while (block[c]) {
                    block = block[c];
                    token = block.token;
                    ++end;
                    if (token) {
                        match = token;
                        matchEnd = end;
                    }
                    if (end < length) {
                        c = text.charAt(end);
                    } else {
                        break;
                    }
                }
                if (match) {
                    me.pos = matchEnd;
                }
                return match;
            },
            
            parseString: function() {
                var me = this,
                    text = me.text,
                    pos = me.pos,
                    start = pos,
                    length = me.end,
                    str = '',
                    c, closed, quote;
                quote = text.charAt(pos++);
                while (pos < length) {
                    c = text.charAt(pos++);
                    if (c === quote) {
                        closed = true;
                        break;
                    }
                    if (c === '\\' && pos < length) {
                        c = text.charAt(pos++);
                    }
                    
                    
                    str += c;
                }
                me.pos = pos;
                if (!closed) {
                    return me.syntaxError(start, 'Unterminated string');
                }
                return {
                    type: 'literal',
                    is: STRING,
                    value: str
                };
            },
            
            syntaxError: function(at, message) {
                if (typeof at === 'string') {
                    message = at;
                    at = this.pos;
                }
                var suffix = (at == null) ? '' : (' (at index ' + at + ')'),
                    error = new Error(message + suffix);
                error.type = 'error';
                error.is = ERROR;
                if (suffix) {
                    error.at = at;
                }
                return this.error = error;
            }
        }
    };
});


Ext.define('Ext.parse.Symbol', {
    priority: 0,
    
    constructor: function(id, config) {
        var me = this,
            defaultProperty = me.defaultProperty;
        if (config && typeof config === 'object') {
            Ext.apply(me, config);
        } else if (config !== undefined && defaultProperty) {
            me[defaultProperty] = config;
        }
        me.id = id;
    },
    
    dump: function() {
        var me = this,
            ret = {
                at: me.at,
                arity: me.arity
            },
            i;
        if ('value' in me) {
            ret.value = me.value;
        }
        if (me.lhs) {
            ret.lhs = me.lhs.dump();
            ret.rhs = me.rhs.dump();
        }
        if (me.operand) {
            ret.operand = me.operand.dump();
        }
        if (me.args) {
            ret.args = [];
            for (i = 0; i < me.args.length; ++i) {
                ret.args.push(me.args[i].dump());
            }
        }
        return ret;
    },
    
    
    led: function() {
        this.parser.syntaxError(this.at, 'Missing operator');
    },
    
    nud: function() {
        this.parser.syntaxError(this.at, 'Undefined');
    },
    
    update: function(config) {
        if (config && typeof config === 'object') {
            var me = this,
                priority = config.priority,
                led = config.led,
                nud = config.nud;
            if (me.priority <= priority) {
                me.priority = priority;
            }
            if (led) {
                me.led = led;
            }
            if (nud) {
                me.nud = nud;
            }
        }
    }
});


Ext.define('Ext.parse.symbol.Constant', {
    extend: Ext.parse.Symbol,
    arity: 'literal',
    isLiteral: true,
    defaultProperty: 'value',
    constructor: function(id, config) {
        this.callParent([
            id,
            config
        ]);
        this._value = this.value;
    },
    nud: function() {
        var me = this;
        
        me.value = me._value;
        
        
        me.arity = 'literal';
        me.isLiteral = true;
        return me;
    }
});


Ext.define('Ext.parse.symbol.Infix', {
    extend: Ext.parse.Symbol,
    arity: 'binary',
    isBinary: true,
    defaultProperty: 'priority',
    led: function(left) {
        var me = this;
        me.lhs = left;
        me.rhs = me.parser.parseExpression(me.priority);
        
        
        me.arity = 'binary';
        me.isBinary = true;
        return me;
    }
});


Ext.define('Ext.parse.symbol.InfixRight', {
    extend: Ext.parse.symbol.Infix,
    led: function(left) {
        var me = this;
        me.lhs = left;
        me.rhs = me.parser.parseExpression(me.priority - 1);
        
        
        me.arity = 'binary';
        me.isBinary = true;
        return me;
    }
});


Ext.define('Ext.parse.symbol.Paren', {
    extend: Ext.parse.Symbol,
    arity: 'binary',
    isBinary: true,
    priority: 80,
    led: function(left) {
        
        var me = this,
            args = [],
            parser = me.parser,
            id = left.id,
            type = left.arity;
        if (id !== '.' && id !== '[') {
            if ((type !== "unary" || id !== "function") && type !== "ident" && id !== "(" && id !== "&&" && id !== "||" && id !== "?") {
                parser.syntaxError(left.at, "Expected a variable name.");
            }
        }
        me.arity = 'invoke';
        me.isInvoke = true;
        me.operand = left;
        me.args = args;
        while (parser.token.id !== ')') {
            if (args.length) {
                parser.advance(',');
            }
            args.push(parser.parseExpression());
        }
        parser.advance(')');
        return me;
    },
    nud: function() {
        
        var parser = this.parser,
            ret = parser.parseExpression();
        parser.advance(")");
        return ret;
    }
});


Ext.define('Ext.parse.symbol.Prefix', {
    extend: Ext.parse.Symbol,
    arity: 'unary',
    isUnary: true,
    priority: 70,
    nud: function() {
        var me = this;
        me.operand = me.parser.parseExpression(me.priority);
        
        
        me.arity = 'unary';
        me.isUnary = true;
        return me;
    }
});


Ext.define('Ext.parse.Parser', function() {
    var ITSELF = function() {
            return this;
        };
    return {
        extend: Ext.util.Fly,
        isParser: true,
        config: {
            
            constants: {
                'null': null,
                'false': false,
                'true': true
            },
            
            infix: {
                
                
                
                
                
                
                '+': 50,
                '-': 50,
                '*': 60,
                '/': 60
            },
            
            infixRight: {
                '&&': 30,
                '||': 30
            },
            
            prefix: {
                '!': 0,
                '-': 0,
                '+': 0
            },
            
            symbols: {
                ':': 0,
                ',': 0,
                ')': 0,
                '[': 0,
                ']': 0,
                '{': 0,
                '}': 0,
                '(end)': 0,
                '(ident)': {
                    arity: 'ident',
                    isIdent: true,
                    nud: ITSELF
                },
                '(literal)': {
                    arity: 'literal',
                    isLiteral: true,
                    nud: ITSELF
                },
                '(': {
                    xclass: 'Ext.parse.symbol.Paren'
                }
            },
            
            tokenizer: {
                keywords: null
            }
        },
        
        
        token: null,
        constructor: function(config) {
            this.symbols = {};
            this.initConfig(config);
        },
        
        advance: function(expected) {
            var me = this,
                tokenizer = me.tokenizer,
                token = tokenizer.peek(),
                symbols = me.symbols,
                index = tokenizer.index,
                is, symbol, value;
            if (me.error) {
                throw me.error;
            }
            if (expected) {
                me.expect(expected);
            }
            if (!token) {
                return me.token = symbols['(end)'];
            }
            tokenizer.next();
            is = token.is;
            value = token.value;
            if (is.ident) {
                symbol = symbols[value] || symbols['(ident)'];
            } else if (is.operator) {
                if (!(symbol = symbols[value])) {
                    me.syntaxError(token.at, 'Unknown operator "' + value + '"');
                }
            } else if (is.literal) {
                symbol = symbols['(literal)'];
            } else {
                me.syntaxError(token.at, 'Unexpected token');
            }
            me.token = symbol = Ext.Object.chain(symbol);
            symbol.at = index;
            symbol.value = value;
            if (!symbol.arity) {
                symbol.arity = token.type;
            }
            return symbol;
        },
        expect: function(expected) {
            var token = this.token;
            if (expected !== token.id) {
                this.syntaxError(token.at, 'Expected "' + expected + '"');
            }
            return this;
        },
        
        parseExpression: function(rightPriority) {
            var me = this,
                token = me.token,
                left;
            rightPriority = rightPriority || 0;
            me.advance();
            left = token.nud();
            while (rightPriority < (token = me.token).priority) {
                me.advance();
                left = token.led(left);
            }
            return left;
        },
        
        reset: function(text, pos, end) {
            var me = this;
            me.error = me.token = null;
            me.tokenizer.reset(text, pos, end);
            me.advance();
            
            return me;
        },
        
        syntaxError: function(at, message) {
            if (typeof at === 'string') {
                message = at;
                at = this.pos;
            }
            var suffix = (at == null) ? '' : (' (at index ' + at + ')'),
                error = new Error(message + suffix);
            error.type = 'error';
            if (suffix) {
                error.at = at;
            }
            throw this.error = error;
        },
        privates: {
            
            error: null,
            addSymbol: function(id, config, type, update) {
                var symbols = this.symbols,
                    symbol = symbols[id],
                    cfg, length, i;
                if (symbol) {
                    
                    
                    
                    
                    if (typeof config === 'object') {
                        cfg = config;
                    } else if (update && type) {
                        update = Ext.Array.from(update);
                        length = update.length;
                        cfg = {};
                        for (i = 0; i < length; i++) {
                            cfg[update[i]] = type.prototype[update[i]];
                        }
                    } else {
                        return symbol;
                    }
                    symbol.update(cfg);
                } else {
                    if (config && config.xclass) {
                        type = Ext.ClassManager.get(config.xclass);
                    } else {
                        type = type || Ext.parse.Symbol;
                    }
                    symbols[id] = symbol = new type(id, config);
                    symbol.parser = this;
                }
                return symbol;
            },
            addSymbols: function(symbols, type, update) {
                for (var id in symbols) {
                    this.addSymbol(id, symbols[id], type, update);
                }
            },
            applyConstants: function(constants) {
                this.addSymbols(constants, Ext.parse.symbol.Constant, 'nud');
            },
            applyInfix: function(operators) {
                this.addSymbols(operators, Ext.parse.symbol.Infix, 'led');
            },
            applyInfixRight: function(operators) {
                this.addSymbols(operators, Ext.parse.symbol.InfixRight, 'led');
            },
            applyPrefix: function(operators) {
                this.addSymbols(operators, Ext.parse.symbol.Prefix, 'nud');
            },
            applySymbols: function(symbols) {
                this.addSymbols(symbols);
            },
            applyTokenizer: function(config) {
                var ret = config;
                if (config && !config.isTokenizer) {
                    ret = new Ext.parse.Tokenizer(config);
                }
                this.tokenizer = ret;
            }
        }
    };
});


Ext.define('Ext.app.bind.Parser', {
    extend: Ext.parse.Parser,
    infix: {
        ':': {
            priority: 70,
            
            
            dump: function() {
                var me = this,
                    ret = {
                        at: me.at,
                        arity: me.arity,
                        value: me.value,
                        operand: me.operand.dump(),
                        fmt: []
                    },
                    fmt = me.fmt,
                    i;
                for (i = 0; i < fmt.length; ++i) {
                    ret.fmt.push(fmt[i].dump());
                }
                return ret;
            },
            
            led: function(left) {
                
                
                var me = this;
                me.arity = 'formatter';
                me.operand = left;
                me.fmt = me.parser.parseFmt();
                return me;
            }
        },
        '?': {
            priority: 20,
            led: function(left) {
                var me = this,
                    parser = me.parser,
                    symbol = parser.symbols[':'],
                    temp;
                me.condition = left;
                
                temp = symbol.priority;
                symbol.priority = 0;
                me.tv = parser.parseExpression(0);
                me.parser.advance(':');
                
                symbol.priority = temp;
                me.fv = parser.parseExpression(0);
                me.arity = 'ternary';
                return me;
            }
        },
        '===': 40,
        '!==': 40,
        '==': 40,
        '!=': 40,
        '<': 40,
        '<=': 40,
        '>': 40,
        '>=': 40
    },
    symbols: {
        '(': {
            nud: function() {
                
                var parser = this.parser,
                    symbol = parser.symbols[':'],
                    ret, temp;
                
                temp = symbol.priority;
                symbol.priority = 70;
                ret = parser.parseExpression();
                parser.advance(")");
                
                symbol.priority = temp;
                return ret;
            }
        }
    },
    prefix: {
        '@': 0
    },
    tokenizer: {
        operators: {
            '@': 'at',
            '?': 'qmark',
            '===': 'feq',
            '!==': 'fneq',
            '==': 'eq',
            '!=': 'neq',
            '<': 'lt',
            '<=': 'lte',
            '>': 'gt',
            '>=': 'gte',
            '&&': 'and',
            '||': 'or'
        }
    },
    
    compileExpression: function(tokens, tokensMaps) {
        var me = this,
            debug, fn;
        me.tokens = tokens;
        me.tokensMap = tokensMaps;
        
        debug = me.token.value === '@' && me.tokenizer.peek();
        if (debug) {
            debug = debug.value === 'debugger';
            if (debug) {
                me.advance();
                me.advance();
            }
        }
        
        fn = me.parseSlot(me.parseExpression(), debug);
        me.tokens = me.tokensMap = null;
        return fn;
    },
    
    compileFormat: function() {
        var fn;
        
        try {
            
            fn = this.parseSlot({
                arity: 'formatter',
                fmt: this.parseFmt(),
                operand: {
                    arity: 'ident',
                    value: 'dummy'
                }
            });
            this.expect('(end)');
        } 
        catch (e) {
            Ext.raise('Invalid format expression: "' + this.tokenizer.text + '"');
        }
        
        return fn;
    },
    privates: {
        
        
        
        useEval: Ext.isGecko,
        escapeRe: /("|'|\\)/g,
        
        parseFmt: function() {
            
            
            var me = this,
                fmt = [],
                priority = me.symbols[':'].priority,
                expr;
            do {
                if (fmt.length) {
                    me.advance();
                }
                expr = me.parseExpression(priority);
                if (expr.isIdent || expr.isInvoke) {
                    fmt.push(expr);
                } else {
                    me.syntaxError(expr.at, 'Expected formatter name');
                }
            } while (me.token.id === ':');
            return fmt;
        },
        
        parseSlot: function(expr, debug) {
            var me = this,
                defs = [],
                body = [],
                tokens = me.tokens || [],
                fn, code, i, length, temp;
            me.definitions = defs;
            me.body = body;
            body.push('return ' + me.compile(expr) + ';');
            
            length = tokens.length;
            code = 'var fm = Ext.util.Format,\nme,';
            temp = 'var a = Ext.Array.from(values);\nme = scope;\n';
            if (tokens.length) {
                for (i = 0; i < length; i++) {
                    code += 'v' + i + ((i == length - 1) ? ';' : ',');
                    temp += 'v' + i + ' = a[' + i + ']; ';
                }
            } else {
                code += 'v0;';
                temp += 'v0 = a[0];';
            }
            defs = Ext.Array.insert(defs, 0, [
                code
            ]);
            body = Ext.Array.insert(body, 0, [
                temp
            ]);
            body = body.join('\n');
            
            if (debug) {
                body = 'debugger;\n' + body;
            }
            
            defs.push((me.useEval ? '$=' : 'return') + ' function (values, scope) {', body, '}');
            code = defs.join('\n');
            fn = me.useEval ? me.evalFn(code) : (new Function('Ext', code))(Ext);
            me.definitions = me.body = null;
            return fn;
        },
        
        compile: function(expr) {
            var me = this,
                v;
            switch (expr.arity) {
                case 'ident':
                    
                    return me.addToken(expr.value);
                case 'literal':
                    v = expr.value;
                    
                    return (typeof v === 'string') ? '"' + String(v).replace(me.escapeRe, '\\$1') + '"' : v;
                case 'unary':
                    return me.compileUnary(expr);
                case 'binary':
                    return me.compileBinary(expr);
                case 'ternary':
                    return me.compileTernary(expr);
                case 'formatter':
                    return me.compileFormatter(expr);
            }
            return this.syntaxError(expr.at, 'Compile error! Unknown symbol');
        },
        
        compileUnary: function(expr) {
            var v = expr.value,
                op = expr.operand;
            if (v === '!' || v === '-' || v === '+') {
                return v + '(' + this.compile(op) + ')';
            } else if (v === '@') {
                
                if (!op.isIdent) {
                    return this.syntaxError(expr.at, 'Compile error! Unexpected symbol');
                }
                return op.value;
            }
            return '';
        },
        
        compileBinary: function(expr) {
            return '(' + this.compile(expr.lhs) + ' ' + expr.value + ' ' + this.compile(expr.rhs) + ')';
        },
        
        compileTernary: function(expr) {
            return '(' + this.compile(expr.condition) + ' ? ' + this.compile(expr.tv) + ' : ' + this.compile(expr.fv) + ')';
        },
        
        compileFormatter: function(expr) {
            var me = this,
                fmt = expr.fmt,
                length = fmt.length,
                body = [
                    'var ret;'
                ],
                i;
            if (fmt.length) {
                body.push('ret = ' + me.compileFormatFn(fmt[0], me.compile(expr.operand)) + ';');
                for (i = 1; i < length; i++) {
                    body.push('ret = ' + me.compileFormatFn(fmt[i], 'ret') + ';');
                }
            }
            body.push('return ret;');
            return me.addFn(body.join('\n'));
        },
        
        compileFormatFn: function(expr, value) {
            var fmt,
                args = [],
                code = '',
                length, i;
            if (expr.isIdent) {
                
                fmt = expr.value;
            } else if (expr.isInvoke) {
                fmt = expr.operand.value;
                args = expr.args;
            }
            if (fmt.substring(0, 5) === 'this.') {
                fmt = 'me.' + fmt.substring(5);
            } else {
                if (!(fmt in Ext.util.Format)) {
                    return this.syntaxError(expr.at, 'Compile error! Invalid format specified "' + fmt + '"');
                }
                fmt = 'fm.' + fmt;
            }
            code += value;
            length = args.length;
            for (i = 0; i < length; i++) {
                code += ', ' + this.compile(args[i]);
            }
            return fmt + '(' + code + ')';
        },
        
        addFn: function(body) {
            var defs = this.definitions,
                name = 'f' + defs.length;
            defs.push('function ' + name + '() {', body, '}');
            return name + '()';
        },
        
        evalFn: function($) {
            eval($);
            return $;
        },
        
        addToken: function(token) {
            var tokensMap = this.tokensMap,
                tokens = this.tokens,
                pos = 0;
            
            if (tokensMap && tokens) {
                if (token in tokensMap) {
                    pos = tokensMap[token];
                } else {
                    tokensMap[token] = pos = tokens.length;
                    tokens.push(token);
                }
            }
            return 'v' + pos;
        }
    }
});


Ext.define('Ext.app.bind.Template', {
    
    escapes: false,
    
    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;
        for (i = 0; i < length; ++i) {
            slot = slots[i];
            if (slot) {
                buffer[i] = slot(values, scope);
            }
        }
        
        
        if (slot && me.single) {
            return buffer[0];
        }
        return buffer.join('');
    },
    getText: function() {
        return this.buffer.join('');
    },
    
    getTokens: function() {
        return this.tokens;
    },
    
    isStatic: function() {
        var tokens = this.getTokens(),
            slots = this.slots;
        return (tokens.length === 0 && slots.length === 0);
    },
    privates: {
        literalChar: '~',
        escapeChar: '\\',
        
        parse: function() {
            
            
            var me = this,
                text = me.text,
                parser = Ext.app.bind.Parser.fly(),
                buffer = (me.buffer = []),
                slots = (me.slots = []),
                length = text.length,
                pos = 0,
                escapes = me.escapes,
                current = '',
                i = 0,
                esc = me.escapeChar,
                lit = me.literalChar,
                escaped, tokens, tokensMap, lastEscaped, c, prev, key;
            
            for (key in me._initters) {
                delete me[key];
            }
            me.tokens = tokens = [];
            me.tokensMap = tokensMap = {};
            
            while (i < length) {
                c = text[i];
                lastEscaped = escaped;
                escaped = escapes && c === esc;
                if (escaped) {
                    c = text[i + 1];
                    ++i;
                } else if (c === lit && prev === lit && !lastEscaped) {
                    current = current.slice(0, -1);
                    current += text.substring(i + 1);
                    break;
                } else if (c === '{') {
                    if (current) {
                        buffer[pos++] = current;
                        current = '';
                    }
                    
                    parser.reset(text, i + 1);
                    i = me.parseExpression(parser, pos);
                    ++pos;
                    
                    continue;
                }
                current += c;
                ++i;
                prev = c;
            }
            if (current) {
                buffer[pos] = current;
            }
            parser.release();
            me.single = buffer.length === 0 && slots.length === 1;
            return me;
        },
        parseExpression: function(parser, pos) {
            var i;
            this.slots[pos] = parser.compileExpression(this.tokens, this.tokensMap);
            i = parser.token.at + 1;
            
            parser.expect('}');
            
            return i;
        }
    }
});


Ext.define('Ext.app.bind.TemplateBinding', {
    extend: Ext.app.bind.BaseBinding,
    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 (!tpl.isStatic()) {
            me.multiBinding = new Ext.app.bind.Multi(tokens, owner, me.onBindData, me);
        } else {
            me.isStatic = true;
            me.onData(tpl.getText());
        }
    },
    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;
    },
    isAvailable: function() {
        var multi = this.multiBinding;
        return multi ? multi.isAvailable() : false;
    },
    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',
    
    isChainedStore: true,
    config: {
        
        source: null,
        remoteFilter: false,
        remoteSort: false
    },
    mixins: [
        Ext.data.LocalStore
    ],
    
    updateRemoteFilter: function(remoteFilter, oldRemoteFilter) {
        if (remoteFilter) {
            Ext.raise('Remote filtering cannot be used with chained stores.');
        }
        this.callParent([
            remoteFilter,
            oldRemoteFilter
        ]);
    },
    updateRemoteSort: function(remoteSort, oldRemoteSort) {
        if (remoteSort) {
            Ext.raise('Remote sorting cannot be used with chained stores.');
        }
        this.callParent([
            remoteSort,
            oldRemoteSort
        ]);
    },
    
    remove: function() {
        var source = this.getSource();
        
        if (!source) {
            Ext.raise('Cannot remove records with no source.');
        }
        
        return source.remove.apply(source, arguments);
    },
    removeAll: function() {
        var source = this.getSource();
        
        if (!source) {
            Ext.raise('Cannot remove records with no source.');
        }
        
        return source.removeAll();
    },
    getData: function() {
        var me = this,
            data = me.data;
        if (!data) {
            me.data = data = me.constructDataCollection();
        }
        return data;
    },
    getTotalCount: function() {
        return this.getCount();
    },
    getSession: function() {
        return this.getSourceValue('getSession', null);
    },
    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.raise(s);
            }
        }
        
        return source;
    },
    updateSource: function(source, oldSource) {
        var me = this,
            data;
        if (oldSource && !oldSource.destroyed) {
            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.getSourceValue('getModel', null);
    },
    getProxy: function() {
        return null;
    },
    onCollectionAdd: function(collection, info) {
        var me = this,
            records = info.items,
            lastChunk = !info.next;
        if (me.ignoreCollectionAdd) {
            return;
        }
        
        
        
        if (me.activeRanges) {
            me.syncActiveRanges();
        }
        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);
        me.fireEvent('datachanged', me);
    },
    onCollectionUpdateKey: function(source, details) {
        
        this.fireEvent('idchanged', this, details.item, details.oldKey, details.newKey);
    },
    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);
        this.callObservers('BeforeLoad', [
            operation
        ]);
    },
    onSourceAfterLoad: function(source, records, successful, operation) {
        this.fireEvent('load', this, records, successful, operation);
        this.callObservers('AfterLoad', [
            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.getSourceValue('hasPendingLoad', false);
    },
    isLoaded: function() {
        return this.getSourceValue('isLoaded', false);
    },
    isLoading: function() {
        return this.getSourceValue('isLoading', false);
    },
    doDestroy: function() {
        var me = this;
        me.observers = null;
        me.setSource(null);
        me.getData().destroy(true);
        me.data = null;
        me.callParent();
    },
    privates: {
        getSourceValue: function(method, defaultValue) {
            var source = this.getSource(),
                val = defaultValue;
            if (source && !source.destroyed) {
                val = source[method]();
            }
            return val;
        },
        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
    ],
    alias: 'viewmodel.default',
    
    isViewModel: true,
    factoryConfig: {
        name: 'viewModel'
    },
    collectTimeout: 100,
    expressionRe: /^(?:\{(?:(\d+)|([a-z_][\w\.]*))\})$/i,
    statics: {
        
        escape: function(value) {
            var ret = value,
                key;
            if (typeof value === 'string') {
                ret = '~~' + value;
            } else if (value && value.constructor === Object) {
                ret = {};
                for (key in value) {
                    ret[key] = this.escape(value[key]);
                }
            }
            return ret;
        }
    },
    $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.bindings = {};
        
        this.initConfig(config);
    },
    destroy: function() {
        var me = this,
            scheduler = me._scheduler,
            stores = me.storeInfo,
            parent = me.getParent(),
            task = me.collectTask,
            children = me.children,
            bindings = me.bindings,
            key, store, autoDestroy, storeBinding;
        me.destroying = true;
        if (task) {
            task.cancel();
            me.collectTask = null;
        }
        
        
        
        
        if (children) {
            for (key in children) {
                children[key].destroy();
            }
        }
        if (stores) {
            for (key in stores) {
                store = stores[key];
                
                
                storeBinding = store.$binding;
                autoDestroy = store.autoDestroy;
                if (autoDestroy || (!store.$wasInstance && autoDestroy !== false)) {
                    store.destroy();
                }
                Ext.destroy(storeBinding);
            }
        }
        if (parent) {
            parent.unregisterChild(me);
        }
        me.getRoot().destroy();
        for (key in bindings) {
            bindings[key].destroy();
        }
        if (scheduler && scheduler.$owner === me) {
            scheduler.$owner = null;
            scheduler.destroy();
        }
        me.children = me.storeInfo = me._session = me._view = me._scheduler = me.bindings = me._root = me._parent = me.formulaFn = me.$formulaData = null;
        me.destroying = false;
        me.callParent();
    },
    
    bind: function(descriptor, callback, scope, options) {
        var me = this,
            track = true,
            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);
            track = false;
        } else {
            binding = new Ext.app.bind.TemplateBinding(descriptor, me, callback, scope, options);
        }
        if (track) {
            me.bindings[binding.id] = binding;
        }
        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, create, id, modelType, linkStub, rec;
        
        if (key.indexOf('.') > -1) {
            Ext.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;
        create = reference.create;
        if (modelType) {
            
            id = reference.id;
            
            if (!reference.create && Ext.isEmpty(id)) {
                Ext.raise('No id specified. To create a phantom model, specify "create: true" as part of the reference.');
            }
            
            if (create) {
                id = undefined;
            }
            rec = me.getRecord(modelType, id);
            if (Ext.isObject(create)) {
                rec.set(create);
                rec.commit();
                rec.phantom = true;
            }
            
            
            stub = me.getRoot().createStubChild(key);
            stub.set(rec);
        } else {
            stub = me.getStub(key);
            if (!stub.isLinkStub) {
                
                linkStub = new Ext.app.bind.LinkStub(me, stub.name);
                stub.graft(linkStub);
                stub = linkStub;
            }
            stub.link(reference);
        }
    },
    
    notify: function() {
        var scheduler = this.getScheduler();
        if (!scheduler.firing) {
            scheduler.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 (!this.destroying && 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.raise('Invalid model name: ' + type);
                    }
                }
                
                if (hasId) {
                    record = Model.createWithId(id);
                    record.load();
                } else {
                    record = new Model();
                }
            }
            return record;
        },
        bindExpression: function(descriptor, callback, scope, options) {
            var stub = this.getStub(descriptor);
            return stub.bind(callback, scope, options);
        },
        applyScheduler: function(scheduler) {
            if (scheduler && !scheduler.isInstance) {
                if (scheduler === true) {
                    scheduler = {};
                }
                if (!('preSort' in scheduler)) {
                    scheduler = Ext.apply({
                        preSort: 'kind,-depth'
                    }, scheduler);
                }
                scheduler = new Ext.util.Scheduler(scheduler);
                scheduler.$owner = this;
            }
            return scheduler;
        },
        getScheduler: function() {
            var me = this,
                scheduler = me._scheduler,
                parent;
            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();
        },
        invalidateChildLinks: function(name, clear) {
            var children = this.children,
                key;
            if (children) {
                for (key in children) {
                    children[key].getRoot().invalidateChildLink(name, clear);
                }
            }
        },
        onBindDestroy: function(binding, fromChild) {
            var me = this,
                parent;
            if (me.destroying) {
                return;
            }
            if (!fromChild) {
                delete me.bindings[binding.id];
            }
            parent = me.getParent();
            if (parent) {
                parent.onBindDestroy(binding, true);
            } 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, true);
            }
        },
        applyParent: function(parent) {
            if (parent) {
                parent.registerChild(this);
            }
            return parent;
        },
        applyStores: function(stores) {
            var me = this,
                root = me.getRoot(),
                key, cfg, storeBind, stub, listeners;
            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.setConfig(cfg);
            }
        },
        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;
            
            cfg.resolveListenerScope = this.listenerScopeFn;
            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.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,
    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,
    mixins: [
        Ext.mixin.Observable
    ],
    
    exceptions: {
        TRANSPORT: 'xhr',
        PARSE: 'parse',
        DATA: 'data',
        LOGIN: 'login',
        SERVER: 'exception'
    },
    
    providerClasses: {},
    
    remotingMethods: {},
    config: {
        
        varName: 'Ext.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.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;
    },
    
    resolveApi: function(api, caller) {
        var prefix, action, method, fullName, fn;
        prefix = api && api.prefix;
        if (prefix && prefix.substr(prefix.length - 1) !== '.') {
            prefix += '.';
        }
        for (action in api) {
            method = api[action];
            if (action !== 'prefix' && typeof method !== 'function') {
                fullName = (prefix || '') + method;
                fn = this.parseMethod(fullName);
                if (typeof fn === 'function') {
                    api[action] = fn;
                } else 
                {
                    Ext.raise("Cannot resolve Direct API method '" + fullName + "' for " + action + " action in " + caller.$className + " instance with id: " + (caller.id != null ? caller.id : 'unknown'));
                }
            }
        }
        
        return api;
    },
    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;
        },
        
        clearAllMethods: function() {
            this.remotingMethods = {};
        }
    }
}, function() {
    
    Ext.Direct = Ext.direct.Manager;
});


Ext.define('Ext.direct.Provider', {
    alias: 'direct.provider',
    mixins: [
        Ext.mixin.Observable
    ],
    isProvider: true,
    $configPrefixed: false,
    $configStrict: false,
    
    
    config: {
        
        headers: undefined
    },
    
    
    
    
    subscribers: 0,
    constructor: function(config) {
        var me = this;
        me.mixins.observable.constructor.call(me, config);
        me.requests = {};
        Ext.applyIf(me, {
            id: Ext.id(null, 'provider-')
        });
    },
    destroy: function() {
        var me = this;
        me.disconnect(true);
        me.callParent();
    },
    
    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 || force) {
            if (force) {
                me.subscribers = 0;
            } else {
                me.subscribers--;
            }
            if (me.subscribers === 0) {
                me.doDisconnect();
                me.fireEventArgs('disconnect', [
                    me
                ]);
            }
        }
    },
    
    doDisconnect: function() {
        var requests = this.requests,
            request, id;
        for (id in requests) {
            request = requests[id];
            request.abort();
        }
        this.requests = {};
    },
    
    sendAjaxRequest: function(params) {
        var request = Ext.Ajax.request(params);
        if (request && request.id) {
            this.requests[request.id] = request;
        }
        return request;
    },
    
    onData: function(options, success, response) {
        if (response && response.request) {
            delete this.requests[response.request.id];
        }
    },
    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,
    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([
            initial
        ]);
    },
    
    updatePageSize: function(value, oldValue) {
        if (oldValue != null) {
            throw "pageMap page size may not be changed";
        }
    },
    
    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.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),
            storeIndex = (pageNumber - 1) * pageSize,
            indexMap = me.indexMap,
            page, i, len, startIdx;
        
        
        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 != null) {
            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.raise('insert operation not suppported into buffered Store');
    },
    
    remove: function() {
        
        Ext.raise('remove operation not suppported from buffered Store');
    },
    
    removeAt: function() {
        
        Ext.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 me = this,
            pageNumber = me.getPageFromRecordIndex(start),
            endPageNumber = me.getPageFromRecordIndex(end);
        for (; pageNumber <= endPageNumber; pageNumber++) {
            if (!me.hasPage(pageNumber)) {
                return false;
            }
        }
        
        return (endPageNumber - 1) * me._pageSize + me.getPage(endPageNumber).length > end;
    },
    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) {
        
        
        if (end) {
            end--;
        }
        if (!this.hasRange(start, end)) {
            Ext.raise('PageMap asked for range which it does not have');
        }
        var me = this,
            Array = Ext.Array,
            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 = sliceBegin > 0;
            } else {
                sliceBegin = 0;
                doSlice = false;
            }
            if (pageNumber === endPageNumber) {
                sliceEnd = pageSize - (dataEnd - end);
                doSlice = doSlice || sliceEnd < pageSize;
            }
            
            if (doSlice) {
                Array.push(result, Array.slice(me.getPage(pageNumber), sliceBegin, sliceEnd));
            } else {
                Array.push(result, me.getPage(pageNumber));
            }
        }
        return result;
    }
});


Ext.define('Ext.data.BufferedStore', {
    extend: Ext.data.ProxyStore,
    alias: 'store.buffered',
    
    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,
        
        trackRemoved: false
    },
    
    applyData: function(data) {
        var dataCollection = this.data || (this.data = this.createDataCollection());
        
        if (data && data !== true) {
            Ext.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;
    },
    applyAutoSort: function() {},
    
    
    createFiltersCollection: function() {
        return new Ext.util.FilterCollection();
    },
    createSortersCollection: function() {
        return new Ext.util.SorterCollection();
    },
    
    updateRemoteFilter: function(remoteFilter, oldRemoteFilter) {
        if (remoteFilter === false) {
            Ext.raise('Buffered stores are always remotely filtered.');
        }
        this.callParent([
            remoteFilter,
            oldRemoteFilter
        ]);
    },
    updateRemoteSort: function(remoteSort, oldRemoteSort) {
        if (remoteSort === false) {
            Ext.raise('Buffered stores are always remotely sorted.');
        }
        this.callParent([
            remoteSort,
            oldRemoteSort
        ]);
    },
    updateTrackRemoved: function(value) {
        if (value !== false) {
            Ext.raise('Cannot use trackRemoved with a buffered store.');
        }
        this.callParent(arguments);
    },
    
    updateGroupField: function(field) {
        this.group(field);
    },
    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.raise('add method may not be called on a buffered store - the store is a map of remote data');
    },
    insert: function() {
        Ext.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');
            }
        }
    },
    flushLoad: function() {
        var me = this,
            options = me.pendingLoadOptions;
        
        me.clearLoadTask();
        if (!options) {
            return;
        }
        
        
        if (!options.preserveOnFlush) {
            me.getData().clear();
            options.page = 1;
            options.start = 0;
            options.limit = me.getViewSize() || me.getDefaultViewSize();
        }
        
        
        options.loadCallback = options.callback;
        
        options.callback = null;
        return me.loadToPrefetch(options);
    },
    reload: function(options) {
        var me = this,
            data = me.getData(),
            
            lastTotal = Number.MAX_VALUE,
            startIdx, endIdx, startPage, endPage, i, waitForReload, bufferZone, records;
        if (!options) {
            options = {};
        }
        
        if (me.loading || me.fireEvent('beforeload', me, options) === false) {
            return;
        }
        waitForReload = function() {
            var newCount = me.totalCount,
                oldRequestSize = endIdx - startIdx;
            
            
            if (endIdx >= newCount) {
                endIdx = newCount - 1;
                startIdx = Math.max(endIdx - oldRequestSize, 0);
            }
            if (me.rangeCached(startIdx, endIdx, false)) {
                me.loadCount = (me.loadCount || 0) + 1;
                me.loading = false;
                data.un('pageadd', waitForReload);
                records = data.getRange(startIdx, endIdx);
                me.fireEvent('refresh', me);
                me.fireEvent('load', me, records, true);
            }
        };
        bufferZone = Math.ceil((me.getLeadingBufferZone() + me.getTrailingBufferZone()) / 2);
        
        
        
        
        if (me.lastRequestStart && me.preserveScrollOnReload) {
            startIdx = me.lastRequestStart;
            endIdx = me.lastRequestEnd;
            lastTotal = me.getTotalCount();
        } else 
        {
            startIdx = options.start || 0;
            endIdx = startIdx + (options.count || me.getPageSize()) - 1;
        }
        
        data.clear(true);
        
        delete me.totalCount;
        
        startIdx = Math.max(startIdx - bufferZone, 0);
        endIdx = Math.min(endIdx + bufferZone, lastTotal);
        
        
        
        
        startIdx = startIdx === 0 ? 0 : startIdx - 1;
        endIdx = endIdx === lastTotal ? endIdx : endIdx + 1;
        startPage = me.getPageFromRecordIndex(startIdx);
        endPage = me.getPageFromRecordIndex(endIdx);
        me.loading = true;
        options.waitForReload = waitForReload;
        
        
        data.on('pageadd', waitForReload);
        
        for (i = startPage; i <= endPage; i++) {
            me.prefetchPage(i, options);
        }
    },
    filter: function() {
        
        if (!this.getRemoteFilter()) {
            Ext.raise('Local filtering may not be used on a buffered store - the store is a map of remote data');
        }
        
        
        this.callParent(arguments);
    },
    filterBy: function(fn, scope) {
        
        Ext.raise('Local filtering may not be used on a buffered store - the store is a map of remote data');
    },
    
    loadData: function(data, append) {
        
        Ext.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;
        
        options.callback = null;
        
        
        options.preserveOnFlush = true;
        return me.load(options);
    },
    clearData: function(isLoad) {
        var me = this,
            data = me.getData();
        if (data) {
            data.clear();
        }
    },
    
    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;
        
        
        
        
        
        if (options.forRender !== false) {
            requiredStart = start === 0 ? 0 : start - 1;
            requiredEnd = end === maxIndex ? end : end + 1;
        } else {
            requiredStart = start;
            requiredEnd = end;
        }
        
        me.lastRequestStart = start;
        me.lastRequestEnd = end;
        
        if (me.rangeCached(start, end, options.forRender)) {
            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(start, end)) {
                    
                    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);
    },
    
    contains: function(record) {
        return this.indexOf(record) > -1;
    },
    
    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 && direction !== undefined) {
                oldGrouper.setDirection(direction);
            } else {
                me.grouper = new Ext.util.Grouper({
                    property: grouper,
                    direction: direction || 'ASC',
                    root: 'data'
                });
            }
        } else {
            me.grouper = grouper ? me.getSorters().decodeSorter(grouper, Ext.util.Grouper) : null;
        }
        me.getData().clear();
        me.loadPage(1, {
            callback: function() {
                me.fireEvent('groupchange', me, me.getGrouper());
            }
        });
    },
    
    getPageFromRecordIndex: function(index) {
        return Math.floor(index / this.getPageSize()) + 1;
    },
    calculatePageCacheSize: function(rangeSizeRequested) {
        var me = this,
            purgePageCount = me.getPurgePageCount();
        
        
        
        return purgePageCount ? Math.max(me.getData().getMaxSize() || 0, Math.ceil((rangeSizeRequested + me.getTrailingBufferZone() + me.getLeadingBufferZone()) / me.getPageSize()) * 2 + purgePageCount) : 0;
    },
    loadToPrefetch: function(options) {
        var me = this,
            prefetchOptions = options,
            i, records, dataSetSize,
            
            startIdx = options.start,
            endIdx = options.start + options.limit - 1,
            rangeSizeRequested = (me.getViewSize() || options.limit),
            
            loadEndIdx = Math.min(endIdx, options.start + rangeSizeRequested - 1),
            
            
            startPage = me.getPageFromRecordIndex(Math.max(startIdx - me.getTrailingBufferZone(), 0)),
            endPage = me.getPageFromRecordIndex(endIdx + me.getLeadingBufferZone()),
            data = me.getData(),
            callbackFn = function() {
                
                records = records || [];
                if (options.loadCallback) {
                    options.loadCallback.call(options.scope || me, records, operation, true);
                }
                if (options.callback) {
                    options.callback.call(options.scope || me, records, startIdx || 0, endIdx || 0, options);
                }
            },
            fireEventsFn = function() {
                me.loadCount = (me.loadCount || 0) + 1;
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
                me.fireEvent('load', me, records, true);
            },
            
            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);
                    }
                    callbackFn();
                    fireEventsFn();
                }
            },
            operation;
        
        if (isNaN(me.pageSize) || !me.pageSize) {
            Ext.raise('Buffered store configured without a pageSize', me);
        }
        
        
        
        data.setMaxSize(me.calculatePageCacheSize(rangeSizeRequested));
        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) {
                
                operation = op;
                if (successful) {
                    
                    
                    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 {
                        callbackFn();
                        fireEventsFn();
                    }
                } else 
                {
                    me.loading = false;
                    callbackFn();
                    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(),
            operation, existingPageRequest;
        
        if (pageSize) {
            if (me.lastPageSize && pageSize != me.lastPageSize) {
                Ext.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;
        }
        
        
        
        existingPageRequest = me.pageRequests[options.page];
        if (!existingPageRequest || existingPageRequest.getOperation().pageMapGeneration !== data.pageMapGeneration) {
            
            options = Ext.apply({
                action: 'read',
                filters: me.getFilters().items,
                sorters: me.getSorters().items,
                grouper: me.getGrouper(),
                internalCallback: me.onProxyPrefetch,
                internalScope: me
            }, options);
            operation = me.createOperation('read', options);
            
            
            operation.pageMapGeneration = data.pageMapGeneration;
            if (me.fireEvent('beforeprefetch', me, operation) !== false) {
                me.pageRequests[options.page] = operation.execute();
                if (me.getProxy().isSynchronous) {
                    delete me.pageRequests[options.page];
                }
            }
        }
        return me;
    },
    
    onPageMapClear: function() {
        var me = this,
            loadingFlag = me.wasLoading,
            reqs = me.pageRequests,
            data = me.getData(),
            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)) {
                reqs[page].getOperation().abort();
            }
        }
        
        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) {
        if (this.destroying || this.destroyed) {
            return;
        }
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful(),
            page = operation.getPage(),
            waitForReload = operation.waitForReload,
            oldTotal = me.totalCount,
            requests = me.pageRequests,
            key, op;
        
        
        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) {
                if (me.totalCount === 0) {
                    if (waitForReload) {
                        for (key in requests) {
                            op = requests[key].getOperation();
                            
                            
                            if (op.waitForReload === waitForReload) {
                                delete op.waitForReload;
                            }
                        }
                        me.getData().un('pageadd', waitForReload);
                        me.fireEvent('refresh', me);
                        me.fireEvent('load', me, [], true);
                    }
                } else {
                    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, forRender) {
        var requiredStart = start,
            requiredEnd = end;
        
        
        
        
        if (forRender !== false) {
            requiredStart = start === 0 ? 0 : start - 1 , requiredEnd = end === this.totalCount - 1 ? end : end + 1;
        }
        return this.getData().hasRange(requiredStart, requiredEnd);
    },
    
    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,
            startPage, endPage, page,
            data = me.getData();
        if (!me.rangeCached(start, end)) {
            startPage = me.getPageFromRecordIndex(start);
            endPage = me.getPageFromRecordIndex(end);
            
            
            
            data.setMaxSize(me.calculatePageCacheSize(end - start + 1));
            
            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.fireEvent('beforesort', me, sorters);
            me.clearAndLoad({
                callback: function() {
                    me.fireEvent('sort', me, sorters);
                }
            });
        } else {
            
            me.fireEvent('sort', me, sorters);
        }
    },
    clearAndLoad: function(options) {
        var me = this;
        me.clearing = true;
        me.getData().clear();
        me.clearing = false;
        me.loadPage(1, options);
    },
    privates: {
        isLast: function(record) {
            return this.indexOf(record) === this.getTotalCount() - 1;
        },
        isMoving: function() {
            return false;
        }
    }
});


Ext.define('Ext.data.proxy.Direct', {
    
    extend: Ext.data.proxy.Server,
    alternateClassName: 'Ext.data.DirectProxy',
    alias: 'proxy.direct',
    
    
    config: {
        
        paramOrder: undefined,
        
        paramsAsHash: true,
        
        directFn: undefined,
        
        api: undefined,
        
        metadata: undefined
    },
    
    paramOrderRe: /[\s,|]/,
    constructor: function(config) {
        this.callParent([
            config
        ]);
        this.canceledOperations = {};
    },
    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(),
            method;
        if (fn) {
            me.setDirectFn(method = Ext.direct.Manager.parseMethod(fn));
            if (!Ext.isFunction(method)) {
                Ext.raise('Cannot resolve directFn ' + fn);
            }
        }
        if (api) {
            api = Ext.direct.Manager.resolveApi(api, me);
            me.setApi(api);
        }
        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 || !fn.directCfg) {
            Ext.raise({
                msg: 'No Ext Direct function specified for Direct proxy "' + action + '" operation',
                proxy: me
            });
        }
        
        if (!me.paramOrder && fn.directCfg.method.len > 1) {
            Ext.raise({
                msg: 'Incorrect parameters for Direct proxy "' + action + '" operation',
                proxy: me
            });
        }
        
        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,
            allowSingle: writer.getAllowSingle(),
            paramOrder: me.getParamOrder(),
            paramsAsHash: me.getParamsAsHash(),
            paramsAsArray: true,
            metadata: me.getMetadata(),
            callback: me.createRequestCallback(request, operation),
            scope: me
        });
        request.setConfig({
            args: args,
            directFn: fn
        });
        fn.apply(window, args);
        
        
        
        return request;
    },
    
    abort: function(operation) {
        var id;
        
        if (operation && operation.isDataRequest) {
            operation = operation.getOperation();
        }
        
        if (operation && operation.isOperation) {
            id = operation.id;
        }
        
        if (id != null) {
            this.canceledOperations[id] = true;
        }
    },
    
    applyEncoding: Ext.identityFn,
    createRequestCallback: function(request, operation) {
        var me = this;
        return function(data, event) {
            if (!me.canceledOperations[operation.id]) {
                me.processResponse(event.status, operation, request, event);
            }
            delete me.canceledOperations[operation.id];
        };
    },
    
    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',
    
    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.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] = me.bindResponse(request);
        me.loadScript(request);
        return request;
    },
    bindResponse: function(request) {
        var me = this;
        return function(result) {
            Ext.elevate(function() {
                me.handleResponse(result, 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;
        Ext.undefer(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
        ]);
    },
    
    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'
    ],
    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',
    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',
    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',
    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', {
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    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: 'glyph',
                    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,
                        phantom = me.phantom,
                        result;
                    commit = {
                        silent: true,
                        commit: commit
                    };
                    
                    if (info.depth != null && info.depth !== me.data.depth) {
                        var childInfo = {
                                depth: info.depth + 1
                            },
                            children = me.childNodes,
                            childCount = children.length,
                            i;
                        for (i = 0; i < childCount; i++) {
                            children[i].updateInfo(commit, childInfo);
                        }
                    }
                    result = me.set(info, commit);
                    
                    me.phantom = phantom;
                    return result;
                },
                
                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(),
                        halfCheckedValue = treeStore && treeStore.triStateCheckbox ? 1 : false,
                        bulkUpdate = treeStore && treeStore.bulkUpdate,
                        meChecked, nodeChecked, modifiedFields;
                    
                    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.fireBubbledEvent('beforeappend', [
                            me,
                            node
                        ]) === false) {
                            Ext.resumeLayouts(true);
                            return false;
                        }
                        index = me.childNodes.length;
                        oldParent = node.parentNode;
                        
                        if (oldParent) {
                            if (suppressEvents !== true && node.fireBubbledEvent('beforemove', [
                                node,
                                oldParent,
                                me,
                                index
                            ]) === false) {
                                Ext.resumeLayouts(true);
                                return false;
                            }
                            
                            if (oldParent.removeChild(node, false, suppressEvents, oldParent.getTreeStore() === treeStore) === false) {
                                Ext.resumeLayouts(true);
                                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;
                        
                        
                        
                        modifiedFields = 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].cascade(me.triggerUIUpdate);
                        }
                        
                        
                        
                        
                        
                        
                        if (treeStore) {
                            treeStore.registerNode(me, !bulkUpdate);
                            if (bulkUpdate) {
                                treeStore.registerNode(node);
                            }
                        }
                        
                        
                        
                        if (suppressEvents !== true) {
                            me.fireBubbledEvent('append', [
                                me,
                                node,
                                index
                            ]);
                            if (oldParent) {
                                node.fireBubbledEvent('move', [
                                    node,
                                    oldParent,
                                    me,
                                    index
                                ]);
                            }
                        }
                        
                        
                        me.callTreeStore('onNodeAppend', [
                            node,
                            index
                        ]);
                        
                        if (modifiedFields) {
                            node.callJoined('afterEdit', [
                                modifiedFields
                            ]);
                        }
                        result = node;
                        
                        
                        if (treeStore) {
                            treeStore.endUpdate();
                        }
                    }
                    
                    Ext.resumeLayouts(true);
                    return result;
                },
                
                getOwnerTree: function() {
                    var store = this.getTreeStore();
                    return store && 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,
                        removeContext,
                        removeRange = [];
                    if (index === -1 || (suppressEvents !== true && me.fireBubbledEvent('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.cascade(me.triggerUIUpdate);
                            } else 
                            {
                                previousSibling.triggerUIUpdate();
                            }
                        }
                    }
                    
                    
                    if (!me.childNodes.length && !bulkUpdate) {
                        me.triggerUIUpdate();
                    }
                    
                    Ext.resumeLayouts(true);
                    if (suppressEvents !== true) {
                        
                        removeContext = {
                            parentNode: node.parentNode,
                            previousSibling: node.previousSibling,
                            nextSibling: node.nextSibling
                        };
                        
                        me.callTreeStore('beforeNodeRemove', [
                            [
                                node
                            ],
                            !!isMove,
                            removeRange
                        ]);
                        node.previousSibling = node.nextSibling = node.parentNode = null;
                        me.fireBubbledEvent('remove', [
                            me,
                            node,
                            !!isMove,
                            removeContext
                        ]);
                        
                        me.callTreeStore('onNodeRemove', [
                            [
                                node
                            ],
                            !!isMove,
                            removeRange
                        ]);
                    }
                    
                    
                    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, session, deep) {
                    var me = this,
                        result,
                        args = [
                            newId
                        ],
                        len = me.childNodes ? me.childNodes.length : 0,
                        i;
                    
                    
                    
                    if (session && session.isSession) {
                        args.push(session);
                    } else if (arguments.length < 3) {
                        deep = session;
                    }
                    result = me.callParent(args);
                    
                    if (deep) {
                        for (i = 0; i < len; i++) {
                            result.appendChild(me.childNodes[i].copy(undefined, true));
                        }
                    }
                    return result;
                },
                
                clear: function(erase, resetChildren) {
                    var me = this,
                        data;
                    
                    me.parentNode = me.previousSibling = me.nextSibling = null;
                    if (erase) {
                        me.firstChild = me.lastChild = me.childNodes = null;
                    }
                    
                    if (resetChildren) {
                        me.firstChild = me.lastChild = null;
                        me.childNodes.length = 0;
                        if (me.data) {
                            me.data.children = null;
                        }
                    }
                },
                drop: function() {
                    var me = this,
                        childNodes = me.childNodes,
                        parentNode = me.parentNode,
                        len, i, node,
                        treeStore = me.getTreeStore();
                    
                    
                    
                    me.callParent();
                    
                    if (parentNode) {
                        
                        parentNode.removeChild(me);
                    }
                    
                    else if (me.get('root')) {
                        treeStore.setRoot(null);
                    }
                    
                    
                    
                    
                    
                    treeStore && treeStore.beginUpdate();
                    
                    
                    for (i = 0 , len = childNodes ? childNodes.length : 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,
                        modifiedFields, sibling, siblingModifiedFields;
                    if (!refNode) {
                        
                        return me.appendChild(node);
                    }
                    
                    if (node === refNode) {
                        return false;
                    }
                    
                    node = me.createNode(node);
                    if (suppressEvents !== true && me.fireBubbledEvent('beforeinsert', [
                        me,
                        node,
                        refNode
                    ]) === false) {
                        return false;
                    }
                    
                    if (oldParent === me && me.indexOf(node) < index) {
                        refIndex--;
                    }
                    
                    if (oldParent) {
                        if (suppressEvents !== true && node.fireBubbledEvent('beforemove', [
                            node,
                            oldParent,
                            me,
                            index,
                            refNode
                        ]) === false) {
                            return false;
                        }
                        
                        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;
                    }
                    
                    
                    
                    modifiedFields = 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++) {
                        sibling = me.childNodes[i];
                        siblingModifiedFields = sibling.updateInfo(false, {
                            index: i
                        });
                        if (siblingModifiedFields) {
                            sibling.callJoined('afterEdit', [
                                siblingModifiedFields
                            ]);
                        }
                    }
                    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.fireBubbledEvent('insert', [
                            me,
                            node,
                            refNode
                        ]);
                        if (oldParent) {
                            node.fireBubbledEvent('move', [
                                node,
                                oldParent,
                                me,
                                refIndex,
                                refNode
                            ]);
                        }
                    }
                    
                    me.callTreeStore('onNodeInsert', [
                        node,
                        refIndex
                    ]);
                    
                    if (modifiedFields) {
                        node.callJoined('afterEdit', [
                            modifiedFields
                        ]);
                    }
                    
                    
                    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,
                        len = childNodes.length,
                        node, treeStore, i,
                        removeRange = [];
                    
                    if (!len) {
                        return me;
                    }
                    
                    if (!fromParent) {
                        treeStore = me.getTreeStore();
                        
                        if (treeStore) {
                            treeStore.beginUpdate();
                            
                            
                            
                            treeStore.suspendEvent('remove');
                            me.callTreeStore('beforeNodeRemove', [
                                childNodes,
                                false,
                                removeRange
                            ]);
                        }
                    }
                    for (i = 0; i < len; ++i) {
                        node = childNodes[i];
                        node.previousSibling = node.nextSibling = node.parentNode = null;
                        me.fireBubbledEvent('remove', [
                            me,
                            node,
                            false
                        ]);
                        if (erase) {
                            node.erase(true);
                        } else 
                        {
                            node.removeAll(false, suppressEvents, true);
                        }
                    }
                    
                    if (!fromParent && treeStore) {
                        treeStore.resumeEvent('remove');
                        me.callTreeStore('onNodeRemove', [
                            childNodes,
                            false,
                            removeRange
                        ]);
                        
                        treeStore.endUpdate();
                    }
                    me.firstChild = me.lastChild = null;
                    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(spec, scope, args, after) {
                    var me = this,
                        before = spec;
                    if (arguments.length === 1 && !Ext.isFunction(spec)) {
                        after = spec.after;
                        scope = spec.scope;
                        args = spec.args;
                        before = spec.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].cascade.call(childNodes[i], before, scope, args, after);
                        }
                        if (after) {
                            after.apply(scope || me, args || [
                                me
                            ]);
                        }
                    }
                },
                cascadeBy: function() {
                    return this.cascade.apply(this, arguments);
                },
                
                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.fireBubbledEvent('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.cascade(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.fireBubbledEvent('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', 'datachanged');
                                                resumeAddEvent = true;
                                            }
                                        }
                                    }
                                    
                                    
                                    me.callTreeStore('onBeforeNodeExpand', [
                                        me.onChildNodesAvailable,
                                        me,
                                        [
                                            recursive,
                                            callback,
                                            scope
                                        ]
                                    ]);
                                    
                                    
                                    if (resumeAddEvent) {
                                        treeStore.resumeEvent('add', 'datachanged');
                                        
                                        treeStore.fireEvent('datachanged', treeStore);
                                        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 || !treeStore.isVisible(me)) {
                        me.data.expanded = true;
                    } else {
                        me.set('expanded', true);
                    }
                    
                    
                    if (collapsedAncestors) {
                        
                        
                        for (i = 1; i < collapsedAncestors.length; i++) {
                            ancestor = collapsedAncestors[i];
                            if (bulkUpdate || !treeStore.isVisible(ancestor)) {
                                ancestor.data.expanded = true;
                            } else {
                                ancestor.set('expanded', true);
                            }
                        }
                        
                        
                        
                        collapsedAncestors[0].expand();
                        
                        for (i = 1; i < collapsedAncestors.length; i++) {
                            ancestor = collapsedAncestors[i];
                            ancestor.fireBubbledEvent('expand', [
                                ancestor,
                                ancestor.childNodes
                            ]);
                        }
                    } else {
                        
                        me.callTreeStore('onNodeExpand', [
                            records,
                            false
                        ]);
                    }
                    me.fireBubbledEvent('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.fireBubbledEvent('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 || !treeStore.contains(me)) {
                                me.data.expanded = false;
                            } else {
                                me.set('expanded', false);
                            }
                            
                            
                            
                            me.callTreeStore('onNodeCollapse', [
                                me.childNodes,
                                callback,
                                scope
                            ]);
                            me.fireBubbledEvent('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.fireBubbledEvent('beforecollapse', [
                        me
                    ]) !== false) {
                        
                        me.data.expanded = false;
                        
                        
                        
                        me.fireBubbledEvent('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.fireBubbledEvent(eventName, Ext.Array.slice(arguments, 1));
                },
                
                
                
                fireBubbledEvent: function(eventName, args) {
                    var result, eventSource, topNode;
                    
                    if (bubbledEvents[eventName]) {
                        for (eventSource = this; result !== false && eventSource; eventSource = (topNode = eventSource).parentNode) {
                            result = eventSource.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 this.fireEventArgs.apply(this, arguments);
                    }
                },
                
                serialize: function(writerParam) {
                    var writer = writerParam || new Ext.data.writer.Json({
                            writeAllFields: true
                        }),
                        result = writer.getRecordData(this),
                        childNodes = this.childNodes,
                        len = childNodes.length,
                        children, i;
                    if (len > 0) {
                        result.children = children = [];
                        for (i = 0; i < len; i++) {
                            children.push(childNodes[i].serialize(writer));
                        }
                    }
                    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);
                    }
                },
                addCls: function(cls) {
                    this.replaceCls(null, cls);
                },
                removeCls: function(cls) {
                    this.replaceCls(cls);
                },
                replaceCls: function(oldCls, newCls) {
                    var pieces = this._parseCls(this.data.cls),
                        parts = this._parseCls(oldCls);
                    if (parts.length) {
                        pieces = Ext.Array.difference(pieces, parts);
                    }
                    parts = this._parseCls(newCls);
                    if (parts.length) {
                        pieces = Ext.Array.unique(pieces.concat(parts));
                    }
                    this.set('cls', pieces.join(' '));
                },
                toggleCls: function(cls, state) {
                    if (state === undefined) {
                        var pieces = this._parseCls(this.data.cls),
                            parts = this._parseCls(cls),
                            len = parts.length,
                            i, p;
                        for (i = 0; i < len; ++i) {
                            p = parts[i];
                            if (Ext.Array.contains(pieces, p)) {
                                Ext.Array.remove(pieces, p);
                            } else {
                                pieces.push(p);
                            }
                        }
                        this.set('cls', pieces.join(' '));
                    } else if (state) {
                        this.addCls(cls);
                    } else {
                        this.removeCls(cls);
                    }
                },
                
                privates: {
                    _noCls: [],
                    spacesRe: /\s+/,
                    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);
                    },
                    _parseCls: function(cls) {
                        if (!cls) {
                            return this._noCls;
                        }
                        if (typeof cls === 'string') {
                            return cls.split(this.spacesRe);
                        }
                        return cls;
                    }
                }
            };
        }
    }
});


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


Ext.define('Ext.data.NodeStore', {
    extend: Ext.data.Store,
    alias: 'store.node',
    
    isNodeStore: true,
    config: {
        
        node: null,
        
        recursive: false,
        
        rootVisible: false,
        
        folderSort: false
    },
    implicitModel: 'Ext.data.TreeModel',
    
    
    
    getTotalCount: function() {
        return this.getCount();
    },
    updateFolderSort: function(folderSort) {
        var data = this.getData();
        data.setTrackGroups(false);
        if (folderSort) {
            data.setGrouper({
                groupFn: this.folderSortFn
            });
        } else {
            data.setGrouper(null);
        }
    },
    folderSortFn: function(node) {
        return node.data.leaf ? 1 : 0;
    },
    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) {
        if (parent === this.getNode()) {
            this.add([
                node
            ].concat(this.retrieveChildNodes(node)));
        }
    },
    onNodeInsert: function(parent, node, refNode) {
        var me = this,
            idx;
        if (parent === me.getNode()) {
            idx = me.indexOf(refNode) || 0;
            me.insert(0, [
                node
            ].concat(me.retrieveChildNodes(node)));
        }
    },
    onNodeRemove: function(parent, node) {
        if (parent === this.getNode()) {
            this.remove([
                node
            ].concat(this.retrieveChildNodes(node)));
        }
    },
    onNodeExpand: function(parent, records) {
        if (parent === this.getNode()) {
            this.loadRecords(records);
        }
    },
    applyNode: function(node) {
        if (node) {
            if (!node.isModel) {
                node = new (this.getModel())(node);
            }
            if (!node.isNode) {
                Ext.data.NodeInterface.decorate(node);
            }
        }
        return node;
    },
    updateNode: function(node, oldNode) {
        var me = this,
            data;
        if (oldNode && !oldNode.destroyed) {
            oldNode.un({
                append: 'onNodeAppend',
                insert: 'onNodeInsert',
                remove: 'onNodeRemove',
                scope: me
            });
            oldNode.unjoin(me);
        }
        if (node) {
            node.on({
                scope: me,
                append: 'onNodeAppend',
                insert: 'onNodeInsert',
                remove: 'onNodeRemove'
            });
            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();
            if (me.isInitializing) {
                me.inlineData = data;
            } else {
                me.add(data);
            }
            me.resumeEvents();
            if (data.length === 0) {
                me.loaded = node.loaded = true;
            }
            me.fireEvent('refresh', me, me.data);
        }
    },
    
    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;
    },
    privates: {
        
        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;
        }
    }
});


Ext.define('Ext.data.Request', {
    isDataRequest: true,
    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.data.TreeStore', {
    extend: Ext.data.Store,
    alias: 'store.tree',
    
    isTreeStore: true,
    config: {
        
        root: null,
        
        rootVisible: false,
        
        defaultRootProperty: 'children',
        
        parentIdProperty: null,
        
        clearOnLoad: true,
        
        clearRemovedOnLoad: true,
        
        nodeParam: 'node',
        
        defaultRootId: 'root',
        
        defaultRootText: 'Root',
        
        folderSort: false,
        
        pageSize: null
    },
    
    
    filterer: 'topdown',
    
    lazyFill: false,
    fillCount: 0,
    bulkUpdate: 0,
    nodesToUnregister: 0,
    
    _silentOptions: {
        silent: true
    },
    
    implicitModel: 'Ext.data.TreeModel',
    
    groupField: null,
    
    groupDir: null,
    
    grouper: null,
    constructor: function(config) {
        var me = this;
        me.byIdMap = {};
        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, oldFields) {
        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.callParent([
            fields,
            oldFields
        ]);
    },
    applyGroupField: function(field) {
        return null;
    },
    applyGroupDir: function(dir) {
        return null;
    },
    applyGrouper: function(grouper) {
        
        if (grouper) {
            Ext.raise('You can\'t group a TreeStore');
        }
        
        return null;
    },
    
    group: Ext.emptyFn,
    
    onSorterEndUpdate: function() {
        var me = this,
            sorterCollection = me.getSorters(),
            sorters = sorterCollection.getRange(),
            rootNode = me.getRoot(),
            folderSort = me.getFolderSort();
        me.fireEvent('beforesort', me, sorters);
        
        if (rootNode && (folderSort || sorters.length)) {
            if (me.getRemoteSort()) {
                if (sorters.length) {
                    me.load({
                        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);
        }
    },
    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);
        };
    },
    getTotalCount: function() {
        return this.getCount();
    },
    afterEdit: function(node, modifiedFieldNames) {
        var me = this,
            parentNode = node.parentNode,
            rootVisible = me.getRootVisible(),
            isHiddenRoot = !parentNode && !rootVisible,
            prevVisibleNodeIndex,
            isVisible = node.get('visible'),
            toAdd, removeStart;
        
        
        
        if (!isHiddenRoot && isVisible !== me.contains(node)) {
            
            
            
            if (isVisible) {
                if (!parentNode || me.isVisible(node)) {
                    toAdd = [
                        node
                    ];
                    
                    if (node.isExpanded()) {
                        me.handleNodeExpand(node, node.childNodes, toAdd);
                    }
                    prevVisibleNodeIndex = node.previousSibling ? me.indexOfPreviousVisibleNode(node.previousSibling) : (parentNode ? me.indexOf(parentNode) : -1);
                    me.insert(prevVisibleNodeIndex + 1, toAdd);
                }
            } else 
            {
                removeStart = me.indexOf(node);
                me.removeAt(removeStart, me.indexOfNextVisibleNode(node) - removeStart);
            }
        }
        
        
        
        
        else if (me.getRoot() && me.needsLocalFilter()) {
            me.onFilterEndUpdate(me.getFilters());
        }
        me.callParent([
            node,
            modifiedFieldNames
        ]);
    },
    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);
        }
    },
    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;
    },
    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;
    },
    updateModel: function(model) {
        if (model) {
            var isNode = model.prototype.isNode;
            
            Ext.data.NodeInterface.decorate(model);
            
            
            if (!isNode && !this.changingProxy) {
                this.getProxy().getReader().buildExtractors(true);
            }
        }
    },
    onCollectionFilter: Ext.emptyFn,
    
    
    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.cascade({
                    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.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() && root.get('visible')) {
            toAdd.push(root);
        }
        me.handleNodeExpand(root, childNodes, toAdd);
        
        
        me.suspendEvents();
        data.splice(0, data.getCount(), toAdd);
        me.resumeEvents();
        if (!me.suppressNextFilter) {
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
    },
    
    onBeforeNodeExpand: function(node, callback, scope, args) {
        var me = this,
            storeReader, nodeProxy, nodeReader, reader, children, callbackArgs;
        
        
        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())) {
                
                if (children) {
                    me.fillNode(node, reader.extractData(children, {
                        model: node.childType,
                        recordCreator: me.recordCreator
                    }));
                }
                callbackArgs = [
                    node.childNodes
                ];
                if (args) {
                    callbackArgs.push.apply(callbackArgs, args);
                }
                Ext.callback(callback, scope || node, callbackArgs);
            } else 
            {
                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);
                    }
                });
                
                me.flushLoad();
            }
        }
    },
    
    
    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.loadCount;
            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, {
                                silent: true
                            });
                            record.expand();
                        }
                    }
                }
            }
        }
    },
    
    onNodeCollapse: function(parent, records, callback, scope) {
        var me = this,
            collapseIndex = me.indexOf(parent) + 1,
            lastNodeIndexPlus;
        
        if (me.needsLocalFilter()) {
            records = Ext.Array.filter(records, me.filterVisible);
        }
        
        
        
        
        if (records.length && me.isVisible(parent)) {
            
            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();
    },
    
    indexOfPreviousVisibleNode: function(node) {
        var result;
        
        for (result = node; result && !result.get('visible'); result = result.previousSibling) {}
        
        
        if (result) {
            if (result.isExpanded() && result.lastChild) {
                return this.indexOfPreviousVisibleNode(result.lastChild);
            }
        } else 
        
        {
            result = node.parentNode;
        }
        return this.indexOf(result);
    },
    
    filterNew: function(item) {
        
        
        return !item.get('root') && this.callParent([
            item
        ]);
    },
    
    filterRejects: function(item) {
        
        
        return !item.get('root') && this.callParent([
            item
        ]);
    },
    getNewRecords: function() {
        return Ext.Array.filter(Ext.Object.getValues(this.byIdMap), this.filterNew, this);
    },
    getRejectRecords: function() {
        return Ext.Array.filter(Ext.Object.getValues(this.byIdMap), this.filterRejects, this);
    },
    getUpdatedRecords: function() {
        return Ext.Array.filter(Ext.Object.getValues(this.byIdMap), this.filterUpdated);
    },
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    beforeNodeRemove: function(parentNode, childNodes, isMove, removeRange) {
        if (!Ext.isArray(childNodes)) {
            childNodes = [
                childNodes
            ];
        }
        var me = this,
            len = childNodes.length,
            
            
            
            
            removed = me.removedNodes,
            i, startNode;
        
        for (i = 0; !startNode && i < len; i++) {
            if (childNodes[i].get('visible')) {
                startNode = childNodes[i];
            }
        }
        
        
        if (startNode) {
            removeRange[0] = me.indexOf(childNodes[0]);
            removeRange[1] = me.indexOfNextVisibleNode(childNodes[childNodes.length - 1]) - removeRange[0];
        } else {
            removeRange[0] = -1;
            removeRange[1] = 0;
        }
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        for (i = 0; i < len; i++) {
            childNodes[i].cascade(function(node) {
                
                me.unregisterNode(node, true);
                
                
                
                if (removed && !isMove) {
                    
                    
                    if (!node.phantom && !node.erasing && !me.loading) {
                        
                        
                        node.removedFrom = me.indexOf(node);
                        removed.push(node);
                        
                        
                        me.needsSync = true;
                    }
                }
            });
        }
    },
    
    
    
    
    
    afterDrop: Ext.emptyFn,
    
    
    onNodeRemove: function(parentNode, childNodes, isMove, removeRange) {
        var me = this;
        
        
        me.suspendAutoSync();
        
        
        
        
        
        if (removeRange[0] !== -1) {
            me.removeIsMove = isMove;
            me.removeAt.apply(me, removeRange);
            me.removeIsMove = false;
        }
        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,
            
            
            
            
            removed = me.removedNodes,
            storeReader, nodeProxy, nodeReader, reader, dataRoot, storeInsertionPoint;
        if (parent && me.needsLocalFilter()) {
            me.doFilter(parent);
        }
        me.beginUpdate();
        
        if (me.isVisible(node)) {
            
            
            if (index === 0 || !node.previousSibling) {
                storeInsertionPoint = me.indexOf(parent);
            } else 
            
            {
                storeInsertionPoint = me.indexOfPreviousVisibleNode(node.previousSibling);
            }
            
            me.insert(storeInsertionPoint + 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();
                }
            }
        }
        
        Ext.Array.remove(removed, node);
        
        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) {
                me.fillNode(node, reader.extractData(dataRoot, {
                    model: node.childType,
                    recordCreator: me.recordCreator
                }));
            }
        }
        me.endUpdate();
    },
    
    registerNode: function(node, includeChildren) {
        var me = this,
            was = me.byIdMap[node.id],
            children, length, i;
        
        me.byIdMap[node.id] = node;
        
        
        if (node.onRegisterTreeNode && node !== was) {
            node.onRegisterTreeNode(me);
        }
        
        
        
        if (node.onUnregisterTreeNode) {
            me.nodesToUnregister++;
        }
        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,
            was = me.byIdMap[node.id],
            children, length, i;
        delete me.byIdMap[node.id];
        if (includeChildren === true) {
            children = node.childNodes;
            length = children.length;
            for (i = 0; i < length; i++) {
                me.unregisterNode(children[i], true);
            }
        }
        
        
        if (node.onUnregisterTreeNode && node === was) {
            node.onUnregisterTreeNode(me);
            me.nodesToUnregister--;
        }
    },
    onNodeSort: function(node, childNodes) {
        var me = this;
        
        
        me.suspendAutoSync();
        
        
        if ((me.indexOf(node) !== -1 && node.isExpanded()) || (node === me.getRoot() && !me.getRootVisible())) {
            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);
        }
        return newRoot;
    },
    updateRoot: function(newRoot, oldRoot) {
        var me = this,
            oldOwner,
            initial = !oldRoot,
            toRemove,
            removeRange = [];
        
        me.getTrackRemoved();
        
        
        me.suspendEvent('add', 'remove');
        if (initial) {
            me.suspendEvent('refresh', 'datachanged');
        }
        
        
        if (oldRoot && oldRoot.isModel) {
            
            if (me.getRootVisible()) {
                toRemove = [
                    oldRoot
                ];
            } else {
                toRemove = oldRoot.childNodes;
            }
            me.beforeNodeRemove(null, toRemove, false, removeRange);
            oldRoot.set('root', false);
            me.onNodeRemove(null, toRemove, false, removeRange);
            oldRoot.fireEvent('remove', null, oldRoot, false);
            oldRoot.fireEvent('rootchange', null);
            oldRoot.clearListeners();
            oldRoot.store = oldRoot.treeStore = null;
            
            
            
            me.unregisterNode(oldRoot);
        }
        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.store = newRoot.treeStore = me;
                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;
            }
        }
        if (!initial) {
            me.fireEvent('rootchange', newRoot, oldRoot);
        }
        
        if (newRoot && (me.getAutoLoad() || newRoot.isExpanded())) {
            
            if (newRoot.isLoaded()) {
                me.onNodeExpand(newRoot, newRoot.childNodes);
                if (!initial) {
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                }
            } else 
            {
                newRoot.data.expanded = false;
                newRoot.expand(false);
                
                
                
                if (newRoot.isLoaded && !me.getProxy().isSynchronous && !initial) {
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                }
            }
        } else if (!initial) {
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        
        me.resumeEvent('add', 'remove');
        if (initial) {
            me.resumeEvent('refresh', 'datachanged');
        }
    },
    doDestroy: function() {
        var me = this,
            root = me.getRoot();
        
        
        
        if (root && me.nodesToUnregister) {
            root.cascade(function(node) {
                if (node.onUnregisterTreeNode) {
                    node.onUnregisterTreeNode(me);
                }
            });
        }
        me.callParent();
    },
    
    
    each: function(fn, scope, includeOptions) {
        var i = 0,
            filtered = includeOptions,
            includeCollapsed;
        if (includeOptions && typeof includeOptions === 'object') {
            includeCollapsed = includeOptions.collapsed;
            filtered = includeOptions.filtered;
        }
        if (includeCollapsed) {
            this.getRoot().cascade(function(node) {
                if (filtered === true || node.get('visible')) {
                    return fn.call(scope || node, node, i++);
                }
            });
        } else {
            return this.callParent([
                fn,
                scope,
                filtered
            ]);
        }
    },
    
    collect: function(dataIndex, options, filtered) {
        var includeCollapsed,
            map = {},
            result = [],
            allowNull = options,
            strValue, value;
        if (options && typeof options === 'object') {
            includeCollapsed = options.collapsed;
            filtered = options.filtered;
            allowNull = options.allowNull;
        }
        if (includeCollapsed || filtered) {
            this.getRoot().cascade(function(node) {
                if (filtered === true || node.get('visible')) {
                    value = node.get(dataIndex);
                    strValue = String(value);
                    if ((allowNull || !Ext.isEmpty(value)) && !map[strValue]) {
                        map[strValue] = 1;
                        result.push(value);
                    }
                }
                
                
                if (!includeCollapsed && !node.isExpanded()) {
                    return false;
                }
            });
        } else {
            result = this.callParent([
                dataIndex,
                allowNull,
                filtered
            ]);
        }
        return result;
    },
    
    getNodeById: function(id) {
        return this.byIdMap[id] || null;
    },
    
    findNode: function(fieldName, value, startsWith, endsWith, ignoreCase) {
        if (Ext.isEmpty(value, false)) {
            return null;
        }
        
        if (fieldName === 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(fieldName))) {
                result = node;
                return false;
            }
        });
        return result;
    },
    
    load: function(options) {
        var node = options && options.node;
        
        
        if (!node && !(node = this.getRoot())) {
            node = this.setRoot({
                expanded: true,
                autoRoot: true
            });
            return;
        }
        
        if (node.isLoading()) {
            return;
        }
        return this.callParent([
            options
        ]);
    },
    
    reload: function(options) {
        var o = Ext.apply({}, options, this.lastOptions);
        
        o.node = this.getRoot();
        return this.load(o);
    },
    
    flushLoad: function() {
        var me = this,
            options = me.pendingLoadOptions,
            node, callback, scope,
            clearOnLoad = me.getClearOnLoad(),
            isRootLoad, operation, doClear;
        
        me.clearLoadTask();
        if (!options) {
            return;
        }
        node = options.node || me.getRoot();
        
        
        
        
        isRootLoad = node && node.isRoot();
        callback = options.callback;
        scope = options.scope;
        options.params = options.params || {};
        
        
        if (node.data.expanded && !isRootLoad) {
            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();
        
        me.setLoadOptions(options);
        if (me.getRemoteSort() && options.sorters) {
            me.fireEvent('beforesort', me, options.sorters);
        }
        options = Ext.apply({
            node: options.node || node,
            internalScope: me,
            internalCallback: me.onProxyLoad
        }, options);
        me.lastOptions = Ext.apply({}, options);
        
        options.isRootLoad = isRootLoad;
        operation = me.createOperation('read', options);
        if (me.fireEvent('beforeload', me, operation) !== false) {
            
            
            me.loading = true;
            
            if (isRootLoad) {
                if (me.getClearRemovedOnLoad()) {
                    me.removedNodes.length = 0;
                }
                if (clearOnLoad) {
                    
                    
                    me.unregisterNode(node, true);
                    node.clear(false, true);
                    me.registerNode(node);
                    doClear = true;
                }
            } else 
            {
                if (me.loading) {
                    
                    node.data.loaded = false;
                }
                if (me.getTrackRemoved() && me.getClearRemovedOnLoad()) {
                    
                    me.clearRemoved(node);
                }
                if (clearOnLoad) {
                    node.removeAll(false);
                }
            }
            
            
            if (me.loading && node) {
                node.set('loading', true, {
                    silent: !(me.contains(node) || node === me.getRoot())
                });
            }
            if (doClear) {
                me.clearData(true);
                
                if (me.getRootVisible()) {
                    me.suspendEvents();
                    me.add(node);
                    me.resumeEvents();
                }
            }
            operation.execute();
        }
        return me;
    },
    onProxyLoad: function(operation) {
        var me = this,
            options = operation.initialConfig,
            successful = operation.wasSuccessful(),
            records = operation.getRecords(),
            node = options.node,
            isRootLoad = options.isRootLoad,
            scope = operation.getScope() || me,
            args = [
                records,
                operation,
                successful
            ];
        if (me.destroyed) {
            return;
        }
        me.loading = false;
        node.set('loading', false);
        if (successful) {
            ++me.loadCount;
            if (!me.getClearOnLoad()) {
                records = me.cleanRecords(node, records);
            }
            
            if (me.getParentIdProperty()) {
                records = me.treeify(node, records);
            }
            if (isRootLoad) {
                me.suspendEvent('add', 'update');
            }
            records = me.fillNode(node, records);
        }
        
        
        
        Ext.callback(options.onChildNodesAvailable, scope, args);
        if (isRootLoad) {
            me.resumeEvent('add', 'update');
            me.callObservers('BeforePopulate');
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
            me.callObservers('AfterPopulate');
        }
        me.fireEvent('load', me, records, successful, operation, node);
    },
    
    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;
        
        if (++me.bulkUpdate === 1) {
            me.suspendEvent('datachanged');
        }
        if (newNodeCount) {
            me.setupNodes(newNodes);
            node.appendChild(newNodes, undefined, true);
        } else 
        
        
        {
            if (me.bulkUpdate === 1) {
                node.set('loaded', true);
            } else {
                node.data.loaded = true;
            }
        }
        if (!--me.bulkUpdate) {
            me.resumeEvent('datachanged');
        }
        
        return newNodes;
    },
    setupNodes: function(newNodes) {
        var me = this,
            sorters = me.getSorters(),
            needsIndexSort = false,
            newNodeCount = newNodes.length,
            performLocalSort = me.sortOnLoad && newNodeCount > 1 && !me.getRemoteSort() && me.getFolderSort() || sorters.length,
            performLocalFilter = me.needsLocalFilter(),
            node1, node2, i;
        
        if (performLocalFilter) {
            me.doFilter(newNodes[0]);
        }
        
        for (i = 1; i < newNodeCount; i++) {
            node1 = newNodes[i];
            node2 = newNodes[i - 1];
            
            if (performLocalFilter) {
                me.doFilter(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);
        }
    },
    
    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);
        }
    },
    
    treeify: function(parentNode, records) {
        var me = this,
            loadParentNodeId = parentNode.getId(),
            parentIdProperty = me.getParentIdProperty(),
            len = records.length,
            result = [],
            nodeMap = {},
            i, node, parentId, parent, id, children;
        
        for (i = 0; i < len; i++) {
            node = records[i];
            node.data.depth = 1;
            nodeMap[node.id] = node;
        }
        
        for (i = 0; i < len; i++) {
            node = records[i];
            parentId = node.data[parentIdProperty];
            if (!(parentId || parentId === 0) || parentId === loadParentNodeId) {
                result.push(node);
            } else {
                
                if (!nodeMap[parentId]) {
                    Ext.raise('Ext.data.TreeStore, Invalid parentId "' + parentId + '"');
                }
                
                parent = nodeMap[parentId];
                parent.$children = parent.$children || [];
                parent.$children.push(node);
                node.data.depth = parent.data.depth + 1;
            }
        }
        for (id in nodeMap) {
            node = nodeMap[id];
            children = node.$children;
            if (children) {
                delete node.$children;
                me.setupNodes(children);
                node.appendChild(children);
            }
            me.registerNode(node);
        }
        me.setupNodes(result);
        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.getRemoteSort()) {
            
            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());
    },
    commitChanges: function() {
        var removed = this.removedNodes;
        if (removed) {
            removed.length = 0;
        }
        this.callParent();
    },
    
    getRootNode: function() {
        return this.getRoot();
    },
    
    setRootNode: function(root) {
        this.setRoot(root);
        return this.getRoot();
    },
    privates: {
        fireChangeEvent: function(record) {
            return !!this.byIdMap[record.id];
        },
        
        getRawRemovedRecords: function() {
            return this.removedNodes;
        },
        createOperation: function(type, options) {
            
            
            var me = this,
                node = options.node,
                proxy;
            
            
            if (me.useModelProxy && node && node !== me.getRootNode()) {
                proxy = node.getProxy();
            }
            
            
            if (proxy && proxy !== me.getProxy()) {
                return proxy.createOperation(type, options);
            } else {
                return me.callParent([
                    type,
                    options
                ]);
            }
        },
        
        recordCreator: function(data, Model) {
            return new Model(data);
        },
        doFilter: function(node) {
            this.filterNodes(node, this.getFilters().getFilterFn(), true);
        },
        
        filterNodes: function(node, filterFn, parentVisible) {
            var me = this,
                bottomUpFiltering = me.filterer === 'bottomup',
                
                
                match = filterFn(node) && parentVisible || (node.isRoot() && !me.getRootVisible()),
                childNodes = node.childNodes,
                len = childNodes && childNodes.length,
                i, matchingChildren;
            if (len) {
                for (i = 0; i < len; ++i) {
                    
                    matchingChildren = me.filterNodes(childNodes[i], filterFn, match || bottomUpFiltering) || matchingChildren;
                }
                if (bottomUpFiltering) {
                    match = matchingChildren || match;
                }
            }
            node.set("visible", match, me._silentOptions);
            return match;
        },
        needsLocalFilter: function() {
            return !this.getRemoteFilter() && this.getFilters().length;
        },
        onRemoteFilterSet: function(filters, remoteFilter) {
            
            
            
            
            var data = this.getData();
            data.setFilters(null);
            if (filters) {
                filters.on('endupdate', this.onFilterEndUpdate, this);
            }
        },
        onRemoteSortSet: function(sorters, remoteSort) {
            
            
            
            
            var data = this.getData();
            data.setSorters(null);
            if (sorters) {
                sorters.on('endupdate', this.onSorterEndUpdate, this);
            }
        }
    },
    deprecated: {
        5: {
            properties: {
                tree: null
            }
        }
    }
});


Ext.define('Ext.data.Types', {
    singleton: true
}, 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;
        this.isBase = record.self === Ext.data.Model;
        
        
        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) {
        
        
        if (this.isBase) {
            return;
        }
        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, null, record);
                    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 (val && val.join) {
                                val = val.join(' ');
                            }
                            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.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.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'
        ],
        _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.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.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, defaultValue);
        },
        
        getNodeValue: function(node, defaultValue) {
            
            
            
            
            if (typeof node.normalize == 'function') {
                node.normalize();
            }
            var firstChild = node && node.firstChild,
                v = firstChild ? firstChild.nodeValue : null;
            
            
            
            if (defaultValue !== undefined && (v == null || v === '')) {
                v = defaultValue;
            }
            return 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', {
    alternateClassName: 'Ext.data.XmlReader',
    extend: Ext.data.reader.Reader,
    alias: 'reader.xml',
    config: {
        
        record: '',
        
        namespace: ''
    },
    
    createAccessor: function(expr) {
        if (Ext.isEmpty(expr)) {
            return Ext.emptyFn;
        }
        if (Ext.isFunction(expr)) {
            return expr;
        }
        return function(root) {
            return this.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) {
        return this.getRootValue(data, this.getRootProperty());
    },
    
    extractData: function(root, readOptions) {
        var recordName = this.getRecord();
        
        if (!recordName) {
            Ext.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, internalReadOptions) {
        
        
        if (Ext.isArray(doc)) {
            doc = doc[0];
        }
        return this.callParent([
            doc,
            readOptions,
            internalReadOptions
        ]);
    },
    
    createFieldAccessor: function(field) {
        var namespace = this.getNamespace(),
            selector, autoMapping, result;
        if (field.mapping) {
            selector = field.mapping;
        } else {
            selector = (namespace ? namespace + '|' : '') + field.name;
            autoMapping = true;
        }
        if (typeof selector === 'function') {
            result = function(raw, self) {
                return field.mapping(raw, self);
            };
        } else {
            
            
            
            
            
            if (autoMapping && !namespace && Ext.supports.XmlQuerySelector) {
                result = function(raw, self) {
                    return self.getNodeValue(raw.querySelector(selector));
                };
            }
            if (!result) {
                result = function(raw, self) {
                    return self.getNodeValue(Ext.DomQuery.selectNode(selector, raw));
                };
            }
        }
        return result;
    },
    privates: {
        getGroupRoot: function(data) {
            return this.getRootValue(data, this.getGroupRootProperty());
        },
        getRootValue: function(data, prop) {
            var nodeName = data.nodeName;
            if (!prop || (nodeName && nodeName == prop)) {
                return data;
            } else if (typeof prop === 'function') {
                return prop(data);
            } else if (Ext.DomQuery.isXml(data)) {
                
                
                
                return Ext.DomQuery.selectNode(prop, data);
            }
        },
        getSummaryRoot: function(data) {
            return this.getRootValue(data, this.getSummaryRootProperty());
        }
    },
    deprecated: {
        '5.1.1': {
            properties: {
                
                xmlData: null
            }
        }
    }
});


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',
    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',
    config: {
        
        id: undefined
    },
    
    
    
    constructor: function(config) {
        this.callParent(arguments);
        
        this.cache = {};
        
        if (this.getStorageObject() === undefined) {
            Ext.raise("Local Storage is not supported in this browser, please use another type of data proxy");
        }
        
        
        if (this.getId() === undefined) {
            Ext.raise("No unique id was provided to the local storage proxy. See Ext.data.proxy.LocalStorage documentation for details");
        }
        
        this.initialize();
    },
    
    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) {
                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);
    },
    
    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');
        }
    },
    
    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);
    },
    
    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, value;
        for (; i < length; i++) {
            field = fields[i];
            name = field.name;
            if (field.persist) {
                value = rawData[name];
                if (field.isDateField && field.dateFormat && Ext.isDate(value)) {
                    value = Ext.Date.format(value, field.dateFormat);
                } else if (field.serialize) {
                    value = field.serialize(value, record);
                }
                data[name] = value;
            }
        }
        
        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.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.summary.Base', {
    mixins: [
        Ext.mixin.Factoryable
    ],
    alias: 'data.summary.base',
    
    isAggregator: true,
    factoryConfig: {
        defaultType: 'base',
        cacheable: true
    },
    constructor: function(config) {
        var calculate = config && config.calculate;
        if (calculate) {
            config = Ext.apply({}, config);
            delete config.calculate;
            this.calculate = calculate;
        }
        this.initConfig(config);
    },
    
    
    extractValue: function(record, property, root) {
        var ret;
        if (record) {
            if (root) {
                record = record[root];
            }
            ret = record[property];
        }
        return ret;
    }
}, function() {
    Ext.Factory.on('dataSummary', function(factory, config) {
        if (typeof config === 'function') {
            return factory({
                calculate: config
            });
        }
    });
});



Ext.define('Ext.data.summary.Sum', {
    extend: Ext.data.summary.Base,
    alias: 'data.summary.sum',
    calculate: function(records, property, root, begin, end) {
        var n = end - begin,
            i, sum, v;
        for (i = 0; i < n; ++i) {
            v = this.extractValue(records[begin + i], property, root);
            sum = i ? sum + v : v;
        }
        return sum;
    }
});


Ext.define('Ext.data.summary.Average', {
    extend: Ext.data.summary.Sum,
    alias: 'data.summary.average',
    calculate: function(records, property, root, begin, end) {
        var len = end - begin,
            value;
        if (len > 0) {
            value = this.callParent([
                records,
                property,
                root,
                begin,
                end
            ]) / len;
        }
        return value;
    }
});


Ext.define('Ext.data.summary.Count', {
    extend: Ext.data.summary.Base,
    alias: 'data.summary.count',
    calculate: function(records, property, root, begin, end) {
        return end - begin;
    }
});


Ext.define('Ext.data.summary.Max', {
    extend: Ext.data.summary.Base,
    alias: 'data.summary.max',
    calculate: function(records, property, root, begin, end) {
        var max = this.extractValue(records[begin], property, root),
            i, v;
        for (i = begin; i < end; ++i) {
            v = this.extractValue(records[i], property, root);
            if (v > max) {
                max = v;
            }
        }
        return max;
    }
});


Ext.define('Ext.data.summary.Min', {
    extend: Ext.data.summary.Base,
    alias: 'data.summary.min',
    calculate: function(records, property, root, begin, end) {
        var min = this.extractValue(records[begin], property, root),
            i, v;
        for (i = begin; i < end; ++i) {
            v = this.extractValue(records[i], property, root);
            if (v < min) {
                min = v;
            }
        }
        return min;
    }
});


Ext.define('Ext.data.validator.AbstractDate', {
    extend: Ext.data.validator.Validator,
    config: {
        
        message: null,
        
        format: ''
    },
    applyFormat: function(format) {
        if (!format) {
            format = this.getDefaultFormat();
        }
        if (!Ext.isArray(format)) {
            format = [
                format
            ];
        }
        return format;
    },
    parse: function(value) {
        if (Ext.isDate(value)) {
            return value;
        }
        var me = this,
            format = me.getFormat(),
            len = format.length,
            ret = null,
            i;
        for (i = 0; i < len && !ret; ++i) {
            ret = Ext.Date.parse(value, format[i], true);
        }
        return ret;
    },
    validate: function(value) {
        return this.parse(value) ? true : this.getMessage();
    }
});


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: 'Value must be greater than {0}',
        
        maxOnlyMessage: 'Value must be less than {0}',
        
        bothMessage: 'Value must be between {0} and {1}'
    },
    resetMessages: function() {
        this._bothMsg = this._minMsg = this._maxMsg = null;
    },
    updateMin: function() {
        this.resetMessages();
    },
    updateMax: function() {
        this.resetMessages();
    },
    updateMinOnlyMessage: function() {
        this.resetMessages();
    },
    updateMaxOnlyMessage: function() {
        this.resetMessages();
    },
    updateBothMessage: function() {
        this.resetMessages();
    },
    validate: function(value) {
        var me = this,
            min = me.getMin(),
            max = me.getMax(),
            hasMin = (min != null),
            hasMax = (max != null),
            msg = this.validateValue(value);
        if (msg !== true) {
            return msg;
        }
        value = me.getValue(value);
        if (hasMin && hasMax) {
            if (value < min || value > max) {
                msg = me._bothMsg || (me._bothMsg = Ext.String.format(me.getBothMessage(), min, max));
            }
        } else if (hasMin) {
            if (value < min) {
                msg = me._minMsg || (me._minMsg = Ext.String.format(me.getMinOnlyMessage(), min));
            }
        } else if (hasMax) {
            if (value > max) {
                msg = me._maxMsg || (me._maxMsg = Ext.String.format(me.getMaxOnlyMessage(), max));
            }
        }
        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.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.CIDRv4', {
    extend: Ext.data.validator.Format,
    alias: 'data.validator.cidrv4',
    type: 'cidrv4',
    
    
    message: 'Is not a valid CIDR block',
    
    
    matcher: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/
});


Ext.define('Ext.data.validator.CIDRv6', {
    extend: Ext.data.validator.Format,
    alias: 'data.validator.cidrv6',
    type: 'cidrv6',
    
    
    message: 'Is not a valid CIDR block',
    
    
    matcher: /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/
});


Ext.define('Ext.data.validator.Number', {
    extend: Ext.data.validator.Validator,
    alias: 'data.validator.number',
    type: 'number',
    config: {
        
        decimalSeparator: undefined,
        
        message: 'Is not a valid number',
        
        thousandSeparator: undefined
    },
    constructor: function(config) {
        this.callParent([
            config
        ]);
        this.rebuildMatcher();
    },
    applyDecimalSeparator: function(v) {
        return v === undefined ? Ext.util.Format.decimalSeparator : v;
    },
    updateDecimalSeparator: function() {
        this.rebuildMatcher();
    },
    applyThousandSeparator: function(v) {
        return v === undefined ? Ext.util.Format.thousandSeparator : v;
    },
    updateThousandSeparator: function() {
        this.rebuildMatcher();
    },
    parse: function(v) {
        var sep = this.getDecimalSeparator(),
            N = Ext.Number;
        if (typeof v === 'string') {
            if (!this.matcher.test(v)) {
                return null;
            }
            v = this.parseValue(v);
        }
        return sep ? N.parseFloat(v) : N.parseInt(v);
    },
    validate: function(value) {
        return this.parse(value) === null ? this.getMessage() : true;
    },
    privates: {
        getMatcherText: function(preventSign) {
            var t = this.getThousandSeparator(),
                d = this.getDecimalSeparator(),
                s = '(?:';
            if (t) {
                t = Ext.String.escapeRegex(t);
                s += '(?:\\d{1,3}(' + t + '\\d{3})*)|';
            }
            s += '\\d*)';
            if (d) {
                d = Ext.String.escapeRegex(d);
                s += '(?:' + d + '\\d*)?';
            }
            if (!preventSign) {
                s = this.getSignPart() + s;
            }
            return s;
        },
        getSignPart: function() {
            return '(\\+|\\-)?';
        },
        parseValue: function(v) {
            var thousandMatcher = this.thousandMatcher,
                decimal;
            if (thousandMatcher) {
                v = v.replace(thousandMatcher, '');
            }
            decimal = this.getDecimalSeparator();
            if (decimal && decimal !== '.') {
                v = v.replace(decimal, '.');
            }
            return v;
        },
        rebuildMatcher: function() {
            var me = this,
                sep;
            if (!me.isConfiguring) {
                sep = me.getThousandSeparator();
                me.matcher = new RegExp('^' + me.getMatcherText() + '$');
                if (sep) {
                    me.thousandMatcher = sep ? new RegExp(Ext.String.escapeRegex(sep), 'g') : null;
                }
            }
        }
    }
});


Ext.define('Ext.data.validator.Currency', {
    extend: Ext.data.validator.Number,
    alias: 'data.validator.currency',
    type: 'currency',
    config: {
        
        symbolAtEnd: undefined,
        
        spacer: undefined,
        
        symbol: undefined
    },
    
    message: 'Is not a valid currency amount',
    applySymbolAtEnd: function(value) {
        return value === undefined ? Ext.util.Format.currencyAtEnd : value;
    },
    updateSymbolAtEnd: function() {
        this.rebuildMatcher();
    },
    applySpacer: function(value) {
        return value === undefined ? Ext.util.Format.currencySpacer : value;
    },
    updateSpacer: function() {
        this.rebuildMatcher();
    },
    applySymbol: function(value) {
        return value === undefined ? Ext.util.Format.currencySign : value;
    },
    updateSymbol: function() {
        this.rebuildMatcher();
    },
    privates: {
        getMatcherText: function() {
            var me = this,
                ret = me.callParent([
                    true
                ]),
                symbolPart = me.getSymbolMatcher();
            if (me.getSymbolAtEnd()) {
                ret += symbolPart;
            } else {
                ret = symbolPart + ret;
            }
            return me.getSignPart() + ret;
        },
        getSymbolMatcher: function() {
            var symbol = Ext.String.escapeRegex(this.getSymbol()),
                spacer = Ext.String.escapeRegex(this.getSpacer() || ''),
                s = this.getSymbolAtEnd() ? (spacer + symbol) : (symbol + spacer);
            return '(?:' + s + ')?';
        },
        parseValue: function(v) {
            
            v = v.replace(this.currencyMatcher, this.atEnd ? '' : '$1');
            return this.callParent([
                v
            ]);
        },
        rebuildMatcher: function() {
            var me = this,
                symbolPart, atEnd, sign;
            me.callParent();
            if (!me.isConfiguring) {
                atEnd = me.getSymbolAtEnd();
                symbolPart = me.getSymbolMatcher();
                sign = me.getSignPart();
                me.atEnd = atEnd;
                me.currencyMatcher = new RegExp(atEnd ? (symbolPart + '$') : ('^' + sign + symbolPart));
            }
        }
    }
});


Ext.define('Ext.data.validator.CurrencyUS', {
    extend: Ext.data.validator.Currency,
    alias: 'data.validator.currency-us',
    type: 'currency-us',
    thousandSeparator: ',',
    decimalSeparator: '.',
    symbol: '$',
    spacer: '',
    symbolAtEnd: false
});


Ext.define('Ext.data.validator.Date', {
    extend: Ext.data.validator.AbstractDate,
    alias: 'data.validator.date',
    type: 'date',
    isDateValidator: true,
    
    message: 'Is not a valid date',
    
    privates: {
        getDefaultFormat: function() {
            return [
                Ext.Date.defaultFormat,
                'm/d/Y',
                'n/j/Y',
                'n/j/y',
                'm/j/y',
                'n/d/y',
                'm/j/Y',
                'n/d/Y',
                'm-d-y',
                'n-d-y',
                'm-d-Y',
                'mdy',
                'mdY',
                'Y-m-d'
            ];
        }
    }
});


Ext.define('Ext.data.validator.DateTime', {
    extend: Ext.data.validator.AbstractDate,
    alias: 'data.validator.datetime',
    type: 'datetime',
    isDateTimeValidator: true,
    
    message: 'Is not a valid date and time',
    
    privates: {
        getDefaultFormat: function() {
            var D = Ext.Date;
            return D.defaultFormat + ' ' + D.defaultTimeFormat;
        }
    }
});


Ext.define('Ext.data.validator.Email', {
    extend: Ext.data.validator.Format,
    alias: 'data.validator.email',
    type: 'email',
    
    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,
        message: 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',
    
    message: 'Is a value that has been excluded',
    
    constructor: function() {
        this.callParent(arguments);
        if (!this.getList()) {
            Ext.raise('validator.Exclusion requires a list');
        }
    },
    
    inclusion: false
});


Ext.define('Ext.data.validator.IPAddress', {
    extend: Ext.data.validator.Format,
    alias: 'data.validator.ipaddress',
    type: 'ipaddress',
    
    message: 'Is not a valid IP address',
    
    
    matcher: new RegExp('^(' + 
    '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' + '|' + 
    '((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))' + ')$')
});


Ext.define('Ext.data.validator.Inclusion', {
    extend: Ext.data.validator.List,
    alias: 'data.validator.inclusion',
    type: 'inclusion',
    
    message: 'Is not in the list of acceptable values',
    
    constructor: function() {
        this.callParent(arguments);
        if (!this.getList()) {
            Ext.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',
    
    
    
    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',
    isPresence: true,
    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.NotNull', {
    extend: Ext.data.validator.Presence,
    alias: 'data.validator.notnull',
    type: 'notnull',
    allowEmpty: true
});


Ext.define('Ext.data.validator.Phone', {
    extend: Ext.data.validator.Format,
    alias: 'data.validator.phone',
    type: 'phone',
    
    
    
    message: 'Is not a valid phone number',
    
    matcher: new RegExp('^ *' + 
    '(?:' + '\\+?' + 
    '(\\d{1,3})' + 
    '[- .]?' + ')?' + 
    '(?:' + '(?:' + '(\\d{3})' + 
    '|' + '\\((\\d{3})\\)' + 
    ')?' + 
    '[- .]?' + ')' + 
    '(?:' + '([2-9]\\d{2})' + 
    '[- .]?' + ')' + 
    '(\\d{4})' + 
    '(?: *(?:e?xt?) *(\\d*))?' + ' *$')
});


Ext.define('Ext.data.validator.Range', {
    extend: Ext.data.validator.Bound,
    alias: 'data.validator.range',
    type: 'range',
    
    
    
    minOnlyMessage: 'Must be at least {0}',
    
    maxOnlyMessage: 'Must be no more than than {0}',
    
    bothMessage: 'Must be between {0} and {1}',
    config: {
        
        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.data.validator.Time', {
    extend: Ext.data.validator.AbstractDate,
    alias: 'data.validator.time',
    type: 'time',
    isTimeValidator: true,
    
    message: 'Is not a valid time',
    
    privates: {
        getDefaultFormat: function() {
            return Ext.Date.defaultTimeFormat;
        }
    }
});


Ext.define('Ext.data.validator.Url', {
    extend: Ext.data.validator.Format,
    alias: 'data.validator.url',
    type: 'url',
    
    message: 'Is not a valid URL',
    
    
    
    
    
    
    
    matcher: /^(http:\/\/|https:\/\/|ftp:\/\/|\/\/)([-a-zA-Z0-9@:%_\+.~#?&//=])+$/
});


Ext.define('Ext.data.virtual.Group', {
    isVirtualGroup: true,
    firstRecords: null,
    id: '',
    summaryRecord: null,
    constructor: function(key) {
        this.id = key;
        this.firstRecords = [];
    },
    first: function() {
        return this.firstRecords[0] || null;
    },
    getGroupKey: function() {
        return this.id;
    },
    getSummaryRecord: function() {
        return this.summaryRecord;
    }
});


Ext.define('Ext.data.virtual.Page', {
    isVirtualPage: true,
    
    begin: 0,
    
    end: 0,
    
    error: null,
    
    locked: null,
    
    number: 0,
    
    operation: null,
    
    pageMap: null,
    
    records: null,
    
    state: null,
    constructor: function(config) {
        var me = this,
            pageSize;
        Ext.apply(me, config);
        pageSize = me.pageMap.store.getPageSize();
        me.begin = me.number * pageSize;
        me.end = me.begin + pageSize;
        me.locks = {
            active: 0,
            prefetch: 0
        };
    },
    destroy: function() {
        var me = this,
            operation = me.operation;
        me.state = 'destroyed';
        if (operation) {
            operation.abort();
        }
        me.callParent();
    },
    
    adjustLock: function(kind, delta) {
        var me = this,
            locks = me.locks,
            pageMap = me.pageMap,
            locked = null,
            lockedWas = me.locked;
        
        if (!(kind in locks)) {
            Ext.raise('Bad lock type (expected "active" or "prefetch"): "' + kind + '"');
        }
        if (delta !== 1 && delta !== -1) {
            Ext.raise('Invalid lock count delta (should be 1 or -1): ' + delta);
        }
        
        locks[kind] += delta;
        if (locks.active) {
            locked = 'active';
        } else if (locks.prefetch) {
            locked = 'prefetch';
        }
        if (locked !== lockedWas) {
            me.locked = locked;
            if (pageMap) {
                pageMap.onPageLockChange(me, locked, lockedWas);
            }
        }
    },
    clearRecords: function(out, by) {
        var me = this,
            begin = me.begin,
            records = me.records,
            i, n;
        
        if (records) {
            n = records.length;
            if (by) {
                for (i = 0; i < n; ++i) {
                    delete out[records[i][by]];
                }
            } else {
                for (i = 0; i < n; ++i) {
                    delete out[begin + i];
                }
            }
        }
    },
    fillRecords: function(out, by, withIndex) {
        var me = this,
            records = me.records,
            begin = me.begin,
            i, n, record;
        if (records) {
            
            
            n = records.length;
            if (by) {
                for (i = 0; i < n; ++i) {
                    record = records[i];
                    out[record[by]] = withIndex ? begin + i : record;
                }
            } else {
                for (i = 0; i < n; ++i) {
                    out[begin + i] = records[i];
                }
            }
        }
    },
    isInitial: function() {
        return this.state === null;
    },
    isLoaded: function() {
        return this.state === 'loaded';
    },
    isLoading: function() {
        return this.state === 'loading';
    },
    load: function() {
        var me = this,
            operation;
        me.state = 'loading';
        operation = me.pageMap.store.loadVirtualPage(me, me.onLoad, me);
        
        if (me.state === 'loading') {
            me.operation = operation;
        }
    },
    privates: {
        onLoad: function(operation) {
            var me = this;
            me.operation = null;
            if (!me.destroyed) {
                if (!(me.error = operation.getError())) {
                    me.records = operation.getRecords();
                    me.state = 'loaded';
                } else {
                    me.state = 'error';
                }
                
                me.pageMap.onPageLoad(me);
            }
        }
    }
});


Ext.define('Ext.data.virtual.PageMap', {
    isVirtualPageMap: true,
    config: {
        
        cacheSize: 10,
        
        concurrentLoading: 1,
        
        pageCount: null
    },
    generation: 0,
    store: null,
    constructor: function(config) {
        var me = this;
        me.prefetchSortFn = me.prefetchSortFn.bind(me);
        me.initConfig(config);
        me.clear();
    },
    destroy: function() {
        this.clear(true);
        this.callParent();
    },
    canSatisfy: function(range) {
        var end = this.getPageIndex(range.end),
            pageCount = this.getPageCount();
        return pageCount === null || end < pageCount;
    },
    clear: function(destroy) {
        var me = this,
            alive = !destroy || null,
            pages = me.pages,
            pg;
        ++me.generation;
        
        me.byId = alive && {};
        
        me.byInternalId = alive && {};
        
        me.cache = alive && [];
        
        me.indexMap = alive && {};
        
        me.pages = alive && {};
        
        me.loading = alive && [];
        
        me.loadQueues = alive && {
            active: [],
            prefetch: []
        };
        if (pages) {
            for (pg in pages) {
                me.destroyPage(pages[pg]);
            }
        }
    },
    getPage: function(number, autoCreate) {
        var me = this,
            pageCount = me.getPageCount(),
            pages = me.pages,
            page;
        if (pageCount === null || number < pageCount) {
            page = pages[number];
            if (!page && autoCreate !== false) {
                pages[number] = page = new Ext.data.virtual.Page({
                    pageMap: me,
                    number: number
                });
            }
        } else 
        {
            Ext.raise('Invalid page number ' + number + ' when limit is ' + pageCount);
        }
        
        return page || null;
    },
    getPageIndex: function(index) {
        if (index.isEntity) {
            index = this.indexOf(index);
        }
        return Math.floor(index / this.store.getPageSize());
    },
    getPageOf: function(index, autoCreate) {
        var pageSize = this.store.getPageSize(),
            n = Math.floor(index / pageSize);
        return this.getPage(n, autoCreate);
    },
    getPages: function(begin, end) {
        var pageSize = this.store.getPageSize(),
            
            first = Math.floor(begin / pageSize),
            last = Math.ceil(end / pageSize),
            ret = {},
            n;
        for (n = first; n < last; ++n) {
            ret[n] = this.getPage(n);
        }
        return ret;
    },
    flushNextLoad: function() {
        var me = this,
            queueTimer = me.queueTimer;
        if (queueTimer) {
            Ext.unasap(queueTimer);
        }
        me.loadNext();
    },
    indexOf: function(record) {
        var ret = this.indexMap[record.internalId];
        return (ret || ret === 0) ? ret : -1;
    },
    getByInternalId: function(internalId) {
        var index = this.indexMap[internalId],
            page;
        if (index || index === 0) {
            page = this.pages[Math.floor(index / this.store.getPageSize())];
            if (page) {
                return page.records[index - page.begin];
            }
        }
    },
    updatePageCount: function(pageCount, oldPageCount) {
        var pages = this.pages,
            pageNumber, page;
        if (oldPageCount === null || pageCount < oldPageCount) {
            
            for (pageNumber in pages) {
                page = pages[pageNumber];
                if (page.number >= pageCount) {
                    this.clearPage(page);
                    this.destroyPage(page);
                }
            }
        }
    },
    privates: {
        queueTimer: null,
        clearPage: function(page, fromCache) {
            var me = this,
                A = Ext.Array,
                loadQueues = me.loadQueues;
            delete me.pages[page.number];
            page.clearRecords(me.byId, 'id');
            page.clearRecords(me.byInternalId, 'internalId');
            page.clearRecords(me.indexMap, 'internalId');
            A.remove(loadQueues.active, page);
            A.remove(loadQueues.prefetch, page);
            if (!fromCache) {
                Ext.Array.remove(me.cache, page);
            }
        },
        destroyPage: function(page) {
            this.store.onPageDestroy(page);
            page.destroy();
        },
        loadNext: function() {
            var me = this,
                concurrency = me.getConcurrentLoading(),
                loading = me.loading,
                loadQueues = me.loadQueues,
                page;
            me.queueTimer = null;
            
            
            while (loading.length < concurrency) {
                if (!(page = loadQueues.active.shift() || loadQueues.prefetch.shift())) {
                    break;
                }
                loading.push(page);
                page.load();
            }
        },
        onPageLoad: function(page) {
            var me = this,
                store = me.store,
                activeRanges = store.activeRanges,
                n = activeRanges.length,
                i;
            Ext.Array.remove(me.loading, page);
            if (!page.error) {
                page.fillRecords(me.byId, 'id');
                page.fillRecords(me.byInternalId, 'internalId');
                page.fillRecords(me.indexMap, 'internalId', true);
                store.onPageDataAcquired(page);
                for (i = 0; i < n; ++i) {
                    activeRanges[i].onPageLoad(page);
                }
            }
            me.flushNextLoad();
        },
        onPageLockChange: function(page, state, oldState) {
            var me = this,
                cache = me.cache,
                loadQueues = me.loadQueues,
                store = me.store,
                cacheSize, concurrency;
            
            
            
            
            if (page.isInitial()) {
                if (oldState) {
                    Ext.Array.remove(loadQueues[oldState], page);
                }
                if (state) {
                    loadQueues[state].push(page);
                    concurrency = me.getConcurrentLoading();
                    
                    
                    if (!me.queueTimer && me.loading.length < concurrency) {
                        me.queueTimer = Ext.asap(me.loadNext, me);
                    }
                }
            }
            if (state) {
                if (!oldState) {
                    
                    
                    
                    Ext.Array.remove(cache, page);
                }
            } else {
                cache.push(page);
                
                for (cacheSize = me.getCacheSize(); cache.length > cacheSize; ) {
                    page = cache.shift();
                    me.clearPage(page, true);
                    
                    store.onPageEvicted(page);
                    me.destroyPage(page);
                }
            }
        },
        prefetchSortFn: function(a, b) {
            a = a.number;
            b = b.number;
            var M = Math,
                firstPage = this.sortFirstPage,
                lastPage = this.sortLastPage,
                direction = this.sortDirection,
                aDir = a < firstPage,
                bDir = b < firstPage,
                ret;
            a = aDir ? M.abs(firstPage - a) : M.abs(lastPage - a);
            b = bDir ? M.abs(firstPage - b) : M.abs(lastPage - b);
            if (a === b) {
                ret = aDir ? direction : -direction;
            } else {
                ret = a - b;
            }
            return ret;
        },
        prioritizePrefetch: function(direction, firstPage, lastPage) {
            var me = this;
            me.sortDirection = direction;
            me.sortFirstPage = firstPage;
            me.sortLastPage = lastPage;
            me.loadQueues.prefetch.sort(me.prefetchSortFn);
        }
    }
});


Ext.define('Ext.data.virtual.Range', {
    extend: Ext.data.Range,
    isVirtualRange: true,
    
    callback: null,
    
    
    prefetch: false,
    
    scope: null,
    
    
    direction: 1,
    constructor: function(config) {
        this.adjustingPages = [];
        this.callParent([
            config
        ]);
    },
    reset: function() {
        var me = this;
        me.records = {};
        me.activePages = me.prefetchPages = null;
    },
    privates: {
        adjustPageLocks: function(kind, adjustment) {
            var me = this,
                pages = me.adjustingPages,
                n = pages.length,
                i;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (n > 1) {
                
                
                
                pages.sort(me.direction < 0 ? me.pageSortBackFn : me.pageSortFwdFn);
            }
            for (i = 0; i < n; ++i) {
                pages[i].adjustLock(kind, adjustment);
            }
            pages.length = 0;
        },
        doGoto: function() {
            var me = this,
                begin = me.begin,
                end = me.end,
                prefetch = me.prefetch,
                records = me.records,
                store = me.store,
                pageMap = store.pageMap,
                limit = store.totalCount,
                beginWas = me.lastBegin,
                endWas = me.lastEnd,
                activePagesWas = me.activePages,
                prefetchPagesWas = me.prefetchPages,
                beginBufferZone = me.trailingBufferZone,
                endBufferZone = me.leadingBufferZone,
                adjustingPages = me.adjustingPages,
                activePages, page, pg, direction, prefetchBegin, prefetchEnd, prefetchPages;
            adjustingPages.length = 0;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if ((begin > beginWas && end < endWas) || (begin < beginWas && end > endWas)) {
                direction = me.direction;
            } else {
                direction = (begin < beginWas) ? -1 : ((begin > beginWas) ? 1 : me.direction);
            }
            if (direction < 0) {
                
                pg = beginBufferZone;
                beginBufferZone = endBufferZone;
                endBufferZone = pg;
            }
            me.direction = direction;
            me.activePages = activePages = pageMap.getPages(begin, end);
            if (prefetch) {
                me.prefetchBegin = prefetchBegin = Math.max(0, begin - beginBufferZone);
                
                if (limit === null) {
                    limit = Number.MAX_VALUE;
                }
                me.prefetchEnd = prefetchEnd = Math.min(limit, end + endBufferZone);
                me.prefetchPages = prefetchPages = pageMap.getPages(prefetchBegin, prefetchEnd);
            }
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            for (pg in activePages) {
                page = activePages[pg];
                
                
                if (prefetchPages) {
                    delete prefetchPages[pg];
                }
                if (activePagesWas && pg in activePagesWas) {
                    
                    
                    delete activePagesWas[pg];
                } else {
                    
                    page.adjustLock('active', 1);
                    page.fillRecords(records);
                }
            }
            if (prefetchPages) {
                for (pg in prefetchPages) {
                    if (prefetchPagesWas && pg in prefetchPagesWas) {
                        
                        
                        delete prefetchPagesWas[pg];
                    } else {
                        prefetchPages[pg].adjustLock('prefetch', 1);
                    }
                }
            }
            
            
            
            
            
            if (prefetchPagesWas) {
                for (pg in prefetchPagesWas) {
                    adjustingPages.push(prefetchPagesWas[pg]);
                }
                if (adjustingPages.length) {
                    me.adjustPageLocks('prefetch', -1);
                }
            }
            if (activePagesWas) {
                for (pg in activePagesWas) {
                    adjustingPages.push(page = activePagesWas[pg]);
                    page.clearRecords(records);
                }
                if (adjustingPages.length) {
                    me.adjustPageLocks('active', -1);
                }
            }
            if (prefetchPages) {
                pageMap.prioritizePrefetch(direction, pageMap.getPageIndex(begin), pageMap.getPageIndex(end - 1));
            }
            me.lastBegin = begin;
            me.lastEnd = end;
        },
        onPageDestroy: function(page) {
            var n = page.number,
                activePages = this.activePages,
                prefetchPages = this.prefetchPages;
            if (activePages) {
                delete activePages[n];
            }
            if (prefetchPages) {
                delete prefetchPages[n];
            }
        },
        onPageLoad: function(page) {
            var me = this,
                callback = me.callback,
                first, last;
            if (me.activePages[page.number]) {
                page.fillRecords(me.records);
                if (callback) {
                    
                    
                    first = Math.max(me.begin, page.begin);
                    last = Math.min(me.end, page.end);
                    Ext.callback(callback, me.scope, [
                        me,
                        first,
                        last
                    ]);
                }
            }
        },
        pageSortBackFn: function(page1, page2) {
            return page2.number - page1.number;
        },
        pageSortFwdFn: function(page1, page2) {
            return page1.number - page2.number;
        },
        refresh: function() {
            
            this.records = this.records || {};
        },
        reload: function() {
            var me = this,
                begin = me.begin,
                end = me.end;
            me.begin = me.end = 0;
            me.direction = 1;
            me.prefetchPages = me.activePages = null;
            me.goto(begin, end);
        }
    }
});


Ext.define('Ext.data.virtual.Store', {
    extend: Ext.data.ProxyStore,
    alias: 'store.virtual',
    isVirtualStore: true,
    config: {
        data: null,
        totalCount: null,
        
        leadingBufferZone: 200,
        
        trailingBufferZone: 50
    },
    
    remoteSort: true,
    
    remoteFilter: true,
    
    sortOnLoad: false,
    
    trackRemoved: false,
    constructor: function(config) {
        var me = this;
        me.sortByPage = me.sortByPage.bind(me);
        me.activeRanges = [];
        me.pageMap = new Ext.data.virtual.PageMap({
            store: me
        });
        me.callParent([
            config
        ]);
    },
    doDestroy: function() {
        this.pageMap.destroy();
        this.callParent();
    },
    applyGrouper: function(grouper) {
        this.group(grouper);
        return this.grouper;
    },
    
    
    contains: function(record) {
        return this.indexOf(record) > -1;
    },
    
    createActiveRange: function(config) {
        var range = Ext.apply({
                leadingBufferZone: this.getLeadingBufferZone(),
                trailingBufferZone: this.getTrailingBufferZone(),
                store: this
            }, config);
        return new Ext.data.virtual.Range(range);
    },
    
    getAt: function(index) {
        var page = this.pageMap.getPageOf(index, 
            false),
            ret;
        if (page && page.records) {
            
            ret = page.records[index - page.begin];
        }
        return ret || null;
    },
    
    getById: function(id) {
        return this.pageMap.byId[id] || null;
    },
    getCount: function() {
        return this.totalCount || 0;
    },
    getGrouper: function() {
        return this.grouper;
    },
    getGroups: function() {
        var me = this,
            groups = me.groupCollection;
        if (!groups) {
            me.groupCollection = groups = new Ext.util.Collection();
        }
        return groups;
    },
    getSummaryRecord: function() {
        return this.summaryRecord || null;
    },
    isGrouped: function() {
        return !!this.grouper;
    },
    group: function(grouper, direction) {
        var me = this;
        grouper = grouper || null;
        if (grouper) {
            if (typeof grouper === 'string') {
                grouper = {
                    property: grouper,
                    direction: direction || 'ASC'
                };
            }
            if (!grouper.isGrouper) {
                grouper = new Ext.util.Grouper(grouper);
            }
            grouper.setRoot('data');
            me.getGroups().getSorters().splice(0, 1, {
                property: 'id',
                direction: grouper.getDirection()
            });
        }
        me.grouper = grouper;
        if (!me.isConfiguring) {
            me.reload();
            me.fireEvent('groupchange', me, grouper);
        }
    },
    getByInternalId: function(internalId) {
        return this.pageMap.getByInternalId(internalId);
    },
    
    indexOf: function(record) {
        return this.pageMap.indexOf(record);
    },
    
    indexOfId: function(id) {
        var rec = this.getById(id);
        return rec ? this.indexOf(rec) : -1;
    },
    load: function(options) {
        if (typeof options === 'function') {
            options = {
                callback: options
            };
        }
        var me = this,
            page = (options && options.page) || 1,
            pageSize = me.getPageSize(),
            operation = me.createOperation('read', Ext.apply({
                start: (page - 1) * pageSize,
                limit: pageSize,
                page: page,
                filters: me.getFilters().items,
                sorters: me.getSorters().items,
                grouper: me.getGrouper()
            }, options));
        operation.execute();
        return operation;
    },
    reload: function(options) {
        if (typeof options === 'function') {
            options = {
                callback: options
            };
        }
        var me = this;
        if (me.fireEvent('beforereload') === false) {
            return null;
        }
        options = Ext.apply({
            internalScope: me,
            internalCallback: me.handleReload,
            page: 1
        }, options);
        me.pageMap.clear();
        me.getGroups().clear();
        return me.load(options);
    },
    
    
    removeAll: function() {
        var activeRanges = this.activeRanges,
            i;
        this.pageMap.clear();
        for (i = activeRanges.length; i-- > 0; ) {
            activeRanges[i].reset();
        }
    },
    
    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();
    },
    onFilterEndUpdate: function() {
        var me = this,
            filters = me.getFilters(false);
        
        if (!me.isConfiguring) {
            me.reload();
            me.fireEvent('filterchange', me, filters.getRange());
        }
    },
    onSorterEndUpdate: function() {
        var me = this,
            sorters = me.getSorters().getRange(),
            fire = !me.isConfiguring;
        if (fire) {
            me.fireEvent('beforesort', me, sorters);
        }
        if (fire) {
            me.reload();
            me.fireEvent('sort', me, sorters);
        }
    },
    updatePageSize: function(pageSize) {
        var totalCount = this.totalCount;
        if (totalCount !== null) {
            this.pageMap.setPageCount(Math.ceil(totalCount / pageSize));
        }
    },
    updateTotalCount: function(totalCount, oldTotalCount) {
        var me = this,
            pageMap = me.pageMap;
        me.totalCount = totalCount;
        pageMap.setPageCount(Math.ceil(totalCount / me.getPageSize()));
        me.fireEvent('totalcountchange', me, totalCount, oldTotalCount);
    },
    
    
    
    add: function() {
        Ext.raise('Virtual stores do not support the add() method');
    },
    insert: function() {
        Ext.raise('Virtual stores do not support the insert() method');
    },
    filter: function() {
        if (!this.getRemoteFilter()) {
            Ext.raise('Virtual stores do not support local filtering');
        }
        
        this.callParent(arguments);
    },
    filterBy: function() {
        Ext.raise('Virtual stores do not support local filtering');
    },
    loadData: function() {
        Ext.raise('Virtual stores do not support the loadData() method');
    },
    applyData: function() {
        Ext.raise('Virtual stores do not support direct data loading');
    },
    updateRemoteFilter: function(remoteFilter, oldRemoteFilter) {
        if (remoteFilter === false) {
            Ext.raise('Virtual stores are always remotely filtered.');
        }
        this.callParent([
            remoteFilter,
            oldRemoteFilter
        ]);
    },
    updateRemoteSort: function(remoteSort, oldRemoteSort) {
        if (remoteSort === false) {
            Ext.raise('Virtual stores are always remotely sorted.');
        }
        this.callParent([
            remoteSort,
            oldRemoteSort
        ]);
    },
    updateTrackRemoved: function(value) {
        if (value !== false) {
            Ext.raise('Virtual stores do not support trackRemoved.');
        }
        this.callParent(arguments);
    },
    
    privates: {
        attachSummaryData: function(resultSet) {
            var me = this,
                summary = resultSet.getSummaryData(),
                grouper, len, i, data, rec;
            if (summary) {
                me.summaryRecord = summary;
            }
            summary = resultSet.getGroupData();
            if (summary) {
                grouper = me.getGrouper();
                if (grouper) {
                    me.groupSummaryData = data = {};
                    for (i = 0 , len = summary.length; i < len; ++i) {
                        rec = summary[i];
                        data[grouper.getGroupString(rec)] = rec;
                    }
                }
            }
        },
        handleReload: function(op) {
            var me = this,
                activeRanges = me.activeRanges,
                len = activeRanges.length,
                pageMap = me.pageMap,
                i, range;
            if (op.wasSuccessful()) {
                me.readTotalCount(op.getResultSet());
                me.fireEvent('reload', me, op);
                for (i = 0; i < len; ++i) {
                    range = activeRanges[i];
                    if (pageMap.canSatisfy(range)) {
                        range.reload();
                    }
                }
            }
        },
        loadVirtualPage: function(page, callback, scope) {
            var me = this,
                pageMapGeneration = me.pageMap.generation;
            return me.load({
                page: page.number + 1,
                
                internalCallback: function(op) {
                    var resultSet = op.getResultSet();
                    if (pageMapGeneration === me.pageMap.generation) {
                        if (op.wasSuccessful()) {
                            me.readTotalCount(resultSet);
                            me.attachSummaryData(resultSet);
                        }
                        callback.call(scope || page, op);
                        me.groupSummaryData = null;
                    }
                }
            });
        },
        lockGroups: function(grouper, page) {
            var groups = this.getGroups(),
                groupInfo = page.groupInfo = {},
                records = page.records,
                len = records.length,
                groupSummaryData = this.groupSummaryData,
                pageMap = this.pageMap,
                n = page.number,
                group, i, groupKey, summaryRec, rec, firstRecords, first;
            for (i = 0; i < len; ++i) {
                rec = records[i];
                groupKey = grouper.getGroupString(rec);
                if (!groupInfo[groupKey]) {
                    groupInfo[groupKey] = rec;
                    group = groups.get(groupKey);
                    if (!group) {
                        group = new Ext.data.virtual.Group(groupKey);
                        groups.add(group);
                    }
                    
                    
                    
                    
                    firstRecords = group.firstRecords;
                    first = firstRecords[0];
                    if (first && n < pageMap.getPageIndex(first)) {
                        firstRecords.unshift(rec);
                    } else {
                        firstRecords.push(rec);
                    }
                    summaryRec = groupSummaryData && groupSummaryData[groupKey];
                    if (summaryRec) {
                        group.summaryRecord = summaryRec;
                    }
                }
            }
        },
        onPageDataAcquired: function(page) {
            var grouper = this.getGrouper();
            if (grouper) {
                this.lockGroups(grouper, page);
            }
        },
        onPageDestroy: function(page) {
            var ranges = this.activeRanges,
                len = ranges.length,
                i;
            for (i = 0; i < len; ++i) {
                ranges[i].onPageDestroy(page);
            }
        },
        onPageEvicted: function(page) {
            var grouper = this.getGrouper();
            if (grouper) {
                this.releaseGroups(grouper, page);
            }
        },
        readTotalCount: function(resultSet) {
            var total = resultSet.getRemoteTotal();
            if (!isNaN(total)) {
                this.setTotalCount(total);
            }
        },
        releaseGroups: function(grouper, page) {
            var groups = this.getGroups(),
                groupInfo = page.groupInfo,
                first, firstRecords, key, group;
            for (key in groupInfo) {
                first = groupInfo[key];
                group = groups.get(key);
                firstRecords = group.firstRecords;
                
                
                if (firstRecords.length === 1) {
                    groups.remove(group);
                } else if (firstRecords[0] === first) {
                    firstRecords.shift();
                    firstRecords.sort(this.sortByPage);
                } else {
                    Ext.Array.remove(firstRecords, first);
                }
            }
        },
        sortByPage: function(rec1, rec2) {
            
            var map = this.pageMap;
            return map.getPageIndex(rec1) - map.getPageIndex(rec2);
        }
    }
});


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',
    
    parseResponse: function(response) {
        var text = response && response.responseText;
        
        if (text != null) {
            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({
                parsingError: true,
                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.util.TaskRunner', {
    
    
    fireIdleEvent: null,
    
    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 = me.onTick.bind(me);
    },
    
    newTask: function(config) {
        var task = new Ext.util.TaskRunner.Task(config);
        task.manager = this;
        
        if (Ext.Timer.track) {
            task.creator = new Error().stack;
        }
        
        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, andRemove) {
        var me = this,
            tasks = me.tasks,
            pendingCount = 0,
            i;
        
        
        
        if (!task.stopped) {
            task.stopped = true;
            task.pending = false;
            if (task.onStop) {
                task.onStop.call(task.scope || task, task);
            }
        }
        if (andRemove) {
            Ext.Array.remove(tasks, task);
        }
        
        
        for (i = 0; !pendingCount && i < tasks.length; i++) {
            if (!tasks[i].stopped) {
                pendingCount++;
            }
        }
        if (!pendingCount) {
            Ext.undefer(me.timerId);
            me.timerId = null;
        }
        return task;
    },
    
    stopAll: function(andRemove) {
        var me = this;
        
        
        Ext.each(this.tasks, function(task) {
            me.stop(task, andRemove);
        }, null, true);
    },
    
    firing: false,
    nextExpires: 1.0E99,
    
    onTick: function() {
        var me = this,
            tasks = me.tasks,
            fireIdleEvent = me.fireIdleEvent,
            
            now = Ext.Date.now(),
            nextExpires = 1.0E99,
            len = tasks.length,
            expires, newTasks, i, task, rt, remove, args;
        
        var timer = Ext.Timer.get(me.timerId);
        if (timer) {
            timer.tasks = [];
        }
        
        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 (fireIdleEvent === null && task.fireIdleEvent !== false) {
                        fireIdleEvent = true;
                    }
                    task.taskRunCount++;
                    if (task.args) {
                        args = task.addCountToArgs ? task.args.concat([
                            task.taskRunCount
                        ]) : task.args;
                    } else {
                        args = [
                            task.taskRunCount
                        ];
                    }
                    
                    
                    if (timer) {
                        timer.tasks.push(task);
                    }
                    if (me.disableTryCatch) {
                        rt = task.run.apply(task.scope || task, args);
                    } else {
                        
                        try {
                            rt = task.run.apply(task.scope || task, args);
                        } catch (taskError) {
                            try {
                                
                                Ext.log({
                                    fn: task.run,
                                    prefix: 'Error while running task',
                                    stack: taskError.stack,
                                    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 === null) {
            fireIdleEvent = false;
        }
        Ext._suppressIdle = !fireIdleEvent;
    },
    
    startTimer: function(timeout, now) {
        var me = this,
            expires = now + timeout,
            timerId = me.timerId;
        
        
        if (timerId && me.nextExpires - expires > me.interval) {
            timerId = Ext.undefer(timerId);
        }
        if (!timerId) {
            if (timeout < me.interval) {
                timeout = me.interval;
            }
            me.timerId = Ext.defer(me.timerFn, timeout);
            me.nextExpires = expires;
            
            var timer = Ext.Timer.get(me.timerId);
            if (timer) {
                timer.runner = me;
            }
        }
    }
}, 
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(andRemove) {
            this.manager.stop(this, andRemove);
        },
        destroy: function() {
            this.stop(true);
        }
    });
    proto = me.Task.prototype;
    
    proto.destroy = proto.stop;
});


Ext.define('Ext.direct.PollingProvider', {
    extend: Ext.direct.JsonProvider,
    alias: 'direct.pollingprovider',
    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.stop(true);
        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.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() {
        if (this.pollTask) {
            this.pollTask.stop();
        }
    },
    getInterval: function() {
        return this.pollTask && this.pollTask.interval;
    },
    setInterval: function(interval) {
        var me = this,
            pollTask = me.pollTask;
        
        if (interval < 100) {
            Ext.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, request;
        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 {
                request = {
                    url: url,
                    callback: me.onData,
                    scope: me,
                    params: baseParams,
                    headers: me.getHeaders()
                };
                if (me.timeout != null) {
                    request.timeout = me.timeout;
                }
                me.sendAjaxRequest(request);
            }
            me.fireEvent('poll', me);
        }
    },
    
    onData: function(opt, success, response) {
        var me = this,
            i, len, events, event;
        if (success) {
            events = me.createEvents(response);
            for (i = 0 , len = events.length; i < len; ++i) {
                event = events[i];
                me.fireEvent('data', me, event);
                if (!event.status) {
                    me.fireEvent('exception', me, event);
                }
            }
        } else {
            event = 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, event);
            me.fireEvent('exception', me, event);
        }
        me.callParent([
            opt,
            success,
            response
        ]);
    },
    
    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;
        me.disableBatching = config.batched != null ? !config.batched : false;
        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.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.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,
            paramsAsArray = config.paramsAsArray,
            metadata = config.metadata,
            options = config.options,
            args = [],
            flatten, i, len;
        if (me.ordered) {
            if (me.len > 0) {
                
                if (paramOrder) {
                    
                    
                    
                    flatten = config.paramsAsArray && me.len === 1 && (paramOrder.length > 1 || Ext.isArray(params));
                    if (flatten) {
                        if (Ext.isArray(params)) {
                            for (i = 0 , len = params.length; i < len; i++) {
                                args.push(me.convertParams(params[i], paramOrder, paramOrder.length, true));
                            }
                        } else {
                            args = me.convertParams(params, paramOrder, paramOrder.length, true);
                        }
                        if (!params.allowSingle || args.length > 1) {
                            args = [
                                args
                            ];
                        }
                    } else {
                        
                        
                        args = me.convertParams(params, paramOrder, me.len, false);
                    }
                } else {
                    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;
    },
    convertParams: function(params, paramOrder, count, flatten) {
        var ret = [],
            paramName, i, len;
        for (i = 0 , len = count; i < len; i++) {
            paramName = paramOrder[i];
            ret.push(params[paramName]);
        }
        if (flatten) {
            return ret.length === 0 ? undefined : ret.length === 1 ? ret[0] : ret;
        } else {
            return ret;
        }
    },
    
    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.raise('options.metadata is not an Array ' + 'for Ext Direct method ' + me.name);
                } else if (options.metadata.length < me.metadata.len) {
                    Ext.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.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.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',
    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',
    type: 'remoting',
    
    
    
    
    
    
    enableBuffer: 10,
    
    bufferLimit: Number.MAX_VALUE,
    
    maxRetries: 1,
    
    
    
    
    constructor: function(config) {
        var me = this;
        me.callParent([
            config
        ]);
        me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || Ext.global;
        me.callBuffer = [];
    },
    destroy: function() {
        if (this.callTask) {
            this.callTask.cancel();
        }
        this.callParent();
    },
    
    connect: function() {
        var me = this;
        
        if (!me.url) {
            Ext.raise('Error initializing RemotingProvider "' + me.id + '", no url configured.');
        }
        
        me.callParent();
    },
    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,
            handler;
        handler = function() {
            me.invokeFunction(action, method, Array.prototype.slice.call(arguments, 0));
        };
        handler.name = handler.$name = action + '.' + method.name;
        handler.$directFn = true;
        handler.directCfg = handler.$directCfg = {
            action: action,
            method: method
        };
        return handler;
    },
    
    invokeFunction: function(action, method, args) {
        var me = this,
            transaction, form, isUpload, postParams;
        transaction = me.configureTransaction(action, method, args);
        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            if (transaction.isForm) {
                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.queueTransaction(transaction);
            me.fireEvent('call', me, transaction, method);
        }
    },
    
    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.raise("Callback argument is not a function " + "for Ext Direct method " + action + "." + method.name);
        }
        
        
        
        cb = cb && scope ? cb.bind(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: !!method.formHandler,
            disableBatching: method.disableBatching
        });
        if (options && options.timeout != null) {
            params.timeout = options.timeout;
        }
        return new Ext.direct.Transaction(params);
    },
    
    queueTransaction: function(transaction) {
        var me = this,
            callBuffer = me.callBuffer,
            enableBuffer = me.enableBuffer;
        if (transaction.isForm || enableBuffer === false || transaction.disableBatching || transaction.timeout != null) {
            me.sendTransaction(transaction);
            return;
        }
        callBuffer.push(transaction);
        if (enableBuffer && callBuffer.length < me.bufferLimit) {
            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.sendTransaction(len === 1 ? buffer[0] : buffer);
            me.callBuffer = [];
        }
    },
    
    sendTransaction: 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,
            headers: me.getHeaders()
        };
        
        if (transaction.timeout != null) {
            request.timeout = transaction.timeout;
        } else if (me.timeout != null) {
            request.timeout = me.timeout;
        }
        if (transaction.isForm) {
            Ext.apply(request, {
                params: transaction.params,
                form: transaction.form,
                isUpload: transaction.isUpload
            });
        } else {
            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;
            }
        }
        return me.sendAjaxRequest(request);
    },
    
    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;
    },
    
    onData: function(options, success, response) {
        var me = this,
            i, len, events, event, transaction, transactions;
        if (me.destroying || me.destroyed) {
            return;
        }
        
        
        
        
        
        events = success && me.createEvents(response);
        
        
        success = events && events.length && !events[0].parsingError;
        if (success) {
            for (i = 0 , len = events.length; i < len; ++i) {
                event = events[i];
                me.fireEvent('data', me, event);
                transaction = me.getTransaction(event);
                if (transaction) {
                    if (me.fireEvent('beforecallback', me, event, transaction) !== false) {
                        me.runCallback(transaction, event, true);
                    }
                    Ext.direct.Manager.removeTransaction(transaction);
                }
            }
        } else {
            transactions = [].concat(options.transaction);
            event = events[0] || new Ext.direct.ExceptionEvent({
                data: null,
                transaction: transaction,
                code: Ext.direct.Manager.exceptions.TRANSPORT,
                message: 'Unable to connect to the server.',
                xhr: response
            });
            for (i = 0 , len = transactions.length; i < len; ++i) {
                transaction = me.getTransaction(transactions[i]);
                if (transaction && transaction.retryCount < me.maxRetries) {
                    transaction.retry();
                } else {
                    me.fireEvent('data', me, event);
                    me.fireEvent('exception', me, event);
                    if (transaction && me.fireEvent('beforecallback', me, event, transaction) !== false) {
                        me.runCallback(transaction, event, false);
                    }
                    Ext.direct.Manager.removeTransaction(transaction);
                }
            }
        }
        me.callParent([
            options,
            success,
            response
        ]);
    },
    
    getTransaction: function(options) {
        return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
    },
    
    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
                ]);
            }
        }
    },
    inheritableStatics: {
        
        checkConfig: function(config) {
            
            
            return config && config.type === 'remoting' && config.url && Ext.isArray(config.actions);
        }
    }
});


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,
            data;
        if (!dom) {
            return me.detach();
        }
        
        me.dom = Ext.getDom(dom);
        
        
        if (!Ext.cache[dom.id]) {
            data = me.peekData();
            if (data) {
                data.isSynchronized = false;
            }
        }
        return me;
    },
    detach: function() {
        return (this.dom = null);
    },
    addListener: 
    function() {
        Ext.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.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 = {},
        detachedBodyEl;
    
    Fly.cache = flyweights;
    
    Ext.fly = function(dom, named) {
        var fly = null,
            fn = Ext.fly,
            nodeType, data;
        
        named = named || (fn.caller && (fn.caller.$name || 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.peekData();
                    if (data) {
                        data.isSynchronized = false;
                    }
                }
            }
        }
        return fly;
    };
    
    Ext.getDetachedBody = function() {
        if (!detachedBodyEl) {
            Ext.detachedBodyEl = detachedBodyEl = new Fly(document.createElement('div'));
            detachedBodyEl.isDetachedBody = true;
        }
        return detachedBodyEl;
    };
});


Ext.define('Ext.dom.CompositeElementLite', {
    alternateClassName: [
        'Ext.CompositeElementLite'
    ],
    
    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() {
        this.invoke('destroy', arguments);
        this.callParent();
    }
}, function(CompositeElementLite) {
    var prototype = CompositeElementLite.prototype;
    CompositeElementLite.importElementMethods();
    prototype.on = prototype.addListener;
});


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.define('Ext.dom.GarbageCollector', {
    singleton: true,
    
    interval: 30000,
    constructor: function() {
        var me = this;
        me.lastTime = Ext.now();
        me.onTick = me.onTick.bind(me);
        
        me.onTick.$skipTimerCheck = true;
        
        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.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) {
                isGarbage = false;
                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 = Ext.dom.Element.cache = t;
        }
        
        me.lastTime = Ext.now();
        
        return collectedIds;
    },
    
    onTick: function() {
        this.timerId = null;
        if (Ext.enableGarbageCollector) {
            this.collect();
        }
        this.resume();
    },
    
    pause: function() {
        var timerId = this.timerId;
        if (timerId) {
            this.timerId = null;
            Ext.undefer(timerId);
        }
    },
    
    resume: function() {
        var me = this,
            lastTime = me.lastTime;
        if (Ext.enableGarbageCollector && (Ext.now() - lastTime) > me.interval) {
            me.collect();
        }
        if (!me.timerId) {
            me.timerId = Ext.defer(me.onTick, me.interval);
        }
    }
});


Ext.define('Ext.dom.TouchAction', {
    singleton: true,
    lastTouchStartTime: 0,
    
    minMoveDistance: 8,
    spaceRe: /\s+/,
    preventSingle: null,
    preventMulti: null,
    disabledOverflowDom: null,
    panXCls: Ext.baseCSSPrefix + 'touch-action-pan-x',
    panYCls: Ext.baseCSSPrefix + 'touch-action-pan-y',
    cssValues: [
        'none',
        'pan-x',
        'pan-y',
        'pan-x pan-y',
        'pinch-zoom',
        'pan-x pinch-zoom',
        'pan-y pinch-zoom',
        'pan-x pan-y pinch-zoom',
        'double-tap-zoom',
        'pan-x double-tap-zoom',
        'pan-y double-tap-zoom',
        'pan-x pan-y double-tap-zoom',
        'pinch-zoom double-tap-zoom',
        'pan-x pinch-zoom double-tap-zoom',
        'pan-y pinch-zoom double-tap-zoom',
        ''
    ],
    objectValues: [
        {
            panX: false,
            panY: false,
            pinchZoom: false,
            doubleTapZoom: false
        },
        {
            panX: true,
            panY: false,
            pinchZoom: false,
            doubleTapZoom: false
        },
        {
            panX: false,
            panY: true,
            pinchZoom: false,
            doubleTapZoom: false
        },
        {
            panX: true,
            panY: true,
            pinchZoom: false,
            doubleTapZoom: false
        },
        {
            panX: false,
            panY: false,
            pinchZoom: true,
            doubleTapZoom: false
        },
        {
            panX: true,
            panY: false,
            pinchZoom: true,
            doubleTapZoom: false
        },
        {
            panX: false,
            panY: true,
            pinchZoom: true,
            doubleTapZoom: false
        },
        {
            panX: true,
            panY: true,
            pinchZoom: true,
            doubleTapZoom: false
        },
        {
            panX: false,
            panY: false,
            pinchZoom: false,
            doubleTapZoom: true
        },
        {
            panX: true,
            panY: false,
            pinchZoom: false,
            doubleTapZoom: true
        },
        {
            panX: false,
            panY: true,
            pinchZoom: false,
            doubleTapZoom: true
        },
        {
            panX: true,
            panY: true,
            pinchZoom: false,
            doubleTapZoom: true
        },
        {
            panX: false,
            panY: false,
            pinchZoom: true,
            doubleTapZoom: true
        },
        {
            panX: true,
            panY: false,
            pinchZoom: true,
            doubleTapZoom: true
        },
        {
            panX: false,
            panY: true,
            pinchZoom: true,
            doubleTapZoom: true
        },
        {
            panX: true,
            panY: true,
            pinchZoom: true,
            doubleTapZoom: true
        }
    ],
    attributeName: 'data-extTouchAction',
    constructor: function() {
        var me = this,
            supports = Ext.supports;
        if (supports.TouchAction) {
            me.cssProp = 'touch-action';
        } else if (supports.MSPointerEvents) {
            me.cssProp = '-ms-touch-action';
        }
        if (supports.TouchEvents) {
            Ext.getWin().on({
                touchstart: 'onTouchStart',
                touchmove: 'onTouchMove',
                touchend: 'onTouchEnd',
                scope: me,
                delegated: false,
                translate: false,
                capture: true,
                priority: 5000
            });
            Ext.on({
                scroll: 'onScroll',
                scope: me,
                destroyable: true
            });
        }
        
        if (Ext.isFunction(Object.freeze)) {
            var objectValues = me.objectValues;
            for (var i = 0,
                ln = objectValues.length; i < ln; i++) {
                Object.freeze(objectValues[i]);
            }
        }
    },
    
    
    containsTargets: function(dom, e) {
        var contains = true,
            event = e.browserEvent,
            touches = e.type === 'touchend' ? event.changedTouches : event.touches,
            i, ln;
        for (i = 0 , ln = touches.length; i < ln; i++) {
            if (!dom.contains(touches[i].target)) {
                contains = false;
                break;
            }
        }
        return contains;
    },
    
    disableOverflow: function(dom, vertical) {
        var me = this,
            overflowName = vertical ? 'overflow-y' : 'overflow-x',
            overflowStyle, cls;
        if (!me.disabledOverflowDom && !Ext.isiOS && !Ext.getScrollbarSize().width) {
            me.disabledOverflowDom = dom;
            cls = vertical ? me.panXCls : me.panYCls;
            while (dom) {
                overflowStyle = Ext.fly(dom).getStyle(overflowName);
                if (overflowStyle === 'auto' || overflowStyle === 'scroll') {
                    Ext.fly(dom).addCls(cls);
                }
                dom = dom.parentNode;
            }
        }
    },
    
    get: function(dom) {
        var flags = dom.getAttribute(this.attributeName),
            ret = null;
        if (flags != null) {
            ret = this.objectValues[flags];
        }
        return ret;
    },
    
    getFlags: function(touchAction) {
        var flags;
        if (typeof touchAction === 'number') {
            flags = touchAction;
        } else {
            flags = 0;
            if (touchAction.panX !== false) {
                flags |= 1;
            }
            if (touchAction.panY !== false) {
                flags |= 2;
            }
            if (touchAction.pinchZoom !== false) {
                flags |= 4;
            }
            if (touchAction.doubleTapZoom !== false) {
                flags |= 8;
            }
        }
        return flags;
    },
    isScrollable: function(el, vertical, forward) {
        var overflowStyle = Ext.fly(el).getStyle(vertical ? 'overflow-y' : 'overflow-x'),
            isScrollable = (overflowStyle === 'auto' || overflowStyle === 'scroll');
        if (isScrollable) {
            if (vertical) {
                isScrollable = forward ? (el.scrollTop + el.clientHeight) < el.scrollHeight : el.scrollTop > 0;
            } else {
                isScrollable = forward ? (el.scrollLeft + el.clientWidth) < el.scrollWidth : el.scrollLeft > 0;
            }
        }
        return isScrollable;
    },
    lookupFlags: function(dom) {
        return parseInt((dom.getAttribute && dom.getAttribute(this.attributeName)) || 15, 10);
    },
    onScroll: function() {
        
        this.scrollOccurred = true;
        
        
        this.isDoubleTap = false;
    },
    onTouchEnd: function(e) {
        var me = this,
            dom = e.target,
            touchCount, flags, doubleTapZoom;
        touchCount = e.browserEvent.touches.length;
        if (touchCount === 0) {
            if (me.isDoubleTap) {
                while (dom) {
                    flags = me.lookupFlags(dom);
                    if (flags != null) {
                        doubleTapZoom = flags & 8;
                        if (!doubleTapZoom) {
                            e.preventDefault();
                        }
                    }
                    dom = dom.parentNode;
                }
            }
            me.isDoubleTap = false;
            me.preventSingle = null;
            me.preventMulti = null;
            me.resetOverflow();
        }
    },
    onTouchMove: function(e) {
        var me = this,
            prevent = null,
            dom = e.target,
            flags, touchCount, panX, panY, point, startPoint, isVertical, scale, distance, deltaX, deltaY, preventSingle, preventMulti;
        preventSingle = me.preventSingle;
        preventMulti = me.preventMulti;
        touchCount = e.browserEvent.touches.length;
        
        
        
        
        
        if ((touchCount === 1 && (preventSingle === false)) || (preventMulti === false)) {
            return;
        }
        if ((touchCount > 1 && (preventMulti === true)) || (touchCount === 1 && (preventSingle === true))) {
            prevent = true;
        } else {
            if (touchCount === 1) {
                point = e.getPoint();
                startPoint = me.startPoint;
                scale = Ext.Element.getViewportScale();
                
                distance = point.getDistanceTo(me.startPoint) * scale;
                deltaX = point.x - startPoint.x;
                deltaY = point.y - startPoint.y;
                isVertical = Math.abs(deltaY) >= Math.abs(deltaX);
            }
            while (dom && (dom.nodeType === 1)) {
                flags = me.lookupFlags(dom);
                if (flags & 0) {
                    
                    prevent = true;
                } else if (touchCount === 1) {
                    panX = !!(flags & 1);
                    panY = !!(flags & 2);
                    if (panX && panY) {
                        prevent = false;
                    } else if (!panX && !panY) {
                        prevent = true;
                    } else if (distance >= me.minMoveDistance) {
                        prevent = !!((panX && isVertical) || (panY && !isVertical));
                    }
                    
                    
                    
                    if (!prevent && me.isScrollable(dom, isVertical, (isVertical ? deltaY : deltaX) < 0)) {
                        break;
                    }
                } else if (me.containsTargets(dom, e)) {
                    
                    prevent = !(flags & 4);
                } else {
                    
                    prevent = false;
                }
                if (prevent) {
                    break;
                }
                dom = dom.parentNode;
            }
        }
        
        
        
        
        
        
        
        if (touchCount === 1) {
            me.preventSingle = prevent;
        } else if (touchCount > 1) {
            me.preventMulti = prevent;
        }
        if (prevent) {
            e.preventDefault();
        }
    },
    onTouchStart: function(e) {
        var me = this,
            time, flags, dom, panX, panY;
        if (e.browserEvent.touches.length === 1) {
            time = e.time;
            
            
            
            
            
            if (!me.scrollOccurred && ((time - me.lastTouchStartTime) <= 500)) {
                me.isDoubleTap = true;
            }
            me.lastTouchStartTime = time;
            me.scrollOccurred = false;
            me.startPoint = e.getPoint();
            dom = e.target;
            while (dom) {
                flags = me.lookupFlags(dom);
                if (flags != null) {
                    panX = !!(flags & 1);
                    panY = !!(flags & 2);
                    if (panX !== panY) {
                        me.disableOverflow(dom, panX);
                        break;
                    }
                }
                dom = dom.parentNode;
            }
        } else {
            
            me.isDoubleTap = false;
        }
    },
    
    resetOverflow: function() {
        var me = this,
            dom = me.disabledOverflowDom;
        while (dom) {
            Ext.fly(dom).removeCls([
                me.panXCls,
                me.panYCls
            ]);
            dom = dom.parentNode;
        }
        me.disabledOverflowDom = null;
    },
    
    set: function(dom, value) {
        var me = this,
            cssProp = me.cssProp,
            flags = me.getFlags(value),
            
            
            supportedFlags = (flags & Ext.supports.TouchAction),
            attributeName = me.attributeName;
        if (cssProp) {
            Ext.fly(dom).setStyle(cssProp, me.cssValues[supportedFlags]);
        }
        if (flags === 15) {
            dom.removeAttribute(attributeName);
        } else {
            dom.setAttribute(attributeName, flags);
        }
    }
});


Ext.define('Ext.drag.Constraint', {
    alias: 'drag.constraint.base',
    mixins: [
        Ext.mixin.Factoryable
    ],
    factoryConfig: {
        defaultType: 'base',
        type: 'drag.constraint'
    },
    config: {
        
        element: null,
        
        horizontal: null,
        
        region: null,
        
        snap: null,
        
        source: null,
        
        vertical: null,
        
        x: null,
        
        y: null
    },
    constructor: function(config) {
        this.initConfig(config);
    },
    applyElement: function(element) {
        if (element && typeof element !== 'boolean') {
            element = Ext.get(element);
        }
        return element || null;
    },
    applySnap: function(snap) {
        if (typeof snap === 'number') {
            snap = {
                x: snap,
                y: snap
            };
        }
        return snap;
    },
    
    constrain: function(xy, info) {
        var me = this,
            x = xy[0],
            y = xy[1],
            constrainInfo = me.constrainInfo,
            initial = constrainInfo.initial,
            constrainX = constrainInfo.x,
            constrainY = constrainInfo.y,
            snap = constrainInfo.snap,
            min, max;
        if (!constrainInfo.vertical) {
            if (snap && snap.x) {
                if (snap.xFn) {
                    x = snap.x.call(me, info, x);
                } else {
                    x = me.doSnap(x, initial.x, snap.x);
                }
            }
            if (constrainX) {
                min = constrainX[0];
                max = constrainX[1];
                if (min !== null && x < min) {
                    x = min;
                }
                if (max !== null && x > max) {
                    x = max;
                }
            }
        } else {
            x = initial.x;
        }
        if (!constrainInfo.horizontal) {
            if (snap && snap.y) {
                if (snap.yFn) {
                    y = snap.y.call(me, info, y);
                } else {
                    y = me.doSnap(y, initial.y, snap.y);
                }
            }
            if (constrainY) {
                min = constrainY[0];
                max = constrainY[1];
                if (min !== null && y < min) {
                    y = min;
                }
                if (max !== null && y > max) {
                    y = max;
                }
            }
        } else {
            y = initial.y;
        }
        return [
            x,
            y
        ];
    },
    destroy: function() {
        this.setSource(null);
        this.setElement(null);
        this.callParent();
    },
    privates: {
        
        constrainValue: function(a, b, resolver) {
            var val = null,
                aNull = a === null,
                bNull = b === null;
            if (!(aNull && bNull)) {
                if (aNull) {
                    val = b;
                } else if (bNull) {
                    val = a;
                } else {
                    val = resolver(a, b);
                }
            }
            return val;
        },
        
        doSnap: function(position, initial, snap) {
            if (!snap) {
                return position;
            }
            var ratio = (position - initial) / snap,
                floor = Math.floor(ratio);
            
            
            if (ratio - floor <= 0.5) {
                ratio = floor;
            } else {
                ratio = floor + 1;
            }
            return initial + (snap * ratio);
        },
        
        onDragStart: function(info) {
            var me = this,
                snap = me.getSnap(),
                vertical = !!me.getVertical(),
                horizontal = !!me.getHorizontal(),
                element = me.getElement(),
                region = me.getRegion(),
                proxy = info.proxy,
                proxyEl = proxy.element,
                x = me.getX(),
                y = me.getY(),
                minX = null,
                maxX = null,
                minY = null,
                maxY = null,
                rminX = null,
                rmaxX = null,
                rminY = null,
                rmaxY = null,
                pos, size;
            if (element) {
                if (typeof element === 'boolean') {
                    element = me.getSource().getElement().parent();
                }
                if (info.local) {
                    pos = element.getStyle('position');
                    if (pos === 'relative' || pos === 'absolute') {
                        size = element.getSize();
                        region = new Ext.util.Region(0, size.width, size.height, 0);
                    } else {
                        region = element.getRegion(true, true);
                    }
                } else {
                    region = element.getRegion(true);
                }
            }
            if (region) {
                if (!vertical) {
                    rminX = region.left;
                    rmaxX = region.right - (proxyEl ? proxy.width : 0);
                }
                if (!horizontal) {
                    rminY = region.top;
                    rmaxY = region.bottom - (proxyEl ? proxy.height : 0);
                }
            }
            
            
            
            
            
            
            
            if (!vertical && (region || x)) {
                if (x) {
                    minX = x[0];
                    maxX = x[1];
                }
                if (minX !== null || maxX !== null || rminX !== null || rmaxX !== null) {
                    minX = me.constrainValue(minX, rminX, Math.max);
                    maxX = me.constrainValue(maxX, rmaxX, Math.min);
                    x = [
                        minX,
                        maxX
                    ];
                }
            }
            if (!horizontal && (region || y)) {
                if (y) {
                    minY = y[0];
                    maxY = y[1];
                }
                if (minY !== null || maxY !== null || rminY !== null || rmaxY !== null) {
                    minY = me.constrainValue(minY, rminY, Math.max);
                    maxY = me.constrainValue(maxY, rmaxY, Math.min);
                    y = [
                        minY,
                        maxY
                    ];
                }
            }
            if (snap) {
                snap = {
                    x: snap.x,
                    xFn: typeof snap.x === 'function',
                    y: snap.y,
                    yFn: typeof snap.y === 'function'
                };
            }
            me.constrainInfo = {
                initial: info.element.initial,
                vertical: vertical,
                horizontal: horizontal,
                x: x,
                y: y,
                snap: snap
            };
        }
    }
});


Ext.define('Ext.drag.Info', {
    constructor: function(source, e) {
        
        if (!source) {
            return;
        }
        var me = this,
            local = source.getLocal(),
            el, proxyEl, proxy, x, xy, y, pageXY, elPageXY;
        me.source = source;
        me.local = local;
        xy = me.getEventXY(e);
        pageXY = e.getXY();
        el = source.getElement();
        elPageXY = el.getXY();
        xy = local ? el.getLocalXY() : elPageXY;
        x = xy[0];
        y = xy[1];
        me.initialEvent = e;
        me.eventTarget = e.target;
        me.cursor = {
            current: {
                x: x,
                y: y
            },
            delta: {
                x: 0,
                y: 0
            },
            initial: {
                x: pageXY[0],
                y: pageXY[1]
            },
            offset: {
                x: pageXY[0] - elPageXY[0],
                y: pageXY[1] - elPageXY[1]
            }
        };
        me.element = {
            current: {
                x: x,
                y: y
            },
            delta: {
                x: 0,
                y: 0
            },
            initial: {
                x: x,
                y: y
            }
        };
        me.proxy = {
            instance: source.getProxy(),
            current: {
                x: x,
                y: y
            },
            delta: {
                x: 0,
                y: 0
            },
            initial: {
                x: x,
                y: y
            },
            element: el,
            isUnderCursor: false,
            isElement: true
        };
        me.types = [];
        me.data = {};
        source.describe(me);
        proxy = me.proxy;
        proxyEl = proxy.instance.setupElement(me);
        proxy.isElement = proxyEl === source.getElement();
        proxy.element = proxyEl;
        if (proxyEl) {
            proxy.width = proxyEl.getWidth();
            proxy.height = proxyEl.getHeight();
        }
        if (proxy.isElement) {
            
            el = me.element;
            el.current = proxy.current;
            el.delta = proxy.delta;
        }
        me.needsCursorCheck = proxy.element && source.manager && source.manager.pointerBug;
    },
    
    cursor: null,
    
    element: null,
    
    eventTarget: null,
    
    files: null,
    
    isNative: false,
    
    proxy: null,
    
    source: null,
    
    target: null,
    
    types: null,
    
    valid: false,
    
    clearData: function(type) {
        Ext.Array.remove(this.types, type);
        delete this.data[type];
    },
    
    clone: function() {
        var me = this,
            ret = new Ext.drag.Info();
        ret.cursor = Ext.merge({}, me.cursor);
        ret.data = Ext.apply({}, me.data);
        ret.element = Ext.merge({}, me.element);
        ret.eventTarget = me.eventTarget;
        ret.proxy = Ext.merge({}, me.proxy);
        ret.source = me.source;
        ret.target = me.target;
        ret.types = Ext.Array.clone(me.types);
        ret.valid = me.valid;
        return ret;
    },
    
    getData: function(type) {
        var me = this,
            data = me.data,
            dt = me.dataTransfer,
            ret;
        if (dt) {
            ret = dt.getData(type);
        } else {
            
            if (!me.finalized) {
                Ext.raise('Unable to call getData until the drop is complete');
            }
            
            ret = data[type];
            if (typeof ret === 'function') {
                data[type] = ret = ret.call(me.source, me);
            }
            if (!ret && ret !== 0) {
                ret = '';
            }
        }
        return Ext.Promise.resolve(ret);
    },
    
    setData: function(type, value) {
        Ext.Array.include(this.types, type);
        this.data[type] = value;
    },
    destroy: function() {
        var me = this;
        me.eventTarget = me.data = me.proxy = me.targetMap = me.targetMap = me.types = me.elementMap = me.possibleTargets = me.target = null;
        me.callParent();
    },
    privates: {
        
        data: null,
        
        dataTransfer: null,
        
        elementMap: null,
        
        possibleTargets: null,
        
        targetMap: null,
        copyNativeData: function(target, e) {
            var dt = e.browserEvent.dataTransfer;
            this.target = target;
            this.dataTransfer = dt;
            this.files = dt.files;
        },
        
        finalize: function() {
            var me = this,
                target = me.target;
            me.finalized = true;
            if (target) {
                target.info = null;
                target.handleDrop(me);
            }
        },
        
        getAlignXY: function(x, y) {
            var me = this,
                source = me.source,
                cursorOffset = me.cursor.offset,
                proxy = source.getProxy(),
                proxyEl = me.proxy.element,
                constrain = source.getConstrain(),
                xy = [
                    x,
                    y
                ];
            if (proxyEl) {
                if (me.proxy.isElement) {
                    xy[0] -= cursorOffset.x;
                    xy[1] -= cursorOffset.y;
                } else {
                    xy = proxy.adjustCursorOffset(me, xy);
                }
                if (constrain) {
                    xy = constrain.constrain(xy, me);
                }
            }
            return xy;
        },
        getEventXY: function(e) {
            var xy = e.getXY(),
                
                source = this.source;
            if (this.local) {
                xy = source.convertToLocalXY(xy);
            }
            return xy;
        },
        onNativeDragEnter: function(target, e) {
            var me = this;
            me.valid = target.accepts(me);
            target.info = me;
            me.copyNativeData(target, e);
        },
        onNativeDragLeave: function(target, e) {
            var me = this;
            
            
            
            if (me.target === target) {
                target.info = null;
                me.valid = false;
                me.target = me.dataTransfer = me.files = null;
            }
        },
        onNativeDragMove: function(target, e) {
            this.copyNativeData(target, e);
        },
        onNativeDrop: function(target, e) {
            this.copyNativeData(target, e);
            target.info = null;
        },
        
        setActive: function(target) {
            var me = this,
                source = me.source,
                current = me.target,
                changed = current !== target;
            if (current && changed) {
                current.handleDragLeave(me);
                current.info = null;
            }
            me.target = target;
            if (target) {
                if (changed) {
                    me.valid = !!me.possibleTargets[target.getId()] && target.accepts(me) !== false;
                    target.handleDragEnter(me);
                    target.info = me;
                }
                target.handleDragMove(me);
            } else {
                me.valid = false;
            }
            if (changed) {
                source.getProxy().update(me);
            }
        },
        
        update: function(event, beforeStart) {
            var me = this,
                xy = me.getEventXY(event),
                x = xy[0],
                y = xy[1],
                alignXY = me.getAlignXY(x, y),
                alignX = alignXY[0],
                alignY = alignXY[1],
                proxyData = me.proxy,
                cursor = me.cursor,
                current = cursor.current,
                delta = cursor.delta,
                initial = cursor.initial,
                proxy = proxyData.instance;
            current.x = x;
            current.y = y;
            delta.x = x - initial.x;
            delta.y = y - initial.y;
            current = proxyData.current;
            delta = proxyData.delta;
            initial = proxyData.initial;
            current.x = alignX;
            current.y = alignY;
            delta.x = alignX - initial.x;
            delta.y = alignY - initial.y;
            if (me.needsCursorCheck) {
                proxyData.isUnderCursor = !(x < alignX || y < alignY || x > proxyData.width + alignX || y > proxyData.height + alignY);
            }
            if (!beforeStart && proxy) {
                proxy.setXY(me, alignXY);
            }
        }
    }
});


Ext.define('Ext.drag.Item', {
    mixins: [
        Ext.mixin.Observable,
        Ext.mixin.Identifiable
    ],
    config: {
        
        autoDestroy: true,
        
        component: null,
        
        element: null,
        
        groups: null
    },
    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);
    },
    
    isDisabled: function() {
        return this.disabled;
    },
    
    disable: function() {
        this.disabled = true;
    },
    
    enable: function() {
        this.disabled = false;
    },
    updateComponent: function(comp, was) {
        var el;
        if (comp) {
            el = comp.el;
        } else if (was && was.el === this.getElement()) {
            el = null;
        } else {
            return;
        }
        this.setElement(el);
    },
    applyElement: function(element) {
        return element ? Ext.get(element) : null;
    },
    updateElement: function() {
        this.setupListeners();
    },
    applyGroups: function(group) {
        if (typeof group === 'string') {
            group = [
                group
            ];
        }
        return group;
    },
    destroy: function() {
        var me = this,
            el = me.getElement();
        me.destroying = true;
        me.setElement(null);
        if (el && me.getAutoDestroy()) {
            el.destroy();
        }
        me.callParent();
        me.destroying = false;
    },
    privates: {
        
        disabled: false,
        convertToLocalXY: function(xy) {
            var c = this.getComponent();
            if (c) {
                xy = c.convertToLocalXY(xy);
            } else {
                xy = this.getElement().translateXY(xy[0], xy[1]);
                xy = [
                    xy.x,
                    xy.y
                ];
            }
            return xy;
        },
        
        getElListeners: Ext.privateFn,
        
        setupListeners: function(element) {
            var me = this,
                elListeners = me.elListeners;
            element = element || me.getElement();
            if (elListeners) {
                elListeners.destroy();
                me.elListeners = null;
            }
            if (element) {
                me.elListeners = element.on(Ext.apply({
                    scope: me,
                    destroyable: true
                }, me.getElListeners()));
            }
        }
    }
});


Ext.define('Ext.drag.Manager', {
    singleton: true,
    
    dragCls: Ext.baseCSSPrefix + 'drag-body',
    
    
    
    
    
    
    pointerBug: Ext.isTouch || (!Ext.supports.CSSPointerEvents || Ext.isIE10m || Ext.isOpera),
    constructor: function() {
        this.targets = {};
        this.nativeTargets = [];
        Ext.onReady(this.init, this);
    },
    init: function() {
        
        
        
        
        
        
        
        
        
        Ext.getDoc().on({
            scope: this,
            dragenter: {
                capture: true,
                fn: 'onNativeDragEnter'
            },
            dragleave: 'onNativeDragLeave',
            dragover: 'onNativeDragOver',
            drop: 'onNativeDrop'
        });
    },
    destroy: function() {
        var me = this,
            targets = me.targets,
            key;
        me.destroying = true;
        for (key in targets) {
            targets[key].destroy();
        }
        me.targets = null;
        me.callParent();
        me.destroying = false;
    },
    privates: {
        
        elementFromPoint: function(x, y) {
            if (Ext.rootInheritedState.rtl) {
                x = Ext.Element.getViewportWidth() - x;
            }
            return Ext.dom.Element.fromPagePoint(x, y, true);
        },
        
        getAtPoint: function(info) {
            var current = info.cursor.current,
                elementMap = info.elementMap,
                isUnderCursor = info.proxy.isUnderCursor,
                proxyEl = this.pointerBug && isUnderCursor ? info.proxy.element.dom : null,
                target, el;
            if (proxyEl) {
                proxyEl.style.visibility = 'hidden';
            }
            el = this.elementFromPoint(current.x, current.y);
            if (proxyEl) {
                proxyEl.style.visibility = 'visible';
            }
            while (el) {
                target = elementMap[el.id];
                if (target) {
                    return target;
                }
                el = el.parentNode;
            }
            return null;
        },
        
        getNativeDragInfo: function(e) {
            var info = this.nativeDragInfo;
            if (!info) {
                this.nativeDragInfo = info = new Ext.drag.Info();
                info.isNative = true;
            }
            return info;
        },
        
        onDragCancel: function() {
            Ext.getBody().removeCls(this.dragCls);
        },
        
        onDragEnd: function(info, e) {
            info.finalize();
            Ext.getBody().removeCls(this.dragCls);
        },
        
        onDragMove: function(info, e) {
            this.processDrag(info);
        },
        
        onDragStart: function(info, e) {
            var me = this,
                source = info.source,
                targets = me.targets,
                groups = source.getGroups(),
                targetMap = {},
                possibleTargets = {},
                elementMap = {},
                id, target, targetGroups, groupMap, groupOk, len, i;
            elementMap = {};
            possibleTargets = {};
            if (groups) {
                groupMap = Ext.Array.toMap(groups);
            }
            
            
            
            for (id in targets) {
                target = targets[id];
                if (!target.isDisabled()) {
                    groupOk = false;
                    targetGroups = target.getGroups();
                    
                    
                    
                    
                    if (!groupMap && !targetGroups) {
                        groupOk = true;
                    } else if (groupMap && targetGroups) {
                        for (i = 0 , len = targetGroups.length; i < len; ++i) {
                            if (groupMap[targetGroups[i]]) {
                                groupOk = true;
                                break;
                            }
                        }
                    }
                    if (groupOk) {
                        possibleTargets[id] = target;
                    }
                }
                targetMap[id] = target;
                elementMap[target.getElement().id] = target;
            }
            info.possibleTargets = possibleTargets;
            info.targetMap = targetMap;
            info.elementMap = elementMap;
            Ext.getBody().addCls(me.dragCls);
            me.processDrag(info);
        },
        
        onNativeDragEnter: function(e) {
            var nativeTargets = this.nativeTargets,
                target = e.target;
            
            e.preventDefault();
            if (nativeTargets[nativeTargets.length - 1] !== target) {
                nativeTargets.push(target);
            }
        },
        
        onNativeDragLeave: function(e) {
            var nativeTargets = this.nativeTargets;
            Ext.Array.remove(nativeTargets, e.target);
            if (nativeTargets.length === 0) {
                this.nativeDragInfo = null;
            }
        },
        
        onNativeDragOver: function(e) {
            
            e.preventDefault();
        },
        
        onNativeDrop: function(e) {
            
            e.preventDefault();
            this.nativeTargets.length = 0;
            this.nativeDragInfo = null;
        },
        
        processDrag: function(info) {
            info.setActive(this.getAtPoint(info));
        },
        
        register: function(target) {
            this.targets[target.getId()] = target;
        },
        
        unregister: function(target) {
            if (this.destroying) {
                return;
            }
            delete this.targets[target.getId()];
        }
    }
});


Ext.define('Ext.drag.Source', {
    extend: Ext.drag.Item,
    defaultIdPrefix: 'source-',
    config: {
        
        activateOnLongPress: false,
        
        activeCls: null,
        
        constrain: null,
        
        handle: null,
        local: null,
        
        
        proxy: 'original',
        
        revert: false
    },
    
    
    
    
    
    
    
    dragging: false,
    constructor: function(config) {
        var describe = config && config.describe;
        if (describe) {
            this.describe = describe;
            
            
            
            config = Ext.apply({}, config);
            delete config.describe;
        }
        this.callParent([
            config
        ]);
        
        
        
        this.manager = Ext.drag['Manager'];
    },
    
    describe: Ext.emptyFn,
    
    isDragging: function() {
        return this.dragging;
    },
    
    beforeDragStart: Ext.emptyFn,
    
    onDragCancel: Ext.emptyFn,
    
    onDragEnd: Ext.emptyFn,
    
    onDragMove: Ext.emptyFn,
    
    onDragStart: Ext.emptyFn,
    applyActivateOnLongPress: function(activateOnLongPress) {
        if (typeof activateOnLongPress === 'string') {
            activateOnLongPress = [
                activateOnLongPress
            ];
        }
        return activateOnLongPress;
    },
    updateActivateOnLongPress: function(activateOnLongPress) {
        if (!this.isConfiguring) {
            this.setupListeners();
        }
    },
    updateActiveCls: function(cls, oldCls) {
        if (this.dragging) {
            var el = this.getElement();
            el.replaceCls(oldCls, cls);
        }
    },
    applyConstrain: function(constrain) {
        if (constrain && !constrain.$isClass) {
            if (constrain.isRegion) {
                constrain = {
                    region: constrain
                };
            } else if (constrain.isElement || !Ext.isObject(constrain)) {
                constrain = {
                    element: constrain
                };
            }
            constrain = Ext.apply({
                source: this
            }, constrain);
            constrain = Ext.Factory.dragConstraint(constrain);
        }
        return constrain;
    },
    updateElement: function(element, oldElement) {
        
        
        if (oldElement && !oldElement.destroyed) {
            oldElement.un('dragstart', 'stopNativeDrag', this);
        }
        if (element && !this.getHandle()) {
            element.setTouchAction({
                panX: false,
                panY: false
            });
            
            
            
            element.on('dragstart', 'stopNativeDrag', this, {
                translate: false,
                delegated: false
            });
        }
        this.callParent([
            element,
            oldElement
        ]);
    },
    updateHandle: function() {
        if (!this.isConfiguring) {
            this.setupListeners();
        }
    },
    applyProxy: function(proxy) {
        if (proxy) {
            proxy = Ext.Factory.dragproxy(proxy);
        }
        return proxy;
    },
    updateProxy: function(proxy, oldProxy) {
        if (oldProxy) {
            oldProxy.destroy();
        }
        if (proxy) {
            proxy.setSource(this);
        }
    },
    resolveListenerScope: function() {
        var ownerCmp = this.ownerCmp,
            a = arguments;
        if (ownerCmp) {
            return ownerCmp.resolveListenerScope.apply(ownerCmp, a);
        }
        return this.callParent(a);
    },
    destroy: function() {
        var me = this;
        me.manager = me.initialEvent = null;
        me.setConstrain(null);
        me.setProxy(null);
        me.callParent();
    },
    privates: {
        
        draggingCls: Ext.baseCSSPrefix + 'drag-dragging',
        
        info: null,
        
        revertCls: Ext.baseCSSPrefix + 'drag-revert',
        canActivateOnLongPress: function(e) {
            var activate = this.getActivateOnLongPress();
            return !!(activate && (activate === true || Ext.Array.contains(activate, e.pointerType)));
        },
        
        dragCleanup: function(info) {
            var me = this,
                cls = me.getActiveCls(),
                proxy = me.getProxy(),
                el = me.getElement(),
                proxyEl = info ? info.proxy.element : null;
            if (cls) {
                el.removeCls(cls);
            }
            if (proxyEl) {
                proxyEl.removeCls(me.draggingCls);
            }
            proxy.cleanup(info);
            me.dragging = false;
            me.initialEvent = me.info = null;
        },
        
        getElListeners: function() {
            var o = {
                    touchstart: 'handleTouchStart',
                    dragstart: 'handleDragStart',
                    drag: 'handleDragMove',
                    dragend: 'handleDragEnd',
                    dragcancel: 'handleDragCancel'
                },
                handle = this.getHandle();
            if (handle) {
                o.dragstart = {
                    fn: o.dragstart,
                    delegate: handle
                };
            }
            if (this.getActivateOnLongPress()) {
                o.longpress = 'handleLongPress';
            }
            return o;
        },
        
        handleDragCancel: function(e) {
            var me = this,
                info = me.info,
                manager = me.manager;
            if (manager) {
                manager.onDragCancel(info, e);
            }
            me.onDragCancel(info);
            if (me.hasListeners.dragcancel) {
                me.fireEvent('dragcancel', me, info, e);
            }
            Ext.fireEvent('dragcancel', me, info, e);
            me.dragCleanup(info);
        },
        
        handleDragEnd: function(e) {
            if (!this.dragging) {
                return;
            }
            var me = this,
                manager = me.manager,
                revert = me.getRevert(),
                info = me.info,
                proxy = info.proxy;
            info.update(e);
            if (manager) {
                manager.onDragEnd(info, e);
            }
            me.onDragEnd(info);
            if (me.hasListeners.dragend) {
                me.fireEvent('dragend', me, info, e);
            }
            Ext.fireEvent('dragend', me, info, e);
            proxy = proxy.instance;
            if (revert && proxy) {
                proxy.dragRevert(info, me.revertCls, revert, function() {
                    me.dragCleanup(info);
                });
            } else {
                me.dragCleanup(info);
            }
        },
        
        handleDragMove: function(e) {
            var me = this,
                info = me.info,
                manager = me.manager;
            if (!me.dragging) {
                return;
            }
            e.stopPropagation();
            e.claimGesture();
            info.update(e);
            if (manager) {
                manager.onDragMove(info, e);
            }
            me.onDragMove(info);
            if (me.hasListeners.dragmove) {
                me.fireEvent('dragmove', me, info, e);
            }
        },
        
        handleDragStart: function(e) {
            var me = this,
                hasListeners = me.hasListeners,
                manager = me.manager,
                constrain = me.getConstrain(),
                initialEvent = me.initialEvent,
                el, cls, info, cancel, proxyEl;
            if (me.preventStart(e)) {
                return false;
            }
            if (hasListeners.initdragconstraints) {
                
                
                me.fireEvent('initdragconstraints', me, e);
            }
            me.info = info = new Ext.drag.Info(me, initialEvent);
            me.setup(info);
            if (constrain) {
                constrain.onDragStart(info);
            }
            info.update(e, true);
            cancel = me.beforeDragStart(info) === false;
            if (!cancel && hasListeners.beforedragstart) {
                cancel = me.fireEvent('beforedragstart', me, info, e) === false;
            }
            if (cancel) {
                me.dragCleanup();
                return false;
            }
            e.claimGesture();
            me.dragging = true;
            cls = me.getActiveCls();
            el = me.getElement();
            if (cls) {
                el.addCls(cls);
            }
            proxyEl = info.proxy.element;
            if (proxyEl) {
                proxyEl.addCls(me.draggingCls);
            }
            info.update(e);
            if (manager) {
                manager.onDragStart(info, e);
            }
            me.onDragStart(info);
            if (hasListeners.dragstart) {
                me.fireEvent('dragstart', me, info, e);
            }
            Ext.fireEvent('dragstart', me, info, e);
        },
        
        handleLongPress: function(e) {
            if (!this.isDisabled() && this.canActivateOnLongPress(e)) {
                this.initialEvent = e;
                e.startDrag();
            }
        },
        
        handleTouchStart: function(e) {
            if (!this.isDisabled()) {
                this.initialEvent = e;
            }
        },
        preventStart: function(e) {
            return this.isDisabled() || (!e.longpress && this.canActivateOnLongPress(e));
        },
        
        setup: Ext.privateFn,
        stopNativeDrag: function(e) {
            e.preventDefault();
        }
    }
});


Ext.define('Ext.drag.Target', {
    extend: Ext.drag.Item,
    defaultIdPrefix: 'target-',
    config: {
        
        invalidCls: '',
        
        validCls: ''
    },
    
    
    
    
    
    
    constructor: function(config) {
        var me = this,
            accepts = config && config.accepts;
        if (accepts) {
            me.accepts = accepts;
            
            
            
            config = Ext.apply({}, config);
            delete config.accepts;
        }
        me.callParent([
            config
        ]);
        Ext.drag.Manager.register(me);
    },
    
    accepts: function(info) {
        return true;
    },
    
    disable: function() {
        this.callParent();
        this.setupListeners(null);
    },
    
    enable: function() {
        this.callParent();
        this.setupListeners();
    },
    
    beforeDrop: Ext.emptyFn,
    
    onDrop: Ext.emptyFn,
    
    onDragEnter: Ext.emptyFn,
    
    onDragLeave: Ext.emptyFn,
    
    onDragMove: Ext.emptyFn,
    updateInvalidCls: function(invalidCls, oldInvalidCls) {
        var info = this.info;
        this.doUpdateCls(info && !info.valid, invalidCls, oldInvalidCls);
    },
    updateValidCls: function(validCls, oldValidCls) {
        var info = this.info;
        this.doUpdateCls(info && info.valid, validCls, oldValidCls);
    },
    destroy: function() {
        Ext.drag.Manager.unregister(this);
        this.callParent();
    },
    privates: {
        
        doUpdateCls: function(needsAdd, cls, oldCls) {
            var el = this.getElement();
            if (oldCls) {
                el.removeCls(oldCls);
            }
            if (cls && needsAdd) {
                el.addCls(cls);
            }
        },
        
        getElListeners: function() {
            return {
                dragenter: 'handleNativeDragEnter',
                dragleave: 'handleNativeDragLeave',
                dragover: 'handleNativeDragMove',
                drop: 'handleNativeDrop'
            };
        },
        
        handleDrop: function(info) {
            var me = this,
                hasListeners = me.hasListeners,
                valid = info.valid;
            me.getElement().removeCls([
                me.getInvalidCls(),
                me.getValidCls()
            ]);
            if (valid && me.beforeDrop(info) !== false) {
                if (hasListeners.beforedrop && me.fireEvent('beforedrop', me, info) === false) {
                    return false;
                }
                me.onDrop(info);
                if (hasListeners.drop) {
                    me.fireEvent('drop', me, info);
                }
            } else {
                return false;
            }
        },
        
        handleDragEnter: function(info) {
            var me = this,
                cls = info.valid ? me.getValidCls() : me.getInvalidCls();
            if (cls) {
                me.getElement().addCls(cls);
            }
            me.onDragEnter(info);
            if (me.hasListeners.dragenter) {
                me.fireEvent('dragenter', me, info);
            }
        },
        
        handleDragLeave: function(info) {
            var me = this;
            me.getElement().removeCls([
                me.getInvalidCls(),
                me.getValidCls()
            ]);
            me.onDragLeave(info);
            if (me.hasListeners.dragleave) {
                me.fireEvent('dragleave', me, info);
            }
        },
        
        handleDragMove: function(info) {
            var me = this;
            me.onDragMove(info);
            if (me.hasListeners.dragmove) {
                me.fireEvent('dragmove', me, info);
            }
        },
        
        handleNativeDragEnter: function(e) {
            var me = this,
                info = Ext.drag.Manager.getNativeDragInfo(e);
            info.onNativeDragEnter(me, e);
            if (me.hasListeners.dragenter) {
                me.fireEvent('dragenter', me, info);
            }
        },
        
        handleNativeDragLeave: function(e) {
            var me = this,
                info = Ext.drag.Manager.getNativeDragInfo(e);
            info.onNativeDragLeave(me, e);
            if (me.hasListeners.dragleave) {
                me.fireEvent('dragleave', me, info);
            }
        },
        
        handleNativeDragMove: function(e) {
            var me = this,
                info = Ext.drag.Manager.getNativeDragInfo(e);
            info.onNativeDragMove(me, e);
            if (me.hasListeners.dragmove) {
                me.fireEvent('dragmove', me, info);
            }
        },
        
        handleNativeDrop: function(e) {
            var me = this,
                hasListeners = me.hasListeners,
                info = Ext.drag.Manager.getNativeDragInfo(e),
                valid = info.valid;
            info.onNativeDrop(me, e);
            if (valid) {
                if (hasListeners.beforedrop && me.fireEvent('beforedrop', me, info) === false) {
                    return;
                }
                if (hasListeners.drop) {
                    me.fireEvent('drop', me, info);
                }
            }
        }
    }
});


Ext.define('Ext.drag.proxy.None', {
    mixins: [
        Ext.mixin.Factoryable
    ],
    alias: 'drag.proxy.none',
    factoryConfig: {
        aliasPrefix: 'drag.proxy.',
        type: 'dragproxy'
    },
    config: {
        source: null
    },
    constructor: function(config) {
        var getElement = config && config.getElement;
        if (getElement) {
            
            
            
            this.getElement = getElement;
            config = Ext.apply({}, config);
            delete config.getElement;
        }
        this.initConfig(config);
    },
    
    cleanup: Ext.emptyFn,
    dragRevert: function(info, revertCls, options, callback) {
        var positionable = this.getPositionable(info),
            initial = info.proxy.initial;
        positionable.addCls(revertCls);
        positionable.setXY([
            initial.x,
            initial.y
        ], Ext.apply({
            callback: function() {
                positionable.removeCls(revertCls);
                callback();
            }
        }, options));
    },
    
    getElement: function() {
        return null;
    },
    getPositionable: function() {
        return this.element;
    },
    setXY: function(info, xy, animation) {
        var positionable = this.getPositionable(info);
        if (positionable) {
            positionable.setXY(xy, animation);
        }
    },
    
    update: Ext.emptyFn,
    privates: {
        setupElement: function(info) {
            return (this.element = this.getElement(info));
        },
        
        adjustCursorOffset: function(info, pos) {
            return pos;
        }
    }
});


Ext.define('Ext.drag.proxy.Original', {
    extend: Ext.drag.proxy.None,
    alias: 'drag.proxy.original',
    getElement: function(info) {
        return info.source.getElement();
    },
    getPositionable: function(info) {
        var source = info.source;
        return source.getComponent() || source.getElement();
    }
});


Ext.define('Ext.drag.proxy.Placeholder', {
    extend: Ext.drag.proxy.None,
    alias: 'drag.proxy.placeholder',
    config: {
        
        cls: '',
        
        cursorOffset: [
            12,
            20
        ],
        
        html: null,
        
        invalidCls: '',
        
        validCls: ''
    },
    placeholderCls: Ext.baseCSSPrefix + 'drag-proxy-placeholder',
    
    cleanup: function() {
        this.element = Ext.destroy(this.element);
    },
    
    getElement: function() {
        var el = Ext.getBody().createChild({
                cls: this.getCls(),
                html: this.getHtml()
            });
        el.addCls(this.placeholderCls);
        el.setTouchAction({
            panX: false,
            panY: false
        });
        return el;
    },
    
    update: function(info) {
        var el = this.element,
            invalidCls = this.getInvalidCls(),
            validCls = this.getValidCls(),
            valid = info.valid;
        if (info.target) {
            
            
            el.replaceCls(valid ? invalidCls : validCls, valid ? validCls : invalidCls);
        } else {
            el.removeCls([
                invalidCls,
                validCls
            ]);
        }
    },
    updateCls: function(cls, oldCls) {
        var el = this.element;
        if (el) {
            if (oldCls) {
                el.removeCls(oldCls);
            }
            if (cls) {
                el.addCls(cls);
            }
        }
    },
    updateHtml: function(html) {
        var el = this.element;
        if (el) {
            el.setHtml(html || '');
        }
    },
    updateInvalidCls: function(invalidCls, oldInvalidCls) {
        this.doUpdateCls(invalidCls, oldInvalidCls);
    },
    updateValidCls: function(validCls, oldValidCls) {
        this.doUpdateCls(validCls, oldValidCls);
    },
    destroy: function() {
        this.element = Ext.destroy(this.element);
        this.callParent();
    },
    privates: {
        
        adjustCursorOffset: function(info, xy) {
            var offset = this.getCursorOffset();
            if (offset) {
                xy[0] += (offset[0] || 0);
                xy[1] += (offset[1] || 0);
            }
            return xy;
        },
        
        doUpdateCls: function(cls, oldCls) {
            var el = this.element,
                hasCls;
            if (el) {
                if (oldCls) {
                    hasCls = cls && el.hasCls(oldCls);
                    el.removeCls(oldCls);
                }
                if (cls && hasCls) {
                    el.addCls(cls);
                }
            }
        }
    }
});


Ext.define('Ext.event.gesture.Recognizer', {
    mixins: [
        Ext.mixin.Identifiable
    ],
    
    priority: 0,
    handledEvents: [],
    isStarted: false,
    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: function() {
        return this.reset();
    },
    onTouchCancel: function(e) {
        return this.cancel(e);
    },
    fire: function(eventName, e, info, isCancel) {
        this.getOnRecognized().call(this.getCallbackScope(), this, eventName, e, info, isCancel);
    },
    cancel: function(e) {
        if (this.isStarted) {
            
            
            
            this.onCancel(e);
        }
        return this.reset();
    },
    onCancel: Ext.emptyFn,
    reset: function() {
        this.isStarted = false;
        return false;
    }
});


Ext.define('Ext.event.gesture.SingleTouch', {
    extend: Ext.event.gesture.Recognizer,
    isSingleTouch: true,
    onTouchStart: function(e) {
        if (e.touches.length > 1) {
            return this.cancel(e);
        }
    }
});


Ext.define('Ext.event.gesture.DoubleTap', {
    extend: Ext.event.gesture.SingleTouch,
    priority: 300,
    config: {
        
        moveDistance: 8,
        
        tapDistance: 24,
        maxDuration: 300
    },
    handledEvents: [
        'singletap',
        'doubletap'
    ],
    
    
    singleTapTimer: null,
    startTime: 0,
    lastTapTime: 0,
    onTouchStart: function(e) {
        var me = this,
            ret = me.callParent([
                e
            ]),
            lastStartPoint;
        if (ret !== false) {
            me.isStarted = true;
            
            lastStartPoint = me.lastStartPoint = e.changedTouches[0].point;
            
            me.startPoint = me.startPoint || lastStartPoint;
            me.startTime = e.time;
            Ext.undefer(me.singleTapTimer);
        }
        return ret;
    },
    onTouchMove: function(e) {
        var me = this,
            point = e.changedTouches[0].point,
            scale = Ext.Element.getViewportScale(),
            
            distance = Math.round(Math.abs(point.getDistanceTo(me.lastStartPoint) * scale));
        if (distance >= me.getMoveDistance()) {
            return me.cancel(e);
        }
    },
    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, scale, distance;
        me.lastTapTime = time;
        me.lastTarget = target;
        if (lastTapTime) {
            duration = time - lastTapTime;
            if (duration <= maxDuration) {
                scale = Ext.Element.getViewportScale();
                
                distance = Math.round(Math.abs(point.getDistanceTo(me.startPoint) * scale));
                if (distance <= me.getTapDistance()) {
                    if (target !== lastTarget) {
                        return me.cancel(e);
                    }
                    me.lastTarget = null;
                    me.lastTapTime = 0;
                    me.fire('doubletap', e, {
                        touch: e.changedTouches[0],
                        duration: duration
                    });
                    return me.callParent([
                        e
                    ]);
                }
            }
        }
        if (time - me.startTime > maxDuration) {
            me.fire('singletap', e);
            me.reset();
        } else {
            me.setSingleTapTimer(e);
        }
    },
    setSingleTapTimer: function(e) {
        var me = this;
        me.singleTapTimer = Ext.defer(function() {
            me.fire('singletap', e);
            me.reset();
        }, me.getMaxDuration());
    },
    reset: function() {
        var me = this;
        Ext.undefer(me.singleTapTimer);
        me.startTime = me.lastTapTime = 0;
        me.lastStartPoint = me.startPoint = me.singleTapTimer = null;
        return me.callParent();
    }
}, 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,
    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
            },
            longpress: false
        };
    },
    onTouchStart: function(e) {
        var me = this,
            ret = me.callParent([
                e
            ]);
        if (ret !== false) {
            me.startTime = e.time;
            me.startPoint = e.changedTouches[0].point;
        }
        return ret;
    },
    tryDragStart: function(e) {
        var me = this,
            point = e.changedTouches[0].point,
            minDistance = me.getMinDistance(),
            scale = Ext.Element.getViewportScale(),
            
            distance = Math.round(Math.abs(point.getDistanceTo(me.startPoint) * scale));
        if (distance >= minDistance) {
            me.doDragStart(e);
        }
    },
    doDragStart: function(e, isLongPress) {
        var me = this,
            touch = e.changedTouches[0],
            point = touch.point,
            info = me.info,
            time;
        if (isLongPress) {
            time = Ext.now();
            me.startTime = time;
            me.startPoint = point;
            info.longpress = true;
        } else {
            time = e.time;
        }
        me.isStarted = true;
        me.previousPoint = me.lastPoint = point;
        me.resetInfo('x', e, touch);
        me.resetInfo('y', e, touch);
        info.time = time;
        me.fire('dragstart', e, info);
    },
    onTouchMove: function(e) {
        var me = this,
            touch, point;
        if (!me.startPoint) {
            return;
        }
        if (!me.isStarted) {
            me.tryDragStart(e);
        }
        if (!me.isStarted) {
            return;
        }
        touch = e.changedTouches[0];
        point = touch.point;
        if (me.lastPoint) {
            me.previousPoint = me.lastPoint;
        }
        me.lastPoint = point;
        me.lastMoveEvent = e;
        me.updateInfo('x', e, touch);
        me.updateInfo('y', e, touch);
        me.info.time = e.time;
        me.fire('drag', e, me.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 me = this,
            value = me.lastPoint[axis],
            startValue = me.startPoint[axis],
            delta = value - startValue,
            capAxis = axis.toUpperCase(),
            info = me.info;
        info.touch = touch;
        info.delta[axis] = delta;
        info.absDelta[axis] = Math.abs(delta);
        info.previousTime[axis] = me.startTime;
        info.previous[axis] = startValue;
        info[axis] = value;
        info.direction[axis] = 0;
        info['start' + capAxis] = me.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 = me.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) {
        var me = this,
            touch, point, info;
        if (me.isStarted) {
            touch = e.changedTouches[0];
            point = touch.point;
            info = me.info;
            me.lastPoint = point;
            me.updateInfo('x', e, touch);
            me.updateInfo('y', e, touch);
            info.time = e.time;
            me.onAxisDragEnd('x', info);
            me.onAxisDragEnd('y', info);
            me.fire('dragend', e, info);
        }
        return this.callParent([
            e
        ]);
    },
    onCancel: function(e) {
        var me = this,
            touch = e.changedTouches[0],
            info = me.info;
        
        
        
        
        
        if (!e.touches.length) {
            me.lastPoint = touch.point;
        }
        me.updateInfo('x', e, touch);
        me.updateInfo('y', e, touch);
        info.time = e.time;
        me.fire('dragcancel', e, info, true);
    },
    reset: function() {
        var me = this;
        me.lastPoint = me.startPoint = me.previousPoint = me.lastPoint = me.lastMoveEvent = null;
        me.initInfo();
        return me.callParent();
    }
}, 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: 600,
    handledEvents: [
        'swipestart',
        'swipe',
        'swipecancel'
    ],
    
    
    
    config: {
        minDistance: 80,
        maxOffset: 35,
        maxDuration: 1000
    },
    onTouchStart: function(e) {
        var me = this,
            ret = me.callParent([
                e
            ]),
            touch;
        if (ret !== false) {
            touch = e.changedTouches[0];
            me.startTime = e.time;
            me.isHorizontal = true;
            me.isVertical = true;
            me.startX = touch.pageX;
            me.startY = touch.pageY;
        }
        return ret;
    },
    onTouchMove: function(e) {
        var me = this,
            touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - me.startX,
            deltaY = y - me.startY,
            absDeltaX = Math.abs(x - me.startX),
            absDeltaY = Math.abs(y - me.startY),
            duration = e.time - me.startTime,
            minDistance, direction, distance;
        
        if ((absDeltaX === 0 && absDeltaY === 0) || (duration > me.getMaxDuration())) {
            return me.cancel(e);
        }
        if (me.isHorizontal && absDeltaY > me.getMaxOffset()) {
            me.isHorizontal = false;
        }
        if (me.isVertical && absDeltaX > me.getMaxOffset()) {
            me.isVertical = false;
        }
        if (!me.isVertical || !me.isHorizontal) {
            minDistance = me.getMinDistance();
            if (me.isHorizontal && absDeltaX < minDistance) {
                direction = (deltaX < 0) ? 'left' : 'right';
                distance = absDeltaX;
            } else if (me.isVertical && absDeltaY < minDistance) {
                direction = (deltaY < 0) ? 'up' : 'down';
                distance = absDeltaY;
            }
        }
        if (!me.isHorizontal && !me.isVertical) {
            return me.cancel(e);
        }
        if (direction && !me.isStarted) {
            me.isStarted = true;
            me.fire('swipestart', e, {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        }
    },
    onTouchEnd: function(e) {
        var me = this,
            touch, x, y, deltaX, deltaY, absDeltaX, absDeltaY, minDistance, duration, direction, distance;
        if (me.onTouchMove(e) !== false) {
            touch = e.changedTouches[0];
            x = touch.pageX;
            y = touch.pageY;
            deltaX = x - me.startX;
            deltaY = y - me.startY;
            absDeltaX = Math.abs(deltaX);
            absDeltaY = Math.abs(deltaY);
            minDistance = me.getMinDistance();
            duration = e.time - me.startTime;
            if (me.isVertical && absDeltaY < minDistance) {
                me.isVertical = false;
            }
            if (me.isHorizontal && absDeltaX < minDistance) {
                me.isHorizontal = false;
            }
            if (me.isHorizontal) {
                direction = (deltaX < 0) ? 'left' : 'right';
                distance = absDeltaX;
            } else if (me.isVertical) {
                direction = (deltaY < 0) ? 'up' : 'down';
                distance = absDeltaY;
            }
            me.fire('swipe', e, {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        }
        return this.callParent([
            e
        ]);
    },
    onCancel: function(e) {
        this.fire('swipecancel', e, null, true);
    },
    reset: function() {
        var me = this;
        me.startTime = me.isHorizontal = me.isVertical = me.startX = me.startY = null;
        return me.callParent();
    }
}, 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: 500,
    handledEvents: [
        'edgeswipe',
        'edgeswipestart',
        'edgeswipeend',
        'edgeswipecancel'
    ],
    config: {
        minDistance: 60
    },
    onTouchStart: function(e) {
        var me = this,
            ret = me.callParent([
                e
            ]),
            touch;
        if (ret !== false) {
            touch = e.changedTouches[0];
            me.direction = null;
            me.isHorizontal = true;
            me.isVertical = true;
            me.startX = touch.pageX;
            me.startY = touch.pageY;
        }
        return ret;
    },
    onTouchMove: function(e) {
        var me = this,
            touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - me.startX,
            deltaY = y - me.startY,
            absDeltaY = Math.abs(y - me.startY),
            absDeltaX = Math.abs(x - me.startX),
            minDistance = me.getMinDistance(),
            maxOffset = me.getMaxOffset(),
            duration = e.time - me.startTime,
            elementWidth = Ext.Viewport && Ext.Element.getViewportWidth(),
            elementHeight = Ext.Viewport && Ext.Element.getViewportHeight(),
            direction, distance;
        
        if (me.isVertical && absDeltaX > maxOffset) {
            me.isVertical = false;
        }
        
        if (me.isHorizontal && absDeltaY > maxOffset) {
            me.isHorizontal = false;
        }
        
        if (me.isVertical && me.isHorizontal) {
            if (absDeltaY > absDeltaX) {
                me.isHorizontal = false;
            } else {
                me.isVertical = false;
            }
        }
        
        if (me.isHorizontal) {
            direction = (deltaX < 0) ? 'left' : 'right';
            distance = deltaX;
        } else if (me.isVertical) {
            direction = (deltaY < 0) ? 'up' : 'down';
            distance = deltaY;
        }
        direction = me.direction || (me.direction = direction);
        
        if (direction === 'up') {
            distance = deltaY * -1;
        } else if (direction === 'left') {
            distance = deltaX * -1;
        }
        me.distance = distance;
        if (!distance) {
            return me.cancel(e);
        }
        if (!me.isStarted) {
            if ((direction === 'right' && me.startX > minDistance) || (direction === 'down' && me.startY > minDistance) || (direction === 'left' && (elementWidth - me.startX) > minDistance) || (direction === 'up' && (elementHeight - me.startY) > minDistance)) {
                return me.cancel(e);
            }
            me.isStarted = true;
            me.startTime = e.time;
            me.fire('edgeswipestart', e, {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        } else {
            me.fire('edgeswipe', e, {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        }
    },
    onTouchEnd: function(e) {
        var me = this,
            duration;
        if (me.onTouchMove(e) !== false) {
            duration = e.time - me.startTime;
            me.fire('edgeswipeend', e, {
                touch: e.changedTouches[0],
                direction: me.direction,
                distance: me.distance,
                duration: duration
            });
        }
        return this.reset();
    },
    onCancel: function(e) {
        this.fire('edgeswipecancel', e, {
            touch: e.changedTouches[0]
        }, true);
    },
    reset: function() {
        var me = this;
        me.direction = me.isHorizontal = me.isVertical = me.startX = me.startY = me.startTime = me.distance = null;
        return me.callParent();
    }
}, 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,
    config: {
        moveDistance: 8,
        minDuration: 1000
    },
    handledEvents: [
        'longpress',
        'taphold'
    ],
    
    
    onTouchStart: function(e) {
        var me = this,
            ret = me.callParent([
                e
            ]);
        if (ret !== false) {
            me.startPoint = e.changedTouches[0].point;
            me.setLongPressTimer(e);
        }
        return ret;
    },
    setLongPressTimer: function(e) {
        var me = this;
        Ext.undefer(me.timer);
        me.timer = Ext.defer(me.fireLongPress, me.getMinDuration(), me, [
            e
        ]);
    },
    onTouchMove: function(e) {
        var me = this,
            point = e.changedTouches[0].point,
            scale = Ext.Element.getViewportScale(),
            
            distance = Math.round(Math.abs(point.getDistanceTo(me.startPoint) * scale));
        if (distance >= me.getMoveDistance()) {
            return me.cancel(e);
        }
    },
    reset: function() {
        var me = this;
        me.timer = me.startPoint = Ext.undefer(me.timer);
        return me.callParent();
    },
    fireLongPress: function(e) {
        var me = this,
            info = {
                touch: e.changedTouches[0],
                duration: me.getMinDuration(),
                startDrag: me.startDrag
            };
        this.fire('taphold', e, info);
        this.fire('longpress', e, info);
        this.reset();
    },
    
    startDrag: function() {
        
        
        var dragRecognizer = Ext.event.gesture.Drag.instance,
            touchStartEvent = this.parentEvent;
        dragRecognizer.doDragStart(touchStartEvent, true);
        Ext.event.publisher.Gesture.instance.claimRecognizer(dragRecognizer, touchStartEvent);
    }
}, 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,
    isMultiTouch: true,
    onTouchStart: function(e) {
        var me = this,
            requiredTouchesCount = me.requiredTouchesCount,
            touches = e.touches,
            touchesCount = touches.length;
        if (touchesCount === requiredTouchesCount) {
            me.isTracking = true;
        } else if (touchesCount > requiredTouchesCount) {
            return me.cancel(e);
        }
    },
    reset: function() {
        this.isTracking = false;
        return this.callParent();
    }
});


Ext.define('Ext.event.gesture.Pinch', {
    extend: Ext.event.gesture.MultiTouch,
    priority: 700,
    handledEvents: [
        'pinchstart',
        'pinch',
        'pinchend',
        'pinchcancel'
    ],
    
    
    
    
    startDistance: 0,
    lastTouches: null,
    onTouchMove: function(e) {
        var me = this,
            touches, firstPoint, secondPoint, distance;
        if (me.isTracking) {
            touches = e.touches;
            firstPoint = touches[0].point;
            secondPoint = touches[1].point;
            distance = firstPoint.getDistanceTo(secondPoint);
            if (distance === 0) {
                return;
            }
            if (!me.isStarted) {
                me.isStarted = true;
                me.startDistance = distance;
                me.fire('pinchstart', e, {
                    touches: touches,
                    distance: distance,
                    scale: 1
                });
            } else {
                me.fire('pinch', e, {
                    touches: touches,
                    distance: distance,
                    scale: distance / me.startDistance
                });
            }
        }
    },
    onTouchEnd: function(e) {
        if (this.isStarted) {
            this.fire('pinchend', e);
        }
        return this.callParent([
            e
        ]);
    },
    onCancel: function(e) {
        this.fire('pinchcancel', e, null, true);
    },
    reset: function() {
        this.lastTouches = null;
        this.startDistance = 0;
        return 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: 800,
    handledEvents: [
        'rotatestart',
        'rotate',
        'rotateend',
        'rotatecancel'
    ],
    
    
    
    
    
    startAngle: 0,
    lastTouches: null,
    lastAngle: null,
    onTouchMove: function(e) {
        var me = this,
            touches, lastAngle, firstPoint, secondPoint, angle, nextAngle, previousAngle, diff;
        if (me.isTracking) {
            touches = e.touches;
            lastAngle = me.lastAngle;
            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;
                }
            }
            me.lastAngle = angle;
            if (!me.isStarted) {
                me.isStarted = true;
                me.startAngle = angle;
                me.fire('rotatestart', e, {
                    touches: touches,
                    angle: angle,
                    rotation: 0
                });
            } else {
                me.fire('rotate', e, {
                    touches: touches,
                    angle: angle,
                    rotation: angle - me.startAngle
                });
            }
            me.lastTouches = Ext.Array.clone(touches);
        }
    },
    onTouchEnd: function(e) {
        if (this.isStarted) {
            this.fire('rotateend', e);
        }
        return this.callParent([
            e
        ]);
    },
    onCancel: function(e) {
        this.fire('rotatecancel', e, null, true);
    },
    reset: function() {
        var me = this;
        me.lastTouches = me.lastAngle = me.startAngle = null;
        return 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) {
        var me = this,
            ret = me.callParent([
                e
            ]);
        if (ret !== false) {
            me.isStarted = true;
            me.startPoint = e.changedTouches[0].point;
        }
        return ret;
    },
    onTouchMove: function(e) {
        var me = this,
            point = e.changedTouches[0].point,
            scale = Ext.Element.getViewportScale(),
            
            distance = Math.round(Math.abs(point.getDistanceTo(me.startPoint) * scale));
        if (distance >= me.getMoveDistance()) {
            return me.cancel(e);
        }
    },
    onTouchEnd: function(e) {
        this.fire('tap', e, {
            touch: e.changedTouches[0]
        });
        return this.callParent([
            e
        ]);
    },
    onCancel: function(e) {
        this.fire('tapcancel', e, {
            touch: e.changedTouches[0]
        }, true);
    },
    reset: function() {
        this.startPoint = null;
        return this.callParent();
    }
}, function(Tap) {
    var gestures = Ext.manifest.gestures;
    Tap.instance = new Tap(gestures && gestures.tap);
});


Ext.define('Ext.event.publisher.Focus', {
    extend: Ext.event.publisher.Dom,
    type: 'focus',
    handledEvents: [
        'focusenter',
        'focusleave',
        'focusmove'
    ],
    
    
    
    handledDomEvents: [
        'focusin',
        'focusout'
    ],
    publishDelegatedDomEvent: function(e) {
        var me = this,
            relatedTarget = e.relatedTarget;
        
        if (me.$suppressEvents) {
            return;
        }
        
        if (e.type === 'focusout') {
            
            
            if (relatedTarget == null) {
                me.processFocusIn(e, e.target, document.body);
            }
        } else {
            
            
            if (relatedTarget == null || !relatedTarget.tagName) {
                relatedTarget = document.body;
            }
            me.processFocusIn(e, relatedTarget, e.target);
        }
    },
    processFocusIn: function(e, fromElement, toElement) {
        var me = this,
            commonAncestor, node,
            targets = [],
            focusFly = me.focusFly,
            backwards, event, focusEnterEvent;
        
        
        if ((fromElement && focusFly.attach(fromElement).isFocusSuspended()) || (toElement && focusFly.attach(toElement).isFocusSuspended())) {
            return;
        }
        if (toElement.compareDocumentPosition) {
            
            backwards = !!(toElement.compareDocumentPosition(fromElement) & 4);
        }
        
        for (node = fromElement , commonAncestor = Ext.dom.Element.getCommonAncestor(toElement, fromElement, true); node && node !== commonAncestor; node = node.parentNode) {
            targets.push(node);
        }
        
        if (targets.length) {
            event = me.createSyntheticEvent('focusleave', e, fromElement, toElement, fromElement, toElement, backwards);
            me.publish(event, targets);
            if (event.stopped) {
                return;
            }
        }
        
        targets.length = 0;
        for (node = toElement; node && node !== commonAncestor; node = node.parentNode) {
            targets.push(node);
        }
        
        focusEnterEvent = me.createSyntheticEvent('focusenter', e, toElement, fromElement, fromElement, toElement, backwards);
        
        if (targets.length) {
            me.publish(focusEnterEvent, targets);
            if (focusEnterEvent.stopped) {
                return;
            }
        }
        
        targets = me.getPropagatingTargets(commonAncestor);
        
        if (targets.length) {
            event = me.createSyntheticEvent('focusmove', e, toElement, fromElement, fromElement, toElement, backwards);
            me.publish(event, targets);
            if (event.stopped) {
                return;
            }
        }
        if (Ext.GlobalEvents.hasListeners.focus) {
            Ext.GlobalEvents.fireEvent('focus', {
                event: focusEnterEvent,
                toElement: toElement,
                fromElement: fromElement,
                backwards: backwards
            });
        }
    },
    createSyntheticEvent: function(eventName, browserEvent, target, relatedTarget, fromElement, toElement, backwards) {
        var event = new Ext.event.Event(browserEvent);
        event.type = eventName;
        event.relatedTarget = relatedTarget;
        event.target = target;
        event.fromElement = fromElement;
        event.toElement = toElement;
        event.backwards = backwards;
        return event;
    }
}, function(Focus) {
    var focusTimeout;
    Focus.prototype.focusFly = new Ext.dom.Fly();
    Focus.instance = new Focus();
    
    
    if (!Ext.supports.FocusinFocusoutEvents) {
        
        
        this.override({
            handledDomEvents: [
                'focus',
                'blur'
            ],
            publishDelegatedDomEvent: function(e) {
                var me = this,
                    targetIsElement;
                me.callSuper([
                    e
                ]);
                
                targetIsElement = e.target !== window && e.target !== document;
                
                
                if (e.type === 'blur') {
                    if (!targetIsElement) {
                        
                        
                        
                        
                        
                        
                        if (e.explicitOriginalTarget === Focus.previousActiveElement) {
                            
                            
                            if (e.target === window) {
                                Ext.undefer(focusTimeout);
                                focusTimeout = 0;
                                me.processFocusIn(e, Focus.previousActiveElement, document.body);
                                Focus.previousActiveElement = null;
                            }
                        }
                    } else {
                        
                        
                        
                        
                        focusTimeout = Ext.defer(function() {
                            focusTimeout = 0;
                            me.processFocusIn(e, e.target, document.body);
                            Focus.previousActiveElement = null;
                        }, 1);
                        
                        
                        if (targetIsElement && Ext.cache[e.target.id]) {
                            Ext.cache[e.target.id].focusinTimeout = focusTimeout;
                        }
                    }
                    Focus.previousActiveElement = targetIsElement ? e.target : null;
                } else {
                    Ext.undefer(focusTimeout);
                    focusTimeout = 0;
                    me.processFocusIn(e, Focus.previousActiveElement || document.body, targetIsElement ? e.target : document.body);
                }
            }
        });
        Ext.define(null, {
            override: 'Ext.dom.Element',
            destroy: function() {
                if (this.focusinTimeout) {
                    Ext.undefer(this.focusinTimeout);
                    this.focusinTimeout = null;
                }
                this.callParent();
            }
        });
    }
});


Ext.define('Ext.field.InputMask', function(InputMask) {
    return {
        cachedConfig: {
            blank: '_',
            characters: {
                '*': '[A-Za-z0-9]',
                'a': '[a-z]',
                'A': '[A-Z]',
                '0': '[0-9]',
                '9': '[0-9]'
            },
            ignoreCase: true
        },
        config: {
            
            pattern: null
        },
        _cached: false,
        _lastEditablePos: null,
        _mask: null,
        statics: {
            active: {},
            from: function(value, existing) {
                var active = InputMask.active,
                    ret;
                if (value === null) {
                    ret = null;
                } else if (typeof value !== 'string') {
                    if (existing && !existing._cached) {
                        ret = existing;
                        ret.setConfig(value);
                    } else {
                        ret = new InputMask(value);
                    }
                } else if (!(ret = active[value])) {
                    
                    
                    
                    if (!(ret = InputMask.cache.remove(value))) {
                        ret = new InputMask({
                            pattern: value
                        });
                    }
                    active[value] = ret;
                    ret._cached = 1;
                } else 
                {
                    
                    
                    ++ret._cached;
                }
                return ret;
            }
        },
        constructor: function(config) {
            this.initConfig(config);
        },
        release: function() {
            var me = this,
                cache = InputMask.cache,
                key;
            if (me._cached && !--me._cached) {
                key = me.getPattern();
                
                if (InputMask.active[key] !== me) {
                    Ext.raise('Invalid call to InputMask#release (not active)');
                }
                if (cache.map[key]) {
                    Ext.raise('Invalid call to InputMask#release (already cached)');
                }
                
                delete InputMask.active[key];
                cache.add(key, me);
                cache.trim(cache.maxSize);
            }
            
            else if (me._cached === 0) {
                Ext.raise('Invalid call to InputMask#release (already released)');
            }
        },
        
        clearRange: function(value, start, len) {
            var me = this,
                blank = me.getBlank(),
                end = start + len,
                n = value.length,
                s = '',
                i, mask, prefixLen;
            if (!blank) {
                prefixLen = me._prefix.length;
                for (i = 0; i < n; ++i) {
                    if (i < prefixLen || i < start || i >= end) {
                        s += value[i];
                    }
                }
                s = me.formatValue(s);
            } else {
                mask = me.getPattern();
                for (i = 0; i < n; ++i) {
                    if (i < start || i >= end) {
                        s += value[i];
                    } else if (me.isFixedChar(i)) {
                        s += mask[i];
                    } else {
                        s += blank;
                    }
                }
            }
            return s;
        },
        formatValue: function(value) {
            var me = this,
                blank = me.getBlank(),
                i, length, mask, prefix, s;
            if (!blank) {
                prefix = me._prefix;
                length = prefix.length;
                s = this.insertRange('', value, 0);
                for (i = s.length; i > length && me.isFixedChar(i - 1); ) {
                    --i;
                }
                s = (i < length) ? prefix : s.slice(0, i - 1);
            } else if (value) {
                s = me.formatValue('');
                s = me.insertRange(s, value, 0);
            } else {
                mask = me.getPattern();
                s = '';
                for (i = 0 , length = mask.length; i < length; ++i) {
                    if (me.isFixedChar(i)) {
                        s += mask[i];
                    } else {
                        s += blank;
                    }
                }
            }
            return s;
        },
        getEditPosLeft: function(pos) {
            for (var i = pos; i >= 0; --i) {
                if (!this.isFixedChar(i)) {
                    return i;
                }
            }
            return null;
        },
        getEditPosRight: function(pos) {
            var mask = this._mask,
                len = mask.length,
                i;
            for (i = pos; i < len; ++i) {
                if (!this.isFixedChar(i)) {
                    return i;
                }
            }
            return null;
        },
        getFilledLength: function(value) {
            var me = this,
                blank = me.getBlank(),
                c, i;
            if (!blank) {
                return value.length;
            }
            for (i = value && value.length; i-- > 0; ) {
                c = value[i];
                if (!me.isFixedChar(i) && me.isAllowedChar(c, i)) {
                    break;
                }
            }
            return ++i || me._prefix.length;
        },
        getSubLength: function(value, substr, pos) {
            var me = this,
                mask = me.getPattern(),
                k = 0,
                maskLen = mask.length,
                substrLen = substr.length,
                i;
            for (i = pos; i < maskLen && k < substrLen; ) {
                if (!me.isFixedChar(i) || mask[i] === substr[k]) {
                    if (me.isAllowedChar(substr[k++], i, true)) {
                        ++i;
                    }
                } else {
                    ++i;
                }
            }
            return i - pos;
        },
        insertRange: function(value, substr, pos) {
            var me = this,
                mask = me.getPattern(),
                blank = me.getBlank(),
                filled = me.isFilled(value),
                prefixLen = me._prefix.length,
                maskLen = mask.length,
                substrLen = substr.length,
                s = value,
                ch, fixed, i, k;
            if (!blank && pos > s.length) {
                s += mask.slice(s.length, pos);
            }
            for (i = pos , k = 0; i < maskLen && k < substrLen; ) {
                fixed = me.isFixedChar(i);
                if (!fixed || mask[i] === substr[k]) {
                    ch = substr[k++];
                    if (me.isAllowedChar(ch, i, true)) {
                        if (i < s.length) {
                            if (blank || filled || i < prefixLen) {
                                s = s.slice(0, i) + ch + s.slice(i + 1);
                            } else {
                                s = me.formatValue(s.substr(0, i) + ch + s.substr(i));
                            }
                        } else if (!blank) {
                            s += ch;
                        }
                        ++i;
                    }
                } else {
                    if (!blank && i >= s.length) {
                        s += mask[i];
                    } else if (blank && fixed && substr[k] === blank) {
                        ++k;
                    }
                    ++i;
                }
            }
            return s;
        },
        isAllowedChar: function(character, pos, allowBlankChar) {
            var me = this,
                mask = me.getPattern(),
                c, characters, rule;
            if (me.isFixedChar(pos)) {
                return mask[pos] === character;
            }
            c = mask[pos];
            characters = me.getCharacters();
            rule = characters[c];
            return !rule || rule.test(character || '') || (allowBlankChar && character === me.getBlank());
        },
        isEmpty: function(value) {
            for (var i = 0,
                len = value.length; i < len; ++i) {
                if (!this.isFixedChar(i) && this.isAllowedChar(value[i], i)) {
                    return false;
                }
            }
            return true;
        },
        
        
        isFilled: function(value) {
            return this.getFilledLength(value) === this._mask.length;
        },
        isFixedChar: function(pos) {
            return Ext.Array.indexOf(this._fixedCharPositions, pos) > -1;
        },
        setCaretToEnd: function(field, value) {
            var filledLen = this.getFilledLength(value),
                pos = this.getEditPosRight(filledLen);
            if (pos !== null) {
                
                
                
                
                Ext.raf(function() {
                    if (!field.destroyed) {
                        field.setCaretPos(pos);
                        Ext.raf(function() {
                            if (!field.destroyed) {
                                field.setCaretPos(pos);
                            }
                        });
                    }
                });
            }
        },
        
        
        onBlur: function(field, value) {
            if (field.getAutoHideInputMask() !== false) {
                if (this.isEmpty(value)) {
                    field.maskProcessed = true;
                    field.setValue('');
                }
            }
        },
        onFocus: function(field, value) {
            
            
            if (field.getAutoHideInputMask() !== false) {
                if (!value) {
                    field.maskProcessed = true;
                    field.setValue(this._mask);
                }
            }
            this.setCaretToEnd(field, value);
        },
        onChange: function(field, value, oldValue) {
            var me = this,
                s;
            if (field.maskProcessed || value === oldValue) {
                field.maskProcessed = false;
                return true;
            }
            if (value) {
                s = me.formatValue(value);
                field.maskProcessed = true;
                field.setValue(s);
            }
        },
        processAutocomplete: function(field, value) {
            var me = this,
                s;
            if (value) {
                if (value.length > me._mask.length) {
                    value = value.substr(0, me._mask.length);
                }
                s = me.formatValue(value);
                field.maskProcessed = true;
                field.inputElement.dom.value = s;
                
                field.setValue(s);
                this.setCaretToEnd(field, value);
            }
        },
        
        showEmptyMask: function(field, adjustCaret) {
            var s = this.formatValue();
            field.maskProcessed = true;
            field.setValue(s);
            if (adjustCaret) {
                this.setCaretToEnd(field);
            }
        },
        onKeyDown: function(field, value, event) {
            if (event.ctrlKey || event.metaKey) {
                return;
            }
            var me = this,
                
                key = event.keyCode === event.DELETE,
                del = key === 'Delete',
                handled = del || (event.keyCode === event.BACKSPACE),
                s = value,
                caret, editPos, len, prefixLen, textSelection, start;
            if (handled) {
                caret = field.getCaretPos();
                prefixLen = me._prefix.length;
                textSelection = field.getTextSelection();
                start = textSelection[0];
                len = textSelection[1] - start;
                if (len) {
                    s = me.clearRange(value, start, len);
                } else if (caret < prefixLen || (!del && caret === prefixLen)) {
                    caret = prefixLen;
                } else {
                    editPos = del ? me.getEditPosRight(caret) : me.getEditPosLeft(caret - 1);
                    if (editPos !== null) {
                        s = me.clearRange(value, editPos, 1);
                        caret = editPos;
                    }
                }
                if (s !== value) {
                    field.maskProcessed = true;
                    field.setValue(s);
                }
                event.preventDefault();
                field.setCaretPos(caret);
            }
        },
        onKeyPress: function(field, value, event) {
            var me = this,
                key = event.keyCode,
                ch = event.getChar(),
                mask = me.getPattern(),
                prefixLen = me._prefix.length,
                s = value,
                caretPos, pos, start, textSelection;
            if (key === event.ENTER || key === event.TAB || event.ctrlKey || event.metaKey) {
                return;
            }
            
            caretPos = field.getCaretPos();
            textSelection = field.getTextSelection();
            if (me.isFixedChar(caretPos) && mask[caretPos] === ch) {
                s = me.insertRange(s, ch, caretPos);
                ++caretPos;
            } else {
                pos = me.getEditPosRight(caretPos);
                if (pos !== null && me.isAllowedChar(ch, pos)) {
                    start = textSelection[0];
                    s = me.clearRange(s, start, textSelection[1] - start);
                    s = me.insertRange(s, ch, pos);
                    caretPos = pos + 1;
                }
            }
            if (s !== value) {
                field.maskProcessed = true;
                field.setValue(s);
            }
            event.preventDefault();
            if (caretPos < me._lastEditablePos && caretPos > prefixLen) {
                caretPos = me.getEditPosRight(caretPos);
            }
            field.setCaretPos(caretPos);
        },
        onPaste: function(field, value, event) {
            
            
            var text,
                clipdData = event.browserEvent.clipboardData;
            if (clipdData && clipdData.getData) {
                text = clipdData.getData('text/plain');
            } else if (Ext.global.clipboardData && Ext.global.clipboardData.getData) {
                text = Ext.global.clipboardData.getData('Text');
            }
            
            if (text) {
                this.paste(field, value, text, field.getTextSelection());
            }
            event.preventDefault();
        },
        paste: function(field, value, text, selection) {
            var me = this,
                caretPos = selection[0],
                len = selection[1] - caretPos,
                s = len ? me.clearRange(value, caretPos, len) : value,
                textLen = me.getSubLength(s, text, caretPos);
            s = me.insertRange(s, text, caretPos);
            caretPos += textLen;
            caretPos = me.getEditPosRight(caretPos) || caretPos;
            if (s !== value) {
                field.maskProcessed = true;
                field.setValue(s);
            }
            field.setCaretPos(caretPos);
        },
        syncPattern: function(field) {
            var fieldValue = field.getValue(),
                s;
            if (field.getAutoHideInputMask() === false) {
                
                if (!fieldValue) {
                    this.showEmptyMask(field);
                } else {
                    
                    s = this.formatValue(fieldValue);
                    field.maskProcessed = true;
                    field.setValue(s);
                }
            } else {
                
                
                if (fieldValue) {
                    s = this.formatValue(fieldValue);
                    field.maskProcessed = true;
                    field.setValue(s);
                }
            }
        },
        
        
        applyCharacters: function(map) {
            var ret = {},
                flags = this.getIgnoreCase() ? 'i' : '',
                c, v;
            for (c in map) {
                v = map[c];
                if (typeof v === 'string') {
                    v = new RegExp(v, flags);
                }
                ret[c] = v;
            }
            return ret;
        },
        updatePattern: function(mask) {
            var me = this,
                characters = me.getCharacters(),
                lastEditablePos = 0,
                n = mask && mask.length,
                blank = me.getBlank(),
                fixedPosArr = [],
                prefix = '',
                str = '',
                c, i;
            for (i = 0; i < n; ++i) {
                c = mask[i];
                if (!characters[c]) {
                    fixedPosArr.push(str.length);
                    str += c;
                } else {
                    lastEditablePos = str.length + 1;
                    str += blank;
                }
            }
            me._lastEditablePos = lastEditablePos;
            me._mask = str;
            me._fixedCharPositions = fixedPosArr;
            
            for (i = 0; i < str.length && me.isFixedChar(i); ++i) {
                prefix += str[i];
            }
            me._prefix = prefix;
        }
    };
}, function(InputMask) {
    InputMask.cache = new Ext.util.LRU();
    InputMask.cache.maxSize = 100;
});


Ext.define('Ext.fx.runner.Css', {
    extend: Ext.Evented,
    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,
        'x': true,
        'y': 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
    },
    DEFAULT_UNIT_LENGTH: 'px',
    DEFAULT_UNIT_ANGLE: 'deg',
    DEFAULT_UNIT_DURATION: 'ms',
    customProperties: {
        x: true,
        y: true
    },
    formattedNameCache: {
        'x': 'left',
        'y': 'top'
    },
    transformMethods3d: [
        'translateX',
        'translateY',
        'translateZ',
        'rotate',
        'rotateX',
        'rotateY',
        'rotateZ',
        'skewX',
        'skewY',
        'scaleX',
        'scaleY',
        'scaleZ'
    ],
    transformMethodsNo3d: [
        'translateX',
        'translateY',
        'rotate',
        'skewX',
        'skewY',
        'scaleX',
        'scaleY'
    ],
    constructor: function() {
        var me = this;
        me.transformMethods = Ext.feature.has.Css3dTransforms ? me.transformMethods3d : me.transformMethodsNo3d;
        me.vendorPrefix = Ext.browser.getStyleDashPrefix();
        me.ruleStylesCache = {};
        me.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);
                    }
                }
            }
        }
        return this;
    },
    applyStyles: function(styles) {
        var id, element, elementStyle, properties, name, value;
        for (id in styles) {
            if (styles.hasOwnProperty(id)) {
                this.activeElement = element = document.getElementById(id);
                if (!element) {
                    
                    continue;
                }
                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);
                        }
                    }
                }
            }
        }
        this.activeElement = null;
        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,
            defaultLengthUnit = this.DEFAULT_UNIT_LENGTH,
            isCustom = this.customProperties[name],
            transformMethods, method, i, ln, transformValues, values;
        if (value === null) {
            return '';
        }
        if (type === 'string') {
            if (this.lengthProperties[name]) {
                if (!Ext.dom.Element.hasUnit(value)) {
                    value = value + defaultLengthUnit;
                    if (isCustom) {
                        value = this.getCustomValue(value, name);
                    }
                }
            }
            return value;
        } else if (type === 'number') {
            if (value === 0) {
                return '0';
            }
            if (this.lengthProperties[name]) {
                value = value + defaultLengthUnit;
                if (isCustom) {
                    value = this.getCustomValue(value, name);
                }
                return value;
            }
            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;
    },
    getCustomValue: function(value, name) {
        var el = Ext.fly(this.activeElement);
        if (name === 'x') {
            value = el.translateXY(parseInt(value, 10)).x;
        } else if (name === 'y') {
            value = el.translateXY(null, parseInt(value, 10)).y;
        }
        return value + this.DEFAULT_UNIT_LENGTH;
    }
});


Ext.define('Ext.fx.runner.CssTransition', {
    extend: Ext.fx.runner.Css,
    alternateClassName: 'Ext.Animator',
    singleton: true,
    listenersAttached: false,
    constructor: function() {
        this.runningAnimationsData = {};
        
        
        this.transitionQueue = {
            toData: {},
            transitionData: {}
        };
        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
            ]);
        }
    },
    getElementId: function(element) {
        
        return element.getId ? element.getId() : element.id;
    },
    onAnimationEnd: function(element, data, animation, isInterrupted, isReplaced) {
        var id = this.getElementId(element),
            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 = this.getElementId(element),
            transitionQueue = this.transitionQueue,
            endRules = {};
        delete this.runningAnimationsData[id];
        endRules[id] = {
            'transition-property': null,
            'transition-duration': null,
            'transition-timing-function': null,
            'transition-delay': null
        };
        delete transitionQueue.toData[id];
        delete transitionQueue.transitionData[id];
        this.applyStyles(endRules);
        this.fireEvent('animationallend', this, element);
    },
    hasRunningAnimations: function(element) {
        var id = this.getElementId(element),
            runningAnimationsData = this.runningAnimationsData;
        return runningAnimationsData.hasOwnProperty(id) && runningAnimationsData[id].sessions.length > 0;
    },
    refreshRunningAnimationsData: function(element, propertyNames, interrupt, replace) {
        var id = this.getElementId(element),
            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 me = this,
            testElement = me.testElement,
            iframe = me.iframe,
            iframeDocument, iframeStyle;
        if (testElement) {
            
            
            
            if (testElement.ownerDocument.defaultView !== iframe.contentWindow) {
                iframeDocument = iframe.contentDocument;
                iframeDocument.body.appendChild(testElement);
                me.testElementComputedStyle = iframeDocument.defaultView.getComputedStyle(testElement);
            }
        } else {
            iframe = me.iframe = document.createElement('iframe');
            
            
            
            iframe.setAttribute('data-sticky', true);
            
            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();
            me.testElement = testElement = iframeDocument.createElement('div');
            testElement.style.setProperty('position', 'absolute', 'important');
            iframeDocument.body.appendChild(testElement);
            me.testElementComputedStyle = iframeDocument.defaultView.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,
            ret = [],
            isLengthPropertyMap = me.lengthProperties,
            fromData = {},
            toData = me.transitionQueue.toData,
            data = {},
            transitionData = me.transitionQueue.transitionData,
            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, messageTimerFn, onBeforeStart;
        if (!me.listenersAttached) {
            me.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);
            ret.push(animation);
            me.activeElement = element = animation.getElement();
            
            Ext.AnimationQueue.start(Ext.emptyFn, animation);
            computedStyle = window.getComputedStyle(element.dom);
            elementId = me.getElementId(element);
            data[elementId] = data = Ext.merge({}, animation.getData());
            onBeforeStart = animation.getOnBeforeStart();
            if (onBeforeStart) {
                onBeforeStart.call(animation.scope || me, element);
            }
            
            animation.fireEvent('animationstart', animation, data);
            me.fireEvent('animationstart', me, animation, 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 = me.formatValue(to[name], name);
                    formattedName = me.formatName(name);
                    isLengthProperty = isLengthPropertyMap.hasOwnProperty(name);
                    if (!isLengthProperty) {
                        toFormattedValue = me.getCssStyleValue(formattedName, toFormattedValue);
                    }
                    if (from.hasOwnProperty(name)) {
                        from[name] = fromFormattedValue = me.formatValue(from[name], name);
                        if (!isLengthProperty) {
                            fromFormattedValue = me.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) {
                me.onAnimationEnd(element, data, animation);
                
                continue;
            }
            runningData = me.getRunningData(elementId);
            runningSessions = runningData.sessions;
            if (runningSessions.length > 0) {
                me.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', me);
            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);
            transitionData[elementId] = {
                'transition-property': toPropertyNames,
                'transition-duration': data.duration,
                'transition-timing-function': data.easing,
                'transition-delay': data.delay
            };
            animation.startTime = Date.now();
        }
        me.activeElement = null;
        message = me.$className;
        me.applyStyles(fromData);
        doApplyTo = function(e) {
            if (e.data === message && e.source === window) {
                window.removeEventListener('message', doApplyTo, false);
                me.applyStyles(me.transitionQueue.toData);
            }
        };
        if (!me.messageTimerId) {
            messageTimerFn = function() {
                var messageFollowupFn;
                me.messageTimerId = null;
                if (Ext.isIE) {
                    
                    
                    
                    
                    
                    
                    me.applyStyles(me.transitionQueue.transitionData);
                    if (!me.messageFollowupId) {
                        messageFollowupFn = function() {
                            me.messageFollowupId = null;
                            window.addEventListener('message', doApplyTo, false);
                            window.postMessage(message, '*');
                        };
                        
                        messageFollowupFn.$skipTimerCheck = true;
                        
                        me.messageFollowupId = Ext.raf(messageFollowupFn);
                    }
                } else {
                    
                    
                    Ext.merge(me.transitionQueue.toData, me.transitionQueue.transitionData);
                    window.addEventListener('message', doApplyTo, false);
                    window.postMessage(message, '*');
                }
            };
            
            messageTimerFn.$skipTimerCheck = true;
            
            me.messageTimerId = Ext.raf(messageTimerFn);
        }
        
        
        
        return ret;
    },
    onAnimationStop: function(animation) {
        var me = this,
            runningAnimationsData = me.runningAnimationsData,
            activeAnimations = 0,
            stoppedAnimations = 0,
            id, runningData, sessions, i, ln, session;
        for (id in runningAnimationsData) {
            if (runningAnimationsData.hasOwnProperty(id)) {
                runningData = runningAnimationsData[id];
                sessions = runningData.sessions;
                activeAnimations++;
                for (i = 0 , ln = sessions.length; i < ln; i++) {
                    session = sessions[i];
                    if (session.animation === animation) {
                        me.refreshRunningAnimationsData(session.element, session.list.slice(), false);
                        if (animation.destroying) {
                            stoppedAnimations++;
                        }
                    }
                }
            }
        }
        if (activeAnimations === stoppedAnimations) {
            if (me.messageFollowupId) {
                Ext.unraf(me.messageFollowupId);
                me.messageFollowupId = null;
            }
            if (me.messageTimerId) {
                Ext.unraf(me.messageTimerId);
                me.messageTimerId = null;
            }
            Ext.apply(me.transitionQueue, {
                toData: {},
                transitionData: {}
            });
        }
    }
});


Ext.define('Ext.fx.Runner', {
    
    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.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.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.BoundMomentum', {
    extend: Ext.fx.easing.Abstract,
    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.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.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.fx.easing.Easing', {
    constructor: function(easing) {
        return Ext.factory(easing, Ext.fx.easing.Linear, null, 'easing');
    }
});


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 = {};
        
        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];
        
        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.list.AbstractTreeItem', {
    extend: Ext.Widget,
    isTreeListItem: true,
    
    
    
    
    
    
    
    
    
    cachedConfig: {
        
        expandable: false,
        
        expanded: false,
        
        iconCls: '',
        
        leaf: true,
        
        loading: false,
        
        selected: false,
        
        selectedParent: false
    },
    config: {
        
        iconClsProperty: 'iconCls',
        indent: null,
        
        owner: null,
        
        node: null,
        
        over: null,
        
        parentItem: null,
        
        text: {
            lazy: true,
            $value: ''
        },
        
        textProperty: 'text'
    },
    updateNode: function(node) {
        if (node) {
            var me = this,
                map = me.itemMap,
                childNodes, owner, len, i, item, child;
            me.element.dom.setAttribute('data-recordId', node.internalId);
            if (!map) {
                childNodes = node.childNodes;
                owner = me.getOwner();
                me.itemMap = map = {};
                for (i = 0 , len = childNodes.length; i < len; ++i) {
                    child = childNodes[i];
                    if (child.data.visible) {
                        item = owner.createItem(child, me);
                        map[child.internalId] = item;
                        me.insertItem(item, null);
                    }
                }
            }
            me.setExpanded(node.isExpanded());
            me.doNodeUpdate(node);
        }
    },
    updateSelected: function(selected) {
        if (!this.isConfiguring) {
            var parent = this.getParentItem();
            while (parent && !parent.isRootListItem) {
                parent.setSelectedParent(selected);
                parent = parent.getParentItem();
            }
        }
    },
    
    collapse: function() {
        this.getNode().collapse();
    },
    
    expand: function() {
        this.getNode().expand();
    },
    
    getToolElement: Ext.emptyFn,
    
    insertItem: Ext.emptyFn,
    
    isExpanded: function() {
        return this.getExpanded();
    },
    
    isSelectionEvent: Ext.emptyFn,
    
    isToggleEvent: Ext.emptyFn,
    
    nodeCollapse: function(node, collapsingForExpand) {
        var me = this,
            owner = me.getOwner(),
            animation = me.preventAnimation ? null : owner.getAnimation();
        me.nodeCollapseBegin(animation, collapsingForExpand);
        if (!animation) {
            me.nodeCollapseEnd(collapsingForExpand);
        }
    },
    nodeCollapseBegin: function(animation, collapsingForExpand) {
        var me = this,
            owner = me.getOwner();
        me.setExpanded(false);
        owner.fireEvent('itemcollapse', owner, me);
    },
    nodeCollapseEnd: function(collapsingForExpand) {
        if (!collapsingForExpand && !this.destroying) {
            this.getOwner().updateLayout();
        }
    },
    
    nodeExpand: function(node) {
        var me = this,
            owner = me.getOwner(),
            floated = me.getFloated(),
            animation = !floated && owner.getAnimation();
        me.nodeExpandBegin(animation);
        if (!animation) {
            me.nodeExpandEnd();
        }
    },
    nodeExpandBegin: function(animation) {
        var me = this,
            owner = me.getOwner();
        me.setExpanded(true);
        owner.fireEvent('itemexpand', owner, me);
    },
    nodeExpandEnd: function() {
        if (!this.destroying) {
            this.getOwner().updateLayout();
        }
    },
    
    nodeInsert: function(node, refNode) {
        var me = this,
            owner = me.getOwner(),
            map = me.itemMap,
            id = node.internalId,
            item = owner.getItem(node),
            refItem = null,
            oldParent;
        if (item) {
            oldParent = item.getParentItem();
            
            oldParent.removeItem(item);
            if (oldParent !== me) {
                oldParent.doUpdateExpandable();
                item.setParentItem(me);
            }
        } else {
            item = me.getOwner().createItem(node, me);
        }
        map[id] = item;
        if (refNode) {
            refItem = map[refNode.internalId];
        }
        me.insertItem(item, refItem);
        me.doUpdateExpandable();
        owner.fireEvent('iteminsert', owner, me, item, refItem);
        owner.updateLayout();
    },
    
    nodeRemove: function(node) {
        var me = this,
            map = me.itemMap,
            owner = me.getOwner(),
            id = node.internalId,
            item = map[id];
        if (item) {
            delete map[id];
            me.removeItem(item);
            item.destroy();
            me.doUpdateExpandable();
            owner.fireEvent('itemremove', owner, me, item);
            owner.updateLayout();
        }
    },
    
    nodeUpdate: function(node, modifiedFieldNames) {
        this.doNodeUpdate(node);
    },
    
    onClick: function(e) {
        var me = this,
            owner = me.getOwner(),
            node = me.getNode(),
            info = {
                event: e,
                item: me,
                node: node,
                tree: owner,
                select: node.get('selectable') !== false && me.isSelectionEvent(e),
                toggle: me.isToggleEvent(e)
            };
        
        if (owner.fireEvent('itemclick', owner, info) !== false) {
            if (info.toggle) {
                me.toggleExpanded();
                e.preventDefault();
            }
            if (info.select) {
                owner.setSelection(me.getNode());
            }
        }
    },
    
    removeItem: Ext.emptyFn,
    
    destroy: function() {
        var me = this,
            map = me.itemMap,
            owner = me.getOwner(),
            key;
        if (map) {
            for (key in map) {
                map[key].destroy();
            }
            me.itemMap = null;
        }
        if (owner) {
            owner.removeItem(me.getNode());
        }
        me.setNode(null);
        me.setParentItem(null);
        me.setOwner(null);
        me.callParent();
    },
    privates: {
        
        doNodeUpdate: function(node, modifiedFieldNames) {
            var me = this,
                textProperty = this.getTextProperty(),
                iconClsProperty = this.getIconClsProperty();
            if (textProperty) {
                me.setText(node.data[textProperty]);
            }
            if (iconClsProperty) {
                me.setIconCls(node.data[iconClsProperty]);
            }
            me.setLoading(node.isLoading());
            me.setLeaf(node.isLeaf());
            me.doUpdateExpandable();
        },
        doUpdateExpandable: function() {
            var node = this.getNode();
            this.setExpandable(node.isExpandable());
        },
        toggleExpanded: function() {
            if (this.isExpanded()) {
                this.collapse();
            } else {
                this.expand();
            }
        },
        updateIndent: function(value) {
            var items = this.itemMap,
                id;
            for (id in items) {
                items[id].setIndent(value);
            }
        },
        
        updateOwner: function(owner) {
            this.parent = owner;
        }
    }
});


Ext.define('Ext.list.RootTreeItem', {
    extend: Ext.list.AbstractTreeItem,
    
    isRootListItem: true,
    element: {
        reference: 'element',
        tag: 'ul',
        cls: Ext.baseCSSPrefix + 'treelist-root-container'
    },
    insertItem: function(item, refItem) {
        if (refItem) {
            item.element.insertBefore(refItem.element);
        } else {
            this.element.appendChild(item.element);
        }
    },
    isToggleEvent: function(e) {
        return false;
    }
});


Ext.define('Ext.mixin.ItemRippler', {
    mixinId: 'itemrippler',
    config: {
        
        itemRipple: null
    },
    shouldRippleItem: function(item, e) {
        var itemRipple, ripple;
        if (e.getTarget(this.noItemRippleSelector, this.element)) {
            return false;
        }
        itemRipple = item && this.getItemRipple();
        if (itemRipple && item.isWidget) {
            ripple = item.shouldRipple(e);
            if (ripple) {
                itemRipple = Ext.apply({}, itemRipple, ripple);
            }
        }
        return itemRipple;
    },
    rippleItem: function(item, e) {
        if (!item) {
            return;
        }
        var me = this,
            start = e.type.match(me.rippleStateRe),
            itemRipple = me.shouldRippleItem(item, e),
            release = itemRipple && itemRipple.release,
            isRelease = release === true,
            el = item.isWidget ? item.el : item,
            pos, delta, rs, rippledItems;
        
        
        if (itemRipple && start && isRelease) {
            me.$rippleStart = e.getXY();
        }
        
        
        
        
        if (itemRipple && el && ((!start && isRelease) || (start && release !== true))) {
            rippledItems = me.$rippledItems || (me.$rippledItems = []);
            rs = me.$rippleStart;
            if (rs) {
                pos = e.getXY();
                
                delta = Math.sqrt(Math.pow((pos[0] - rs[0]), 2) + Math.pow((pos[1] - rs[1]), 2));
                if (delta <= 8) {
                    el.ripple(e, itemRipple);
                    rippledItems.push(el);
                }
            } else {
                el.ripple(e, itemRipple);
                rippledItems.push(el);
            }
            me.$rippleStart = null;
        }
    },
    destroyAllRipples: function() {
        for (var items = this.$rippledItems; items && items.length; ) {
            items.pop().destroyAllRipples();
        }
    },
    privates: {
        noItemRippleSelector: '.' + Ext.baseCSSPrefix + 'item-no-ripple, ' + '.' + Ext.baseCSSPrefix + 'item-no-tap',
        rippleStateRe: /start|down/
    }
});


Ext.define('Ext.list.TreeItem', {
    extend: Ext.list.AbstractTreeItem,
    xtype: 'treelistitem',
    collapsedCls: Ext.baseCSSPrefix + 'treelist-item-collapsed',
    expandedCls: Ext.baseCSSPrefix + 'treelist-item-expanded',
    floatedToolCls: Ext.baseCSSPrefix + 'treelist-item-tool-floated',
    leafCls: Ext.baseCSSPrefix + 'treelist-item-leaf',
    expandableCls: Ext.baseCSSPrefix + 'treelist-item-expandable',
    hideIconCls: Ext.baseCSSPrefix + 'treelist-item-hide-icon',
    loadingCls: Ext.baseCSSPrefix + 'treelist-item-loading',
    selectedCls: Ext.baseCSSPrefix + 'treelist-item-selected',
    selectedParentCls: Ext.baseCSSPrefix + 'treelist-item-selected-parent',
    withIconCls: Ext.baseCSSPrefix + 'treelist-item-with-icon',
    hoverCls: Ext.baseCSSPrefix + 'treelist-item-over',
    rowHoverCls: Ext.baseCSSPrefix + 'treelist-row-over',
    
    isTreeListItem: true,
    config: {
        
        rowCls: null
    },
    
    rowClsProperty: 'rowCls',
    element: {
        reference: 'element',
        tag: 'li',
        cls: Ext.baseCSSPrefix + 'treelist-item',
        children: [
            {
                reference: 'rowElement',
                cls: Ext.baseCSSPrefix + 'treelist-row',
                children: [
                    {
                        reference: 'wrapElement',
                        cls: Ext.baseCSSPrefix + 'treelist-item-wrap',
                        children: [
                            {
                                reference: 'iconElement',
                                cls: Ext.baseCSSPrefix + 'treelist-item-icon'
                            },
                            {
                                reference: 'textElement',
                                cls: Ext.baseCSSPrefix + 'treelist-item-text'
                            },
                            {
                                reference: 'expanderElement',
                                cls: Ext.baseCSSPrefix + 'treelist-item-expander'
                            }
                        ]
                    }
                ]
            },
            {
                reference: 'itemContainer',
                tag: 'ul',
                cls: Ext.baseCSSPrefix + 'treelist-container'
            },
            {
                reference: 'toolElement',
                cls: Ext.baseCSSPrefix + 'treelist-item-tool'
            }
        ]
    },
    constructor: function(config) {
        this.callParent([
            config
        ]);
        var toolDom = this.toolElement.dom;
        
        
        toolDom.parentNode.removeChild(toolDom);
    },
    getToolElement: function() {
        return this.toolElement;
    },
    insertItem: function(item, refItem) {
        if (refItem) {
            item.element.insertBefore(refItem.element);
        } else {
            this.itemContainer.appendChild(item.element);
        }
    },
    isSelectionEvent: function(e) {
        var owner = this.getOwner();
        return (!this.isToggleEvent(e) || !owner.getExpanderOnly() || owner.getSelectOnExpander());
    },
    isToggleEvent: function(e) {
        var isExpand = false;
        if (this.getOwner().getExpanderOnly()) {
            isExpand = e.target === this.expanderElement.dom;
        } else {
            
            isExpand = !this.itemContainer.contains(e.target);
        }
        return isExpand;
    },
    nodeCollapseBegin: function(animation, collapsingForExpand) {
        var me = this,
            itemContainer = me.itemContainer,
            height;
        if (me.expanding) {
            me.stopAnimation(me.expanding);
        }
        
        
        
        height = animation && itemContainer.getHeight();
        me.callParent([
            animation,
            collapsingForExpand
        ]);
        if (animation) {
            
            itemContainer.dom.style.display = 'block';
            me.collapsingForExpand = collapsingForExpand;
            me.collapsing = this.runAnimation(Ext.merge({
                from: {
                    height: height
                },
                to: {
                    height: 0
                },
                callback: me.nodeCollapseDone,
                scope: me
            }, animation));
        }
    },
    nodeCollapseDone: function(animation) {
        var me = this,
            itemContainer = me.itemContainer;
        
        
        if (!me.destroying && !me.destroyed) {
            me.collapsing = null;
            itemContainer.dom.style.display = '';
            itemContainer.setHeight(null);
            me.nodeCollapseEnd(me.collapsingForExpand);
        }
    },
    nodeExpandBegin: function(animation) {
        var me = this,
            itemContainer = me.itemContainer,
            height;
        if (me.collapsing) {
            me.stopAnimation(me.collapsing);
        }
        me.callParent([
            animation
        ]);
        if (animation) {
            
            height = itemContainer.getHeight();
            itemContainer.setHeight(0);
            me.expanding = me.runAnimation(Ext.merge({
                to: {
                    height: height
                },
                callback: me.nodeExpandDone,
                scope: me
            }, animation));
        }
    },
    nodeExpandDone: function() {
        this.expanding = null;
        this.itemContainer.setHeight(null);
        this.nodeExpandEnd();
    },
    removeItem: function(item) {
        this.itemContainer.removeChild(item.element);
    },
    
    
    updateNode: function(node, oldNode) {
        this.syncIndent();
        this.callParent([
            node,
            oldNode
        ]);
    },
    updateExpandable: function(expandable) {
        var node = this.getNode();
        this.updateExpandCls();
        if (node) {
            node.set('expandable', expandable);
        }
    },
    updateExpanded: function(expanded) {
        var node = this.getNode();
        this.updateExpandCls();
        if (node) {
            node.set('expanded', expanded);
        }
    },
    updateIconCls: function(iconCls, oldIconCls) {
        var me = this,
            el = me.element;
        me.doIconCls(me.iconElement, iconCls, oldIconCls);
        me.doIconCls(me.toolElement, iconCls, oldIconCls);
        el.toggleCls(me.withIconCls, !!iconCls);
        
        el.toggleCls(me.hideIconCls, iconCls === null);
    },
    updateLeaf: function(leaf) {
        this.element.toggleCls(this.leafCls, leaf);
    },
    updateLoading: function(loading) {
        this.element.toggleCls(this.loadingCls, loading);
    },
    updateOver: function(over) {
        var me = this;
        me.element.toggleCls(me.hoverCls, !!over);
        
        me.rowElement.toggleCls(me.rowHoverCls, over > 1);
    },
    
    updateRowCls: function(value, oldValue) {
        this.rowElement.replaceCls(oldValue, value);
    },
    updateSelected: function(selected, oldSelected) {
        var me = this,
            cls = me.selectedCls,
            tool = me.getToolElement();
        me.callParent([
            selected,
            oldSelected
        ]);
        me.element.toggleCls(cls, selected);
        if (tool) {
            tool.toggleCls(cls, selected);
        }
    },
    updateSelectedParent: function(selectedParent) {
        var me = this;
        me.element.toggleCls(me.selectedParentCls, selectedParent);
        var tool = me.getToolElement();
        if (tool) {
            tool.toggleCls(me.selectedCls, selectedParent);
        }
    },
    updateText: function(text) {
        this.textElement.update(text);
    },
    
    
    privates: {
        doNodeUpdate: function(node) {
            this.callParent([
                node
            ]);
            this.setRowCls(node && node.data[this.rowClsProperty]);
        },
        doIconCls: function(element, iconCls, oldIconCls) {
            if (oldIconCls) {
                element.removeCls(oldIconCls);
            }
            if (iconCls) {
                element.addCls(iconCls);
            }
        },
        syncIndent: function() {
            var me = this,
                indent = me.getIndent(),
                node = me.getNode(),
                depth;
            if (node) {
                depth = node.data.depth - 1;
                me.wrapElement.dom.style.marginLeft = (depth * indent) + 'px';
            }
        },
        updateExpandCls: function() {
            if (!this.updatingExpandCls) {
                var me = this,
                    expandable = me.getExpandable(),
                    element = me.element,
                    expanded = me.getExpanded(),
                    expandedCls = me.expandedCls,
                    collapsedCls = me.collapsedCls;
                me.updatingExpandCls = true;
                element.toggleCls(me.expandableCls, expandable);
                if (expandable) {
                    element.toggleCls(expandedCls, expanded);
                    element.toggleCls(collapsedCls, !expanded);
                } else {
                    element.removeCls([
                        expandedCls,
                        collapsedCls
                    ]);
                }
                me.updatingExpandCls = false;
            }
        },
        updateIndent: function(value, oldValue) {
            this.syncIndent();
            this.callParent([
                value,
                oldValue
            ]);
        }
    }
}, function(TreeItem) {
    TreeItem.prototype.floatedCls = [
        Ext.Widget.prototype.floatedCls,
        Ext.baseCSSPrefix + 'treelist-item-floated'
    ];
});


Ext.define('Ext.overrides.list.TreeItem', {
    override: 'Ext.list.TreeItem',
    runAnimation: function(animation) {
        return this.itemContainer.animate(animation);
    },
    stopAnimation: function(animation) {
        animation.end();
    },
    refreshInnerState: Ext.emptyFn,
    applyFloated: function(floated, wasFloated) {
        this.initialized = true;
        this.callParent([
            floated,
            wasFloated
        ]);
        return floated;
    },
    updateFloated: function(floated, wasFloated) {
        var me = this,
            ownerTree,
            toolElement = me.getToolElement(),
            node, wasExpanded;
        if (floated) {
            me.wasExpanded = me.getExpanded();
            me.nextElementSibling = me.el.dom.nextSibling;
            me.setExpanded(true);
        } else {
            wasExpanded = me.wasExpanded;
            node = me.getNode();
            me.setExpanded(me.wasExpanded);
            if (!wasExpanded && node.isExpanded()) {
                me.preventAnimation = true;
                node.collapse();
                me.preventAnimation = false;
            }
        }
        me.callParent([
            floated,
            wasFloated
        ]);
        if (floated) {
            
            ownerTree = me.getOwner();
            me.floatWrap.addCls([
                Ext.baseCSSPrefix + 'treelist',
                ownerTree.uiPrefix + ownerTree.getUi(),
                Ext.baseCSSPrefix + 'treelist-float-wrap'
            ]);
            me.floatWrap.alignTo(toolElement, 'tl-tr');
            me.floatWrap.on({
                click: ownerTree.onClick,
                mouseover: ownerTree.onMouseOver,
                scope: ownerTree
            });
        } else {
            
            me.getOwner().rootItem.el.dom.insertBefore(me.el.dom, me.nextElementSibling);
        }
        toolElement.toggleCls(me.floatedToolCls, floated);
    }
});


Ext.define('Ext.list.Tree', {
    extend: Ext.Gadget,
    xtype: 'treelist',
    mixins: [
        Ext.mixin.ItemRippler
    ],
    expanderFirstCls: Ext.baseCSSPrefix + 'treelist-expander-first',
    expanderOnlyCls: Ext.baseCSSPrefix + 'treelist-expander-only',
    highlightPathCls: Ext.baseCSSPrefix + 'treelist-highlight-path',
    microCls: Ext.baseCSSPrefix + 'treelist-micro',
    uiPrefix: Ext.baseCSSPrefix + 'treelist-',
    
    element: {
        reference: 'element',
        cls: Ext.baseCSSPrefix + 'treelist ' + Ext.baseCSSPrefix + 'unselectable',
        listeners: {
            click: 'onClick',
            touchstart: 'onTouchStart',
            touchend: 'onTouchEnd',
            mouseenter: 'onMouseEnter',
            mouseleave: 'onMouseLeave',
            mouseover: 'onMouseOver'
        },
        children: [
            {
                reference: 'toolsElement',
                cls: Ext.baseCSSPrefix + 'treelist-toolstrip',
                listeners: {
                    click: 'onToolStripClick',
                    mouseover: 'onToolStripMouseOver'
                }
            }
        ]
    },
    cachedConfig: {
        animation: {
            duration: 500,
            easing: 'ease'
        },
        
        expanderFirst: true,
        
        expanderOnly: true
    },
    config: {
        
        floatLeafItems: false,
        
        defaults: {
            xtype: 'treelistitem'
        },
        
        highlightPath: null,
        iconSize: null,
        indent: null,
        
        micro: false,
        overItem: null,
        
        selection: null,
        
        selectOnExpander: false,
        
        singleExpand: null,
        
        store: null,
        
        ui: null
    },
    
    
    twoWayBindable: {
        selection: 1
    },
    
    publishes: {
        selection: 1
    },
    
    defaultBindProperty: 'store',
    constructor: function(config) {
        this.callParent([
            config
        ]);
        
        
        this.publishState('selection', this.getSelection());
    },
    destroy: function() {
        var me = this;
        me.unfloatAll();
        me.activeFloater = null;
        me.setSelection(null);
        me.setStore(null);
        me.callParent();
    },
    updateOverItem: function(over, wasOver) {
        var map = {},
            state = 2,
            c, node;
        
        
        
        for (c = over; c; c = this.getItem(node.parentNode)) {
            node = c.getNode();
            map[node.internalId] = true;
            c.setOver(state);
            state = 1;
        }
        
        
        
        if (wasOver && !wasOver.destroyed) {
            
            
            
            
            for (c = wasOver; c; c = this.getItem(node.parentNode)) {
                node = c.getNode();
                if (map[node.internalId]) {
                    break;
                }
                c.setOver(0);
            }
        }
    },
    applyMicro: function(micro) {
        return Boolean(micro);
    },
    applySelection: function(selection, oldSelection) {
        var store = this.getStore();
        if (!store) {
            selection = null;
        }
        if (store && selection !== null && !(selection instanceof Ext.data.Model)) {
            selection = store.getNodeById(selection);
        }
        if (selection && selection.get('selectable') === false) {
            selection = oldSelection;
        }
        return selection;
    },
    updateSelection: function(selection, oldSelection) {
        var me = this,
            item, parent;
        if (!me.destroying) {
            
            
            item = me.getItem(oldSelection);
            if (item) {
                item.setSelected(false);
            }
            item = me.getItem(selection);
            if (item) {
                item.setSelected(true);
                while (parent = item.getParentItem()) {
                    parent.setExpanded(true);
                    item = parent;
                }
            }
            me.fireEvent('selectionchange', me, selection);
        }
    },
    applyStore: function(store) {
        return store && Ext.StoreManager.lookup(store, 'tree');
    },
    updateStore: function(store, oldStore) {
        var me = this,
            root;
        if (oldStore) {
            
            if (!oldStore.destroyed) {
                if (oldStore.getAutoDestroy()) {
                    oldStore.destroy();
                } else {
                    me.storeListeners.destroy();
                }
            }
            me.removeRoot();
            me.storeListeners = null;
        }
        if (store) {
            me.storeListeners = store.on({
                destroyable: true,
                scope: me,
                filterchange: 'onFilterChange',
                nodeappend: 'onNodeAppend',
                nodecollapse: 'onNodeCollapse',
                nodeexpand: 'onNodeExpand',
                nodeinsert: 'onNodeInsert',
                noderemove: 'onNodeRemove',
                rootchange: 'onRootChange',
                update: 'onNodeUpdate'
            });
            root = store.getRoot();
            if (root) {
                me.createRootItem(root);
            }
        }
        if (!me.destroying) {
            me.updateLayout();
        }
    },
    updateExpanderFirst: function(expanderFirst) {
        this.element.toggleCls(this.expanderFirstCls, expanderFirst);
    },
    updateExpanderOnly: function(value) {
        this.element.toggleCls(this.expanderOnlyCls, !value);
    },
    updateHighlightPath: function(updatePath) {
        this.element.toggleCls(this.highlightPathCls, updatePath);
    },
    updateMicro: function(micro) {
        var me = this;
        if (!micro) {
            me.unfloatAll();
            me.activeFloater = null;
        }
        me.element.toggleCls(me.microCls, micro);
    },
    updateUi: function(ui, oldValue) {
        var me = this,
            el = me.element,
            uiPrefix = me.uiPrefix;
        if (oldValue) {
            el.removeCls(uiPrefix + oldValue);
        }
        if (ui) {
            el.addCls(uiPrefix + ui);
        }
        
        delete me.iconSize;
        me.syncIconSize();
    },
    
    getItem: function(node) {
        var map = this.itemMap,
            ret;
        if (node && map) {
            ret = map[node.internalId];
        }
        return ret || null;
    },
    
    getItemConfig: function(node, parent) {
        return Ext.apply({
            parentItem: parent.isRootListItem ? null : parent,
            owner: this,
            node: node,
            indent: this.getIndent()
        }, this.getDefaults());
    },
    privates: {
        checkForOutsideClick: function(e) {
            var floater = this.activeFloater;
            if (!floater.element.contains(e.target)) {
                this.unfloatAll();
            }
        },
        collapsingForExpand: false,
        
        createItem: function(node, parent) {
            var me = this,
                item = Ext.create(me.getItemConfig(node, parent)),
                toolsElement = me.toolsElement,
                toolEl, previousSibling;
            if (parent.isRootListItem) {
                toolEl = item.getToolElement();
                if (toolEl) {
                    previousSibling = me.findVisiblePreviousSibling(node);
                    if (!previousSibling) {
                        toolsElement.insertFirst(toolEl);
                    } else {
                        previousSibling = me.getItem(previousSibling);
                        toolEl.insertAfter(previousSibling.getToolElement());
                    }
                    toolEl.dom.setAttribute('data-recordId', node.internalId);
                    toolEl.isTool = true;
                }
            }
            me.itemMap[node.internalId] = item;
            return item;
        },
        
        createRootItem: function(root) {
            var me = this,
                item;
            me.itemMap = {};
            me.rootItem = item = new Ext.list.RootTreeItem({
                indent: me.getIndent(),
                node: root,
                owner: me
            });
            me.element.appendChild(item.element);
            me.itemMap[root.internalId] = item;
        },
        findVisiblePreviousSibling: function(node) {
            var sibling = node.previousSibling;
            while (sibling) {
                if (sibling.data.visible) {
                    return sibling;
                }
                sibling = sibling.previousSibling;
            }
            return null;
        },
        floatItem: function(item, byHover) {
            var me = this,
                floater;
            if (item.getFloated()) {
                return;
            }
            
            if (me.toolMouseListeners) {
                me.toolMouseListeners.destroy();
                me.floaterMouseListeners.destroy();
                me.floaterMouseListeners = me.toolMouseListeners = null;
            }
            me.unfloatAll();
            if (!byHover && !me.getFloatLeafItems() && item.getNode().isLeaf()) {
                return;
            }
            me.activeFloater = floater = item;
            me.floatedByHover = byHover;
            item.setFloated(true);
            if (byHover) {
                
                me.toolMouseListeners = item.getToolElement().monitorMouseLeave(300, me.checkForMouseLeave, me);
                me.floaterMouseListeners = (item.floater || item).el.monitorMouseLeave(300, me.checkForMouseLeave, me);
                floater.element.on('mouseover', 'onMouseOver', me);
            } else {
                Ext.on('mousedown', 'checkForOutsideClick', me);
            }
        },
        shouldRippleItem: function(item, e) {
            if (item && item.getSelected()) {
                return false;
            }
            return this.mixins.itemrippler.shouldRippleItem.call(this, item, e);
        },
        onTouchStart: function(e) {
            this.doItemRipple(e);
        },
        onTouchEnd: function(e) {
            this.doItemRipple(e);
        },
        doItemRipple: function(e) {
            var me = this,
                item = e.getTarget('[data-recordId]'),
                id;
            if (item) {
                id = item.getAttribute('data-recordId');
                item = me.itemMap[id];
                if (item && me.shouldRippleItem(item, e)) {
                    this.rippleItem(item, e);
                }
            }
        },
        
        onClick: function(e) {
            var item = e.getTarget('[data-recordId]'),
                id;
            if (item) {
                id = item.getAttribute('data-recordId');
                item = this.itemMap[id];
                if (item) {
                    item.onClick(e);
                }
            }
        },
        onMouseEnter: function(e) {
            this.onMouseOver(e);
        },
        onMouseLeave: function() {
            this.setOverItem(null);
        },
        onMouseOver: function(e) {
            var comp = Ext.Component.from(e);
            this.setOverItem(comp && comp.isTreeListItem && comp);
        },
        checkForMouseLeave: function(e) {
            var floater = this.activeFloater,
                relatedTarget = e.getRelatedTarget();
            if (floater) {
                if (relatedTarget !== floater.getToolElement().dom && !floater.element.contains(relatedTarget)) {
                    this.unfloatAll();
                }
            }
        },
        onFilterChange: function(store) {
            
            
            this.onRootChange(store.getRoot());
        },
        
        onNodeAppend: function(parentNode, node) {
            
            if (parentNode) {
                var item = this.itemMap[parentNode.internalId];
                if (item) {
                    item.nodeInsert(node, null);
                }
            }
        },
        
        onNodeCollapse: function(node) {
            var item = this.itemMap[node.internalId];
            if (item) {
                item.nodeCollapse(node, this.collapsingForExpand);
            }
        },
        
        onNodeExpand: function(node) {
            var me = this,
                item = me.itemMap[node.internalId],
                childNodes, len, i, parentNode, child;
            if (item) {
                if (!item.isRootItem && me.getSingleExpand()) {
                    me.collapsingForExpand = true;
                    parentNode = (item.getParentItem() || me.rootItem).getNode();
                    childNodes = parentNode.childNodes;
                    for (i = 0 , len = childNodes.length; i < len; ++i) {
                        child = childNodes[i];
                        if (child !== node) {
                            child.collapse();
                        }
                    }
                    me.collapsing = false;
                }
                item.nodeExpand(node);
            }
        },
        
        onNodeInsert: function(parentNode, node, refNode) {
            var item = this.itemMap[parentNode.internalId];
            if (item) {
                item.nodeInsert(node, refNode);
            }
        },
        
        onNodeRemove: function(parentNode, node, isMove) {
            
            
            
            if (parentNode && !isMove) {
                var item = this.itemMap[parentNode.internalId];
                if (item) {
                    item.nodeRemove(node);
                }
            }
        },
        
        onNodeUpdate: function(store, node, type, modifiedFieldNames) {
            var item = this.itemMap[node.internalId];
            if (item) {
                item.nodeUpdate(node, modifiedFieldNames);
            }
        },
        
        onRootChange: function(root) {
            var me = this;
            me.removeRoot();
            if (root) {
                me.createRootItem(root);
            }
            me.updateLayout();
            me.fireEvent('refresh', me);
        },
        
        removeItem: function(node) {
            var map = this.itemMap,
                id = node.internalId,
                item, toolEl;
            if (map) {
                item = map[id];
                
                if (item.getParentItem() === null) {
                    toolEl = item.getToolElement();
                    if (toolEl) {
                        this.toolsElement.removeChild(toolEl);
                    }
                }
                delete map[id];
            }
        },
        removeRoot: function() {
            var me = this,
                rootItem = me.rootItem;
            if (rootItem) {
                me.element.removeChild(rootItem.element);
                me.rootItem = me.itemMap = Ext.destroy(rootItem);
            }
        },
        
        onToolStripClick: function(e) {
            var item = e.getTarget('[data-recordId]'),
                id;
            if (item) {
                id = item.getAttribute('data-recordId');
                item = this.itemMap[id];
                if (item) {
                    if (item === this.activeFloater) {
                        this.unfloatAll();
                    } else {
                        this.floatItem(item, false);
                    }
                }
            }
        },
        
        onToolStripMouseOver: function(e) {
            var item = e.getTarget('[data-recordId]'),
                id;
            if (item) {
                id = item.getAttribute('data-recordId');
                item = this.itemMap[id];
                if (item) {
                    this.floatItem(item, true);
                }
            }
        },
        syncIconSize: function() {
            var me = this,
                size = me.iconSize || (me.iconSize = parseInt(me.element.getStyle('background-position'), 10));
            me.setIconSize(size);
        },
        unfloatAll: function() {
            var me = this,
                floater = me.activeFloater;
            if (floater) {
                floater.setFloated(false);
                me.activeFloater = null;
                if (me.floatedByHover) {
                    if (me.toolMouseListeners) {
                        me.toolMouseListeners.destroy();
                        me.floaterMouseListeners.destroy();
                        me.floaterMouseListeners = me.toolMouseListeners = null;
                    }
                    floater.element.un('mouseover', 'onMouseOver', me);
                } else {
                    Ext.un('mousedown', 'checkForOutsideClick', me);
                }
            }
        },
        defaultIconSize: 22,
        updateIconSize: function(value) {
            this.setIndent(value || this.defaultIconSize);
        },
        updateIndent: function(value) {
            var rootItem = this.rootItem;
            if (rootItem) {
                rootItem.setIndent(value);
            }
        }
    }
});


Ext.define('Ext.overrides.list.Tree', {
    override: 'Ext.list.Tree',
    constructor: function(config) {
        var me = this,
            el;
        me.callParent([
            config
        ]);
        el = me.element;
        if (el.isPainted()) {
            me.syncIconSize();
        } else {
            el.on({
                scope: me,
                painted: me.syncIconSize,
                single: true
            });
        }
    }
});


Ext.define('Ext.mixin.ConfigProxy', function(ConfigProxy) {
    return {
        extend: Ext.Mixin,
        mixinConfig: {
            id: 'configproxy',
            extended: function(baseClass, derivedClass, classBody) {
                var proxyConfig = classBody.proxyConfig;
                derivedClass.$configProxies = Ext.apply({}, derivedClass.superclass.self.$configProxies);
                if (proxyConfig) {
                    delete classBody.proxyConfig;
                    ConfigProxy.processClass(derivedClass, proxyConfig);
                }
            }
        },
        onClassMixedIn: function(targetClass) {
            var prototype = targetClass.prototype,
                proxyConfig = prototype.proxyConfig,
                initConfig = prototype.initConfig;
            prototype.$proxiedConfigs = null;
            
            targetClass.$configProxies = {};
            
            prototype.initConfig = function(config) {
                initConfig.apply(this, arguments);
                
                this.$proxiedConfigs = null;
                return this;
            };
            if (proxyConfig) {
                delete prototype.proxyConfig;
                ConfigProxy.processClass(targetClass, proxyConfig);
            }
        },
        
        getProxiedConfigs: function(name) {
            var me = this,
                configs = me.config,
                
                configProxies = me.self.$configProxies[name],
                i = configProxies && configProxies.length,
                cfg, proxiedConfigs, ret, s, v;
            if (i && me.isConfiguring) {
                
                
                proxiedConfigs = me.$proxiedConfigs || (me.$proxiedConfigs = {});
                while (i-- > 0) {
                    cfg = configProxies[i];
                    proxiedConfigs[s = cfg.name] = cfg;
                    if ((v = configs[s]) !== undefined) {
                        (ret || (ret = {}))[s] = v;
                    }
                }
            }
            return ret;
        },
        
        mergeProxiedConfigs: function(name, itemConfig, alwaysClone) {
            var me = this,
                ret = itemConfig,
                proxied = me.getProxiedConfigs(name),
                configurator;
            if (proxied) {
                if (!itemConfig) {
                    ret = proxied;
                } else if (itemConfig.constructor === Object) {
                    configurator = me.self.getConfigurator();
                    
                    ret = configurator.merge(me, Ext.clone(itemConfig), proxied);
                }
            }
            if (alwaysClone && ret === itemConfig) {
                ret = Ext.clone(ret);
            }
            return ret;
        },
        statics: {
            processClass: function(targetClass, proxyConfig) {
                var ExtConfig = Ext.Config,
                    targetProto = targetClass.prototype,
                    add = {},
                    proxies = targetClass.$configProxies,
                    cfg, configs, itemGetter, i, item, methods, n, name, proxiedConfigs, s;
                for (item in proxyConfig) {
                    itemGetter = ExtConfig.get(item).names.get;
                    configs = proxyConfig[item];
                    if (Ext.isArray(configs)) {
                        methods = null;
                    } else {
                        methods = configs.methods;
                        configs = configs.configs;
                    }
                    if (!(proxiedConfigs = proxies[item])) {
                        proxies[item] = proxiedConfigs = [];
                    } else {
                        
                        proxies[item] = proxiedConfigs = proxiedConfigs.slice();
                    }
                    for (i = 0 , n = methods && methods.length; i < n; ++i) {
                        if (!targetProto[name = methods[i]]) {
                            targetProto[name] = ConfigProxy.wrapFn(itemGetter, name);
                        } else 
                        {
                            Ext.raise('Cannot proxy method "' + name + '"');
                        }
                    }
                    
                    for (i = 0 , n = configs && configs.length; i < n; ++i) {
                        cfg = ExtConfig.get(s = configs[i]);
                        
                        if (s in add) {
                            Ext.raise('Duplicate proxy config definitions for "' + s + '"');
                        }
                        if (s in targetProto.config) {
                            Ext.raise('Config "' + s + '" already defined for class ' + targetProto.$className);
                        }
                        
                        add[s] = undefined;
                        
                        proxiedConfigs.push(cfg);
                        if (!targetProto[name = cfg.names.get]) {
                            targetProto[name] = ConfigProxy.wrapGet(itemGetter, name);
                        } else 
                        {
                            Ext.raise('Cannot proxy "' + s + '" config getter');
                        }
                        
                        if (!targetProto[name = cfg.names.set]) {
                            targetProto[name] = ConfigProxy.wrapSet(itemGetter, name, s);
                        } else 
                        {
                            Ext.raise('Cannot proxy "' + s + '" config setter');
                        }
                    }
                }
                
                targetClass.addConfig(add);
            },
            wrapFn: function(itemGetter, name) {
                return function() {
                    var item = this[itemGetter]();
                    return item && item[name].apply(item, arguments);
                };
            },
            wrapGet: function(itemGetter, configGetter) {
                return function() {
                    var item = this[itemGetter]();
                    return item && item[configGetter]();
                };
            },
            wrapSet: function(itemGetter, configSetter, itemName) {
                return function(value) {
                    var me = this,
                        item, proxiedConfigs;
                    
                    
                    if (!me.isConfiguring || value !== undefined) {
                        
                        
                        
                        item = me[itemGetter]();
                        proxiedConfigs = me.$proxiedConfigs;
                        
                        if (proxiedConfigs && proxiedConfigs[itemName]) {
                            delete proxiedConfigs[itemName];
                            
                            item = null;
                        }
                        if (item) {
                            item[configSetter](value);
                        }
                    }
                    return me;
                };
            }
        }
    };
});


Ext.define('Ext.mixin.ConfigState', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'configstate'
    },
    alternateStateConfig: '',
    toggleConfigState: function(isAlternate) {
        var me = this,
            state = me.capturedConfigState,
            cfg = me.getConfig(me.alternateStateConfig),
            key;
        if (!cfg) {
            return;
        }
        if (isAlternate) {
            state = {};
            for (key in cfg) {
                state[key] = me.getConfig(key);
            }
            me.capturedConfigState = state;
            me.setConfig(cfg);
        }
        
        else if (!me.isConfiguring && state) {
            me.setConfig(state);
            delete me.capturedConfigState;
        }
    }
});



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,
                    xtypes = targetClass.prototype.xtypes,
                    mashup = Ext.manifest.mashup || {},
                    options = body.mashupConfig,
                    i, script;
                if (requiredScripts) {
                    delete body.requiredScripts;
                    hooks.onCreated = function() {
                        var me = this,
                            scripts = [],
                            args = Ext.Array.slice(arguments),
                            redirect = mashup.redirect || {};
                        requiredScripts = scripts.concat(requiredScripts);
                        options = options && mashup[options.key];
                        if (xtypes) {
                            for (i = 0; !options && i < xtypes.length; ++i) {
                                options = mashup[xtypes[i]];
                            }
                        }
                        for (i = 0; i < requiredScripts.length; i++) {
                            script = requiredScripts[i];
                            if (redirect[script] === false) {
                                
                                continue;
                            }
                            script = redirect[script] || script;
                            if (script.indexOf('{') > -1) {
                                if (options) {
                                    script = new Ext.Template(script).apply(options);
                                } else 
                                {
                                    Ext.log.error('Missing mashup options for ' + body.$className + ' script "' + script + '"');
                                }
                            }
                            
                            scripts.push(script);
                        }
                        if (!scripts.length) {
                            hooks.onCreated = onCreated;
                            hooks.onCreated.call(me, args);
                            return;
                        }
                        Ext.Loader.loadScripts({
                            url: scripts,
                            cache: true,
                            
                            onError: function(opts, error) {
                                targetClass.scriptError = targetClass.prototype.scriptError = error;
                                hooks.onCreated = onCreated;
                                hooks.onCreated.call(me, args);
                            },
                            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,
        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);
        },
        
        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 = Ext.unasap(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.asap(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.self.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,
        
        selected: true,
        
        pruneRemoved: true,
        
        selection: null,
        
        twoWayBindable: {
            selection: 1
        },
        
        publishes: {
            selection: 1
        }
    },
    modes: {
        SINGLE: true,
        SIMPLE: true,
        MULTI: true
    },
    onNavigate: function(event) {},
    selectableEventHooks: {
        add: 'onSelectionStoreAdd',
        remove: 'onSelectionStoreRemove',
        update: 'onSelectionStoreUpdate',
        clear: {
            fn: 'onSelectionStoreClear',
            priority: 1000
        },
        load: 'refreshSelection',
        refresh: 'refreshSelection'
    },
    initSelectable: function() {
        this.publishState('selection', this.getSelection());
    },
    applySelected: function(selected) {
        if (!selected.isCollection) {
            selected = new Ext.util.Collection(selected);
        }
        
        
        selected.addObserver(this);
        return selected;
    },
    
    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.on(bindEvents);
            me.refreshSelection();
        }
    },
    
    selectAll: function(silent) {
        var me = this,
            selections = me.getStore().getRange();
        me.select(selections, true, silent);
    },
    
    deselectAll: function(supress) {
        var me = this;
        me.deselect(me.getSelected().getRange(), supress);
        me.setLastSelected(null);
        me.setLastFocused(null);
    },
    updateSelection: function(selection) {
        if (this.changingSelection) {
            return;
        }
        if (selection) {
            this.select(selection);
        } else {
            this.deselectAll();
        }
    },
    
    
    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) {
        this.doMultiSelect([
            record
        ], false, suppressEvent);
    },
    
    doMultiSelect: function(records, keepExisting, suppressEvent) {
        if (records === null || this.getDisableSelection()) {
            return;
        }
        records = !Ext.isArray(records) ? [
            records
        ] : records;
        var me = this,
            selected = me.getSelected(),
            selectionCount = selected.getCount(),
            toRemove = [],
            ln = records.length,
            change = false,
            i = 0,
            record;
        if (!keepExisting && selectionCount) {
            toRemove = selected.getRange();
        }
        
        for (i = 0; i < ln; i++) {
            record = records[i];
            if (typeof record === 'number') {
                records[i] = store.getAt(record);
            }
        }
        
        
        
        selected.suppressEvent = suppressEvent;
        selected.splice(selectionCount, toRemove, records);
        selected.suppressEvent = false;
    },
    
    deselect: function(records, suppressEvent) {
        var me = this;
        if (me.getDisableSelection()) {
            return;
        }
        records = Ext.isArray(records) ? records : [
            records
        ];
        var selected = me.getSelected(),
            store = me.getStore(),
            len = records.length,
            i, record;
        
        for (i = 0; i < len; i++) {
            record = records[i];
            if (typeof record === 'number') {
                records[i] = store.getAt(record);
            }
        }
        
        
        
        selected.suppressEvent = suppressEvent;
        selected.remove(records);
        selected.suppressEvent = false;
    },
    
    onCollectionRemove: function(selectedCollection, chunk) {
        var me = this,
            lastSelected = me.getLastSelected(),
            records = chunk.items;
        
        if (lastSelected && !selectedCollection.contains(lastSelected)) {
            me.setLastSelected(selectedCollection.last());
        }
        me.onItemDeselect(records, selectedCollection.suppressEvent);
        if (!selectedCollection.suppressEvent) {
            me.fireSelectionChange(records);
        }
    },
    
    onCollectionAdd: function(selectedCollection, adds) {
        var me = this,
            records = adds.items;
        
        me.setLastSelected(selectedCollection.last());
        me.onItemSelect(records, selectedCollection.suppressEvent);
        if (!selectedCollection.suppressEvent) {
            me.fireSelectionChange(records);
        }
    },
    
    
    updateLastFocused: function(newRecord, oldRecord) {
        this.onLastFocusChanged(oldRecord, newRecord);
    },
    fireSelectionChange: function(records) {
        var me = this;
        me.changingSelection = true;
        me.setSelection(me.getLastSelected() || null);
        me.changingSelection = false;
        me.fireAction('selectionchange', [
            me,
            records
        ], 'getSelections');
    },
    
    getSelections: function() {
        return this.getSelected().getRange();
    },
    
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.getStore().getAt(record) : record;
        return this.getSelected().indexOf(record) !== -1;
    },
    
    hasSelection: function() {
        return this.getSelected().getCount() > 0;
    },
    
    refreshSelection: function() {
        var me = this,
            selected = me.getSelected(),
            selections = selected.getRange(),
            selectionLength = selections.length,
            storeCollection = me.getStore().getData(),
            toDeselect = [],
            toReselect = [],
            i, rec, matchingSelection;
        
        if (me.getPruneRemoved()) {
            
            
            
            storeCollection = storeCollection.getSource() || storeCollection;
            for (i = 0; i < selectionLength; i++) {
                rec = selections[i];
                matchingSelection = storeCollection.get(storeCollection.getKey(rec));
                if (matchingSelection) {
                    if (matchingSelection !== rec) {
                        toDeselect.push(rec);
                        toReselect.push(matchingSelection);
                    }
                } else {
                    toDeselect.push(rec);
                }
            }
        }
        
        
        
        
        
        selected.suppressEvent = true;
        selected.splice(selected.getCount(), toDeselect, toReselect);
        selected.suppressEvent = false;
    },
    
    
    
    onSelectionStoreRemove: function(store, records) {
        var me = this,
            selected = me.getSelected(),
            ln = records.length,
            removed, 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);
                }
                removed = removed || [];
                removed.push(record);
            }
        }
        if (removed) {
            me.fireSelectionChange([
                removed
            ]);
        }
    },
    onSelectionStoreClear: function(store) {
        var records = store.getData().items;
        this.onSelectionStoreRemove(store, records);
    },
    
    getSelectionCount: function() {
        return this.getSelected().getCount();
    },
    onSelectionStoreAdd: Ext.emptyFn,
    onSelectionStoreUpdate: Ext.emptyFn,
    onItemSelect: Ext.emptyFn,
    onItemDeselect: Ext.emptyFn,
    onLastFocusChanged: Ext.emptyFn,
    onEditorKey: Ext.emptyFn
}, function() {});











Ext.define('Ext.mixin.StoreWatcher', {
    mixinId: 'storewatcher',
    config: {
        dataSource: null,
        
        owner: null,
        
        ownerListeners: {
            destroyable: true,
            storechange: 'onStoreChange'
        },
        
        sourceListeners: null,
        store: null,
        
        storeListeners: null
    },
    afterClassMixedIn: function(targetClass) {
        var configurator = this.getConfigurator(),
            prototype = targetClass.prototype,
            config = {},
            prop;
        for (prop in configurator.configs) {
            
            
            
            if (prototype.hasOwnProperty(prop)) {
                config[prop] = prototype[prop];
                delete prototype[prop];
            }
        }
        targetClass.addConfig(config);
    },
    onFilterChange: function(store) {
        var source;
        if (!store) {
            source = null;
        } else if (store.getDataSource) {
            source = store.getDataSource();
        } else {
            source = store.getData();
        }
        this.setDataSource(source);
    },
    onStoreChange: function(comp, store) {
        this.setStore(store);
    },
    
    
    updateDataSource: function(source) {
        this.syncListeners(source, '$sourceListeners', 'getSourceListeners');
    },
    
    updateOwner: function(owner) {
        this.syncListeners(owner, '$ownerListeners', 'getOwnerListeners');
        this.setStore(owner ? owner.getStore() : null);
    },
    
    applyStore: function(store) {
        return (store && !store.isEmptyStore) ? store : null;
    },
    updateStore: function(store) {
        this.syncListeners(store, '$storeListeners', 'getStoreListeners');
        this.onFilterChange(store);
    },
    privates: {
        syncListeners: function(instance, token, listeners) {
            var me = this,
                old = me[token];
            if (old) {
                me[token] = null;
                old.destroy();
            }
            if (instance) {
                listeners = me[listeners]();
                listeners = Ext.applyIf({
                    destroyable: true,
                    scope: me
                }, listeners);
                me[token] = instance.on(listeners);
            }
        }
    }
});


Ext.define('Ext.mixin.StyleCacher', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'stylecacher'
    },
    getCachedStyle: function(el, style) {
        var cache = this.$styleCache;
        if (!cache) {
            cache = this.$styleCache = {};
        }
        if (!(style in cache)) {
            cache[style] = Ext.fly(el).getStyle(style);
        }
        return cache[style];
    }
});


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 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 {
        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',
    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();
    },
    
    setupLog: function(config) {
        var className, cls, methods, method, override;
        for (className in config) {
            if (config.hasOwnProperty(className)) {
                cls = Ext.ClassManager.get(className);
                if (cls) {
                    methods = config[className];
                    override = {};
                    for (method in methods) {
                        override[method] = (function(methodName, idProp) {
                            return function() {
                                var before, diff, id, idHolder, ret;
                                before = +Date.now();
                                ret = this.callParent(arguments);
                                diff = +Date.now() - before;
                                if (window.console && diff > 0) {
                                    idHolder = idProp === 'this' ? this : typeof idProp === 'string' ? this[idProp] : typeof idProp === 'number' ? arguments[idProp] : null;
                                    if (idHolder) {
                                        id = idHolder.id;
                                    }
                                    if (id != null) {
                                        console.log(methodName + ' for ' + id + ': ' + diff + 'ms');
                                    } else {
                                        console.log(methodName + ' for unknown: ' + diff + 'ms');
                                    }
                                    if (console.trace) {
                                        console.trace();
                                    }
                                }
                                return ret;
                            };
                        })(method, methods[method]);
                    }
                    Ext.override(cls, override);
                }
            }
        }
    }
});


Ext.define('Ext.plugin.AbstractClipboard', {
    extend: Ext.plugin.Abstract,
    cachedConfig: {
        
        formats: {
            text: {
                get: 'getTextData',
                put: 'putTextData'
            }
        }
    },
    config: {
        
        memory: null,
        
        source: 'system',
        
        system: 'text',
        gridListeners: null
    },
    destroy: function() {
        var me = this,
            keyMap = me.keyMap,
            shared = me.shared;
        Ext.destroy(me.destroyListener);
        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,
            listeners = me.getGridListeners();
        if (comp.rendered) {
            me.finishInit(comp);
        } else if (listeners) {
            me.renderListener = comp.on(Ext.apply({
                scope: me,
                destroyable: true,
                single: true
            }, listeners));
        }
    },
    onCmpReady: function() {
        this.renderListener = null;
        this.finishInit(this.getCmp());
    },
    
    getTarget: function(comp) {
        return comp.el;
    },
    
    
    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.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.raise('Invalid clipboard format "' + value[i] + '"');
                    }
                }
            }
            
            return value || null;
        },
        
        applySystem: function(value) {
            var formats = this.getFormats();
            if (!formats[value]) {
                Ext.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;
            if (me.validateAction(event) === false) {
                return;
            }
            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: me.getTarget(comp),
                ignoreInputFields: true,
                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;
            me.destroyListener = comp.on({
                destroyable: true,
                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.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.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,
                el;
            el = shared.textArea;
            if (!el) {
                el = shared.textArea = Ext.getBody().createChild({
                    tag: 'textarea',
                    tabIndex: -1,
                    
                    style: {
                        position: 'absolute',
                        top: '-1000px',
                        width: '1px',
                        height: '1px'
                    }
                });
                
                el.suspendFocusEvents();
            }
            return el;
        },
        onCopy: function(keyCode, event) {
            this.doCutCopy(event, false);
        },
        onCut: function(keyCode, event) {
            this.doCutCopy(event, true);
        },
        onPaste: function(keyCode, event) {
            var me = this,
                sharedData = me.shared.data,
                source = me.getSource(),
                i, n, s;
            if (me.validateAction(event) === false) {
                return;
            }
            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,
                clippy = window.clipboardData,
                area, focusEl;
            if (clippy && clippy.getData) {
                me.doPaste(format, clippy.getData("text"));
            } else {
                focusEl = Ext.Element.getActiveElement(true);
                area = me.getHiddenTextArea().dom;
                area.value = '';
                
                if (focusEl) {
                    focusEl.suspendFocusEvents();
                }
                area.focus();
                
                
                
                Ext.defer(function() {
                    
                    if (focusEl) {
                        focusEl.focus();
                        
                        focusEl.resumeFocusEvents();
                    }
                    me.doPaste(format, area.value);
                    area.value = '';
                }, 100, me);
            }
        },
        setClipboardData: function(data) {
            var clippy = window.clipboardData;
            if (clippy && clippy.setData) {
                clippy.setData("text", data);
            } else {
                var me = this,
                    area = me.getHiddenTextArea().dom,
                    focusEl = Ext.Element.getActiveElement(true);
                area.value = data;
                
                if (focusEl) {
                    focusEl.suspendFocusEvents();
                }
                area.focus();
                area.select();
                
                
                
                Ext.defer(function() {
                    area.value = '';
                    if (focusEl) {
                        focusEl.focus();
                        
                        focusEl.resumeFocusEvents();
                    }
                }, 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;
        },
        validateAction: Ext.privateFn
    }
});


Ext.define('Ext.plugin.MouseEnter', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.mouseenter',
    
    element: 'el',
    
    
    
    
    
    init: function(component) {
        
        if (!this.delegate) {
            Ext.raise('mouseenter plugin must be configured with a delegate selector');
        }
        if (!this.handler) {
            Ext.raise('mouseenter plugin must be configured with handler callback');
        }
        
        var me = this,
            listeners = {
                mouseover: 'onMouseEvent',
                scope: me,
                destroyable: true
            },
            element = me.element;
        
        
        if (me.leaveHandler || me.delay) {
            listeners.mouseout = 'onMouseEvent';
        }
        
        if (typeof element === 'string') {
            element = component[me.element];
        }
        
        
        if (element) {
            me.mouseListener = Ext.get(element).on(listeners);
        } else 
        
        
        {
            component.on({
                render: function() {
                    me.mouseListener = component[me.element].on(listeners);
                },
                single: true
            });
        }
    },
    onMouseEvent: function(e) {
        var me = this,
            delegate = e.getTarget(me.delegate);
        
        if (delegate && delegate !== e.getRelatedTarget(me.delegate)) {
            if (me.delay) {
                Ext.undefer(me.mouseEventTimer);
                me.mouseEventTimer = Ext.defer(me.handleMouseEvent, me.delay, me, [
                    e,
                    delegate
                ]);
            } else {
                me.handleMouseEvent(e, delegate);
            }
        }
    },
    handleMouseEvent: function(e, delegate) {
        var me = this;
        if (e.type === 'mouseover') {
            Ext.callback(me.handler, null, [
                e,
                delegate
            ], 0, me.cmp, me.scope);
        } else if (me.leaveHandler) {
            Ext.callback(me.leaveHandler, null, [
                e,
                delegate
            ], 0, me.cmp, me.scope);
        }
    },
    destroy: function() {
        Ext.destroy(this.mouseListener);
        this.callParent();
    }
});


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


Ext.define('Ext.sparkline.CanvasBase', {
    shapeCount: 0,
    _pxregex: /(\d+)(px)?\s*$/i,
    constructor: function(ownerSparkLine) {
        this.owner = ownerSparkLine;
        this.rtl = this.owner.getInherited().rtl;
    },
    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() {
        
        Ext.raise('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) {
        
        Ext.raise('appendShape not implemented');
    },
    
    
    replaceWithShape: function(shapeid, shape) {
        
        Ext.raise('replaceWithShape not implemented');
    },
    
    
    insertAfterShape: function(shapeid, shape) {
        
        Ext.raise('insertAfterShape not implemented');
    },
    
    
    removeShapeId: function(shapeid) {
        
        Ext.raise('removeShapeId not implemented');
    },
    
    
    getShapeAt: function(x, y) {
        
        Ext.raise('getShapeAt not implemented');
    },
    
    
    render: function() {
        
        Ext.raise('render not implemented');
    }
});



Ext.define('Ext.sparkline.CanvasCanvas', {
    extend: Ext.sparkline.CanvasBase,
    statics: {
        contextOverrides: (function() {
            var ratio = window.devicePixelRatio || 1;
            return {
                moveTo: function(x, y) {
                    
                    if (this.rtl) {
                        x = this.canvas.width - x - 1;
                    }
                    this.$moveTo(x * ratio, y * ratio);
                },
                lineTo: function(x, y) {
                    
                    if (this.rtl) {
                        x = this.canvas.width - x - 1;
                    }
                    this.$lineTo(x * ratio, y * ratio);
                },
                arc: function(x, y, radius, startAngle, endAngle, counterclockwise) {
                    
                    if (this.rtl) {
                        x = this.canvas.width - x - 1;
                    }
                    this.$arc(x * ratio, y * ratio, radius * ratio, startAngle, endAngle, counterclockwise);
                },
                clearRect: function(x, y, width, height) {
                    
                    if (this.rtl) {
                        x = this.canvas.width - x - width;
                    }
                    this.$clearRect(x * ratio, y * ratio, width * ratio, height * ratio);
                }
            };
        })()
    },
    setWidth: function(width) {
        this.callParent(arguments);
        this.owner.element.dom.width = width * (window.devicePixelRatio || 1);
    },
    setHeight: function(height) {
        this.callParent(arguments);
        this.owner.element.dom.height = height * (window.devicePixelRatio || 1);
    },
    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.context,
            overrides, name;
        if (!context) {
            this.context = context = this.el.dom.getContext('2d');
            overrides = Ext.sparkline.CanvasCanvas.contextOverrides;
            for (name in overrides) {
                context['$' + name] = context[name];
            }
            Ext.apply(context, overrides);
            context.rtl = this.rtl;
        }
        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),
            xIncr = this.rtl ? -0.5 : 0.5,
            i, plen;
        context.beginPath();
        context.moveTo(path[0][0] + xIncr, path[0][1] + 0.5);
        for (i = 1 , plen = path.length; i < plen; i++) {
            context.lineTo(path[i][0] + xIncr, 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 != null) {
            context.stroke();
        }
        if (fillColor != null) {
            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) {
        
        if (this.rtl) {
            x = this.el.dom.width - x - 1;
        }
        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,
    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.onInternalReady(function() {
        var doc = document;
        if (doc.namespaces && !doc.namespaces.svml) {
            doc.namespaces.add("svml", "urn:schemas-microsoft-com:vml", '#default#VML');
        }
    });
});


Ext.define('Ext.util.Color', {
    alternateClassName: 'Ext.draw.Color',
    statics: {
        colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,
        rgbToHexRe: /\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/,
        rgbaToHexRe: /\s*rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\.\d]+)\)/,
        hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/,
        
        
        
        NONE: 'none',
        RGBA_NONE: 'rgba(0, 0, 0, 0)'
    },
    isColor: true,
    
    lightnessFactor: 0.2,
    
    constructor: function(red, green, blue, alpha) {
        this.setRGB(red, green, blue, alpha);
    },
    clone: function() {
        var me = this;
        return new this.self(me.r, me.g, me.b, me.a);
    },
    setRGB: function(red, green, blue, alpha) {
        var me = this;
        me.r = Math.min(255, Math.max(0, red));
        me.g = Math.min(255, Math.max(0, green));
        me.b = Math.min(255, Math.max(0, blue));
        if (alpha === undefined) {
            me.a = 1;
        } else {
            me.a = Math.min(1, Math.max(0, alpha));
        }
    },
    
    getBrightness: function() {
        var r = this.r / 255 * 100,
            g = this.g / 255 * 100,
            b = this.b / 255 * 100;
        return ((r * 299) + (g * 587) + (b * 114)) / 1000;
    },
    
    getGrayscale: function() {
        
        return this.r * 0.3 + this.g * 0.59 + this.b * 0.11;
    },
    
    getHSL: function() {
        var me = this,
            r = me.r / 255,
            g = me.g / 255,
            b = me.b / 255,
            max = Math.max(r, g, b),
            min = Math.min(r, g, b),
            delta = max - min,
            h,
            s = 0,
            l = 0.5 * (max + min);
        
        if (min !== max) {
            s = (l <= 0.5) ? delta / (max + min) : delta / (2 - max - min);
            if (r === max) {
                h = 60 * (g - b) / delta;
            } else if (g === max) {
                h = 120 + 60 * (b - r) / delta;
            } else {
                h = 240 + 60 * (r - g) / delta;
            }
            if (h < 0) {
                h += 360;
            }
            if (h >= 360) {
                h -= 360;
            }
        }
        return [
            h,
            s,
            l
        ];
    },
    
    getHSV: function() {
        var me = this,
            r = me.r / 255,
            g = me.g / 255,
            b = me.b / 255,
            max = Math.max(r, g, b),
            min = Math.min(r, g, b),
            C = max - min,
            h,
            s = 0,
            v = max;
        
        if (min != max) {
            s = v ? C / v : 0;
            if (r === max) {
                h = 60 * (g - b) / C;
            } else if (g === max) {
                h = 60 * (b - r) / C + 120;
            } else {
                h = 60 * (r - g) / C + 240;
            }
            if (h < 0) {
                h += 360;
            }
            if (h >= 360) {
                h -= 360;
            }
        }
        return [
            h,
            s,
            v
        ];
    },
    
    setHSL: function(h, s, l) {
        var me = this,
            abs = Math.abs,
            c, x, m;
        h = (h % 360 + 360) % 360;
        s = s > 1 ? 1 : s < 0 ? 0 : s;
        l = l > 1 ? 1 : l < 0 ? 0 : l;
        if (s === 0 || h === null) {
            l *= 255;
            me.setRGB(l, l, l);
        } else {
            
            h /= 60;
            c = s * (1 - abs(2 * l - 1));
            
            x = c * (1 - abs(h % 2 - 1));
            
            m = l - c / 2;
            
            m *= 255;
            c *= 255;
            x *= 255;
            switch (Math.floor(h)) {
                case 0:
                    me.setRGB(c + m, x + m, m);
                    break;
                case 1:
                    me.setRGB(x + m, c + m, m);
                    break;
                case 2:
                    me.setRGB(m, c + m, x + m);
                    break;
                case 3:
                    me.setRGB(m, x + m, c + m);
                    break;
                case 4:
                    me.setRGB(x + m, m, c + m);
                    break;
                case 5:
                    me.setRGB(c + m, m, x + m);
                    break;
            }
        }
        return me;
    },
    
    setHSV: function(h, s, v) {
        var me = this,
            c, x, m;
        h = (h % 360 + 360) % 360;
        s = s > 1 ? 1 : s < 0 ? 0 : s;
        v = v > 1 ? 1 : v < 0 ? 0 : v;
        if (s === 0 || h === null) {
            v *= 255;
            me.setRGB(v, v, v);
        } else {
            
            h /= 60;
            c = v * s;
            
            x = c * (1 - Math.abs(h % 2 - 1));
            
            m = v - c;
            
            m *= 255;
            c *= 255;
            x *= 255;
            switch (Math.floor(h)) {
                case 0:
                    me.setRGB(c + m, x + m, m);
                    break;
                case 1:
                    me.setRGB(x + m, c + m, m);
                    break;
                case 2:
                    me.setRGB(m, c + m, x + m);
                    break;
                case 3:
                    me.setRGB(m, x + m, c + m);
                    break;
                case 4:
                    me.setRGB(x + m, m, c + m);
                    break;
                case 5:
                    me.setRGB(c + m, m, x + m);
                    break;
            }
        }
        return me;
    },
    
    createLighter: function(factor) {
        var color = this.clone();
        color.lighten(factor);
        return color;
    },
    
    lighten: function(factor) {
        if (!factor && factor !== 0) {
            factor = this.lightnessFactor;
        }
        var hsl = this.getHSL();
        this.setHSL(hsl[0], hsl[1], Ext.Number.constrain(hsl[2] + factor, 0, 1));
    },
    
    createDarker: function(factor) {
        var color = this.clone();
        color.darken(factor);
        return color;
    },
    
    darken: function(factor) {
        if (!factor && factor !== 0) {
            factor = this.lightnessFactor;
        }
        return this.lighten(-factor);
    },
    
    toString: function() {
        var me = this,
            round = Math.round;
        if (me.a === 1) {
            var r = round(me.r).toString(16),
                g = round(me.g).toString(16),
                b = round(me.b).toString(16);
            r = (r.length === 1) ? '0' + r : r;
            g = (g.length === 1) ? '0' + g : g;
            b = (b.length === 1) ? '0' + b : b;
            return [
                '#',
                r,
                g,
                b
            ].join('');
        } else {
            return 'rgba(' + [
                round(me.r),
                round(me.g),
                round(me.b),
                me.a === 0 ? 0 : me.a.toFixed(15)
            ].join(', ') + ')';
        }
    },
    
    
    
    
    
    
    toHex: function(color) {
        var r = this.r,
            g = this.g,
            b = this.b,
            rgb = b | (g << 8) | (r << 16);
        return '#' + ('000000' + rgb.toString(16)).slice(-6);
    },
    
    setFromString: function(str) {
        var values, r, g, b,
            a = 1,
            parse = parseInt;
        if (str === Ext.util.Color.NONE) {
            this.r = this.g = this.b = this.a = 0;
            return this;
        }
        if ((str.length === 4 || str.length === 7) && str.substr(0, 1) === '#') {
            values = str.match(Ext.util.Color.hexRe);
            if (values) {
                r = parse(values[1], 16) >> 0;
                g = parse(values[2], 16) >> 0;
                b = parse(values[3], 16) >> 0;
                if (str.length === 4) {
                    r += (r * 16);
                    g += (g * 16);
                    b += (b * 16);
                }
            }
        } else if ((values = str.match(Ext.util.Color.rgbToHexRe))) {
            r = +values[1];
            g = +values[2];
            b = +values[3];
        } else if ((values = str.match(Ext.util.Color.rgbaToHexRe))) {
            r = +values[1];
            g = +values[2];
            b = +values[3];
            a = +values[4];
        } else {
            if (Ext.util.Color.ColorList.hasOwnProperty(str.toLowerCase())) {
                return this.setFromString(Ext.util.Color.ColorList[str.toLowerCase()]);
            }
        }
        if (typeof r === 'undefined') {
            return this;
        }
        this.r = r;
        this.g = g;
        this.b = b;
        this.a = a;
        return this;
    }
}, function() {
    var flyColor = new this();
    this.addStatics({
        
        fly: function(red, green, blue, alpha) {
            switch (arguments.length) {
                case 1:
                    flyColor.setFromString(red);
                    break;
                case 3:
                case 4:
                    flyColor.setRGB(red, green, blue, alpha);
                    break;
                default:
                    return null;
            }
            return flyColor;
        },
        ColorList: {
            aliceblue: '#f0f8ff',
            antiquewhite: '#faebd7',
            aqua: '#00ffff',
            aquamarine: '#7fffd4',
            azure: '#f0ffff',
            beige: '#f5f5dc',
            bisque: '#ffe4c4',
            black: '#000000',
            blanchedalmond: '#ffebcd',
            blue: '#0000ff',
            blueviolet: '#8a2be2',
            brown: '#a52a2a',
            burlywood: '#deb887',
            cadetblue: '#5f9ea0',
            chartreuse: '#7fff00',
            chocolate: '#d2691e',
            coral: '#ff7f50',
            cornflowerblue: '#6495ed',
            cornsilk: '#fff8dc',
            crimson: '#dc143c',
            cyan: '#00ffff',
            darkblue: '#00008b',
            darkcyan: '#008b8b',
            darkgoldenrod: '#b8860b',
            darkgray: '#a9a9a9',
            darkgreen: '#006400',
            darkkhaki: '#bdb76b',
            darkmagenta: '#8b008b',
            darkolivegreen: '#556b2f',
            darkorange: '#ff8c00',
            darkorchid: '#9932cc',
            darkred: '#8b0000',
            darksalmon: '#e9967a',
            darkseagreen: '#8fbc8f',
            darkslateblue: '#483d8b',
            darkslategray: '#2f4f4f',
            darkturquoise: '#00ced1',
            darkviolet: '#9400d3',
            deeppink: '#ff1493',
            deepskyblue: '#00bfff',
            dimgray: '#696969',
            dodgerblue: '#1e90ff',
            firebrick: '#b22222',
            floralwhite: '#fffaf0',
            forestgreen: '#228b22',
            fuchsia: '#ff00ff',
            gainsboro: '#dcdcdc',
            ghostwhite: '#f8f8ff',
            gold: '#ffd700',
            goldenrod: '#daa520',
            gray: '#808080',
            green: '#008000',
            greenyellow: '#adff2f',
            honeydew: '#f0fff0',
            hotpink: '#ff69b4',
            indianred: '#cd5c5c',
            indigo: '#4b0082',
            ivory: '#fffff0',
            khaki: '#f0e68c',
            lavender: '#e6e6fa',
            lavenderblush: '#fff0f5',
            lawngreen: '#7cfc00',
            lemonchiffon: '#fffacd',
            lightblue: '#add8e6',
            lightcoral: '#f08080',
            lightcyan: '#e0ffff',
            lightgoldenrodyellow: '#fafad2',
            lightgray: '#d3d3d3',
            lightgrey: '#d3d3d3',
            lightgreen: '#90ee90',
            lightpink: '#ffb6c1',
            lightsalmon: '#ffa07a',
            lightseagreen: '#20b2aa',
            lightskyblue: '#87cefa',
            lightslategray: '#778899',
            lightsteelblue: '#b0c4de',
            lightyellow: '#ffffe0',
            lime: '#00ff00',
            limegreen: '#32cd32',
            linen: '#faf0e6',
            magenta: '#ff00ff',
            maroon: '#800000',
            mediumaquamarine: '#66cdaa',
            mediumblue: '#0000cd',
            mediumorchid: '#ba55d3',
            mediumpurple: '#9370d8',
            mediumseagreen: '#3cb371',
            mediumslateblue: '#7b68ee',
            mediumspringgreen: '#00fa9a',
            mediumturquoise: '#48d1cc',
            mediumvioletred: '#c71585',
            midnightblue: '#191970',
            mintcream: '#f5fffa',
            mistyrose: '#ffe4e1',
            moccasin: '#ffe4b5',
            navajowhite: '#ffdead',
            navy: '#000080',
            oldlace: '#fdf5e6',
            olive: '#808000',
            olivedrab: '#6b8e23',
            orange: '#ffa500',
            orangered: '#ff4500',
            orchid: '#da70d6',
            palegoldenrod: '#eee8aa',
            palegreen: '#98fb98',
            paleturquoise: '#afeeee',
            palevioletred: '#d87093',
            papayawhip: '#ffefd5',
            peachpuff: '#ffdab9',
            peru: '#cd853f',
            pink: '#ffc0cb',
            plum: '#dda0dd',
            powderblue: '#b0e0e6',
            purple: '#800080',
            red: '#ff0000',
            rosybrown: '#bc8f8f',
            royalblue: '#4169e1',
            saddlebrown: '#8b4513',
            salmon: '#fa8072',
            sandybrown: '#f4a460',
            seagreen: '#2e8b57',
            seashell: '#fff5ee',
            sienna: '#a0522d',
            silver: '#c0c0c0',
            skyblue: '#87ceeb',
            slateblue: '#6a5acd',
            slategray: '#708090',
            snow: '#fffafa',
            springgreen: '#00ff7f',
            steelblue: '#4682b4',
            tan: '#d2b48c',
            teal: '#008080',
            thistle: '#d8bfd8',
            tomato: '#ff6347',
            turquoise: '#40e0d0',
            violet: '#ee82ee',
            wheat: '#f5deb3',
            white: '#ffffff',
            whitesmoke: '#f5f5f5',
            yellow: '#ffff00',
            yellowgreen: '#9acd32'
        },
        
        fromHSL: function(h, s, l) {
            return (new this(0, 0, 0, 0)).setHSL(h, s, l);
        },
        
        fromHSV: function(h, s, v) {
            return (new this(0, 0, 0, 0)).setHSL(h, s, v);
        },
        
        fromString: function(color) {
            return (new this(0, 0, 0, 0)).setFromString(color);
        },
        
        create: function(arg) {
            if (arg instanceof this) {
                return arg;
            } else if (Ext.isArray(arg)) {
                return new Ext.util.Color(arg[0], arg[1], arg[2], arg[3]);
            } else if (Ext.isString(arg)) {
                return Ext.util.Color.fromString(arg);
            } else if (arguments.length > 2) {
                return new Ext.util.Color(arguments[0], arguments[1], arguments[2], arguments[3]);
            } else {
                return new Ext.util.Color(0, 0, 0, 0);
            }
        }
    });
});


Ext.define('Ext.sparkline.Base', {
    extend: Ext.Gadget,
    xtype: 'sparkline',
    cachedConfig: {
        
        lineColor: '#157fcc',
        defaultPixelsPerValue: 3,
        tagValuesAttribute: 'values',
        enableTagOptions: false,
        enableHighlight: true,
        
        highlightColor: null,
        
        highlightLighten: 0.1,
        
        tooltipSkipNull: true,
        
        tooltipPrefix: '',
        
        tooltipSuffix: '',
        
        disableTooltips: false,
        disableInteraction: false,
        
        tipTpl: null
    },
    config: {
        
        values: null
    },
    baseCls: Ext.baseCSSPrefix + 'sparkline',
    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: {
        
        onClassCreated: function(cls) {
            var configUpdater = cls.prototype.updateConfigChange,
                proto = cls.prototype,
                configs = cls.getConfigurator().configs,
                config, updaterName;
            
            for (config in configs) {
                
                if (config !== 'tipTpl') {
                    updaterName = Ext.Config.get(config).names.update;
                    if (proto[updaterName]) {
                        proto[updaterName] = Ext.Function.createSequence(proto[updaterName], configUpdater);
                    } else {
                        proto[updaterName] = configUpdater;
                    }
                }
            }
        }
    },
    constructor: function(config) {
        var me = this,
            ns = Ext.sparkline;
        
        me.canvas = Ext.supports.Canvas ? new ns.CanvasCanvas(me) : new ns.VmlCanvas(me);
        me.callParent([
            config
        ]);
    },
    
    
    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;
    },
    
    
    
    updateConfigChange: function(newValue) {
        var me = this;
        
        
        if (me.bufferRedraw || !me.height || !me.width) {
            me.redrawQueue[me.getId()] = me;
            
            if (!me.redrawTimer) {
                Ext.sparkline.Base.prototype.redrawTimer = Ext.raf(me.processRedrawQueue);
            }
        } else {
            me.redraw();
        }
        return newValue;
    },
    
    
    applyTipTpl: function(tipTpl) {
        if (tipTpl && !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;
    },
    updateWidth: function(width, oldWidth) {
        var me = this,
            dom = me.element.dom,
            measurer = me.measurer;
        me.callParent([
            width,
            oldWidth
        ]);
        me.canvas.setWidth(width);
        me.width = width;
        if (me.height == null && measurer) {
            me.setHeight(parseInt(measurer.getCachedStyle(dom.parentNode, 'line-height'), 10));
        }
    },
    updateHeight: function(height, oldHeight) {
        var me = this;
        me.callParent([
            height,
            oldHeight
        ]);
        me.canvas.setHeight(height);
        me.height = height;
    },
    setValues: function(values) {
        var me = this,
            oldValues = me.getValues();
        
        values = values == null ? [] : Ext.Array.from(values);
        me.values = values;
        me.callParent([
            values
        ]);
        
        
        
        
        
        if (values === oldValues) {
            me.updateValues([
                values,
                oldValues
            ]);
        }
    },
    redraw: function() {
        var me = this;
        if (!me.destroyed) {
            me.canvas.onOwnerUpdate();
            me.canvas.reset();
            if (me.getValues()) {
                me.onUpdate();
                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) {
        var me = this;
        
        
        
        me.canvasRegion = me.canvasRegion || me.canvas.el.getRegion();
        me.currentPageXY = e.getPoint();
        me.redraw();
    },
    onMouseLeave: function() {
        var me = this;
        
        me.canvasRegion = me.currentPageXY = me.targetX = me.targetY = null;
        me.redraw();
        me.hideTip();
    },
    updateDisplay: function() {
        var me = this,
            values = me.getValues(),
            tipHtml, region;
        
        
        
        if (values && values.length && me.currentPageXY && me.canvasRegion.contains(me.currentPageXY)) {
            region = me.getRegion(me.currentPageXY[0] - me.canvasRegion.left, (me.canvasRegion.bottom - 1) - me.currentPageXY[1]);
            if (region != null && me.isValidRegion(region, values)) {
                if (!me.disableHighlight) {
                    me.renderHighlight(region);
                }
                if (!me.getDisableTooltips()) {
                    tipHtml = me.getRegionTooltip(region);
                }
            }
            if (me.hasListeners.sparklineregionchange) {
                me.fireEvent('sparklineregionchange', me);
            }
            if (tipHtml) {
                me.getSharedTooltip().setHtml(tipHtml);
                me.showTip();
            }
        }
        
        if (!tipHtml) {
            me.hideTip();
        }
    },
    
    getRegion: Ext.emptyFn,
    
    getRegionTooltip: function(region) {
        var me = this,
            entries = [],
            tipTpl = me.getTipTpl(),
            fields, showFields, showFieldsKey, newFields, fv, formatter, fieldlen, i, 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 (entries.length) {
            return entries.join('<br>');
        }
        return '';
    },
    getRegionFields: Ext.emptyFn,
    calcHighlightColor: function(color) {
        var me = this,
            highlightColor = me.getHighlightColor(),
            lighten = me.getHighlightLighten(),
            o;
        if (highlightColor) {
            return highlightColor;
        }
        if (lighten) {
            o = Ext.util.Color.fromString(color);
            if (o) {
                o.lighten(lighten);
                color = o.toHex();
            }
        }
        return color;
    },
    destroy: function() {
        delete this.redrawQueue[this.getId()];
        this.callParent();
    },
    privates: {
        hideTip: Ext.privateFn,
        isValidRegion: function(region, values) {
            return region < values.length;
        },
        showTip: Ext.privateFn
    }
}, function(SparklineBase) {
    var proto = SparklineBase.prototype;
    proto.getSharedTooltip = function() {
        var me = this,
            tooltip = me.tooltip;
        if (!tooltip) {
            proto.tooltip = tooltip = SparklineBase.constructTip();
        }
        return tooltip;
    };
    SparklineBase.onClassCreated(SparklineBase);
    proto.processRedrawQueue = function() {
        var redrawQueue = proto.redrawQueue,
            id;
        for (id in redrawQueue) {
            redrawQueue[id].redraw();
        }
        proto.redrawQueue = {};
        proto.redrawTimer = 0;
    };
});


Ext.define('Ext.override.sparkline.Base', {
    override: 'Ext.sparkline.Base',
    statics: {
        constructTip: function() {
            return new Ext.tip['ToolTip']({
                id: 'sparklines-tooltip',
                trackMouse: true,
                showDelay: 0,
                dismissDelay: 0,
                hideDelay: 400
            });
        }
    },
    onMouseMove: function(e) {
        this.currentEvent = e;
        this.getSharedTooltip().currentTarget.attach(this.element);
        this.callParent([
            e
        ]);
    },
    privates: {
        hideTip: function() {
            var tip = this.getSharedTooltip();
            
            
            tip.delayHide();
        },
        showTip: function() {
            this.getSharedTooltip().handleTargetOver(this.currentEvent, this.element);
        }
    }
});


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,
    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: '&#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);
            }
        }
        me.updateConfigChange();
        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) {
            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 != null) {
            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: [
        '{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);
        
        if (!this.raw) {
            newValues.sort(function(a, b) {
                return a - b;
            });
        }
        this.disabled = !(newValues && newValues.length);
        this.updateConfigChange();
        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 {
            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.canvasRegion.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: [
        '{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);
        this.updateConfigChange();
        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[parseInt(region.substr(1), 10)],
            region: region
        };
    },
    renderHighlight: function(region) {
        var me = this,
            valueShapes = me.valueShapes,
            shapes = me.shapes,
            shapeId = valueShapes[region],
            shape;
        delete shapes[shapeId];
        switch (region.substr(0, 1)) {
            case 'r':
                shape = me.renderRange(parseInt(region.substr(1), 10), true);
                break;
            case 'p':
                shape = me.renderPerformance(true);
                break;
            case 't':
                shape = me.renderTarget(true);
                break;
        }
        valueShapes[region] = shape.id;
        shapes[shape.id] = region;
        me.canvas.replaceWithShape(shapeId, shape);
    },
    renderRange: function(region, highlight) {
        var me = this,
            rangeval = me.values[region],
            rangewidth = Math.round(me.getWidth() * ((rangeval - me.min) / me.range)),
            colors = me.getRangeColors(),
            color = colors[Math.min(region - 2, colors.length - 1)];
        if (highlight) {
            color = me.calcHighlightColor(color);
        }
        return me.canvas.drawRect(0, 0, rangewidth - 1, me.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.canvasRegion.contains(me.currentPageXY)) {
            me.updateDisplay();
        }
        canvas.render();
    },
    privates: {
        isValidRegion: function(region, values) {
            return parseInt(region.substr(1), 10) < values.length;
        }
    }
});


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: '{prefix}{value}{suffix}',
    
    applyValues: function(newValues) {
        newValues = Ext.Array.map(Ext.Array.from(newValues), Number);
        this.disabled = !(newValues && newValues.length);
        this.updateConfigChange();
        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,
    alias: 'widget.sparklineline',
    config: {
        
        fillColor: '#def',
        
        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,
        
        valueSpots: null
    },
    tipTpl: '&#9679; {prefix}{y}{suffix}',
    applyValueSpots: function(valueSpots) {
        if (valueSpots && !valueSpots.get) {
            valueSpots = new Ext.sparkline.RangeMap(valueSpots);
        }
        this.updateConfigChange();
        return valueSpots;
    },
    onUpdate: function() {
        this.vertices = [];
        this.regionMap = [];
        this.xvalues = [];
        this.yvalues = [];
        this.yminmax = [];
    },
    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;
        for (i = 0; i < valcount; i++) {
            val = values[i];
            if (typeof val === 'string') {
                val = val.split(':');
            }
            
            if (val && val.length === 2) {
                xvalues.push(Number(val[0]));
                yvalues.push(val = Number(val[1]));
                yminmax.push(val);
            } else 
            {
                xvalues.push(i);
                if (val == null || val === 'null') {
                    yvalues.push(null);
                } else {
                    yvalues.push(val = Number(val));
                    yminmax.push(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.canvasRegion.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: '&#9679; {value} ({percent:number("0.0")}%)',
    
    applyValues: function(newValues) {
        newValues = Ext.Array.map(Ext.Array.from(newValues), Number);
        this.disabled = !(newValues && newValues.length);
        this.updateConfigChange();
        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 ratio = window.devicePixelRatio || 1,
            shapeid = this.canvas.getShapeAt(x * ratio, y * ratio);
        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.canvasRegion.contains(me.currentPageXY)) {
            me.currentRegion = null;
            me.updateDisplay();
        }
        canvas.render();
    }
});


Ext.define('Ext.sparkline.TriState', {
    extend: Ext.sparkline.BarBase,
    alias: 'widget.sparklinetristate',
    config: {
        
        barWidth: 4,
        
        barSpacing: 1,
        
        posBarColor: '#6f6',
        
        negBarColor: '#f44',
        
        zeroBarColor: '#999',
        
        colorMap: {}
    },
    tipTpl: [
        '&#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);
            }
        }
        me.updateConfigChange();
        return colorMap;
    },
    
    applyValues: function(newValues) {
        newValues = Ext.Array.map(Ext.Array.from(newValues), Number);
        this.disabled = !(newValues && newValues.length);
        this.updateConfigChange();
        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.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: '"',
    lineBreakRe: /\r?\n/g,
    
    lastLineBreakRe: /(\r?\n|\r)$/,
    constructor: function(config) {
        if (config) {
            Ext.apply(this, config);
        }
        this.parseREs = {};
        this.quoteREs = {};
    },
    
    decode: function(input, delimiter, quoteChar) {
        if (!input) {
            return [];
        }
        var me = this,
            
            
            delim = delimiter || me.delimiter,
            row = [],
            result = [
                row
            ],
            quote = quoteChar !== undefined ? quoteChar : me.quote,
            quoteREs = me.quoteREs,
            parseREs = me.parseREs,
            parseRE, dblQuoteRE, arrMatches, strMatchedDelimiter, strMatchedValue;
        
        
        parseRE = quote === me.quote ? parseREs[delim] : null;
        parseRE = parseRE || new RegExp(
        "(\\" + delim + "|\\r?\\n|\\r|^)" + 
        "(?:" + (quote === null ? '()' : "\\" + quote + "([^\\" + quote + "]*(?:\\" + quote + "\\" + quote + "[^\\" + quote + "]*)*)\\" + quote + "|") + 
        "([^" + (quote === null ? '' : quote) + delim + "\\r\\n]*))", "gi");
        dblQuoteRE = quote === me.quote ? quoteREs[quote] : null;
        dblQuoteRE = dblQuoteRE || new RegExp('\\' + quote + '\\' + quote, 'g');
        input = input.replace(me.lastLineBreakRe, '');
        
        
        while ((arrMatches = parseRE.exec(input))) {
            strMatchedDelimiter = arrMatches[1];
            
            
            
            
            if (strMatchedDelimiter.length && strMatchedDelimiter !== delim) {
                
                
                result.push(row = []);
            }
            
            if (!arrMatches.index && arrMatches[0].charAt(0) === delim) {
                row.push('');
            }
            
            
            
            if (arrMatches[2]) {
                
                
                strMatchedValue = arrMatches[2].replace(dblQuoteRE, quote);
            } else {
                
                strMatchedValue = arrMatches[3];
            }
            row.push(strMatchedValue);
        }
        return result;
    },
    
    encode: function(input, delimiter, quoteChar) {
        var me = this,
            delim = delimiter || me.delimiter,
            dateFormat = me.dateFormat,
            quote = quoteChar !== undefined ? quoteChar : me.quote,
            twoQuotes = quote + quote,
            rowIndex = input.length,
            lineBreakRe = me.lineBreakRe,
            result = [],
            outputRow = [],
            col, columnIndex, inputRow;
        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 && quote !== null) {
                        
                        if (col.indexOf(quote) > -1) {
                            col = quote + col.split(quote).join(twoQuotes) + quote;
                        } else if (col.indexOf(delim) > -1 || lineBreakRe.test(col)) {
                            col = quote + col + quote;
                        }
                    }
                } else if (Ext.isDate(col)) {
                    col = Ext.Date.format(col, dateFormat);
                }
                
                else if (col && (isNaN(col) || Ext.isArray(col))) {
                    Ext.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.ClickRepeater', {
    alternateClassName: 'Ext.util.TapRepeater',
    mixins: [
        Ext.mixin.Observable
    ],
    
    
    
    config: {
        
        el: null,
        
        target: null,
        disabled: null
    },
    
    
    
    
    interval: 20,
    
    delay: 250,
    
    preventDefault: true,
    
    stopDefault: false,
    timer: 0,
    
    handler: null,
    
    scope: null,
    
    constructor: function(config) {
        var me = this;
        
        if (arguments.length === 2) {
            me.setEl(config);
            config = arguments[1];
        }
        me.mixins.observable.constructor.call(this, config);
    },
    destroy: function() {
        this.setEl(null);
        this.callParent();
    },
    privates: {
        fireClick: function(e) {
            var me = this;
            me.fireEvent("click", me, e);
            Ext.callback(me.handler, me.scope, [
                me,
                e
            ], 0, me.getTarget());
        },
        updateDisabled: function(disabled) {
            var me = this;
            if (disabled) {
                me.savedEl = me.getEl();
                me.setEl(null);
            } else if (me.savedEl) {
                me.setEl(me.savedEl);
            }
        },
        updateTarget: function(target) {
            this.setEl(target.el);
        },
        updateEl: function(newEl, oldEl) {
            var me = this,
                elListeners;
            if (oldEl) {
                oldEl.selectable();
                Ext.undefer(me.timer);
                if (me.pressedCls) {
                    oldEl.removeCls(me.pressedCls);
                }
                Ext.getDoc().un('mouseup', me.handleMouseUp, me);
                me.elListeners = Ext.destroy(me.elListeners);
            }
            if (newEl) {
                newEl.unselectable();
                elListeners = {
                    mousedown: me.handleMouseDown,
                    scope: me,
                    destroyable: true
                };
                if (me.preventDefault || me.stopDefault) {
                    elListeners.click = me.eventOptions;
                }
                me.elListeners = newEl.on(elListeners);
            }
        },
        eventOptions: function(e) {
            if (this.preventDefault) {
                e.preventDefault();
            }
            if (this.stopDefault) {
                e.stopEvent();
            }
        },
        handleMouseDown: function(e) {
            var me = this,
                el = me.getEl();
            Ext.undefer(me.timer);
            if (me.pressedCls) {
                el.addCls(me.pressedCls);
            }
            me.mousedownTime = Ext.now();
            if (e.pointerType === 'mouse') {
                el.on("mouseout", me.handleMouseOut, me);
            }
            Ext.getDoc().on("mouseup", me.handleMouseUp, me);
            me.fireEvent("mousedown", me, e);
            me.fireClick(e);
            
            if (me.accelerate) {
                me.delay = 400;
            }
            me.timer = Ext.defer(me.click, me.delay || me.interval, me, [
                e
            ]);
            if (me.mousedownPreventDefault) {
                e.preventDefault();
            }
            if (me.mousedownStopEvent) {
                e.stopEvent();
            }
        },
        click: function(e) {
            var me = this;
            me.fireClick(e);
            me.timer = Ext.defer(me.click, me.accelerate ? me.easeOutExpo(Ext.now() - me.mousedownTime, 400, -390, 12000) : me.interval, me, [
                e
            ]);
        },
        easeOutExpo: function(t, b, c, d) {
            return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
        },
        handleMouseOut: function() {
            var me = this,
                el = me.getEl();
            Ext.undefer(me.timer);
            if (me.pressedCls) {
                el.removeCls(me.pressedCls);
            }
            el.on("mouseover", me.handleMouseReturn, me);
        },
        handleMouseReturn: function(e) {
            var me = this,
                el = me.getEl();
            el.un("mouseover", me.handleMouseReturn, me);
            if (me.pressedCls) {
                el.addCls(me.pressedCls);
            }
            me.click(e);
        },
        handleMouseUp: function(e) {
            var me = this,
                el = me.getEl();
            Ext.undefer(me.timer);
            el.un("mouseover", me.handleMouseReturn, me);
            el.un("mouseout", me.handleMouseOut, me);
            Ext.getDoc().un("mouseup", me.handleMouseUp, me);
            if (me.pressedCls) {
                el.removeCls(me.pressedCls);
            }
            me.fireEvent("mouseup", me, e);
        }
    }
});


Ext.define('Ext.util.Cookies', {
    singleton: true,
    
    set: function(name, value) {
        var argv = arguments,
            argc = argv.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.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.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.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.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.callParent();
    },
    
    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.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) {
                me._timer = Ext.undefer(me._timer);
                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.Spans', {
    isSpans: true,
    constructor: function() {
        this.spans = this.spans || [];
    },
    
    clear: function() {
        this.spans.length = 0;
        return this;
    },
    
    add: function(begin, end) {
        if (end === undefined) {
            if (typeof begin === 'number') {
                end = begin + 1;
            } else {
                end = begin[1];
                begin = begin[0];
            }
        }
        var me = this,
            spans = me.spans,
            b, e, first, last, span;
        first = me.bisect(begin);
        if (first) {
            
            span = spans[first - 1];
            b = span[0];
            e = span[1];
            if (begin <= e) {
                
                
                if (end <= e) {
                    return false;
                }
                
                
                
                begin = b;
                spans.splice(--first, 1);
            }
        }
        
        
        last = me.bisect(end);
        if (last > first) {
            
            
            span = spans[last - 1];
            end = Math.max(end, span[1]);
        }
        if (last < spans.length) {
            span = spans[last];
            
            
            
            
            if (end === span[0]) {
                end = span[1];
                ++last;
            }
        }
        spans.splice(first, last - first, [
            begin,
            end
        ]);
        return true;
    },
    
    contains: function(begin, end) {
        if (end === undefined) {
            if (typeof begin === 'number') {
                end = begin + 1;
            } else {
                end = begin[1];
                begin = begin[0];
            }
        }
        var spans = this.spans,
            index = this.bisect(begin),
            ret = false,
            e, span;
        if (index && begin < (e = spans[index - 1][1])) {
            ret = end <= e;
        } else if (index < spans.length) {
            span = spans[index];
            ret = span[0] <= begin && end <= span[1];
        }
        return ret;
    },
    
    each: function(fn, scope) {
        var spans = this.spans,
            len = spans.length,
            i, span, j;
        for (i = 0; i < len; i++) {
            span = spans[i];
            for (j = span[0]; j < span[1]; j++) {
                if (fn.call(scope || this, i) === false) {
                    return;
                }
            }
        }
    },
    
    intersects: function(begin, end) {
        if (end === undefined) {
            if (typeof begin === 'number') {
                end = begin + 1;
            } else {
                end = begin[1];
                begin = begin[0];
            }
        }
        var spans = this.spans,
            index = this.bisect(begin),
            ret = false;
        if (index && begin < spans[index - 1][1]) {
            ret = true;
        } else if (index < spans.length) {
            ret = spans[index][0] < end;
        }
        return ret;
    },
    
    remove: function(begin, end) {
        if (end === undefined) {
            if (typeof begin === 'number') {
                end = begin + 1;
            } else {
                end = begin[1];
                begin = begin[0];
            }
        }
        var me = this,
            spans = me.spans,
            first = me.bisect(begin),
            ret = false,
            last, span, tmp;
        if (first) {
            span = spans[first - 1];
            tmp = span[1];
            if (begin < tmp) {
                span[1] = begin;
                if (end < tmp) {
                    spans.splice(first, 0, [
                        end,
                        tmp
                    ]);
                    return true;
                }
                ret = true;
            }
        }
        last = me.bisect(end);
        if (first < last) {
            ret = true;
            span = spans[last - 1];
            if (end < span[1]) {
                span[0] = end;
                --last;
            }
            last -= first;
            if (last) {
                spans.splice(first, last);
            }
        }
        return ret;
    },
    
    stash: function() {
        return this.spans.slice();
    },
    
    unstash: function(pickle) {
        this.spans = pickle;
        return this;
    },
    
    getCount: function() {
        var spans = this.spans,
            len = spans.length,
            result = 0,
            i, span;
        for (i = 0; i < len; i++) {
            span = spans[i];
            result += span[1] - span[0];
        }
        return result;
    },
    privates: {
        bisect: function(value) {
            return Ext.Number.bisectTuples(this.spans, value, 0);
        }
    }
});


Ext.define('Ext.util.TsvDecoder', {
    extend: Ext.util.DelimitedValue,
    alternateClassName: 'Ext.util.TSV',
    delimiter: '\t'
}, function(TSVClass) {
    
    Ext.util.TSV = new TSVClass();
});



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


Ext.define('Ext.util.TextMetrics', {
    statics: {
        shared: null,
        
        measure: function(el, text, fixedWidth) {
            var me = this,
                shared = me.shared || (me.shared = new me(el, fixedWidth));
            shared.bind(el);
            shared.setFixedWidth(fixedWidth || 'auto');
            return shared.getSize(text);
        },
        
        destroy: function() {
            this.shared = Ext.destroy(this.shared);
        }
    },
    
    constructor: function(bindTo, fixedWidth) {
        var me = this,
            measure = me.measure = Ext.getBody().createChild({
                
                
                'data-sticky': true,
                
                role: 'presentation',
                cls: Ext.baseCSSPrefix + 'textmetrics',
                style: {
                    position: 'absolute',
                    left: '-1000px',
                    top: '-1000px',
                    visibility: 'hidden'
                }
            });
        if (bindTo) {
            me.bind(bindTo);
        }
        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) {
        this.measure.setStyle(
        
        (this.el || (this.self.prototype.el = new Ext.dom.Fly())).attach(el).getStyle([
            'font-size',
            'font-size-adjust',
            'font-style',
            'font-weight',
            'font-family',
            'font-kerning',
            'font-stretch',
            'line-height',
            'text-transform',
            'text-decoration',
            '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.el = me.measure = Ext.destroy(me.measure);
        me.callParent();
    }
}, 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.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.sizemonitor.OverflowChange', {
    extend: Ext.util.sizemonitor.Abstract,
    constructor: function(config) {
        this.onExpand = this.onExpand.bind(this);
        this.onShrink = this.onShrink.bind(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 (!(this.destroyed || (Ext.browser.is.Webkit && e.horizontalOverflow && e.verticalOverflow))) {
            Ext.TaskQueue.requestRead('refresh', this);
        }
    },
    onShrink: function(e) {
        if (!(this.destroyed || (Ext.browser.is.Webkit && !e.horizontalOverflow && !e.verticalOverflow))) {
            Ext.TaskQueue.requestRead('refresh', this);
        }
    },
    refreshMonitors: function() {
        if (this.destroying || this.destroyed) {
            return;
        }
        var expandHelper = this.expandHelper,
            shrinkHelper = this.shrinkHelper,
            contentBounds = this.getContentBounds(),
            width = contentBounds.width,
            height = contentBounds.height,
            style;
        if (expandHelper && !expandHelper.destroyed) {
            style = expandHelper.style;
            style.width = (width + 1) + 'px';
            style.height = (height + 1) + 'px';
        }
        if (shrinkHelper && !shrinkHelper.destroyed) {
            style = shrinkHelper.style;
            style.width = width + 'px';
            style.height = height + 'px';
        }
        Ext.TaskQueue.requestRead('refresh', this);
    },
    destroy: function() {
        
        this.onExpand = this.onShrink = null;
        this.callParent();
    }
});


Ext.define('Ext.util.translatable.ScrollParent', {
    extend: Ext.util.translatable.Dom,
    alias: 'translatable.scrollparent',
    
    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));
        this.callParent([
            x,
            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.AbstractComponent', {
    extend: Ext.Widget
});


Ext.define('Ext.Tool', {
    extend: Ext.Component,
    alternateClassName: 'Ext.panel.Tool',
    xtype: [
        'tool',
        'paneltool'
    ],
    
    isPanelTool: true,
    
    isTool: true,
    focusable: true,
    tabIndex: 0,
    focusEl: 'element',
    ariaEl: 'element',
    inheritUi: true,
    classCls: Ext.baseCSSPrefix + 'tool',
    pressedCls: Ext.baseCSSPrefix + 'pressed',
    hoveredCls: Ext.baseCSSPrefix + 'hovered',
    passiveCls: Ext.baseCSSPrefix + 'passive',
    element: {
        reference: 'element',
        tabIndex: 0,
        listeners: {
            click: 'onClick',
            mousedown: 'onMouseDown',
            mouseover: 'onMouseOver',
            mouseout: 'onMouseOut'
        },
        children: [
            {
                reference: 'iconElement',
                cls: Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon'
            }
        ]
    },
    
    handler: null,
    
    scope: null,
    
    toolOwner: null,
    config: {
        
        iconCls: null,
        
        type: null,
        
        passive: null
    },
    
    stopEvent: true,
    weight: 10,
    updateIconCls: function(iconCls, oldValue) {
        this.iconElement.replaceCls(oldValue, iconCls);
    },
    updateType: function(type, oldType) {
        var me = this,
            baseCls = Ext.Tool.prototype.classCls,
            iconElement = me.iconElement;
        me.type = type;
        iconElement.replaceCls(oldType && (baseCls + '-type-' + oldType), type && (baseCls + '-type-' + type));
    },
    updateDisabled: function(disabled, oldDisabled) {
        this.callParent([
            disabled,
            oldDisabled
        ]);
        this.el.removeCls([
            this.hoveredCls,
            this.pressedCls
        ]);
    },
    updatePassive: function(passive) {
        var me = this;
        me.setDisabled(passive);
        me.el.toggleCls(me.passiveCls, passive);
        me.el.setTabIndex(passive ? null : me.getTabIndex());
        me.focusable = !passive;
    },
    shouldRipple: function(e) {
        return !this.getPassive() && this.callParent();
    },
    privates: {
        
        _toolTypes: {
            close: 1,
            collapse: 1,
            disclosure: 1,
            down: 1,
            expand: 1,
            gear: 1,
            help: 1,
            left: 1,
            maximize: 1,
            minimize: 1,
            minus: 1,
            menu: 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
        },
        
        invokeToolHandler: function(me, handler, scope, args, ev) {
            if (handler) {
                Ext.callback(handler, me.scope, args, 0, me);
            }
            
            me.fireEvent('click', me, ev, me.toolOwner || me.ownerCt);
            return true;
        },
        
        onClick: function(ev) {
            var me = this,
                handler = me.handler || me.callback,
                
                toolOwner = me.toolOwner,
                args, invoker;
            if (me.getDisabled()) {
                return false;
            }
            if (me.stopEvent !== false) {
                ev.stopEvent();
            }
            args = [
                me.parent,
                me,
                ev
            ];
            if (toolOwner) {
                args[0] = toolOwner;
                if (toolOwner.augmentToolHandler) {
                    toolOwner.augmentToolHandler(me, args);
                }
            }
            invoker = (toolOwner && toolOwner.invokeToolHandler) ? toolOwner : me;
            return invoker.invokeToolHandler(me, handler, me.scope, args, ev);
        },
        
        onMouseDown: function(e) {
            var me = this;
            if (!me.focusable) {
                e.preventDefault();
            }
            if (me.getDisabled()) {
                return false;
            }
            me.addCls(me.pressedCls);
            Ext.GlobalEvents.setPressedComponent(me, e);
        },
        
        onRelease: function() {
            this.removeCls(this.pressedCls);
        },
        
        onMouseOver: function() {
            if (this.getDisabled()) {
                return false;
            }
            this.addCls(this.hoveredCls);
        },
        
        onMouseOut: function() {
            this.removeCls(this.hoveredCls);
        }
    }
});


Ext.define('Ext.mixin.Toolable', {
    mixinId: 'toolable',
    config: {
        
        defaultToolWeights: {
            cached: true,
            $value: {
                toggle: 10,
                gear: 20,
                prev: 30,
                next: 40,
                left: 50,
                right: 60,
                down: 70,
                up: 80,
                refresh: 90,
                disclosure: 100,
                
                plus: 100,
                minus: 110,
                search: 120,
                save: 130,
                print: 140,
                expand: 150,
                collapse: 160,
                help: 170,
                pin: 180,
                unpin: 190,
                minimize: 200,
                maximize: 210,
                restore: 220,
                close: 230
            }
        },
        
        toolDefaults: {
            xtype: 'tool',
            zone: 'end'
        },
        
        
        tools: null
    },
    
    toolAnchorName: 'bodyElement',
    afterClassMixedIn: function(targetClass) {
        var proto = targetClass.prototype,
            already = proto.toolDefaults,
            getRefItems = proto.getRefItems;
        if (already) {
            delete proto.toolDefaults;
            targetClass.getConfigurator().add({
                toolDefaults: Ext.apply({
                    xtype: 'tool',
                    weight: 0,
                    zone: 'end'
                }, already)
            });
        }
        already = proto.tools;
        if (already) {
            delete proto.tools;
            targetClass.getConfigurator().add({
                tools: already
            });
        }
        
        
        if (getRefItems) {
            proto.getRefItems = function(deep) {
                return Ext.Array.push(getRefItems.call(this, deep), this.getTools() || Ext.emptyArray);
            };
        } else 
        {
            proto.getRefItems = function() {
                return this.getTools() || Ext.emptyArray;
            };
        }
    },
    lookupTool: function(id) {
        var tools = this.getTools(),
            n = tools && tools.length,
            i, tool;
        for (i = 0; i < n; ++i) {
            tool = tools[i];
            if (tool.type === id || tool.getItemId() === id) {
                return tool;
            }
        }
        return null;
    },
    
    applyTools: function(tools) {
        if (tools) {
            var me = this,
                array = me.createTools(tools),
                n = array.length,
                i, tool, zone;
            Ext.Array.sort(array, Ext.weightSortFn);
            for (i = 0; i < n; ++i) {
                tool = array[i];
                tool.ownerCmp = tool.toolOwner = me;
                array[i] = tool = Ext.create(tool);
                tool.doInheritUi();
                zone = tool.zone;
                tool.addCls(me._toolPositionClsMap[zone]);
                me.getToolZone(tool.zone).el.appendChild(tool.el);
            }
            tools = array;
        }
        return tools;
    },
    updateTools: function(tools, oldTools) {
        Ext.destroy(oldTools);
    },
    privates: {
        _toolZoneNames: {
            end: '_endZone',
            head: '_headZone',
            start: '_startZone',
            tail: '_tailZone'
        },
        _tailedCls: Ext.baseCSSPrefix + 'tailed',
        _headedCls: Ext.baseCSSPrefix + 'headed',
        _toolZoneCls: Ext.baseCSSPrefix + 'tool-zone',
        
        _toolZoneClsMap: {
            end: Ext.baseCSSPrefix + 'end',
            head: Ext.baseCSSPrefix + 'head',
            tail: Ext.baseCSSPrefix + 'tail',
            start: Ext.baseCSSPrefix + 'start'
        },
        
        
        
        
        
        
        
        
        _toolPositionClsMap: {
            end: Ext.baseCSSPrefix + 'end',
            head: Ext.baseCSSPrefix + 'start',
            
            tail: Ext.baseCSSPrefix + 'end',
            
            start: Ext.baseCSSPrefix + 'start'
        },
        _toolDockAlignCls: {
            left: Ext.baseCSSPrefix + 'align-left',
            center: Ext.baseCSSPrefix + 'align-center',
            right: Ext.baseCSSPrefix + 'align-right'
        },
        hasToolZones: false,
        adjustToolDefaults: function(tool, toolDefaults, defaultToolWeights) {
            toolDefaults = toolDefaults || this.getToolDefaults();
            if (defaultToolWeights === undefined) {
                defaultToolWeights = this.getDefaultToolWeights();
            }
            if (toolDefaults) {
                Ext.applyIf(tool, toolDefaults);
                tool.instanceCls = this.toolCls;
            }
            if (!tool.type && !tool.iconCls) {
                tool.type = tool.itemId;
            }
            if (defaultToolWeights && !('weight' in tool)) {
                tool.weight = defaultToolWeights[tool.type];
            }
            return tool;
        },
        createTools: function(tools, toolOwner) {
            var me = this,
                array = Ext.convertKeyedItems(tools, 'handler', 'handler'),
                n = array.length,
                defaultToolWeights = me.getDefaultToolWeights(),
                toolDefaults = me.getToolDefaults(),
                i, tool;
            toolOwner = toolOwner || me;
            if (array === tools) {
                
                array = [];
                for (i = 0; i < n; ++i) {
                    tool = tools[i];
                    if (typeof tool === 'string') {
                        tool = me.adjustToolDefaults({
                            type: tool
                        }, toolDefaults, null);
                    } else {
                        tool = Ext.apply(me.adjustToolDefaults({}, toolDefaults, null), tool);
                    }
                    tool.toolOwner = toolOwner;
                    array[i] = tool;
                }
            } else {
                
                
                
                for (i = 0; i < n; ++i) {
                    me.adjustToolDefaults(tool = array[i], toolDefaults, defaultToolWeights);
                    tool.toolOwner = toolOwner;
                }
            }
            return array;
        },
        getToolZone: function(zoneName) {
            var me = this,
                zonePropName = me._toolZoneNames[zoneName],
                zone = me[zonePropName],
                dockWrapName = '_toolDockWrap',
                anchorElement;
            
            if (!zonePropName) {
                Ext.raise('Invalid zone name: "' + zoneName + '"');
            }
            
            if (!zone) {
                zone = Ext.Element.create({
                    classList: [
                        me._toolZoneCls,
                        me._toolZoneClsMap[zoneName]
                    ]
                });
                anchorElement = me[me.toolAnchorName];
                
                if (!anchorElement) {
                    Ext.raise('Invalid tool anchor. No element named "' + me.toolAnchorName + '".');
                }
                
                
                
                
                
                if (!me[dockWrapName]) {
                    me[dockWrapName] = anchorElement.wrap({
                        cls: Ext.baseCSSPrefix + 'tool-dock'
                    });
                    anchorElement.addCls(Ext.baseCSSPrefix + 'tool-anchor');
                    
                    
                    
                    
                    me.initUiReference(dockWrapName, 'tool-dock');
                    me.syncToolableAlign();
                }
                if (zoneName === 'head') {
                    zone.insertBefore(anchorElement);
                    anchorElement.addCls(me._headedCls);
                } else if (zoneName === 'tail') {
                    zone.insertAfter(anchorElement);
                    anchorElement.addCls(me._tailedCls);
                } else if (zoneName === 'start') {
                    zone.insertBefore(me._headZone || anchorElement);
                } else if (zoneName === 'end') {
                    zone.insertAfter(me._tailZone || anchorElement);
                }
                me[zonePropName] = zone;
                me.hasToolZones = true;
            }
            return zone;
        },
        
        syncToolableAlign: function() {
            var me = this,
                dockWrap = me._toolDockWrap,
                alignCls = me._toolDockAlignCls,
                align;
            if (dockWrap && (typeof me.getAlign === 'function')) {
                align = me.getAlign();
                dockWrap.replaceCls(alignCls[me._toolDockAlign], alignCls[align]);
                me._toolDockAlign = align;
            }
        },
        doDestroy: function() {
            var me = this;
            me.setTools(null);
            Ext.destroy(me._startZone, me._endZone, me._headZone, me._tailZone, me._toolDockWrap);
        }
    }
});


Ext.define('Ext.layout.Box', {
    extend: Ext.layout.Auto,
    alias: 'layout.box',
    isBox: true,
    config: {
        orient: 'horizontal',
        
        align: 'stretch',
        
        constrainAlign: false,
        
        pack: 'start',
        
        vertical: false,
        
        reverse: false,
        
        
        overflow: null,
        
        wrap: null
    },
    cls: Ext.baseCSSPrefix + 'layout-box',
    baseItemCls: Ext.baseCSSPrefix + 'layout-box-item',
    constrainAlignCls: Ext.baseCSSPrefix + 'constrain-align',
    flexedCls: Ext.baseCSSPrefix + 'flexed',
    wrapClsMap: {
        "true": Ext.baseCSSPrefix + 'wrap',
        'wrap': Ext.baseCSSPrefix + 'wrap',
        'wrap-reverse': Ext.baseCSSPrefix + 'wrap-reverse'
    },
    
    boxRe: /^(?:box|hbox|vbox)$/,
    
    orientMap: {
        horizontal: {
            sizeProp: 'width',
            containerCls: [
                Ext.baseCSSPrefix + 'layout-hbox',
                Ext.baseCSSPrefix + 'horizontal'
            ],
            itemCls: Ext.baseCSSPrefix + 'layout-hbox-item'
        },
        vertical: {
            sizeProp: 'height',
            containerCls: [
                Ext.baseCSSPrefix + 'layout-vbox',
                Ext.baseCSSPrefix + 'vertical'
            ],
            itemCls: Ext.baseCSSPrefix + 'layout-vbox-item'
        }
    },
    constructor: function(config) {
        var me = this;
        me.callParent([
            config
        ]);
        me.positionSortFn = me.positionSortFn.bind(me);
    },
    setConfig: function(name, value, options) {
        var config = name,
            type;
        
        
        if (name) {
            if (typeof name === 'string') {
                config = {};
                config[name] = value;
            } else {
                Ext.apply({}, name);
                options = value;
            }
            type = config.type;
            delete config.type;
            
            if (type && !this.boxRe.test(type)) {
                Ext.raise('Cannot change layout from ' + this.$className + ' to "' + type + '"');
            }
            
            if (config.vertical == null) {
                if (type === 'vbox') {
                    config.vertical = true;
                } else if (type === 'hbox') {
                    config.vertical = false;
                }
            }
            this.callParent([
                config,
                options
            ]);
        }
        return this;
    },
    destroy: function() {
        Ext.destroy(this.getOverflow());
        this.positionSortFn = null;
        this.callParent();
    },
    updateContainer: function(container, oldContainer) {
        var listener = {
                flexchange: 'onItemFlexChange',
                scope: this,
                delegate: '> component'
            };
        this.callParent([
            container,
            oldContainer
        ]);
        if (container) {
            container.on(listener);
        }
        if (oldContainer) {
            oldContainer.un(listener);
        }
    },
    updateVertical: function(vertical) {
        this.setOrient(vertical ? 'vertical' : 'horizontal');
    },
    
    applyOrient: function(orient) {
        if (orient !== 'horizontal' && orient !== 'vertical') {
            Ext.log.error("Invalid box orient of: '" + orient + "', must be either 'horizontal' or 'vertical'");
        }
        return orient;
    },
    
    updateOrient: function(orient, oldOrient) {
        var me = this,
            container = me.getContainer(),
            overflow = me.getOverflow(),
            renderTarget = container.getRenderTarget(),
            innerItems = container.innerItems,
            len = innerItems.length,
            map = me.orientMap,
            newMap = map[orient],
            oldMap = map[oldOrient],
            vertical = orient === 'vertical',
            i, itemCls, item;
        me.sizePropertyName = newMap.sizeProp;
        if (oldOrient) {
            renderTarget.removeCls(oldMap.containerCls);
            for (i = 0; i < len; ++i) {
                innerItems[i].removeCls(oldMap.itemCls);
            }
        }
        renderTarget.addCls(newMap.containerCls);
        me.itemCls = itemCls = [
            me.baseItemCls,
            newMap.itemCls
        ];
        for (i = 0; i < len; ++i) {
            item = innerItems[i];
            item.addCls(itemCls);
        }
        me.setVertical(vertical);
        me.positionFn = vertical ? 'getTop' : 'getLeft';
        if (overflow) {
            overflow.setVertical(vertical);
        }
    },
    updateConstrainAlign: function(constrainAlign) {
        this.getContainer().getRenderTarget().toggleCls(this.constrainAlignCls, constrainAlign);
    },
    onItemInnerStateChange: function(item, isInner) {
        var me = this,
            flex;
        me.callParent(arguments);
        if (isInner) {
            flex = item.getFlex();
            if (flex) {
                me.setItemFlex(item, flex);
            }
        } else {
            me.setItemFlex(item, null);
        }
    },
    onItemFlexChange: function(item, flex) {
        if (item.isInnerItem()) {
            this.setItemFlex(item, flex);
        }
    },
    
    setItemFlex: function(item, flex) {
        var el = item.el,
            type = typeof flex,
            isNumber = (type === 'number'),
            isString = (type === 'string'),
            parts, grow;
        if (!flex || isNumber || isString) {
            if (isNumber) {
                grow = flex;
                flex = flex + ' ' + flex;
            } else if (isString) {
                parts = Ext.String.splitWords(flex);
                grow = parts[0];
                if (parts.length === 1) {
                    flex = grow + ' ' + grow;
                }
            }
            el.setStyle('flex', flex);
        } else {
            grow = flex.grow;
            el.setStyle({
                flexGrow: grow,
                flexShrink: flex.shrink,
                flexBasis: flex.basis
            });
        }
        item.toggleCls(this.flexedCls, !!grow);
    },
    convertPosition: function(position) {
        var positionMap = this.positionMap;
        if (positionMap.hasOwnProperty(position)) {
            return positionMap[position];
        }
        return position;
    },
    applyAlign: function(align) {
        return this.convertPosition(align);
    },
    updateAlign: function(align, oldAlign) {
        this.getContainer().getRenderTarget().swapCls(align, oldAlign, true, Ext.baseCSSPrefix + 'align');
    },
    applyPack: function(pack) {
        return this.convertPosition(pack);
    },
    updatePack: function(pack, oldPack) {
        this.getContainer().getRenderTarget().swapCls(pack, oldPack, true, Ext.baseCSSPrefix + 'pack');
    },
    updateReverse: function(reverse) {
        this.getContainer().getRenderTarget().toggleCls(Ext.baseCSSPrefix + 'reverse', reverse);
    },
    
    ensureVisible: function(item, options) {
        if (!item.isWidget) {
            options = item;
            item = options.item;
        }
        if (options && !isNaN(options.offset)) {
            item = this.getItemByOffset(options.offset);
        }
        var me = this,
            container = this.getContainer(),
            scrollable = container.getScrollable(),
            scrollerTarget = scrollable.getElement(),
            vertical = me.getVertical(),
            targetInfo = me.getItemInfo(scrollerTarget),
            itemInfo = me.getItemInfo(item),
            oversized = itemInfo.size > targetInfo.size,
            scroll = (options && options.scroll) || 'min',
            delta, deltaX, deltaY;
        if (me._currentEnsureVisibleItem === item && scrollable.translatable.isAnimating) {
            return;
        }
        if (scroll === 'min') {
            if ((!oversized && (itemInfo.start < targetInfo.start)) || (oversized && (itemInfo.start > targetInfo.start))) {
                delta = itemInfo.start - targetInfo.start;
            } else if ((!oversized && (itemInfo.end > targetInfo.end)) || (oversized && itemInfo.end < targetInfo.end)) {
                delta = itemInfo.end - targetInfo.end;
            } else if (oversized && itemInfo.start < targetInfo.start && itemInfo.end > targetInfo.end) {
                delta = itemInfo.start - targetInfo.start;
            }
        } else {
            
            if (itemInfo.start < targetInfo.start) {
                delta = itemInfo.end - targetInfo.end;
            } else {
                
                delta = itemInfo.start - targetInfo.start;
            }
        }
        if (delta) {
            deltaX = !vertical ? delta : null;
            deltaY = vertical ? delta : null;
            me._currentEnsureVisibleItem = item;
            scrollable.scrollBy(deltaX, deltaY, options.animation);
        }
    },
    
    getItemByOffset: function(indexOffset) {
        var me = this,
            container = this.getContainer(),
            scrollerTarget = container.getScrollable().getElement(),
            targetInfo = me.getItemInfo(scrollerTarget),
            items = container.getInnerItems(),
            len = items.length,
            minFrontDistance = -Infinity,
            minEndDistance = -Infinity,
            startIndex = 0,
            endIndex = len - 1,
            index, i, itemFrontDistance, itemEndDistance, item, itemInfo;
        if (!indexOffset) {
            return;
        }
        items.sort(me.positionSortFn);
        for (i = 0; i < len; i++) {
            item = items[i];
            itemInfo = me.getItemInfo(item);
            itemFrontDistance = itemInfo.start - targetInfo.start;
            itemEndDistance = targetInfo.end - itemInfo.end;
            if ((itemFrontDistance > minFrontDistance) && (itemFrontDistance < 0) && itemEndDistance > 0) {
                minFrontDistance = itemFrontDistance;
                startIndex = i;
            }
            if ((itemEndDistance > minEndDistance) && (itemEndDistance < 0) && itemFrontDistance > 0) {
                minEndDistance = itemEndDistance;
                endIndex = i;
                break;
            }
        }
        if (indexOffset > 0) {
            indexOffset--;
            index = endIndex += indexOffset;
            if (endIndex >= len) {
                index = len - 1;
            }
        } else {
            indexOffset++;
            index = startIndex += indexOffset;
            if (startIndex < 0) {
                index = 0;
            }
        }
        return items[index];
    },
    getItemInfo: function(item) {
        var me = this,
            vertical = me.getVertical(),
            el = item.el;
        return {
            start: el[vertical ? 'getTop' : 'getLeft'](),
            end: el[vertical ? 'getBottom' : 'getRight'](),
            size: el[vertical ? 'getHeight' : 'getWidth']()
        };
    },
    createOverflow: function(config) {
        return Ext.apply({
            owner: this,
            vertical: this.getVertical()
        }, config);
    },
    applyOverflow: function(config, existing) {
        return Ext.Factory.layoutOverflow.update(existing, config, this, 'createOverflow');
    },
    updateWrap: function(wrap, oldWrap) {
        var me = this,
            el = me.getContainer().getRenderTarget(),
            map = me.wrapClsMap,
            cls;
        if (oldWrap) {
            cls = map[oldWrap];
            if (cls) {
                el.removeCls(cls);
            }
        }
        if (wrap) {
            cls = map[wrap];
            if (cls) {
                el.addCls(cls);
            }
        }
    },
    privates: {
        positionSortFn: function(a, b) {
            var fn = this.positionFn;
            a = a.el[fn]();
            b = b.el[fn]();
            if (a < b) {
                return -1;
            } else if (b < a) {
                return 1;
            }
            return 0;
        }
    }
});


Ext.define('Ext.Button', {
    extend: Ext.Component,
    xtype: 'button',
    isButton: true,
    
    
    cachedConfig: {
        
        buttonType: 'button',
        
        iconCls: null,
        
        textAlign: null,
        
        menuAlign: 'tl-bl?',
        
        destroyMenu: true,
        
        stretchMenu: false,
        
        eventHandlers: {
            click: 'onClick'
        }
    },
    config: {
        
        allowDepress: true,
        
        badgeText: null,
        
        text: null,
        
        icon: false,
        
        iconAlign: 'left',
        
        pressedDelay: 0,
        
        
        menu: {
            lazy: true,
            $value: null
        },
        
        arrow: null,
        
        arrowAlign: 'right',
        
        handler: null,
        
        toggleHandler: null,
        
        scope: null,
        
        autoEvent: null,
        
        ui: null,
        
        
        enableToggle: false,
        
        value: null
    },
    eventedConfig: {
        
        pressed: false
    },
    
    preventDefaultAction: true,
    isMenuOwner: true,
    
    baseCls: Ext.baseCSSPrefix + 'button',
    hasMenuCls: Ext.baseCSSPrefix + 'has-menu',
    hoveredCls: Ext.baseCSSPrefix + 'hovered',
    pressedCls: Ext.baseCSSPrefix + 'pressed',
    pressingCls: Ext.baseCSSPrefix + 'pressing',
    hasBadgeCls: Ext.baseCSSPrefix + 'has-badge',
    hasIconCls: Ext.baseCSSPrefix + 'has-icon',
    hasTextCls: Ext.baseCSSPrefix + 'has-text',
    hasArrowCls: Ext.baseCSSPrefix + 'has-arrow',
    noArrowCls: Ext.baseCSSPrefix + 'no-arrow',
    
    defaultBindProperty: 'text',
    
    publishes: [
        'pressed'
    ],
    
    element: {
        reference: 'element',
        onclick: 'return Ext.doEv(this, event);'
    },
    
    focusable: true,
    
    focusEl: 'buttonElement',
    
    ariaEl: 'buttonElement',
    backgroundColorEl: 'innerElement',
    
    focusClsEl: 'el',
    initialize: function() {
        var me = this,
            el = me.el;
        me.callParent();
        
        if (me.getConfig('menu', true)) {
            me.addCls(me.hasMenuCls);
        }
        el.on({
            scope: me,
            touchstart: 'onPress'
        });
        el.addClsOnOver(me.hoveredCls, me.isEnabled, me);
    },
    getTemplate: function() {
        return [
            {
                reference: 'innerElement',
                cls: Ext.baseCSSPrefix + 'inner-el',
                children: [
                    {
                        reference: 'bodyElement',
                        cls: Ext.baseCSSPrefix + 'body-el',
                        children: [
                            {
                                cls: Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon',
                                reference: 'iconElement'
                            },
                            {
                                reference: 'textElement',
                                cls: Ext.baseCSSPrefix + 'text-el'
                            }
                        ]
                    },
                    {
                        reference: 'arrowElement',
                        cls: Ext.baseCSSPrefix + 'arrow-el ' + Ext.baseCSSPrefix + 'font-icon'
                    }
                ]
            },
            {
                reference: 'badgeElement',
                cls: Ext.baseCSSPrefix + 'badge-el'
            },
            this.getButtonTemplate()
        ];
    },
    
    getButtonTemplate: function() {
        return {
            tag: 'button',
            reference: 'buttonElement',
            cls: Ext.baseCSSPrefix + 'button-el',
            onfocus: 'return Ext.doEv(this, event);',
            onblur: 'return Ext.doEv(this, event);'
        };
    },
    
    shouldRipple: function(e) {
        var me = this,
            ui = me.getUi(),
            ripple = me.getRipple(),
            isFab = ui ? ui.split(" ").indexOf("fab") >= 0 : false,
            text, icon;
        if (!isFab && ripple && ripple.bound === undefined) {
            text = me.getText();
            icon = me.getIconCls();
            if ((!text || text.length === 0) && icon) {
                ripple = Ext.clone(ripple);
                ripple.bound = false;
                ripple.measureSelector = 'x-icon-el';
            }
        }
        return ripple;
    },
    
    isPressed: function() {
        return Boolean(this.getPressed());
    },
    
    toggle: function() {
        this.setPressed(!this.isPressed());
    },
    updateBadgeText: function(badgeText) {
        var me = this,
            el = me.el,
            badgeElement = me.badgeElement,
            hasBadgeCls = me.hasBadgeCls;
        if (badgeText) {
            badgeElement.setText(badgeText);
            el.addCls(hasBadgeCls);
        } else {
            el.removeCls(hasBadgeCls);
        }
    },
    updateButtonType: function(buttonType) {
        this.buttonElement.dom.setAttribute('type', buttonType);
    },
    updateText: function(text) {
        this.textElement.setHtml(text);
        this.toggleCls(this.hasTextCls, !!text);
    },
    updateHtml: function(html) {
        this.setText(html);
    },
    applyPressed: function(pressed) {
        return Boolean(pressed);
    },
    updatePressed: function(pressed) {
        var me = this,
            toggleHandler = me.getToggleHandler();
        if (toggleHandler && !me.isConfiguring) {
            Ext.callback(toggleHandler, me.getScope(), [
                me,
                pressed
            ], 0, me);
        }
        me.element.toggleCls(me.pressedCls, pressed);
    },
    updateIcon: function(icon) {
        var me = this,
            element = me.iconElement,
            hasIconCls = me.hasIconCls;
        if (icon) {
            me.addCls(hasIconCls);
            element.setStyle('background-image', 'url(' + icon + ')');
        } else {
            element.setStyle('background-image', '');
            if (!me.getIconCls()) {
                me.removeCls(hasIconCls);
            }
        }
    },
    updateIconCls: function(iconCls, oldIconCls) {
        var me = this,
            element = me.iconElement,
            hasIconCls = me.hasIconCls;
        if (iconCls) {
            me.addCls(hasIconCls);
            element.replaceCls(oldIconCls, iconCls);
        } else {
            element.removeCls(oldIconCls);
            if (!me.getIcon()) {
                me.removeCls(hasIconCls);
            }
        }
    },
    updateIconAlign: function(iconAlign, oldIconAlign) {
        var el = this.el,
            prefix = Ext.baseCSSPrefix + 'icon-align-';
        el.removeCls(prefix + oldIconAlign);
        el.addCls(prefix + iconAlign);
    },
    _textAlignCls: {
        left: Ext.baseCSSPrefix + 'text-align-left',
        right: Ext.baseCSSPrefix + 'text-align-right',
        center: ''
    },
    updateTextAlign: function(textAlign, oldTextAlign) {
        var textAlignClasses = this._textAlignCls,
            add = textAlignClasses[textAlign || 'center'],
            remove = textAlignClasses[oldTextAlign || 'center'];
        this.replaceCls(remove, add);
    },
    updateArrowAlign: function(align, oldAlign) {
        var element = this.element,
            cls = Ext.baseCSSPrefix + 'arrow-align-';
        if (oldAlign) {
            element.removeCls(cls + oldAlign);
        }
        element.addCls(cls + align);
    },
    applyMenu: function(menu) {
        if (menu) {
            if (!menu.isMenu) {
                if (Ext.isArray(menu)) {
                    menu = {
                        items: menu
                    };
                }
                if (!menu.xtype) {
                    menu.xtype = 'menu';
                }
                menu.ownerCmp = this;
                menu = Ext.widget(menu);
            }
            this.menuMinWidth = menu.getMinWidth();
        }
        return menu;
    },
    updateMenu: function(menu, oldMenu) {
        var listener = {
                scope: this,
                hide: 'onMenuHide'
            };
        if (oldMenu && !oldMenu.destroyed) {
            if (this.getDestroyMenu()) {
                oldMenu.destroy();
            } else if (oldMenu.isMenu) {
                oldMenu.un(listener);
            }
        }
        this.toggleCls(this.hasMenuCls, !!menu);
        if (menu && menu.isMenu) {
            menu.on(listener);
        }
    },
    updateArrow: function(arrow) {
        this.toggleCls(this.noArrowCls, !arrow);
        this.toggleCls(this.hasArrowCls, !!arrow);
    },
    applyAutoEvent: function(autoEvent) {
        var me = this;
        if (typeof autoEvent == 'string') {
            autoEvent = {
                name: autoEvent,
                scope: me.scope || me
            };
        }
        return autoEvent;
    },
    updateAutoEvent: function(autoEvent) {
        var name = autoEvent.name,
            scope = autoEvent.scope;
        this.setHandler(function() {
            scope.fireEvent(name, scope, this);
        });
        this.setScope(scope);
    },
    applyPressedDelay: function(delay) {
        if (Ext.isNumber(delay)) {
            return delay;
        }
        return (delay) ? 100 : 0;
    },
    enableFocusable: function() {
        this.buttonElement.dom.disabled = false;
        this.callParent();
    },
    disableFocusable: function() {
        this.callParent();
        this.buttonElement.dom.disabled = true;
    },
    
    findEventTarget: function(e) {
        return this.element;
    },
    
    onPress: function(e) {
        var me = this,
            element = this.findEventTarget(e),
            pressedDelay = me.getPressedDelay(),
            pressingCls = me.pressingCls;
        
        if (!me.getDisabled() && !e.button) {
            if (pressedDelay > 0) {
                me.pressedTimeout = Ext.defer(function() {
                    delete me.pressedTimeout;
                    if (element) {
                        element.addCls(pressingCls);
                    }
                }, pressedDelay);
            } else {
                element.addCls(pressingCls);
            }
            Ext.GlobalEvents.setPressedComponent(me, e);
        }
    },
    
    onRelease: function(e) {
        this.fireAction('release', [
            this,
            e
        ], 'doRelease');
    },
    
    doRelease: function(me, e) {
        var element = me.findEventTarget(e);
        if (!me.getDisabled()) {
            if (me.hasOwnProperty('pressedTimeout')) {
                Ext.undefer(me.pressedTimeout);
                delete me.pressedTimeout;
            } else {
                if (element) {
                    element.removeCls(me.pressingCls);
                }
            }
        }
    },
    onClick: function(e) {
        return this.onTap(e);
    },
    
    onTap: function(e) {
        if (this.getDisabled()) {
            return false;
        }
        this.fireAction('tap', [
            this,
            e
        ], 'doTap');
    },
    
    doTap: function(me, e) {
        var menu = me.getMenu(),
            handler = me.getHandler();
        
        
        if (e && e.preventDefault && me.preventDefaultAction) {
            e.preventDefault();
        }
        if (menu) {
            me.toggleMenu(e, menu);
        } else {
            if ((me.getToggleHandler() || me.getEnableToggle()) && (me.getAllowDepress() || !me.isPressed())) {
                me.toggle();
            }
            if (handler) {
                Ext.callback(handler, me.getScope(), [
                    me,
                    e
                ], 0, me);
            }
        }
    },
    onEnterKey: function(e) {
        this.onTap(e);
        e.stopEvent();
        return false;
    },
    onDownKey: function(e) {
        var menu = this.getMenu();
        if (menu && !this.getDisabled()) {
            this.showMenu(e, menu);
            e.stopEvent();
            return false;
        }
    },
    onEscKey: function(e) {
        var menu = this.getMenu();
        if (menu && !this.getDisabled() && menu.isVisible()) {
            menu.hide();
            e.stopEvent();
            return false;
        }
    },
    onFocus: function(e) {
        if (!this.keyHandlersAdded) {
            this.setKeyMap({
                scope: 'this',
                SPACE: 'onEnterKey',
                ENTER: 'onEnterKey',
                DOWN: 'onDownKey',
                ESC: 'onEscKey'
            });
            this.keyHandlersAdded = true;
        }
        this.callParent([
            e
        ]);
    },
    onMenuHide: function(menu) {
        if (menu.isMenu && !this.$buttonWasPressed) {
            this.setPressed(false);
        }
    },
    toggleMenu: function(e, menu) {
        var me = this;
        menu = menu || me.getMenu();
        if (menu) {
            if (menu.isVisible()) {
                me.hideMenu(e, menu);
            } else {
                me.showMenu(e, menu);
            }
        }
    },
    hideMenu: function(e, menu) {
        menu = menu || this.getMenu();
        if (menu) {
            menu.hide();
        }
    },
    showMenu: function(e, menu) {
        var me = this,
            isPointerEvent = !e || e.pointerType,
            pressed;
        menu = menu || me.getMenu();
        me.setupMenuStretch(menu);
        if (menu) {
            if (menu.isVisible()) {
                
                if (isPointerEvent) {
                    menu.hide();
                } else {
                    menu.focus();
                }
            } else {
                menu.autoFocus = !isPointerEvent;
                if (menu.isMenu) {
                    
                    me.$buttonWasPressed = pressed = me.getPressed();
                    menu.showBy(me.element, me.getMenuAlign());
                    if (!pressed) {
                        me.setPressed(true);
                    }
                } else if (menu.isViewportMenu) {
                    menu.setDisplayed(!menu.getDisplayed());
                } else {
                    menu.show();
                }
            }
        }
    },
    doDestroy: function() {
        var me = this;
        if (me.hasOwnProperty('pressedTimeout')) {
            Ext.undefer(me.pressedTimeout);
        }
        me.setMenu(null);
        me.callParent();
    },
    getFocusClsEl: function() {
        return this.element;
    },
    privates: {
        setupMenuStretch: function(menu) {
            var me = this;
            
            if (!me.menuMinWidth) {
                if (me.getStretchMenu()) {
                    menu.setMinWidth(me.el.measure('w'));
                } else {
                    menu.setMinWidth(null);
                }
            }
        }
    }
});


Ext.define('Ext.Title', {
    extend: Ext.Component,
    xtype: 'title',
    config: {
        
        title: ''
    },
    baseCls: Ext.baseCSSPrefix + 'title',
    
    updateTitle: function(newTitle) {
        this.setHtml(newTitle);
    }
});


Ext.define('Ext.Spacer', {
    extend: Ext.Component,
    alias: 'widget.spacer',
    config: {},
    
    
    
    constructor: function(config) {
        config = config || {};
        if (!config.width) {
            config.flex = 1;
        }
        this.callParent([
            config
        ]);
    }
});


Ext.define('Ext.Toolbar', {
    extend: Ext.Container,
    xtype: 'toolbar',
    
    isToolbar: true,
    config: {
        
        title: null,
        
        defaultType: 'button',
        
        defaultButtonUI: null,
        
        
        minHeight: null,
        
        layout: {
            type: 'box',
            align: 'center'
        }
    },
    statics: {
        shortcuts: {
            '->': true
        }
    },
    autoSize: null,
    border: false,
    classCls: Ext.baseCSSPrefix + 'toolbar',
    constructor: function(config) {
        config = config || {};
        if (config.docked == 'left' || config.docked == 'right') {
            config.layout = Ext.apply({
                type: 'box',
                align: 'stretch',
                vertical: true
            }, config.layout);
        }
        this.callParent([
            config
        ]);
    },
    
    applyTitle: function(title) {
        if (typeof title == 'string') {
            title = {
                title: title,
                centered: true
            };
        }
        return Ext.factory(title, Ext.Title, this.getTitle());
    },
    
    updateTitle: function(newTitle, oldTitle) {
        if (newTitle) {
            this.add(newTitle);
        }
        if (oldTitle) {
            oldTitle.destroy();
        }
    },
    
    showTitle: function() {
        var title = this.getTitle();
        if (title) {
            title.show();
        }
    },
    
    hideTitle: function() {
        var title = this.getTitle();
        if (title) {
            title.hide();
        }
    },
    
    
    onItemAdd: function(item, index) {
        var defaultButtonUI = this.getDefaultButtonUI();
        if (defaultButtonUI) {
            if (item.isSegmentedButton) {
                if (item.getDefaultUI() == null) {
                    item.setDefaultUI(defaultButtonUI);
                }
            } else if (item.isButton && (item.getUi() == null)) {
                item.setUi(defaultButtonUI);
            }
        }
        this.callParent([
            item,
            index
        ]);
    },
    factoryItem: function(config) {
        if (config === '->') {
            config = {
                xtype: 'component',
                flex: 1
            };
        }
        return this.callParent([
            config
        ]);
    }
});


Ext.define('Ext.Panel', function(Panel) {
    var mac = Ext.platformTags.ios || Ext.platformTags.mac;
    return {
        
        extend: Ext.Container,
        xtype: 'panel',
        mixins: [
            Ext.mixin.Toolable
        ],
        alternateClassName: 'Ext.panel.Panel',
        
        defaultBindProperty: 'title',
        isPanel: true,
        config: {
            
            headerPosition: 'top',
            
            header: null,
            
            icon: null,
            
            iconCls: null,
            
            iconAlign: null,
            
            title: null,
            
            titleAlign: null,
            
            anchor: null,
            
            anchorPosition: null,
            
            closable: null,
            
            buttonAlign: null,
            buttonDefaults: null,
            
            standardButtons: {
                ok: {
                    text: 'OK',
                    weight: mac ? 120 : 10
                },
                abort: {
                    text: 'Abort',
                    weight: mac ? 110 : 20
                },
                retry: {
                    text: 'Retry',
                    weight: mac ? 100 : 30
                },
                ignore: {
                    text: 'Ignore',
                    weight: mac ? 90 : 40
                },
                yes: {
                    text: 'Yes',
                    weight: mac ? 80 : 50
                },
                no: {
                    text: 'No',
                    weight: mac ? 70 : 60
                },
                cancel: {
                    text: 'Cancel',
                    weight: mac ? 60 : 70
                },
                apply: {
                    text: 'Apply',
                    weight: mac ? 50 : 80
                },
                save: {
                    text: 'Save',
                    weight: mac ? 40 : 90
                },
                submit: {
                    text: 'Submit',
                    weight: mac ? 30 : 100
                },
                help: {
                    text: 'Help',
                    weight: mac ? 10 : 110
                },
                close: {
                    text: 'Close',
                    weight: mac ? 20 : 120
                }
            },
            
            minButtonWidth: 75,
            
            buttons: null,
            
            
            bbar: null,
            
            
            lbar: null,
            
            
            rbar: null,
            
            
            tbar: null
        },
        cachedConfig: {
            
            border: false,
            
            bodyBorder: null,
            
            bodyPadding: null,
            
            bodyStyle: null,
            
            buttonToolbar: {
                xtype: 'toolbar',
                itemId: 'buttonToolbar',
                docked: 'bottom',
                defaultType: 'button',
                weighted: true,
                ui: 'footer',
                defaultButtonUI: 'action',
                layout: {
                    type: 'box',
                    vertical: false,
                    pack: 'center'
                }
            },
            
            closeAction: 'destroy',
            
            closeToolText: 'Close panel'
        },
        
        classCls: Ext.baseCSSPrefix + 'panel',
        headerCls: null,
        titleCls: null,
        toolCls: Ext.baseCSSPrefix + 'paneltool',
        sideCls: {
            top: Ext.baseCSSPrefix + 'top',
            right: Ext.baseCSSPrefix + 'right',
            bottom: Ext.baseCSSPrefix + 'bottom',
            left: Ext.baseCSSPrefix + 'left'
        },
        
        manageBorders: true,
        allowHeader: true,
        
        template: [
            {
                reference: 'bodyWrapElement',
                cls: Ext.baseCSSPrefix + 'body-wrap-el',
                uiCls: 'body-wrap-el',
                children: [
                    {
                        reference: 'bodyElement',
                        cls: Ext.baseCSSPrefix + 'body-el',
                        uiCls: 'body-el'
                    }
                ]
            }
        ],
        
        addBodyCls: function(cls) {
            this.bodyElement.addCls(cls);
            return this;
        },
        
        removeBodyCls: function(cls) {
            this.bodyElement.removeCls(cls);
            return this;
        },
        applyBodyPadding: function(bodyPadding) {
            if (bodyPadding === true) {
                bodyPadding = 5;
            }
            if (bodyPadding) {
                bodyPadding = Ext.dom.Element.unitizeBox(bodyPadding);
            }
            return bodyPadding;
        },
        applyBodyStyle: function(bodyStyle, oldBodyStyle) {
            
            
            
            
            
            
            if (oldBodyStyle && bodyStyle === oldBodyStyle && Ext.isObject(oldBodyStyle)) {
                bodyStyle = Ext.apply({}, bodyStyle);
            }
            this.bodyElement.applyStyles(bodyStyle);
            return null;
        },
        
        getBodyStyle: function() {
            Ext.Error.raise("'bodyStyle' is a write-only config.  To query element styles use the Ext.dom.Element API.");
        },
        
        
        addTool: function(tool) {
            var header = this.ensureHeader(),
                
                items;
            if (header) {
                items = this.createTools(Ext.Array.from(tool));
                if (items && items.length) {
                    items = header.add(items);
                }
            }
            return items;
        },
        applyHeader: function(newHeader, oldHeader) {
            
            var me = this,
                header = oldHeader,
                isTrue;
            me.allowHeader = newHeader !== false;
            if (oldHeader && !newHeader) {
                header = Ext.destroy(header);
            }
            if (newHeader && me.allowHeader) {
                isTrue = newHeader === true;
                if (header) {
                    if (!isTrue) {
                        header.setConfig(newHeader);
                    }
                } else {
                    if (isTrue) {
                        newHeader = {};
                    }
                    newHeader.$initParent = me;
                    header = Ext.factory(me.createHeader(newHeader));
                    me.header = header;
                    delete header.$initParent;
                    delete newHeader.$initParent;
                    
                    
                    header.ownerCmp = me;
                    (me.maxHeightElement || me.el).insertFirst(header.el);
                    header.doInheritUi();
                }
            }
            return header || null;
        },
        updateHeader: function(header) {
            if (header) {
                this.positionHeader(header);
            } else {
                this.syncBorders();
            }
        },
        applyTools: function(tools) {
            var header = this.ensureHeader(),
                
                items;
            if (header) {
                
                header.clearTools();
                items = this.createTools(tools);
                if (items && items.length) {
                    header.add(items);
                }
            }
        },
        
        
        close: function() {
            var me = this,
                action = me.getCloseAction(),
                destroy = action === 'destroy';
            if (me.fireEvent('beforeclose', me) !== false) {
                if (action && !destroy) {
                    me[action]();
                }
                me.fireEvent('close', me);
                if (destroy) {
                    me.destroy();
                }
            }
        },
        createHeader: function(config) {
            var me = this,
                ret = {
                    xtype: 'panelheader',
                    instanceCls: me.headerCls,
                    docked: 'top'
                },
                icon, title;
            me._isCreatingHeader = true;
            if (config && config !== true) {
                Ext.merge(ret, config);
            }
            if (me.initialized) {
                
                
                
                title = me.getTitle();
                if (title != null) {
                    if (typeof title === 'string') {
                        title = {
                            text: title
                        };
                    }
                    Ext.merge(ret, {
                        title: title
                    });
                }
                icon = me.getIconCls();
                if (icon != null) {
                    ret.iconCls = icon;
                } else {
                    icon = me.getIcon();
                    if (icon != null) {
                        ret.icon = icon;
                    }
                }
            }
            me._isCreatingHeader = false;
            return ret;
        },
        applyAnchor: function(anchor, oldAnchor) {
            var me = this,
                el = me.el.dom,
                svgEl, pathEl;
            
            if (anchor) {
                
                if (oldAnchor) {
                    return;
                } else {
                    anchor = me.el.insertFirst({
                        cls: Ext.baseCSSPrefix + 'anchor-el'
                    });
                    svgEl = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
                    svgEl.setAttribute('class', Ext.baseCSSPrefix + 'pointer-el');
                    pathEl = document.createElementNS("http://www.w3.org/2000/svg", 'path');
                    svgEl.appendChild(pathEl);
                    anchor.dom.appendChild(svgEl);
                }
                
                
                el.style.overflow = 'visible';
            }
            
            else if (oldAnchor) {
                me.anchorSize = oldAnchor.destroy();
                el.style.overflow = '';
            }
            return anchor;
        },
        initAnchor: function() {
            var me = this,
                el = me.el,
                anchor = me.getAnchor(),
                cls = me.sideCls.top,
                svgEl = anchor.dom.firstChild,
                pathEl = svgEl.firstChild,
                anchorSize;
            anchor.addCls(cls);
            anchor.show();
            anchorSize = anchor.measure();
            me.anchorSize = anchorSize = new Ext.util.Offset(anchorSize.width, anchorSize.height);
            
            me.anchorMargin = parseFloat(anchor.getStyle('marginLeft')) || 0;
            anchor.dom.style.margin = '0';
            
            svgEl.setAttribute('height', anchorSize.y);
            svgEl.setAttribute('width', anchorSize.x);
            pathEl.setAttribute('d', 'M0 ' + anchorSize.y + ' L' + anchorSize.x / 2 + ' 0.5 L' + anchorSize.x + ' ' + anchorSize.y);
            anchorSize.y -= parseFloat(Ext.fly(pathEl).getStyle('stroke-width'));
            anchor.removeCls(cls);
            anchor.hide();
        },
        updateAnchorPosition: function(anchorPosition, oldAnchorPosition) {
            var me = this,
                anchorEl = me.getAnchor(),
                sideCls = me.sideCls,
                el = me.el;
            
            if (anchorEl) {
                if (oldAnchorPosition) {
                    anchorEl.removeCls(sideCls[oldAnchorPosition.side]);
                }
                if (anchorPosition) {
                    anchorEl.addCls(sideCls[anchorPosition.side]);
                    anchorEl.translate(anchorPosition.x, anchorPosition.y);
                    anchorEl.show();
                } else {
                    anchorEl.hide();
                }
            }
        },
        updateBorder: function(border, oldBorder) {
            var me = this;
            me.callParent([
                border,
                oldBorder
            ]);
            if (me.getBodyBorder() === null) {
                me.setBodyBorderEnabled(border !== false);
            }
            me.syncBorders();
        },
        updateBodyPadding: function(newBodyPadding) {
            this.bodyElement.setStyle('padding', newBodyPadding);
        },
        updateBodyBorder: function(bodyBorder) {
            var me = this;
            bodyBorder = (bodyBorder === null) ? me.getBorder() : bodyBorder;
            me.setBodyBorderEnabled(bodyBorder !== false);
            me.syncBorders();
        },
        updateClosable: function(closable) {
            var me = this,
                tools;
            if (closable) {
                tools = me.addTool({
                    type: 'close',
                    weight: 1000,
                    scope: me,
                    handler: 'onCloseTool',
                    tooltip: me.getCloseToolText(),
                    $internal: true
                });
                if (tools && tools.length) {
                    me.closeTool = tools[0];
                }
            } else {
                Ext.destroy(me.closeTool);
            }
        },
        updateHeaderPosition: function(headerPosition, oldHeaderPosition) {
            this.moveHeaderPosition(headerPosition, oldHeaderPosition);
        },
        updateIcon: function(icon) {
            var header = this.ensureHeader();
            
            if (header) {
                header.setIcon(icon);
            }
        },
        updateIconCls: function(iconCls) {
            var header = this.ensureHeader();
            
            if (header) {
                header.setIconCls(iconCls);
            }
        },
        updateIconAlign: function(iconAlign) {
            var header = this.ensureHeader();
            
            if (header) {
                header.setIconAlign(iconAlign);
            }
        },
        applyBbar: function(toolbar, previous) {
            return this.normalizeDockedBars(toolbar, previous, 'bottom');
        },
        applyButtons: function(buttons, oldButtons) {
            var me = this,
                array = Ext.convertKeyedItems(buttons, 'xxx', 'xxx'),
                
                buttonDefaults = me.getButtonDefaults(),
                standardButtons = me.getStandardButtons(),
                toolbar = me.getButtonToolbar(),
                n = array ? array.length : 0,
                button, defaults, handler, i;
            if (buttons && typeof buttons === 'object') {
                if (buttons.xtype || buttons.itemId || buttons.items || buttons.reference) {
                    
                    
                    return me.normalizeDockedBars(buttons, oldButtons, 'bottom', toolbar);
                }
            }
            if (buttons) {
                if (array === buttons) {
                    
                    array = [];
                    for (i = 0; i < n; ++i) {
                        button = buttons[i];
                        if (typeof button === 'string') {
                            if (!Ext.Toolbar.shortcuts[button]) {
                                button = Ext.applyIf({
                                    itemId: button,
                                    text: button
                                }, buttonDefaults);
                            }
                        } else if (buttonDefaults) {
                            button = Ext.apply({}, button, buttonDefaults);
                        }
                        array[i] = button;
                    }
                } else {
                    
                    
                    
                    
                    for (i = 0; i < n; ++i) {
                        button = array[i];
                        handler = button.xxx;
                        defaults = standardButtons[button.itemId];
                        if (defaults) {
                            Ext.applyIf(button, defaults);
                        }
                        
                        
                        else if (handler) {
                            Ext.raise('Button handler short-hand is only valid for standardButtons');
                        }
                        
                        if (handler) {
                            delete button.xxx;
                            button.handler = handler;
                        }
                        
                        if (buttonDefaults) {
                            Ext.applyIf(button, buttonDefaults);
                        }
                    }
                }
            }
            return me.normalizeDockedBars(array, oldButtons, 'bottom', toolbar);
        },
        applyLbar: function(toolbar, previous) {
            return this.normalizeDockedBars(toolbar, previous, 'left');
        },
        applyRbar: function(toolbar, previous) {
            return this.normalizeDockedBars(toolbar, previous, 'right');
        },
        applyTbar: function(toolbar, previous) {
            return this.normalizeDockedBars(toolbar, previous, 'top');
        },
        updateTitle: function(title) {
            var header = this.ensureHeader(),
                tab = this.tab;
            if (header) {
                header.setTitle(title);
            }
            if (tab && tab.isTab && !tab.destroying && !tab.destroyed) {
                tab.setText(title);
            }
        },
        updateTitleAlign: function(titleAlign) {
            var header = this.ensureHeader();
            
            if (header) {
                header.setTitleAlign(titleAlign);
            }
        },
        updateUi: function(ui, oldUi) {
            this.callParent([
                ui,
                oldUi
            ]);
            if (this.hasResizable) {
                this.onResizableUiChange(ui, oldUi);
            }
            
            this.anchorSize = null;
        },
        alignTo: function(component, alignment, options) {
            var me = this,
                anchorElement = me.getAnchor(),
                config = me.initialConfig,
                positioned = me.isPositioned(),
                setX = positioned ? me.setLeft : me.setX,
                setY = positioned ? me.setTop : me.setY,
                x, y, target, anchorMargin, alignmentInfo, resultRegion, oldHeight, parent;
            
            if (anchorElement) {
                if (!me.anchorSize) {
                    me.initAnchor();
                }
            } else 
            
            
            {
                return me.callParent([
                    component,
                    alignment,
                    options
                ]);
            }
            anchorMargin = me.anchorMargin;
            
            target = component.isRegion ? component : (component.isWidget ? component.el : Ext.fly(component)).getRegion();
            target.adjust(-anchorMargin, anchorMargin, anchorMargin, -anchorMargin);
            alignmentInfo = me.getAlignmentInfo(target, alignment);
            if (alignmentInfo.isAligned) {
                return;
            }
            parent = me.getParent();
            if (!me.getFloated()) {
                if (!parent) {
                    me.setFloated(true);
                } else {
                    me.positioned = true;
                }
            }
            if ('unconstrainedWidth' in me) {
                me.setWidth(me.unconstrainedWidth);
            }
            if ('unconstrainedHeight' in me) {
                me.setHeight(me.unconstrainedHeight);
            }
            
            
            
            me.alignToArgs = [
                component,
                alignment,
                options
            ];
            resultRegion = me.getAlignRegion(target, alignment, Ext.apply({
                anchorSize: me.anchorSize,
                axisLock: me.getAxisLock()
            }, options));
            
            if (resultRegion) {
                setX.call(me, resultRegion.x);
                setY.call(me, resultRegion.y);
                if (resultRegion.constrainWidth) {
                    me.unconstrainedWidth = config.width || me.self.prototype.width;
                    
                    oldHeight = me.el.getHeight();
                    me.setWidth(alignmentInfo.stats.width = resultRegion.getWidth());
                    
                    
                    if (resultRegion.align.position === 0) {
                        setY.call(me, resultRegion.y + (oldHeight - me.el.getHeight()));
                    }
                }
                if (resultRegion.constrainHeight) {
                    me.unconstrainedHeight = config.height || me.self.prototype.height;
                    me.setHeight(alignmentInfo.stats.height = resultRegion.getHeight());
                }
                if (resultRegion.anchor) {
                    x = 0;
                    y = 0;
                    
                    if (resultRegion.anchor.align & 1) {
                        y = resultRegion.anchor.y - resultRegion.y;
                    } else {
                        x = resultRegion.anchor.x - resultRegion.x;
                    }
                    me.setAnchorPosition({
                        side: resultRegion.anchor.position,
                        x: x,
                        y: y
                    });
                } else {
                    me.setAnchorPosition(null);
                }
                me.setCurrentAlignmentInfo(alignmentInfo);
            } else if (anchorElement) {
                
                anchorElement.show();
            }
            if (!me.viewportResizeListener) {
                me.viewportResizeListener = Ext.on({
                    resize: 'onViewportResize',
                    scope: me,
                    destroyable: true
                });
            }
        },
        getRefItems: function(deep) {
            var items = this.callParent([
                    deep
                ]),
                header = this.getConfig('header', false, true);
            if (header) {
                
                
                if (deep) {
                    items.unshift.apply(items, header.getRefItems(deep));
                }
                items.unshift(header);
            }
            return items;
        },
        onCloseTool: function() {
            this.close();
        },
        onRender: function() {
            var me = this,
                header;
            me.callParent();
            header = me.getHeader();
            if (header) {
                header.setRendered(true);
            }
            if (me.hasCollapsible) {
                me.onCollapsibleRendered();
            }
        },
        doDestroy: function() {
            Ext.destroy(this.header, this.anchor);
            this.callParent();
        },
        privates: {
            headerPositionMap: {
                top: {
                    cls: Ext.baseCSSPrefix + 'header-position-top',
                    dom: 0,
                    horz: true
                },
                right: {
                    cls: Ext.baseCSSPrefix + 'header-position-right',
                    dom: 1,
                    vert: true
                },
                bottom: {
                    cls: Ext.baseCSSPrefix + 'header-position-bottom',
                    dom: 1,
                    horz: true
                },
                left: {
                    cls: Ext.baseCSSPrefix + 'header-position-left',
                    dom: 0,
                    vert: true
                }
            },
            ensureHeader: function() {
                var me = this,
                    header;
                if (!me._isCreatingHeader) {
                    me.getItems();
                    header = me.getHeader();
                    if (!header && me.allowHeader) {
                        me.setHeader(true);
                        header = me.getHeader();
                    }
                }
                return header;
            },
            moveHeaderPosition: function(headerPosition, oldHeaderPosition) {
                var me = this,
                    el = me.element,
                    map = me.headerPositionMap,
                    oldItem = map[oldHeaderPosition],
                    newItem = map[headerPosition],
                    oldCls = oldItem ? oldItem.cls : '',
                    newCls = newItem.cls,
                    positionedHeader, header;
                if (oldCls !== newCls) {
                    if (oldHeaderPosition) {
                        el.removeCls(oldCls);
                    }
                    el.addCls(newCls);
                }
                if (oldHeaderPosition || headerPosition !== 'top') {
                    header = me.ensureHeader();
                    if (header) {
                        if (!me.isConfiguring) {
                            me.positionHeader(header, headerPosition);
                            positionedHeader = true;
                        }
                    }
                }
                if (!positionedHeader) {
                    me.syncBorders();
                }
                return header;
            },
            _packButtonAlign: {
                left: 'start',
                right: 'end',
                center: 'center'
            },
            normalizeDockedBars: function(toolbar, previous, pos, buttonToolbarCfg, disableFocusableContainer) {
                if (!toolbar) {
                    if (previous) {
                        previous.destroy();
                    }
                    return toolbar;
                }
                var me = this,
                    isComponent = toolbar.isComponent,
                    buttonAlign, buttonToolbarDefaults, index, layout, minButtonWidth, pack;
                if (Ext.isArray(toolbar)) {
                    toolbar = {
                        xtype: 'toolbar',
                        items: toolbar
                    };
                } else if (!isComponent) {
                    
                    toolbar = Ext.clone(toolbar);
                }
                if (!toolbar.xtype) {
                    toolbar.xtype = 'toolbar';
                }
                if (isComponent) {
                    toolbar.setDocked(pos);
                } else {
                    toolbar.docked = pos;
                }
                if (disableFocusableContainer) {
                    if (isComponent) {
                        toolbar.setEnableFocusableContainer(false);
                    } else {
                        toolbar.enableFocusableContainer = false;
                    }
                }
                
                if (buttonToolbarCfg && !isComponent) {
                    toolbar = Ext.merge(Ext.clone(buttonToolbarCfg), toolbar);
                    toolbar.layout = Ext.merge(layout = {}, toolbar.layout);
                    buttonAlign = me.getButtonAlign();
                    if (buttonAlign) {
                        pack = me._packButtonAlign[buttonAlign];
                        if (pack) {
                            layout.pack = pack;
                        }
                    }
                    minButtonWidth = this.getMinButtonWidth();
                    buttonToolbarDefaults = toolbar.defaults;
                    toolbar.defaults = function(config) {
                        var defaults = buttonToolbarDefaults || {},
                            
                            isButton = !config.xtype || config.isButton,
                            cls;
                        
                        
                        if (!isButton) {
                            cls = Ext.ClassManager.getByAlias('widget.' + config.xtype);
                            if (cls) {
                                isButton = cls.prototype.isButton;
                            }
                        }
                        if (isButton && minButtonWidth && !('minWidth' in defaults)) {
                            defaults = Ext.apply({
                                minWidth: minButtonWidth
                            }, defaults);
                        }
                        return defaults;
                    };
                }
                if (previous) {
                    
                    
                    index = me.indexOf(previous);
                    previous.destroy();
                    toolbar = me.insert(index, toolbar);
                } else {
                    toolbar = me.add(toolbar);
                }
                return toolbar;
            },
            positionHeader: function(header, position) {
                var me = this,
                    pos = position || me.getHeaderPosition();
                header.setPosition(pos);
                me.syncBorders();
            },
            setBodyBorderEnabled: function(enabled) {
                this.bodyElement.setStyle('border-width', enabled ? '' : '0');
            },
            syncBorders: function() {
                if (!this.isConfiguring) {
                    this.getLayout().handleDockedItemBorders(true);
                }
            }
        }
    };
});


Ext.define('Ext.panel.Collapsible', {
    override: 'Ext.Panel',
    config: {
        
        collapsed: null,
        
        collapsible: null
    },
    
    
    
    
    
    
    
    hasCollapsible: true,
    defaultCollapserCls: 'Ext.panel.Collapser',
    doDestroy: function() {
        this.setCollapsible(null);
        this.callParent();
    },
    
    collapse: function(animation) {
        return this.getCollapsible().collapse(animation);
    },
    
    expand: function(animation) {
        return this.getCollapsible().expand(animation);
    },
    
    toggleCollapsed: function(collapsed, animation) {
        return this.getCollapsible().toggleCollapsed(collapsed, animation);
    },
    getCollapsed: function() {
        
        var collapsible = this.getCollapsible();
        return collapsible ? collapsible.getCollapsed() : false;
    },
    updateCollapsed: function(collapsed) {
        var collapsible = this.getCollapsible();
        if (collapsible) {
            collapsible.setCollapsed(collapsed);
        }
    },
    applyCollapsible: function(collapsible, collapser) {
        if (collapsible === true) {
            collapsible = {
                direction: this.getHeaderPosition()
            };
        } else if (typeof collapsible === 'string') {
            collapsible = {
                direction: collapsible
            };
        } else if (!collapsible) {
            return null;
        }
        if (collapser) {
            collapser.setConfig(collapsible);
        } else {
            collapsible = Ext.apply({
                xclass: this.defaultCollapserCls,
                target: this
            }, collapsible);
            collapser = Ext.create(collapsible);
        }
        return collapser;
    },
    updateCollapsible: function(collapsible, oldCollapsible) {
        if (oldCollapsible) {
            
            if (!this.destroying) {
                oldCollapsible.doExpandCollapse(false);
            }
            oldCollapsible.destroy();
        }
        if (collapsible && this.rendered) {
            this.initCollapsible(collapsible);
        }
    },
    updateHeader: function(header, oldHeader) {
        var collapsible = this.getCollapsible();
        this.callParent([
            header,
            oldHeader
        ]);
        if (this.isConfiguring && collapsible) {
            collapsible.toggleCollapsed(collapsible.getCollapsed(), false);
        }
    },
    updateHeaderPosition: function(headerPosition, oldHeaderPosition) {
        var collapsible = this.getCollapsible();
        if (collapsible && collapsible.getCollapsed()) {
            headerPosition = collapsible.getDirection();
        }
        this.moveHeaderPosition(headerPosition, oldHeaderPosition);
    },
    privates: {
        initCollapsible: function(collapsible) {
            this.ensureHeader();
            collapsible.initialize();
        },
        onCollapsibleRendered: function() {
            var collapsible = this.getCollapsible();
            if (collapsible) {
                this.initCollapsible(collapsible);
            }
        },
        reattachBodyWrap: function() {
            var me = this,
                header = me._header,
                el = me.maxHeightElement || me.element,
                bodyWrap = me.bodyWrapElement;
            if (bodyWrap.parent() !== el) {
                
                
                
                
                if (header) {
                    bodyWrap.insertAfter(header.element);
                } else {
                    el.insertFirst(bodyWrap);
                }
            }
        }
    }
});


Ext.define('Ext.panel.Resizable', {
    override: 'Ext.Panel',
    config: {
        
        resizable: null
    },
    
    
    
    
    
    
    hasResizable: true,
    defaultResizerCls: 'Ext.panel.Resizer',
    applyResizable: function(resizable) {
        if (resizable) {
            if (resizable === true) {
                resizable = {};
            }
            resizable = Ext.create(Ext.apply({
                xclass: this.defaultResizerCls,
                target: this,
                ui: this.getUi()
            }, resizable));
        }
        return resizable;
    },
    updateResizable: function(resizable, oldResizable) {
        if (oldResizable) {
            oldResizable.destroy();
        }
    },
    doDestroy: function() {
        this.setResizable(null);
        this.callParent();
    },
    privates: {
        onResizableUiChange: function(ui, oldUi) {
            var resizable = this.getResizable();
            if (resizable) {
                resizable.setUi(ui);
            }
        }
    }
});


Ext.define('Ext.Mask', {
    extend: Ext.Component,
    xtype: 'mask',
    config: {
        
        transparent: false,
        
        top: 0,
        
        left: 0,
        
        right: 0,
        
        bottom: 0
    },
    baseCls: Ext.baseCSSPrefix + 'mask',
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.element.on('tap', 'onTap', me);
        me.on('hide', 'onHide', me);
    },
    onHide: function() {
        Ext.util.InputBlocker.unblockInputs();
        
        if (Ext.browser.is.AndroidStock4 && Ext.os.version.getMinor() === 0) {
            var firstChild = this.element.getFirstChild();
            if (firstChild) {
                firstChild.redraw();
            }
        }
    },
    onTap: function(e) {
        this.fireEvent('tap', this, e);
    },
    updateTransparent: function(transparent) {
        this.toggleCls(this.baseCls + '-transparent', transparent);
    }
});


Ext.define('Ext.Sheet', {
    extend: Ext.Panel,
    xtype: 'sheet',
    
    isViewportMenu: false,
    
    hidden: true,
    config: {
        
        reveal: null,
        
        cover: null,
        
        side: null,
        
        stretchX: null,
        
        stretchY: null,
        
        enter: 'bottom',
        
        exit: 'bottom'
    },
    translatable: {
        type: 'csstransform'
    },
    showAnimation: {
        type: 'slideIn',
        duration: 250,
        easing: 'ease-out'
    },
    hideAnimation: {
        type: 'slideOut',
        duration: 250,
        easing: 'ease-in'
    },
    modal: true,
    hideOnMaskTap: true,
    
    centered: true,
    classCls: Ext.baseCSSPrefix + 'sheet',
    manageBorders: false,
    autoSize: null,
    border: true,
    bodyBorder: false,
    floated: true,
    isInputRegex: /^(input|textarea|select|a)$/i,
    destroy: function() {
        var me = this;
        me.setSide(null);
        me.callParent();
    },
    applyHideAnimation: function(config) {
        var exit = this.getExit(),
            direction = exit;
        if (exit === null) {
            return null;
        }
        if (config === true) {
            config = {
                type: 'slideOut'
            };
        }
        var anim = this.callParent([
                config
            ]);
        if (anim) {
            if (exit === 'bottom') {
                direction = 'down';
            } else if (exit === 'top') {
                direction = 'up';
            }
            anim.setDirection(direction);
        }
        return anim;
    },
    applyShowAnimation: function(config) {
        var enter = this.getEnter(),
            direction = enter;
        if (enter === null) {
            return null;
        }
        if (config === true) {
            config = {
                type: 'slideIn'
            };
        }
        var anim = this.callParent([
                config
            ]);
        if (anim) {
            if (enter === 'bottom') {
                direction = 'down';
            }
            if (enter === 'top') {
                direction = 'up';
            }
            anim.setBefore({
                display: null
            });
            anim.setReverse(true);
            anim.setDirection(direction);
        }
        return anim;
    },
    hide: function(animation) {
        var side = this.getSide();
        if (side) {
            Ext.Viewport.hideMenu(side, animation);
        } else {
            this.callParent([
                animation
            ]);
        }
    },
    show: function(animation, options) {
        var me = this,
            VP = Ext.Viewport,
            
            side = options && ('side' in options) ? options.side : me.getSide();
        if (side) {
            VP.setMenu(me);
            VP.showMenu(side);
        } else {
            me.callParent([
                animation,
                options
            ]);
        }
    },
    updateSide: function(newSide, oldSide) {
        var me = this,
            reShow = !me.isConfiguring && me.isVisible();
        me.isViewportMenu = !!newSide;
        if (oldSide) {
            Ext.Viewport.removeMenu(oldSide);
        }
        if (newSide) {
            Ext.Viewport.setMenu(me, {
                side: newSide
            });
            
            if (reShow) {
                me.show();
            }
        }
    },
    updateStretchX: function(newStretchX) {
        this.getLeft();
        this.getRight();
        if (newStretchX) {
            this.setLeft(0);
            this.setRight(0);
        }
    },
    updateStretchY: function(newStretchY) {
        this.getTop();
        this.getBottom();
        if (newStretchY) {
            this.setTop(0);
            this.setBottom(0);
        }
    }
});


Ext.define('Ext.ActionSheet', {
    extend: Ext.Sheet,
    xtype: 'actionsheet',
    classCls: Ext.baseCSSPrefix + 'actionsheet',
    centered: false,
    layout: 'vbox',
    side: 'bottom',
    defaultType: 'button'
});


Ext.define('Ext.Anim', {
    isAnim: true,
    
    disableAnimations: false,
    defaultConfig: {
        
        from: {},
        
        to: {},
        
        duration: 250,
        
        delay: 0,
        
        easing: 'ease-in-out',
        
        autoClear: true,
        
        out: true,
        
        direction: null,
        
        reverse: false
    },
    
    
    
    opposites: {
        'left': 'right',
        'right': 'left',
        'up': 'down',
        'down': 'up'
    },
    
    constructor: function(config) {
        config = Ext.apply({}, config || {}, this.defaultConfig);
        this.config = config;
        this.callParent([
            config
        ]);
        this.running = [];
    },
    initConfig: function(el, runConfig) {
        var me = this,
            config = Ext.apply({}, runConfig || {}, me.config);
        config.el = el = Ext.get(el);
        if (config.reverse && me.opposites[config.direction]) {
            config.direction = me.opposites[config.direction];
        }
        if (me.config.before) {
            me.config.before.call(config, el, config);
        }
        if (runConfig.before) {
            runConfig.before.call(config.scope || config, el, config);
        }
        return config;
    },
    
    run: function(el, config) {
        el = Ext.get(el);
        config = config || {};
        var me = this,
            style = el.dom.style,
            property,
            after = config.after;
        if (me.running[el.id]) {
            me.onTransitionEnd(null, el, {
                config: config,
                after: after
            });
        }
        config = this.initConfig(el, config);
        if (this.disableAnimations) {
            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    
                    continue;
                }
                style[property] = config.to[property];
            }
            this.onTransitionEnd(null, el, {
                config: config,
                after: after
            });
            return me;
        }
        el.un('transitionend', me.onTransitionEnd, me);
        style.webkitTransitionDuration = '0ms';
        style.transitionDuration = '0ms';
        for (property in config.from) {
            if (!config.from.hasOwnProperty(property)) {
                
                continue;
            }
            style[property] = config.from[property];
        }
        Ext.defer(function() {
            
            if (!el.dom) {
                return;
            }
            
            if (config.is3d === true) {
                el.parent().setStyle({
                    
                    '-webkit-perspective': '1200',
                    '-webkit-transform-style': 'preserve-3d'
                });
            }
            style.webkitTransitionDuration = config.duration + 'ms';
            style.webkitTransitionProperty = 'all';
            style.webkitTransitionTimingFunction = config.easing;
            
            style.transitionDuration = config.duration + 'ms';
            style.transitionProperty = 'all';
            style.transitionTimingFunction = config.easing;
            
            el.on('transitionend', me.onTransitionEnd, me, {
                single: true,
                config: config,
                after: after
            });
            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    
                    continue;
                }
                style[property] = config.to[property];
            }
        }, config.delay || 5);
        me.running[el.id] = config;
        return me;
    },
    onTransitionEnd: function(ev, el, o) {
        el = Ext.get(el);
        if (this.running[el.id] === undefined) {
            return;
        }
        var style = el.dom.style,
            config = o.config,
            me = this,
            property;
        if (config.autoClear) {
            for (property in config.to) {
                if (!config.to.hasOwnProperty(property) || config[property] === false) {
                    
                    continue;
                }
                style[property] = '';
            }
        }
        style.webkitTransitionDuration = null;
        style.webkitTransitionProperty = null;
        style.webkitTransitionTimingFunction = null;
        style.transitionDuration = '';
        style.transitionProperty = '';
        style.transitionTimingFunction = '';
        if (config.is3d) {
            el.parent().setStyle({
                '-webkit-perspective': '',
                '-webkit-transform-style': ''
            });
        }
        if (me.config.after) {
            me.config.after.call(config, el, config);
        }
        if (o.after) {
            o.after.call(config.scope || me, el, config);
        }
        delete me.running[el.id];
    }
}, function() {
    Ext.Anim.seed = 1000;
    
    Ext.Anim.run = function(el, anim, config) {
        if (el.isComponent) {
            el = el.element;
        } else {
            el = Ext.get(el);
        }
        config = config || {};
        if (anim.isAnim) {
            anim.run(el, config);
        } else {
            if (Ext.isObject(anim)) {
                if (config.before && anim.before) {
                    config.before = Ext.createInterceptor(config.before, anim.before, anim.scope);
                }
                if (config.after && anim.after) {
                    config.after = Ext.createInterceptor(config.after, anim.after, anim.scope);
                }
                config = Ext.apply({}, config, anim);
                anim = anim.type || 'raw';
            }
            if (!Ext.anims[anim]) {
                throw anim + ' is not a valid animation type.';
            } else {
                
                if (el && el.dom) {
                    Ext.anims[anim].run(el, config);
                }
            }
        }
    };
    
    Ext.anims = {
        
        fade: new Ext.Anim({
            type: 'fade',
            before: function(el) {
                var fromOpacity = 1,
                    toOpacity = 1,
                    curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                    zIndex = curZ;
                if (this.out) {
                    toOpacity = 0;
                } else {
                    zIndex = Math.abs(curZ) + 1;
                    fromOpacity = 0;
                }
                this.from = {
                    'opacity': fromOpacity,
                    'z-index': zIndex
                };
                this.to = {
                    'opacity': toOpacity,
                    'z-index': zIndex
                };
            }
        }),
        
        slide: new Ext.Anim({
            direction: 'left',
            cover: false,
            reveal: false,
            opacity: false,
            'z-index': false,
            before: function(el) {
                var currentZIndex = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                    currentOpacity = el.getStyle('opacity'),
                    zIndex = currentZIndex + 1,
                    out = this.out,
                    direction = this.direction,
                    toX = 0,
                    toY = 0,
                    fromX = 0,
                    fromY = 0,
                    elH = el.getHeight(),
                    elW = el.getWidth();
                if (direction == 'left' || direction == 'right') {
                    if (out) {
                        toX = -elW;
                    } else {
                        fromX = elW;
                    }
                } else if (direction == 'up' || direction == 'down') {
                    if (out) {
                        toY = -elH;
                    } else {
                        fromY = elH;
                    }
                }
                if (direction == 'right' || direction == 'down') {
                    toY *= -1;
                    toX *= -1;
                    fromY *= -1;
                    fromX *= -1;
                }
                if (this.cover && out) {
                    toX = 0;
                    toY = 0;
                    zIndex = currentZIndex;
                } else if (this.reveal && !out) {
                    fromX = 0;
                    fromY = 0;
                    zIndex = currentZIndex;
                }
                this.from = {
                    '-webkit-transform': 'translate3d(' + fromX + 'px, ' + fromY + 'px, 0)',
                    'z-index': zIndex,
                    'opacity': currentOpacity - 0.01
                };
                this.to = {
                    '-webkit-transform': 'translate3d(' + toX + 'px, ' + toY + 'px, 0)',
                    'z-index': zIndex,
                    'opacity': currentOpacity
                };
            }
        }),
        
        pop: new Ext.Anim({
            scaleOnExit: true,
            before: function(el) {
                var fromScale = 1,
                    toScale = 1,
                    fromOpacity = 1,
                    toOpacity = 1,
                    curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                    fromZ = curZ,
                    toZ = curZ;
                if (!this.out) {
                    fromScale = 0.01;
                    fromZ = curZ + 1;
                    toZ = curZ + 1;
                    fromOpacity = 0;
                } else {
                    if (this.scaleOnExit) {
                        toScale = 0.01;
                        toOpacity = 0;
                    } else {
                        toOpacity = 0.8;
                    }
                }
                this.from = {
                    '-webkit-transform': 'scale(' + fromScale + ')',
                    '-webkit-transform-origin': '50% 50%',
                    'opacity': fromOpacity,
                    'z-index': fromZ
                };
                this.to = {
                    '-webkit-transform': 'scale(' + toScale + ')',
                    '-webkit-transform-origin': '50% 50%',
                    'opacity': toOpacity,
                    'z-index': toZ
                };
            }
        }),
        
        flip: new Ext.Anim({
            is3d: true,
            direction: 'left',
            before: function(el) {
                var rotateProp = 'Y',
                    fromScale = 1,
                    toScale = 1,
                    fromRotate = 0,
                    toRotate = 0;
                if (this.out) {
                    toRotate = -180;
                    toScale = 0.8;
                } else {
                    fromRotate = 180;
                    fromScale = 0.8;
                }
                if (this.direction == 'up' || this.direction == 'down') {
                    rotateProp = 'X';
                }
                if (this.direction == 'right' || this.direction == 'left') {
                    toRotate *= -1;
                    fromRotate *= -1;
                }
                this.from = {
                    '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg) scale(' + fromScale + ')',
                    '-webkit-backface-visibility': 'hidden'
                };
                this.to = {
                    '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) scale(' + toScale + ')',
                    '-webkit-backface-visibility': 'hidden'
                };
            }
        }),
        
        cube: new Ext.Anim({
            is3d: true,
            direction: 'left',
            style: 'outer',
            before: function(el) {
                var origin = '0% 0%',
                    fromRotate = 0,
                    toRotate = 0,
                    rotateProp = 'Y',
                    fromZ = 0,
                    toZ = 0,
                    elW = el.getWidth(),
                    elH = el.getHeight(),
                    showTranslateZ = true,
                    fromTranslate = ' translateX(0)',
                    toTranslate = '';
                if (this.direction == 'left' || this.direction == 'right') {
                    if (this.out) {
                        origin = '100% 100%';
                        toZ = elW;
                        toRotate = -90;
                    } else {
                        origin = '0% 0%';
                        fromZ = elW;
                        fromRotate = 90;
                    }
                } else if (this.direction == 'up' || this.direction == 'down') {
                    rotateProp = 'X';
                    if (this.out) {
                        origin = '100% 100%';
                        toZ = elH;
                        toRotate = 90;
                    } else {
                        origin = '0% 0%';
                        fromZ = elH;
                        fromRotate = -90;
                    }
                }
                if (this.direction == 'down' || this.direction == 'right') {
                    fromRotate *= -1;
                    toRotate *= -1;
                    origin = (origin == '0% 0%') ? '100% 100%' : '0% 0%';
                }
                if (this.style == 'inner') {
                    fromZ *= -1;
                    toZ *= -1;
                    fromRotate *= -1;
                    toRotate *= -1;
                    if (!this.out) {
                        toTranslate = ' translateX(0px)';
                        origin = '0% 50%';
                    } else {
                        toTranslate = fromTranslate;
                        origin = '100% 50%';
                    }
                }
                this.from = {
                    '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg)' + (showTranslateZ ? ' translateZ(' + fromZ + 'px)' : '') + fromTranslate,
                    '-webkit-transform-origin': origin
                };
                this.to = {
                    '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) translateZ(' + toZ + 'px)' + toTranslate,
                    '-webkit-transform-origin': origin
                };
            },
            duration: 250
        }),
        
        wipe: new Ext.Anim({
            before: function(el) {
                var curZ = el.getStyle('z-index'),
                    zIndex,
                    mask = '';
                if (!this.out) {
                    zIndex = curZ + 1;
                    mask = '-webkit-gradient(linear, left bottom, right bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    this.from = {
                        '-webkit-mask-image': mask,
                        '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
                        'z-index': zIndex,
                        '-webkit-mask-position-x': 0
                    };
                    this.to = {
                        '-webkit-mask-image': mask,
                        '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
                        'z-index': zIndex,
                        '-webkit-mask-position-x': -el.getWidth() * 2 + 'px'
                    };
                }
            },
            duration: 500
        }),
        
        raw: new Ext.Anim({
            duration: 250
        })
    };
});


Ext.define('Ext.Media', {
    extend: Ext.Component,
    xtype: 'media',
    
    
    
    
    
    
    
    config: {
        
        url: '',
        
        enableControls: true,
        
        controls: true,
        
        autoResume: false,
        
        autoPause: true,
        
        preload: true,
        
        loop: false,
        
        media: null,
        
        volume: 1,
        
        muted: false
    },
    constructor: function(config) {
        this.mediaEvents = {};
        this.callParent([
            config
        ]);
    },
    initialize: function() {
        var me = this;
        me.callParent();
        me.on({
            scope: me,
            show: 'onActivate',
            hide: 'onDeactivate'
        });
        me.addMediaListener({
            canplay: 'onCanPlay',
            play: 'onPlay',
            pause: 'onPause',
            ended: 'onEnd',
            volumechange: 'onVolumeChange',
            timeupdate: 'onTimeUpdate'
        });
    },
    addMediaListener: function(event, fn) {
        var me = this,
            dom = me.media.dom,
            bind = Ext.Function.bind;
        Ext.Object.each(event, function(e, fn) {
            fn = bind(me[fn], me);
            me.mediaEvents[e] = fn;
            dom.addEventListener(e, fn);
        });
    },
    onPlay: function() {
        this.fireEvent('play', this);
    },
    onCanPlay: function() {
        this.fireEvent('canplay', this);
    },
    onPause: function() {
        this.fireEvent('pause', this, this.getCurrentTime());
    },
    onEnd: function() {
        this.fireEvent('ended', this, this.getCurrentTime());
    },
    onVolumeChange: function() {
        this.fireEvent('volumechange', this, this.media.dom.volume);
    },
    onTimeUpdate: function() {
        this.fireEvent('timeupdate', this, this.getCurrentTime());
    },
    
    isPlaying: function() {
        return !Boolean(this.media.dom.paused);
    },
    
    onActivate: function() {
        var me = this;
        if (me.getAutoResume() && !me.isPlaying()) {
            me.play();
        }
    },
    
    onDeactivate: function() {
        var me = this;
        if (me.getAutoPause() && me.isPlaying()) {
            me.pause();
        }
    },
    
    updateUrl: function(newUrl) {
        var dom = this.media.dom;
        
        
        dom.src = newUrl;
        if ('load' in dom) {
            dom.load();
        }
        if (this.isPlaying()) {
            this.play();
        }
    },
    
    updateEnableControls: function(enableControls) {
        this.setControls(enableControls);
    },
    updateControls: function(value) {
        this.media.set({
            controls: value ? 'controls' : undefined
        });
    },
    
    updateLoop: function(loop) {
        this.media.dom.loop = loop ? 'loop' : false;
    },
    
    play: function() {
        var dom = this.media.dom;
        if ('play' in dom) {
            dom.play();
            Ext.defer(function() {
                dom.play();
            }, 10);
        }
    },
    
    pause: function() {
        var dom = this.media.dom;
        if ('pause' in dom) {
            dom.pause();
        }
    },
    
    toggle: function() {
        if (this.isPlaying()) {
            this.pause();
        } else {
            this.play();
        }
    },
    
    stop: function() {
        var me = this;
        me.setCurrentTime(0);
        me.fireEvent('stop', me);
        me.pause();
    },
    
    updateVolume: function(volume) {
        this.media.dom.volume = volume;
    },
    
    updateMuted: function(muted) {
        this.fireEvent('mutedchange', this, muted);
        this.media.dom.muted = muted;
    },
    
    getCurrentTime: function() {
        return this.media.dom.currentTime;
    },
    
    setCurrentTime: function(time) {
        this.media.dom.currentTime = time;
        return time;
    },
    
    getDuration: function() {
        return this.media.dom.duration;
    },
    doDestroy: function() {
        var me = this,
            dom = me.media.dom,
            mediaEvents = me.mediaEvents;
        Ext.Object.each(mediaEvents, function(event, fn) {
            dom.removeEventListener(event, fn);
        });
        me.callParent();
    },
    deprecated: {
        '6.5': {
            configs: {
                enableControls: {
                    message: 'Please use "controls" instead.'
                }
            }
        }
    }
});


Ext.define('Ext.Audio', {
    extend: Ext.Media,
    xtype: 'audio',
    config: {
        
        cls: Ext.baseCSSPrefix + 'audio'
    },
    
    
    onActivate: function() {
        var me = this;
        me.callParent();
        if (Ext.os.is.Phone) {
            me.element.show();
        }
    },
    
    onDeactivate: function() {
        var me = this;
        me.callParent();
        if (Ext.os.is.Phone) {
            me.element.hide();
        }
    },
    template: [
        {
            reference: 'media',
            preload: 'auto',
            tag: 'audio',
            cls: Ext.baseCSSPrefix + 'component'
        }
    ]
});


Ext.define('Ext.Decorator', {
    extend: Ext.Component,
    isDecorator: true,
    config: {
        
        
        component: {
            xtype: 'component'
        }
    },
    statics: {
        generateProxySetter: function(name) {
            return function(value) {
                var component = this.getComponent();
                component[name].call(component, value);
                return this;
            };
        },
        generateProxyGetter: function(name) {
            return function() {
                var component = this.getComponent();
                return component[name].call(component);
            };
        }
    },
    onClassExtended: function(Class, members) {
        if (!members.hasOwnProperty('proxyConfig')) {
            return;
        }
        var ExtClass = Ext.Class,
            proxyConfig = members.proxyConfig,
            config = members.config;
        members.config = (config) ? Ext.applyIf(config, proxyConfig) : proxyConfig;
        var name, nameMap, setName, getName;
        for (name in proxyConfig) {
            if (proxyConfig.hasOwnProperty(name)) {
                nameMap = Ext.Config.get(name).names;
                setName = nameMap.set;
                getName = nameMap.get;
                members[setName] = this.generateProxySetter(setName);
                members[getName] = this.generateProxyGetter(getName);
            }
        }
    },
    getRefItems: function(deep) {
        var c = this.getComponent(),
            ret;
        if (c) {
            ret = [
                c
            ];
            if (deep && c.getRefItems) {
                ret = ret.concat(c.getRefItems(deep));
            }
        }
        return ret || [];
    },
    
    applyComponent: function(config) {
        var result = Ext.factory(config);
        result.ownerCmp = this;
        return result;
    },
    
    updateComponent: function(newComponent, oldComponent) {
        var me = this;
        if (oldComponent) {
            if (me.isRendered() && oldComponent.rendered) {
                oldComponent.setRendered(false);
                oldComponent.fireEventedAction('renderedchange', [
                    me,
                    oldComponent,
                    false
                ], me.doUnsetComponent, me, false);
            } else {
                me.doUnsetComponent(oldComponent);
            }
        }
        if (newComponent) {
            if (me.isRendered() && !newComponent.rendered) {
                newComponent.fireEventedAction('renderedchange', [
                    me,
                    newComponent,
                    true
                ], me.doSetComponent, me, false);
            } else {
                me.doSetComponent(newComponent);
            }
        }
    },
    
    doUnsetComponent: function(component) {
        var dom = component.renderElement.dom;
        if (dom) {
            this.bodyElement.dom.removeChild(dom);
        }
    },
    
    doSetComponent: function(component) {
        var dom = component.renderElement.dom;
        if (dom) {
            this.bodyElement.dom.appendChild(dom);
            if (this.rendered) {
                component.setRendered(true);
            }
        }
    },
    
    setDisabled: function(disabled) {
        var component;
        
        this.callParent(arguments);
        
        
        
        
        component = this.getComponent();
        if (component) {
            component.setDisabled(disabled);
        }
    },
    doDestroy: function() {
        Ext.destroy(this.getComponent());
        this.callParent();
    }
});


Ext.define('Ext.Dialog', {
    extend: Ext.Panel,
    xtype: [
        'dialog',
        'window'
    ],
    
    alternateClassName: [
        'Ext.Window',
        'Ext.window.Window'
    ],
    
    isDialog: true,
    isWindow: true,
    
    
    ariaRole: 'dialog',
    
    classCls: Ext.baseCSSPrefix + 'dialog',
    
    
    
    
    cachedConfig: {
        
        dismissAction: [
            'cancel',
            'abort',
            'no',
            'close'
        ],
        
        maximizeAnimation: {
            easing: 'ease-in',
            from: {
                opacity: 0.6
            },
            to: {
                opacity: 1
            }
        },
        
        maximizeProxy: {
            centered: false,
            draggable: false,
            modal: false,
            showAnimation: null,
            hideAnimation: null
        },
        
        maximizeTool: {
            itemId: 'maximize',
            
            tooltip: 'Maximize to fullscreen'
        },
        
        restoreAnimation: {
            easing: 'ease-in',
            from: {
                opacity: 1
            },
            to: {
                opacity: 0.6
            }
        },
        
        restoreTool: {
            itemId: 'restore',
            
            tooltip: 'Restore to original size'
        }
    },
    config: {
        
        constrainDrag: true,
        
        dismissHandler: null,
        
        maximizable: null,
        
        maximized: null,
        
        maskTapHandler: null,
        
        restorable: null
    },
    
    
    
    border: true,
    
    bodyBorder: false,
    
    centered: true,
    
    floated: true,
    
    focusable: false,
    
    tabIndex: -1,
    
    draggable: {
        handle: '.' + Ext.baseCSSPrefix + 'draggable',
        listeners: {
            beforedragstart: 'onBeforeDragDialog',
            scope: 'this'
        }
    },
    
    keyMap: {
        ESC: 'onEscape',
        scope: 'this'
    },
    
    modal: true,
    
    shadow: true,
    headerCls: Ext.baseCSSPrefix + 'dialogheader',
    titleCls: Ext.baseCSSPrefix + 'dialogtitle',
    toolCls: [
        Ext.baseCSSPrefix + 'paneltool',
        Ext.baseCSSPrefix + 'dialogtool'
    ],
    
    hideMode: 'offsets',
    
    hideAnimation: {
        type: 'popOut',
        duration: 250,
        easing: 'ease-out'
    },
    
    showAnimation: {
        type: 'popIn',
        duration: 150,
        easing: 'ease-out'
    },
    
    initialize: function() {
        var me = this;
        me.callParent();
        if (me.tabGuard) {
            me.addPlugin({
                type: 'tabguard',
                tabGuardBeforeIndex: me.tabGuardBeforeIndex,
                tabGuardAfterIndex: me.tabGuardAfterIndex
            });
        }
    },
    doDestroy: function() {
        Ext.destroy(this.maximizeTool, this.restoreTool);
        this.callParent();
    },
    close: function(event) {
        var me = this,
            buttons = me.getButtons(),
            actions = me.getDismissAction(),
            handler = me.getDismissHandler(),
            action, closeAction, closeActionIsDestroy, done, i, n;
        event = event || null;
        if (me.fireEvent('beforeclose', me, event) !== false) {
            
            
            
            if (handler !== true) {
                if (handler) {
                    Ext.callback(handler, null, [
                        me,
                        event
                    ], 0, me);
                    done = true;
                } else if (actions && buttons) {
                    if (typeof actions === 'string') {
                        actions = [
                            actions
                        ];
                    }
                    for (i = 0 , n = actions.length; i < n; ++i) {
                        action = buttons.getComponent(actions[i]);
                        if (action && action.isButton) {
                            action.onTap(event);
                            done = true;
                            break;
                        }
                    }
                }
            }
            
            if (!done) {
                closeAction = me.getCloseAction();
                if (closeAction) {
                    if (!(closeActionIsDestroy = closeAction === 'destroy')) {
                        me[closeAction]();
                    }
                }
            }
            if (!me.destroyed) {
                me.fireEvent('close', me, event);
                if (closeActionIsDestroy) {
                    me.destroy();
                }
            }
        }
    },
    createMaximizeProxy: function(config) {
        var me = this;
        return Ext.apply({
            title: me.getTitle(),
            cls: me.getCls(),
            userCls: me.getUserCls(),
            iconCls: me.getIconCls(),
            ui: me.getUi()
        }, config);
    },
    
    maximize: function(animation) {
        var me = this,
            maximizing = me.maximizing;
        if (!maximizing && !me.getMaximized()) {
            me._maximizeAnim = animation;
            me.setMaximized(true);
            if (!(maximizing = me.maximizing)) {
                return Ext.Promise.resolve(false);
            }
        }
        return maximizing ? maximizing.promise : Ext.Promise.resolve(true);
    },
    
    restore: function(animation) {
        var me = this,
            restoring = me.restoring;
        if (!restoring && me.getMaximized()) {
            me._maximizeAnim = animation;
            me.setMaximized(false);
            if (!(restoring = me.restoring)) {
                return Ext.Promise.resolve(false);
            }
        }
        return restoring ? restoring.promise : Ext.Promise.resolve(true);
    },
    shouldRecenter: function() {
        return !this.getMaximized() && this.callParent();
    },
    
    
    
    updateConstrainDrag: function(constrain) {
        var dragger = this.getDraggable();
        if (dragger) {
            if (constrain === true) {
                constrain = Ext.getBody();
            }
            dragger.setConstrain(constrain);
        }
    },
    
    updateDraggable: function(draggable, existing) {
        this.callParent([
            draggable,
            existing
        ]);
        if (!this.isConfiguring) {
            this.syncHeaderItems();
        }
    },
    
    updateHeader: function(header, oldHeader) {
        var me = this,
            beforeGuard;
        me.callParent([
            header,
            oldHeader
        ]);
        if (header) {
            me.syncHeaderItems();
            if (me.tabGuard && me.getTabGuard) {
                beforeGuard = me.getTabGuard('before');
                
                if (beforeGuard && beforeGuard.dom) {
                    beforeGuard.insertBefore(header.el);
                }
            }
        }
    },
    
    applyMaximizable: function(maximizable) {
        var me = this;
        me.maximizeTool = Ext.updateWidget(me.maximizeTool, maximizable, me, 'createMaximizeTool', 'maximizeTool');
        me.syncHeaderItems();
        return maximizable;
    },
    
    applyMaximized: function(maximized) {
        var me = this,
            event;
        if (!me.isConfiguring) {
            event = maximized ? 'beforemaximize' : 'beforerestore';
            if (me.fireEvent(event, me) === false) {
                
                me._maximizeAnim = undefined;
                return;
            }
        }
        
        return !!maximized;
    },
    updateMaximized: function(maximized) {
        var me = this,
            el = me.el,
            maximizedCls = me.maximizedCls,
            maximizeTool = me.maximizeTool,
            pendingName = maximized ? 'restoring' : 'maximizing',
            pending = me[pendingName],
            after, anim, before, center;
        if (me.isConfiguring) {
            me.needsCenter = maximized;
        } else {
            anim = me._maximizeAnim;
            center = me.needsCenter && !maximized;
            me.needsCenter = false;
            if (anim === undefined) {
                anim = me[maximized ? 'getMaximizeAnimation' : 'getRestoreAnimation']();
            }
        }
        me._maximizeAnim = undefined;
        
        if (pending) {
            pending.destroy();
        }
        
        if (me.getMaximizable()) {
            me.setRestorable(maximized);
        } else {
            
            
            me.syncHeaderItems();
        }
        if (maximizeTool) {
            maximizeTool.setHidden(maximized);
        }
        if (!anim) {
            el.toggleCls(maximizedCls, maximized);
            if (center) {
                me.center();
            }
            me.fireEvent(maximized ? 'maximize' : 'restore', me);
        } else {
            if (maximized) {
                pendingName = 'maximizing';
                
                
                
                before = me.captureSize();
                after = me.captureSize(true);
            } else {
                pendingName = 'restoring';
                
                
                el.removeCls(maximizedCls);
                if (center) {
                    me.center();
                }
                before = me.captureSize(true);
                after = me.captureSize();
            }
            me[pendingName] = me.animateMaximizeRestore(before, after, anim, function() {
                if (maximized) {
                    
                    
                    el.addCls(maximizedCls);
                }
                me[pendingName] = null;
                me.fireEvent(maximized ? 'maximize' : 'restore', me);
            });
        }
    },
    
    createMaximizeTool: function(config) {
        var tool = this.adjustToolDefaults(Ext.clone(config));
        tool.handler = 'onMaximize';
        tool.scope = this;
        return tool;
    },
    
    applyRestorable: function(restorable) {
        var me = this;
        me.restoreTool = Ext.updateWidget(me.restoreTool, restorable, me, 'createRestoreTool', 'restoreTool');
        me.syncHeaderItems();
        return restorable;
    },
    createRestoreTool: function(config) {
        var tool = this.adjustToolDefaults(Ext.clone(config));
        tool.handler = 'onRestore';
        tool.scope = this;
        return tool;
    },
    
    afterShow: function() {
        this.callParent();
        if (this.getModal()) {
            this.focus();
        }
    },
    onBeforeDragDialog: function(draggable, info, event) {
        var header = this.getHeader();
        
        
        if (!header || !header.el.contains(event.target)) {
            return false;
        }
    },
    onCloseTool: function(dialog, tool, event) {
        this.close(event);
    },
    onEscape: function(event) {
        this.close(event);
    },
    onMaximize: function() {
        this.setMaximized(true);
    },
    onRestore: function() {
        this.setMaximized(false);
    },
    onModalMaskTap: function(e) {
        var me = this,
            handler = me.getMaskTapHandler(),
            ret;
        if (handler) {
            Ext.callback(handler, null, [
                me,
                e
            ], 0, me);
        } else {
            ret = me.callParent([
                e
            ]);
        }
        
        return ret;
    },
    
    privates: {
        draggableCls: Ext.baseCSSPrefix + 'draggable',
        needsCenter: false,
        maximizedCls: Ext.baseCSSPrefix + 'maximized',
        animateMaximizeRestore: function(before, after, anim, callback) {
            var me = this,
                pending = new Ext.Deferred(),
                proxy = me.getMaximizeProxy(),
                a = Ext.merge({
                    
                    from: {
                        width: before.w + 'px',
                        height: before.h + 'px',
                        transform: {
                            translateX: before.x + 'px',
                            translateY: before.y + 'px'
                        }
                    },
                    to: {
                        width: after.w + 'px',
                        height: after.h + 'px',
                        transform: {
                            translateX: after.x + 'px',
                            translateY: after.y + 'px'
                        }
                    }
                }, anim);
            proxy = me.createMaximizeProxy(proxy);
            proxy = new me.self(proxy);
            proxy.show();
            a.element = proxy.el;
            a.callback = function() {
                proxy.destroy();
                callback();
                pending.resolve(true);
            };
            a = new Ext.fx.animation.Abstract(a);
            Ext.Animator.run(a);
            pending.destroy = function() {
                pending.destroy = Ext.emptyFn;
                a.destroy();
                pending.destroyed = true;
            };
            return pending;
        },
        captureSize: function(maximized) {
            if (maximized) {
                return {
                    x: 0,
                    y: 0,
                    w: Ext.getViewportWidth(),
                    h: Ext.getViewportHeight()
                };
            }
            var me = this,
                size = me.el.measure();
            return {
                x: me.getX(),
                y: me.getY(),
                w: size.width,
                h: size.height
            };
        },
        syncHeaderItems: function() {
            var me = this,
                maximizeTool = me.maximizeTool,
                restoreTool = me.restoreTool,
                header = (maximizeTool || restoreTool) ? me.ensureHeader() : me.getHeader(),
                draggableCls = me.draggableCls,
                draggable, title;
            if (header) {
                
                draggable = me.getDraggable() && !me.getMaximized();
                header.toggleCls(draggableCls, draggable);
                title = header.getTitle();
                if (title) {
                    title.toggleCls(draggableCls, draggable);
                }
                if (maximizeTool && maximizeTool.parent !== header) {
                    header.add(maximizeTool);
                }
                if (restoreTool && restoreTool.parent !== header) {
                    header.add(restoreTool);
                }
            }
        },
        updateX: function(x, oldX) {
            this.callParent([
                x,
                oldX
            ]);
            
            
            if (!this._centering && this.getCentered()) {
                this.setCentered(false);
            }
        }
    }
});



Ext.define('Ext.field.Field', {
    extend: Ext.Component,
    alternateClassName: 'Ext.form.Field',
    xtype: 'field',
    
    isField: true,
    
    isFormField: true,
    
    
    
    
    
    
    cachedConfig: {
        
        bodyAlign: 'start',
        
        labelAlign: 'left',
        
        labelCls: null,
        
        labelTextAlign: 'left',
        
        labelWidth: null,
        
        labelMinWidth: null,
        
        labelWrap: false
    },
    config: {
        
        name: null,
        
        label: null,
        
        required: false,
        
        requiredMessage: 'This field is required',
        
        value: null,
        
        validators: null,
        
        validationMessage: 'Is in the wrong format',
        
        validateDisabled: null,
        
        
        
        autoFitErrors: null,
        
        inline: null,
        
        error: null,
        
        errorMessage: null,
        
        errorTarget: 'qtip',
        
        errorTpl: null,
        
        errorTip: null,
        
        sideError: null,
        
        tipError: null,
        
        titleError: null,
        
        underError: null
    },
    
    htmlErrorsTpl: [
        '<tpl if="count == 1">',
        '<tpl for="errors">{.:htmlEncode}</tpl>',
        '<tpl elseif="count">',
        '<ul class="{listCls}">',
        '<tpl for="errors"><li>{.:htmlEncode}</li></tpl>',
        '</ul>',
        '</tpl>'
    ],
    
    plainErrorsTpl: [
        '<tpl if="count">',
        '<tpl for="errors" between="\\n">{.}</tpl>',
        '</tpl>'
    ],
    _errorTplMap: {
        title: 'plainErrorsTpl'
    },
    
    originalValue: null,
    focusable: true,
    classCls: Ext.baseCSSPrefix + 'field',
    requiredCls: Ext.baseCSSPrefix + 'required',
    noLabelWrapCls: Ext.baseCSSPrefix + 'no-label-wrap',
    invalidCls: Ext.baseCSSPrefix + 'invalid',
    noAutoFitErrorsCls: Ext.baseCSSPrefix + 'no-auto-fit-errors',
    inlineCls: Ext.baseCSSPrefix + 'inline',
    labelAlignVerticalCls: Ext.baseCSSPrefix + 'label-align-vertical',
    labelAlignHorizontalCls: Ext.baseCSSPrefix + 'label-align-horizontal',
    labeledCls: Ext.baseCSSPrefix + 'labeled',
    verticalLabelMap: {
        top: 1,
        placeholder: 1,
        bottom: 1
    },
    horizontalLabelMap: {
        left: 1,
        right: 1
    },
    nameable: true,
    
    validateOnInit: 'auto',
    errorElement: null,
    errorIconElement: null,
    errorMessageElement: null,
    element: {
        reference: 'element',
        classList: [
            Ext.supports.CSSMinContent ? '' : (Ext.baseCSSPrefix + 'no-min-content'),
            Ext.supports.PercentageSizeFlexBug ? (Ext.baseCSSPrefix + 'has-percentage-size-flex-bug') : ''
        ]
    },
    initialize: function() {
        var me = this;
        me.callParent();
        if (me.getValue() === '' && me.validateOnInit === 'all') {
            me.validate();
        }
    },
    
    didValueChange: function(newVal, oldVal) {
        return !this.isEqual(newVal, oldVal);
    },
    getTemplate: function() {
        return [
            {
                reference: 'labelElement',
                cls: Ext.baseCSSPrefix + 'label-el',
                tag: 'label',
                children: [
                    {
                        reference: 'labelTextElement',
                        cls: Ext.baseCSSPrefix + 'label-text-el',
                        tag: 'span'
                    }
                ]
            },
            {
                reference: 'bodyWrapElement',
                cls: Ext.baseCSSPrefix + 'body-wrap-el',
                children: [
                    {
                        reference: 'bodyElement',
                        cls: Ext.baseCSSPrefix + 'body-el',
                        children: this.getBodyTemplate()
                    },
                    {
                        reference: 'errorElement',
                        cls: Ext.baseCSSPrefix + 'error-el',
                        children: [
                            {
                                reference: 'errorIconElement',
                                cls: Ext.baseCSSPrefix + 'error-icon-el ' + Ext.baseCSSPrefix + 'font-icon'
                            },
                            {
                                reference: 'errorMessageElement',
                                cls: Ext.baseCSSPrefix + 'error-message-el'
                            }
                        ]
                    }
                ]
            }
        ];
    },
    getBodyTemplate: Ext.emptyFn,
    initElement: function() {
        this.callParent();
        
        this.innerElement = this.innerElement || this.bodyElement;
    },
    onFocusLeave: function(e) {
        this.callParent([
            e
        ]);
        this.completeEdit();
    },
    
    completeEdit: Ext.emptyFn,
    updateBodyAlign: function(bodyAlign, oldBodyAlign) {
        var element = this.element;
        if (oldBodyAlign) {
            element.removeCls(Ext.baseCSSPrefix + 'body-align-' + oldBodyAlign);
        }
        if (bodyAlign) {
            element.addCls(Ext.baseCSSPrefix + 'body-align-' + bodyAlign);
        }
    },
    updateAutoFitErrors: function(autoFitErrors) {
        this.toggleCls(this.noAutoFitErrorsCls, autoFitErrors === false);
    },
    applyErrorTpl: function(tpl) {
        if (tpl && !tpl.isTemplate) {
            tpl = Ext.XTemplate.get(tpl);
        }
        return tpl;
    },
    
    formatErrors: function(errors) {
        var me = this,
            tpl = me.getErrorTpl();
        if (!tpl) {
            tpl = me.lookupTpl(me._errorTplMap[me.getErrorTarget()] || 'htmlErrorsTpl');
        }
        return tpl.apply({
            count: errors ? errors.length : 0,
            label: me.getLabel(),
            errors: errors
        });
    },
    updateError: function(value) {
        var msg = this.formatErrors(Ext.Array.from(value));
        this.setErrorMessage(msg);
    },
    updateErrorMessage: function(msg) {
        var me = this,
            errorTarget;
        me.fireEvent('errorchange', me, msg);
        if (me.preventMark) {
            return;
        }
        me.toggleInvalidCls(!!msg);
        errorTarget = me.getErrorTarget();
        switch (errorTarget) {
            case 'side':
                me.setSideError(msg);
                break;
            case 'qtip':
                me.setTipError(msg);
                break;
            case 'title':
                me.setTitleError(msg);
                break;
            case 'under':
                me.setUnderError(msg);
                break;
            case 'parent':
                var owner = me.up('[onFieldErrorChange]');
                if (owner) {
                    owner.onFieldErrorChange(me, msg);
                };
                break;
        }
    },
    
    
    
    
    updateErrorTarget: function(target, oldTarget) {
        var me = this,
            error, owner;
        if (oldTarget) {
            me.removeCls(Ext.baseCSSPrefix + 'error-target-' + oldTarget);
            if (oldTarget === 'qtip') {
                me.setTipError(null);
            } else if (oldTarget === 'title') {
                me.setTitleError(null);
            } else if (oldTarget === 'side') {
                me.setSideError(null);
            } else if (oldTarget === 'under') {
                me.setUnderError(null);
            } else if (oldTarget === 'parent') {
                owner = me.up('[onFieldErrorChange]');
                if (owner) {
                    owner.onFieldErrorChange(me);
                }
            }
        }
        if (target) {
            me.addCls(Ext.baseCSSPrefix + 'error-target-' + target);
            if (!me.isConfiguring) {
                error = me.getError();
                if (error) {
                    if (target === 'qtip') {
                        me.setTipError(error);
                    } else if (target === 'title') {
                        me.setTitleError(error);
                    } else if (target === 'side') {
                        me.setSideError(error);
                    } else if (target === 'under') {
                        me.setUnderError(error);
                    } else if (target === 'parent') {
                        owner = me.up('[onFieldErrorChange]');
                        if (owner) {
                            owner.onFieldErrorChange(me, error);
                        }
                    }
                }
            }
        }
    },
    updateInline: function(inline) {
        this.toggleCls(this.inlineCls, inline);
    },
    updateSideError: function(error) {
        if (error) {
            error = Ext.apply({
                html: error
            }, this.getErrorTip());
        }
        this.errorElement.getData().qtip = error;
    },
    updateTipError: function(error) {
        if (error) {
            error = Ext.apply({
                html: error
            }, this.getErrorTip());
        }
        
        
        
        this.bodyElement.getData().qtip = error;
    },
    updateTitleError: function(error) {
        var dom = this.el.dom;
        if (error) {
            dom.setAttribute('title', error);
        } else {
            dom.removeAttribute('title');
        }
    },
    updateUnderError: function(error) {
        this.errorMessageElement.dom.innerHTML = error || '';
    },
    updateLabel: function(label) {
        this.labelTextElement.setHtml(label);
        this.el.toggleCls(this.labeledCls, !!label);
    },
    updateLabelAlign: function(newLabelAlign, oldLabelAlign) {
        var me = this,
            element = me.element;
        if (oldLabelAlign) {
            element.removeCls(Ext.baseCSSPrefix + 'label-align-' + oldLabelAlign);
        }
        if (newLabelAlign) {
            element.addCls(Ext.baseCSSPrefix + 'label-align-' + newLabelAlign);
        }
        element.toggleCls(me.labelAlignVerticalCls, newLabelAlign in me.verticalLabelMap);
        element.toggleCls(me.labelAlignHorizontalCls, newLabelAlign in me.horizontalLabelMap);
    },
    updateLabelTextAlign: function(labelTextAlign, oldLabelTextAlign) {
        var element = this.element;
        if (oldLabelTextAlign) {
            element.removeCls(Ext.baseCSSPrefix + 'label-text-align-' + oldLabelTextAlign);
        }
        if (labelTextAlign) {
            element.addCls(Ext.baseCSSPrefix + 'label-text-align-' + labelTextAlign);
        }
    },
    updateLabelCls: function(newLabelCls, oldLabelCls) {
        var labelElement = this.labelElement;
        if (newLabelCls) {
            labelElement.addCls(newLabelCls);
        }
        if (oldLabelCls) {
            labelElement.removeCls(oldLabelCls);
        }
    },
    updateLabelWidth: function(labelWidth) {
        this.labelElement.setWidth(labelWidth);
    },
    updateLabelMinWidth: function(labelMinWidth) {
        this.labelElement.setStyle('min-width', Ext.Element.addUnits(labelMinWidth));
    },
    updateLabelWrap: function(labelWrap) {
        this.element.toggleCls(this.noLabelWrapCls, !labelWrap);
    },
    updateName: function(newName) {
        this.name = newName;
    },
    updateRequired: function(required) {
        var me = this;
        me.element.toggleCls(me.requiredCls, required);
        if (!me.isConfiguring) {
            me.validate();
        }
    },
    updateRequiredMessage: function() {
        if (!this.isConfiguring) {
            this.validate();
        }
    },
    updateDisabled: function(disabled, oldDisabled) {
        this.callParent([
            disabled,
            oldDisabled
        ]);
        if (!this.isConfiguring) {
            this.validate();
        }
    },
    updateValidateDisabled: function() {
        if (!this.isConfiguring) {
            this.validate();
        }
    },
    applyValue: function(value) {
        if (this.isConfiguring) {
            this.originalValue = value;
        }
        return value;
    },
    updateValue: function(value, oldValue) {
        var me = this;
        
        
        
        if (!(Ext.isEmpty(value) && Ext.isEmpty(oldValue))) {
            me.validate();
        }
        if (!me.isConfiguring && value !== oldValue) {
            me.fireEvent('change', me, value, oldValue);
        }
    },
    
    reset: function() {
        this.setValue(this.originalValue);
        return this;
    },
    
    resetOriginalValue: function() {
        this.originalValue = this.getValue();
    },
    
    isDirty: function() {
        return this.getValue() !== this.originalValue;
    },
    
    toggleInvalidCls: function(hasError) {
        this.el[hasError ? 'addCls' : 'removeCls'](this.invalidCls);
    },
    
    markInvalid: function(messages) {
        this.setError(messages);
    },
    
    clearInvalid: function() {
        this.setError(null);
    },
    
    isValid: function() {
        return !this.getError();
    },
    
    isEqual: function(value1, value2) {
        return String(value1) === String(value2);
    },
    
    applyValidators: function(validators) {
        var me = this,
            i, len, ret;
        validators = (validators && !Ext.isArray(validators)) ? [
            validators
        ] : validators;
        len = validators && validators.length;
        ret = len ? [] : null;
        for (i = 0; i < len; ++i) {
            ret.push(me.decodeValidator(validators[i]));
        }
        return ret;
    },
    wrapValidatorFn: function(fn, validator) {
        var me = this,
            scope = validator && validator.scope;
        return new Ext.data.validator['Validator'](function(value) {
            return Ext.callback(fn, scope, [
                value
            ], 0, me);
        });
    },
    
    doValidate: function(value, errors, skipLazy) {
        var validators = this.getValidators(),
            len = validators && validators.length,
            i, result, validator;
        for (i = 0; i < len; ++i) {
            validator = validators[i];
            if (!skipLazy || !validator.lazy) {
                result = validator.validate(value);
                if (result !== true) {
                    
                    if (!result || typeof result !== 'string') {
                        Ext.raise('Validator did not return a valid result.');
                    }
                    
                    errors.push(result);
                }
            }
        }
    },
    parseValue: Ext.identityFn,
    
    
    validate: function(skipLazy) {
        var me = this,
            empty, errors, field, record, validity, value;
        
        if (me.isConfiguring && me.validateOnInit === 'none') {
            return true;
        }
        
        if (!me.getDisabled() || me.getValidateDisabled()) {
            errors = [];
            
            
            
            if (me.isInputField && !me.isSelectField) {
                value = me.getInputValue();
                empty = !value;
                validity = empty && me.inputElement.dom.validity;
                if (validity && validity.badInput) {
                    errors.push(me.badFormatMessage);
                    empty = false;
                }
            } else {
                value = me.getValue();
                empty = value === '' || value == null;
            }
            if (empty && me.getRequired()) {
                errors.push(me.getRequiredMessage());
            } else if (!errors.length) {
                if (!empty) {
                    
                    
                    
                    
                    
                    value = me.parseValue(value, errors);
                }
                if (!errors.length) {
                    field = me._validationField;
                    record = me._validationRecord;
                    if (field && record) {
                        field.validate(value, null, errors, record);
                    }
                    if (!empty) {
                        me.doValidate(value, errors, skipLazy);
                    }
                }
            }
            if (errors.length) {
                me.setError(errors);
                return false;
            }
        }
        me.setError(null);
        return true;
    },
    getFocusClsEl: function() {
        return this.element;
    },
    updateHeight: function(height, oldHeight) {
        this.callParent([
            height,
            oldHeight
        ]);
        this.syncFormLayoutHeight();
    },
    onAdded: function(parent, instanced) {
        this.callParent([
            parent,
            instanced
        ]);
        this.syncFormLayoutHeight();
        this.validateLayout();
    },
    onRemoved: function(destroying) {
        this.callParent([
            destroying
        ]);
        this.syncFormLayoutHeight();
    },
    privates: {
        syncFormLayoutHeight: function() {
            var me = this,
                parent = me.parent,
                height = me.getHeight();
            if (!(height && parent && parent.getLayout().isFormLayout)) {
                height = null;
            }
            me.bodyElement.setHeight(height);
        },
        validateLayout: function() {
            var errorTarget = this.getErrorTarget(),
                parent = this.parent;
            if (this.isInner && parent && parent.getLayout().isFormLayout) {
                
                this.setLabelAlign('left');
                
                if (errorTarget === 'under') {
                    this.setErrorTarget('side');
                }
            }
        },
        applyBind: function(bind, currentBindings) {
            var me = this,
                valueBinding = currentBindings && currentBindings.value,
                bindings, newValueBind;
            bindings = me.callParent([
                bind,
                currentBindings
            ]);
            if (bindings) {
                newValueBind = bindings.value;
                me.hasBindingValue = !!newValueBind;
                if (newValueBind !== valueBinding && me.getInherited().modelValidation) {
                    me.updateValueBinding(bindings);
                }
            }
            return bindings;
        },
        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);
            }
        },
        setValidationField: function(field, record) {
            this._validationField = field;
            this._validationRecord = record;
        },
        decodeValidator: function(validator) {
            var type = Ext.typeOf(validator),
                result = validator.fn;
            if (type === 'function') {
                result = this.wrapValidatorFn(validator);
            } else if (type === 'regexp') {
                result = Ext.Factory.validator({
                    type: 'format',
                    matcher: validator
                });
            } else if (type === 'object' && result && !validator.isValidator) {
                result = this.wrapValidatorFn(result, validator);
            } else {
                result = Ext.Factory.validator(validator);
            }
            return result;
        }
    }
});


Ext.define('Ext.field.Input', {
    extend: Ext.field.Field,
    xtype: 'inputfield',
    isInputField: true,
    
    tag: 'input',
    config: {
        
        inputType: {
            cached: true,
            $value: 'text'
        },
        
        readOnly: false,
        
        inputValue: null
    },
    focusEl: 'inputElement',
    ariaEl: 'inputElement',
    inputTabIndex: 0,
    eventHandlers: {
        input: 'onInput'
    },
    getBodyTemplate: function() {
        return [
            this.getInputTemplate()
        ];
    },
    getInputTemplate: function() {
        return {
            tag: this.tag,
            reference: 'inputElement',
            tabindex: this.inputTabIndex,
            cls: Ext.baseCSSPrefix + 'input-el',
            oninput: 'return Ext.doEv(this, event);'
        };
    },
    initElement: function() {
        this.callParent();
        this.labelElement.dom.setAttribute('for', this.inputElement.id);
    },
    updateDisabled: function(disabled, oldDisabled) {
        this.callParent([
            disabled,
            oldDisabled
        ]);
        this.inputElement.dom.disabled = !!disabled;
    },
    updateInputType: function(newInputType) {
        this.setInputAttribute('type', newInputType);
    },
    updateName: function(name, oldName) {
        this.callParent([
            name,
            oldName
        ]);
        this.setInputAttribute('name', name);
    },
    updateReadOnly: function(readOnly) {
        this.setInputAttribute('readonly', readOnly ? true : null);
    },
    updateValue: function(value, oldValue) {
        
        
        if (this.canSetInputValue()) {
            this.setInputValue(value);
        }
        this.callParent([
            value,
            oldValue
        ]);
    },
    applyInputValue: function(value) {
        return (value != null) ? (value + '') : '';
    },
    completeEdit: function() {
        var me = this,
            value = me.getInputValue(),
            parsedValue = me.parseValue(value);
        if (parsedValue !== null) {
            me.setInputValue(me.getValue());
        }
    },
    updateInputValue: function(value) {
        var inputElement = this.inputElement.dom;
        if (inputElement.value !== value) {
            inputElement.value = value;
        }
    },
    reset: function() {
        var me = this,
            original = me.originalValue;
        if (me.isEqual(original, me.getValue())) {
            me.setInputValue(original);
            if (!me.isValid()) {
                me.validate();
            }
        } else {
            me.setValue(original);
        }
        return me;
    },
    privates: {
        canSetInputValue: function() {
            return true;
        },
        
        setInputAttribute: function(attribute, newValue) {
            var inputElement = this.inputElement.dom;
            if (!Ext.isEmpty(newValue, true)) {
                inputElement.setAttribute(attribute, newValue);
            } else {
                inputElement.removeAttribute(attribute);
            }
        }
    },
    deprecated: {
        '6.5': {
            configs: {
                
                
                
                inputCls: null
            }
        }
    }
});


Ext.define('Ext.field.trigger.Base', {
    extend: Ext.Widget,
    alias: 'trigger.base',
    mixins: [
        Ext.mixin.Factoryable
    ],
    factoryConfig: {
        defaultType: 'trigger',
        aliasPrefix: 'trigger.'
    },
    isTrigger: true,
    config: {
        
        field: null,
        
        group: null,
        
        side: null,
        
        name: null,
        
        triggers: null
    },
    classCls: Ext.baseCSSPrefix + 'trigger',
    groupedCls: Ext.baseCSSPrefix + 'grouped',
    inheritUi: true,
    statics: {
        
        sort: function(triggers) {
            Ext.Array.sort(triggers, Ext.weightSortFn);
            return triggers;
        }
    },
    doDestroy: function() {
        var triggers = this.getTriggers(),
            i, ln;
        if (triggers) {
            for (i = 0 , ln = triggers.length; i < ln; i++) {
                triggers[i].destroy();
            }
        }
        this.setTriggers(null);
        this.callParent();
    },
    updateField: function(field) {
        
        this.field = this.ownerCmp = field;
        this.doInheritUi();
    },
    updateGroup: function(group) {
        if (!this.isConfiguring) {
            this.getField().syncTriggers();
        }
    },
    updateSide: function() {
        if (!this.isConfiguring) {
            this.getField().syncTriggers();
        }
    },
    updateTriggers: function(triggers) {
        var me = this,
            dom = me.element.dom,
            iconElement = me.iconElement,
            i, ln;
        me.toggleCls(me.groupedCls, !!(triggers && triggers.length));
        if (triggers) {
            for (i = 0 , ln = triggers.length; i < ln; i++) {
                dom.appendChild(triggers[i].element.dom);
            }
        }
    }
});


Ext.define('Ext.field.trigger.Trigger', {
    extend: Ext.field.trigger.Base,
    xtype: 'trigger',
    alias: 'trigger.trigger',
    config: {
        
        handler: null,
        
        iconCls: null,
        
        repeat: null,
        
        scope: null,
        
        focusOnTap: true
    },
    interactiveCls: Ext.baseCSSPrefix + 'interactive',
    
    template: [
        {
            reference: 'iconElement',
            classList: [
                Ext.baseCSSPrefix + 'icon-el',
                Ext.baseCSSPrefix + 'font-icon'
            ]
        }
    ],
    constructor: function(config) {
        var me = this,
            repeat;
        me.callParent([
            config
        ]);
        repeat = me.getRepeat();
        if (repeat) {
            me.repeater = new Ext.util.ClickRepeater(Ext.apply({
                target: me,
                preventDefault: true,
                listeners: {
                    mousedown: 'onClickRepeaterTouchStart',
                    mouseup: 'onClickRepeaterTouchEnd',
                    click: 'onClickRepeaterClick',
                    scope: me
                }
            }, repeat));
        } else {
            me.element.on({
                click: 'onClick',
                mousedown: 'onMouseDown',
                scope: me
            });
        }
    },
    doDestroy: function() {
        Ext.destroyMembers(this, 'repeater');
        this.callParent();
    },
    onClickRepeaterClick: function(clickRepeater, e) {
        this.onClick(e);
    },
    onClick: function(e) {
        var me = this,
            handler = !me.getDisabled() && me.getHandler(),
            field = me.getField(),
            focusEl;
        if (field) {
            if (e.pointerType !== 'mouse') {
                
                
                e.preventDefault();
                if (me.getFocusOnTap()) {
                    focusEl = field.getFocusTrap ? field.getFocusTrap() : field.getFocusEl();
                    if (focusEl.dom !== document.activeElement) {
                        focusEl.focus();
                    }
                }
            }
            if (handler) {
                Ext.callback(handler, me.getScope(), [
                    field,
                    me,
                    e
                ], null, field);
            }
        }
    },
    onMouseDown: function(e) {
        if (e.pointerType === 'mouse') {
            var field = this.getFocusOnTap() && this.getField();
            
            if (field) {
                field.focus();
            }
            e.preventDefault();
        }
    },
    onClickRepeaterTouchStart: function(clickRepeater, e) {
        this.onMouseDown(e);
    },
    onClickRepeaterTouchEnd: function(clickRepeater, e) {
        var me = this,
            field = me.field;
        Ext.callback(me.endHandler, me.scope, [
            field,
            me,
            e
        ], 0, field);
    },
    updateHandler: function(handler) {
        this.toggleCls(this.interactiveCls, !!handler);
    },
    updateIconCls: function(iconCls, oldIconCls) {
        this.iconElement.replaceCls(oldIconCls, iconCls);
    }
});


Ext.define('Ext.field.trigger.Clear', {
    extend: Ext.field.trigger.Trigger,
    xtype: 'cleartrigger',
    alias: 'trigger.clear',
    classCls: Ext.baseCSSPrefix + 'cleartrigger',
    weight: -1000,
    hidden: true,
    handler: 'onClearIconTap',
    scope: 'this'
});


Ext.define('Ext.field.Text', {
    extend: Ext.field.Input,
    xtype: 'textfield',
    alternateClassName: 'Ext.form.Text',
    
    
    
    
    
    
    
    config: {
        
        clearable: true,
        
        
        placeholder: null,
        
        maxLength: null,
        
        autoComplete: null,
        
        autoCapitalize: null,
        
        autoCorrect: null,
        
        autoHideInputMask: null,
        
        inputMask: null,
        
        pattern: null,
        
        
        triggers: {
            clear: {
                type: 'clear'
            }
        },
        
        editable: true,
        bubbleEvents: [
            'action'
        ],
        
        bodyAlign: 'stretch',
        
        labelInPlaceholder: {
            lazy: true,
            $value: true
        },
        
        textAlign: null
    },
    cachedConfig: {
        
        animateUnderline: false,
        
        parseValidator: null
    },
    
    badFormatMessage: 'Value does not match the required format',
    
    
    defaultBindProperty: 'value',
    
    twoWayBindable: {
        value: 1
    },
    
    publishes: {
        value: 1
    },
    
    inputType: 'text',
    
    classCls: Ext.baseCSSPrefix + 'textfield',
    focusedCls: Ext.baseCSSPrefix + 'focused',
    emptyCls: Ext.baseCSSPrefix + 'empty',
    webkitBorderBoxBugCls: Ext.baseCSSPrefix + 'webkit-border-box-bug',
    requiredIndicator: '*',
    getBodyTemplate: function() {
        var template = [
                {
                    reference: 'beforeInputElement',
                    cls: Ext.baseCSSPrefix + 'before-input-el'
                }
            ];
        template.push(this.getInputTemplate());
        template.push({
            reference: 'afterInputElement',
            cls: Ext.baseCSSPrefix + 'after-input-el'
        });
        return [
            {
                reference: 'inputWrapElement',
                cls: Ext.baseCSSPrefix + 'input-wrap-el' + (Ext.supports.WebKitInputTableBoxModelBug ? (' ' + this.webkitBorderBoxBugCls) : ''),
                children: template
            },
            {
                reference: 'underlineElement',
                cls: Ext.baseCSSPrefix + 'underline-el'
            }
        ];
    },
    initialize: function() {
        var me = this;
        if (Ext.isRobot) {
            me.focusedInputDelay = 0;
        }
        me.callParent();
        me.inputElement.on({
            keyup: 'onKeyUp',
            keydown: 'onKeyDown',
            keypress: 'onKeyPress',
            paste: 'onPaste',
            mousedown: 'onMouseDown',
            scope: me
        });
        me.syncEmptyState();
    },
    
    clearValue: function() {
        var me = this,
            inputMask = me.getInputMask();
        if (inputMask) {
            
            inputMask.showEmptyMask(me, true);
        } else {
            me.forceInputChange = true;
            me.setValue('');
            me.forceInputChange = false;
        }
        me.syncEmptyState();
    },
    transformValue: function(value) {
        if (value == null) {
            value = '';
        }
        return value;
    },
    applyInputMask: function(value, instance) {
        var InputMask = Ext.field['InputMask'];
        
        
        if (value) {
            if (!InputMask) {
                Ext.raise('Missing Ext.field.InputMask (required to use inputMask)');
            }
        }
        
        return value ? InputMask.from(value, instance) : null;
    },
    updateInputMask: function(inputMask, previous) {
        this.hasMask = false;
        if (previous) {
            previous.release();
        }
        if (inputMask) {
            this.hasMask = true;
            
            inputMask.syncPattern(this);
        }
    },
    doValidate: function(value, errors, skipLazy) {
        this.callParent([
            value,
            errors,
            skipLazy
        ]);
        if (!skipLazy) {
            var inputMask = this.getInputMask();
            
            if (inputMask && !inputMask.isFilled(value) && value !== inputMask._mask) {
                errors.push(this.badFormatMessage);
            }
        }
    },
    
    parseValue: function(value, errors) {
        var me = this,
            parser = me.getParseValidator(),
            field, i, k, len, v, validators;
        if (parser) {
            
            
            
            field = me._validationField;
            for (k = 2; k-- > 0; ) {
                validators = k ? me.getValidators() : (field && field.getValidators());
                len = validators && validators.length;
                for (i = 0; i < len; ++i) {
                    v = validators[i];
                    if (v.parse) {
                        v = v.parse(value);
                        
                        
                        if (v !== null) {
                            return v;
                        }
                    }
                }
            }
            
            
            value = parser.parse(value);
            if (value === null && errors) {
                errors.push(me.badFormatMessage);
            }
        }
        return value;
    },
    applyValue: function(value, oldValue) {
        
        
        
        if (value && typeof value === 'string') {
            value = this.parseValue(value);
            if (value === null) {
                return;
            }
        }
        return this.transformValue(this.callParent([
            value,
            oldValue
        ]));
    },
    updateInputValue: function(value, oldValue) {
        var me = this,
            inputMask = me.getInputMask();
        me.callParent([
            value,
            oldValue
        ]);
        me.syncEmptyState();
        me.syncLabelPlaceholder(false);
        if (inputMask) {
            inputMask.onChange(this, value, oldValue);
        }
    },
    updateTextAlign: function(newAlign, oldAlign) {
        var element = this.element;
        if (oldAlign) {
            element.removeCls(Ext.baseCSSPrefix + 'text-align-' + oldAlign);
        }
        if (newAlign) {
            element.addCls(Ext.baseCSSPrefix + 'text-align-' + newAlign);
        }
    },
    updatePlaceholder: function(value) {
        this.setInputAttribute('placeholder', value);
    },
    
    applyMaxLength: function(maxLength) {
        if (maxLength !== null && typeof maxLength != 'number') {
            throw new Error("Ext.field.Text: [applyMaxLength] trying to pass a value which is not a number");
        }
        return maxLength;
    },
    
    updateMaxLength: function(newMaxLength) {
        this.setInputAttribute('maxlength', newMaxLength);
    },
    applyAutoComplete: function(value) {
        return value === true || value === 'on';
    },
    updateAutoComplete: function(value) {
        this.setInputAttribute('autocomplete', value ? 'on' : 'off');
    },
    applyAutoCapitalize: function(value) {
        return value === true || value === 'on';
    },
    updateAutoCapitalize: function(value) {
        this.setInputAttribute('autocapitalize', value ? 'on' : 'off');
    },
    applyAutoCorrect: function(value) {
        return value === true || value === 'on';
    },
    updateAutoCorrect: function(value) {
        this.setInputAttribute('autocorrect', value ? 'on' : 'off');
    },
    updateReadOnly: function(newReadOnly) {
        var me = this,
            triggers = me.getTriggers(),
            isEditable = me.getEditable(),
            triggerName, trigger;
        me.callParent([
            newReadOnly || !isEditable
        ]);
        for (triggerName in triggers) {
            trigger = triggers[triggerName];
            if (trigger.disableOnReadOnly !== false) {
                trigger.setDisabled(newReadOnly);
            }
        }
        me.syncEmptyState();
    },
    updateEditable: function(newEditable) {
        var triggers = this.getTriggers(),
            isReadOnly = this.getReadOnly(),
            triggerName, trigger;
        this.updateReadOnly(!newEditable);
        for (triggerName in triggers) {
            trigger = triggers[triggerName];
            if (trigger.disableOnReadOnly !== false) {
                triggers[triggerName].setDisabled(isReadOnly);
            }
        }
    },
    updatePattern: function(pattern) {
        this.setInputAttribute('pattern', pattern);
    },
    updateDisabled: function(disabled, oldDisabled) {
        this.callParent([
            disabled,
            oldDisabled
        ]);
        this.syncEmptyState();
    },
    updateClearable: function(clearable, oldClearable) {
        var me = this,
            triggers, clearTrigger;
        if (!me.isConfiguring) {
            triggers = me.getTriggers();
            clearTrigger = triggers && triggers.clear;
            if (clearable) {
                if (!clearTrigger) {
                    me.addTrigger('clear', 'clear');
                }
            } else if (clearTrigger) {
                me.removeTrigger('clear');
            }
        }
    },
    applyTriggers: function(triggers, oldTriggers) {
        var me = this,
            instances = oldTriggers || {},
            clearable = me.getClearable(),
            name, trigger, oldTrigger;
        for (name in triggers) {
            trigger = triggers[name];
            oldTrigger = instances[name];
            
            
            
            
            if (oldTrigger) {
                oldTrigger.destroy();
            }
            if (trigger) {
                if (!clearable && (name === 'clear')) {
                    
                    continue;
                }
                instances[name] = me.createTrigger(name, trigger);
            }
        }
        return instances;
    },
    updateTriggers: function() {
        this.syncTriggers();
    },
    
    addTrigger: function(name, trigger) {
        var me = this,
            triggers = me.getTriggers(),
            triggerConfig;
        
        if (triggers && triggers[name]) {
            Ext.raise('Trigger with name "' + name + '" already exists.');
        }
        if (typeof name !== 'string') {
            Ext.raise('Cannot add trigger. Key must be a string.');
        }
        if (typeof trigger !== 'string' && !Ext.isObject(trigger)) {
            Ext.raise('Cannot add trigger "' + name + '". A trigger config or instance is required.');
        }
        
        trigger = me.createTrigger(name, trigger);
        if (triggers) {
            triggers[name] = trigger;
            me.syncTriggers();
        } else {
            triggerConfig = {};
            triggerConfig[name] = trigger;
            me.setTriggers(triggerConfig);
        }
        return trigger;
    },
    
    removeTrigger: function(trigger, destroy) {
        var me = this,
            triggers = me.getTriggers(),
            name = trigger,
            triggerEl;
        if (name.isTrigger) {
            name = trigger.getName();
        } else {
            trigger = triggers[name];
        }
        
        if (!name) {
            Ext.raise('Trigger not found.');
        } else if (!triggers[name]) {
            Ext.raise('Cannot remove trigger. Trigger with name "' + name + '" not found.');
        }
        
        delete triggers[name];
        if (destroy !== false) {
            trigger.destroy();
        } else {
            triggerEl = trigger.el.dom;
            triggerEl.parentNode.removeChild(triggerEl);
        }
        this.syncTriggers();
        return trigger;
    },
    onKeyDown: function(event) {
        var me = this,
            inputMask = me.getInputMask();
        me.lastKeyTime = Date.now();
        if (inputMask) {
            inputMask.onKeyDown(me, me.getValue(), event);
        }
        
        
        me.ignoreInput = true;
        if (Ext.supports.SpecialKeyDownRepeat) {
            me.fireKey(event);
        }
    },
    onInput: function(e) {
        var me = this,
            inputEl = me.inputElement.dom,
            value = inputEl.value,
            inputMask = me.getInputMask(),
            parseErrors, oldValue;
        if (inputMask) {
            inputMask.processAutocomplete(this, value);
            value = inputEl.value;
        }
        
        me._inputValue = value;
        
        
        
        if (value) {
            parseErrors = [];
            value = me.parseValue(value, parseErrors);
        }
        if (parseErrors && parseErrors.length) {
            me.setError(parseErrors);
        } else {
            oldValue = me.getValue();
            me.setValue(value);
            
            
            if (me.getValue() === oldValue) {
                me.validate();
            }
        }
        me.syncEmptyState();
        
        if (me.ignoreInput) {
            me.ignoreInput = false;
            return;
        }
        
        
        Ext.defer(function() {
            if (!me.ignoreInput && !me.destroyed) {
                me.fireEvent('keyup', e);
                me.ignoreInput = false;
            }
        }, 10);
    },
    
    fireKey: function(e) {
        if (e.isSpecialKey()) {
            this.fireEvent('specialkey', this, e);
        }
    },
    onKeyPress: function(event) {
        var me = this,
            inputMask = me.getInputMask();
        if (inputMask) {
            inputMask.onKeyPress(me, me.getValue(), event);
        }
        me.fireEvent('keypress', me, event);
        if (!Ext.supports.SpecialKeyDownRepeat) {
            me.fireKey(event);
        }
    },
    onKeyUp: function(e) {
        this.fireAction('keyup', [
            this,
            e
        ], 'doKeyUp');
    },
    
    doKeyUp: function(me, e) {
        me.syncEmptyState();
        if (e.browserEvent.keyCode === 13) {
            me.fireAction('action', [
                me,
                e
            ], 'doAction');
        }
    },
    doAction: function() {
        
        
        if (document.documentElement.getBoundingClientRect().top < 0) {
            this.blur();
        }
    },
    onClearIconTap: function(input, e) {
        this.fireAction('clearicontap', [
            this,
            input,
            e
        ], 'doClearIconTap');
    },
    
    doClearIconTap: function() {
        this.clearValue();
    },
    onFocusEnter: function(event) {
        var me = this,
            inputMask = me.getInputMask();
        me.callParent([
            event
        ]);
        me.addCls(me.focusedCls);
        me.syncLabelPlaceholder(true);
        if (inputMask) {
            inputMask.onFocus(me, me.getValue());
        }
    },
    onFocusLeave: function(event) {
        var me = this,
            inputMask = me.getInputMask();
        me.callParent([
            event
        ]);
        me.removeCls(me.focusedCls);
        me.syncLabelPlaceholder(true);
        if (inputMask) {
            inputMask.onBlur(me, me.getValue());
        }
    },
    onPaste: function(e) {
        this.forceInputChange = true;
        this.handlePaste(e);
        this.forceInputChange = false;
    },
    getCaretPos: function() {
        return this.inputElement.getCaretPos();
    },
    setCaretPos: function(pos) {
        this.inputElement.setCaretPos(pos);
    },
    
    getTextSelection: function() {
        return this.inputElement.getTextSelection();
    },
    
    select: function(start, end, direction) {
        
        
        
        if (this.containsFocus) {
            this.inputElement.selectText(start, end, direction);
        }
        return this;
    },
    reset: function() {
        this.callParent();
        this.syncEmptyState();
    },
    onClick: function(e) {
        this.fireEvent('click', e);
    },
    onMouseDown: function(e) {
        this.fireEvent('mousedown', e);
    },
    trimValueToMaxLength: function() {
        var me = this,
            maxLength = me.getMaxLength(),
            value = me.getValue();
        if (maxLength && value.length > maxLength) {
            me.setValue(value.slice(0, maxLength));
        }
    },
    doDestroy: function() {
        var me = this,
            triggers = me.getTriggers(),
            triggerGroups = me.triggerGroups,
            name, animation;
        animation = me.labelElement && me.labelElement.getActiveAnimation();
        if (animation) {
            animation.stop();
        }
        if (triggerGroups) {
            for (name in triggerGroups) {
                triggerGroups[name].destroy();
            }
            me.triggerGroups = null;
        }
        for (name in triggers) {
            triggers[name].destroy();
        }
        me.setTriggers(null);
        me.setInputMask(null);
        me.callParent();
    },
    onRender: function() {
        this.callParent();
        this.syncLabelPlaceholder();
    },
    getRefItems: function(deep) {
        var me = this,
            triggers = me.getTriggers(),
            items = [],
            triggerName, trigger;
        for (triggerName in triggers) {
            trigger = triggers[triggerName];
            items.push(trigger);
            
            if (deep && trigger.getRefItems) {
                items.push.apply(items, trigger.getRefItems(deep));
            }
        }
        return items;
    },
    privates: {
        focusedInputDelay: 300,
        forceInputChange: false,
        hasMask: false,
        lastKeyTime: 0,
        applyParseValidator: function(config) {
            return this.decodeValidator(config);
        },
        updateLabelInPlaceholder: function(inside) {
            var me = this,
                placeHolder = me.getPlaceholder() || '',
                anim = me._animPlaceholderLabel;
            if (me.getLabelAlign() !== 'placeholder' || !me.getLabel()) {
                me.clearWhenVisible('doPositionPlaceholder');
                me.setInputAttribute('placeholder', placeHolder);
                return;
            }
            me.whenVisible('doPositionPlaceholder', [
                inside,
                anim
            ]);
            me.el.toggleCls(Ext.baseCSSPrefix + 'label-inside', inside);
        },
        updateAnimateUnderline: function(value) {
            this.toggleCls(Ext.baseCSSPrefix + 'animate-underline', value);
        },
        canSetInputValue: function() {
            var me = this;
            
            
            
            
            
            
            
            
            return me.hasMask || me.forceInputChange || !me.hasFocus || Date.now() - me.lastKeyTime > me.focusedInputDelay;
        },
        doPositionPlaceholder: function(inside, doAnimate) {
            var me = this,
                labelElement = me.labelElement,
                anim, animation, info, insideInfo, outsideInfo;
            animation = labelElement.getActiveAnimation();
            if (animation) {
                animation.stop();
            }
            info = me.lastPlaceholderAnimInfo;
            if (!info) {
                me.lastPlaceholderAnimInfo = info = me.getPlaceholderAnimInfo();
            }
            insideInfo = info.inside;
            outsideInfo = info.outside;
            anim = {
                from: inside ? outsideInfo : insideInfo,
                to: inside ? insideInfo : outsideInfo,
                preserveEndState: true,
                duration: 250,
                easing: 'ease-out'
            };
            if (doAnimate) {
                labelElement.animate(anim);
            } else {
                labelElement.setStyle(anim.to);
            }
        },
        getPlaceholderLabel: function() {
            var me = this,
                label = me.getLabel();
            if (label && me.getRequired()) {
                label += ' ' + me.requiredIndicator;
            }
            return label;
        },
        getPlaceholderAnimInfo: function() {
            var me = this,
                element = me.element,
                labelElement = me.labelElement,
                inputElement = me.inputElement,
                labelOffsets = labelElement.getOffsetsTo(element),
                inputOffsets = inputElement.getOffsetsTo(element),
                labelLeftPadding = labelElement.getPadding('l'),
                inputLeftPadding = inputElement.getPadding('l'),
                labelTopPadding = labelElement.getPadding('t'),
                inputTopPadding = inputElement.getPadding('t'),
                translateX = inputOffsets[0] - labelOffsets[0] + (inputLeftPadding - labelLeftPadding),
                translateY = inputOffsets[1] - labelOffsets[1] + (inputTopPadding - labelTopPadding);
            return {
                inside: {
                    transform: {
                        translateX: translateX + 'px',
                        translateY: translateY + 'px'
                    },
                    'font-size': inputElement.getStyle('font-size')
                },
                outside: {
                    transform: {
                        translateX: '0px',
                        translateY: '0px'
                    },
                    'font-size': labelElement.getStyle('font-size')
                }
            };
        },
        handlePaste: function(e) {
            var me = this,
                inputMask = me.getInputMask();
            if (inputMask) {
                inputMask.onPaste(me, me.getValue(), e);
            }
            me.fireEvent('paste', me, e);
        },
        
        createTrigger: function(name, trigger) {
            if (!trigger.isTrigger) {
                if (trigger === true) {
                    trigger = {
                        type: name
                    };
                } else if (typeof trigger === 'string') {
                    trigger = {
                        type: trigger
                    };
                }
                trigger = Ext.apply({
                    name: name,
                    field: this
                }, trigger);
                trigger = trigger.xtype ? Ext.create(trigger) : Ext.Factory.trigger(trigger);
            }
            return trigger;
        },
        syncLabelPlaceholder: function(animate) {
            var me = this,
                inputEl = me.inputElement,
                value = inputEl && inputEl.dom.value,
                inside;
            me._animPlaceholderLabel = animate;
            if (me.rendered) {
                if (value !== '') {
                    
                    
                    inside = false;
                } else {
                    
                    
                    
                    
                    
                    
                    inside = !me.hasFocus || me.getDisabled() || me.getReadOnly();
                }
                me.setLabelInPlaceholder(inside);
            }
            me._animPlaceholderLabel = false;
        },
        
        syncTriggers: function() {
            var me = this,
                triggers = me.getTriggers(),
                beforeInputElement = me.beforeInputElement,
                afterInputElement = me.afterInputElement,
                triggerGroups = me.triggerGroups || (me.triggerGroups = {}),
                beforeTriggers = [],
                afterTriggers = [],
                triggersByGroup = {},
                TriggerBase = Ext.field.trigger['Base'],
                name, trigger, groupName, triggerGroup, i, ln;
            for (name in triggers) {
                trigger = triggers[name];
                groupName = trigger.getGroup();
                if (groupName) {
                    (triggersByGroup[groupName] || (triggersByGroup[groupName] = [])).push(trigger);
                } else if (trigger.getSide() === 'left') {
                    beforeTriggers.push(trigger);
                } else {
                    afterTriggers.push(trigger);
                }
            }
            for (groupName in triggersByGroup) {
                triggerGroup = triggerGroups[groupName];
                if (!triggerGroup) {
                    triggerGroup = triggers[groupName];
                    
                    if (!triggerGroup) {
                        triggerGroup = new TriggerBase();
                    }
                    triggerGroups[groupName] = triggerGroup;
                }
                triggerGroup.setTriggers(TriggerBase.sort(triggersByGroup[groupName]));
                if (triggerGroup.getSide() === 'left') {
                    beforeTriggers.push(triggerGroup);
                } else {
                    afterTriggers.push(triggerGroup);
                }
            }
            TriggerBase.sort(beforeTriggers);
            TriggerBase.sort(afterTriggers);
            for (i = 0 , ln = beforeTriggers.length; i < ln; i++) {
                beforeInputElement.appendChild(beforeTriggers[i].element);
            }
            for (i = 0 , ln = afterTriggers.length; i < ln; i++) {
                afterInputElement.appendChild(afterTriggers[i].element);
            }
            for (groupName in triggerGroups) {
                if (!(groupName in triggersByGroup)) {
                    
                    triggerGroup = triggerGroups[groupName];
                    triggerGroup.setTriggers(null);
                    triggerGroup.destroy();
                    delete triggerGroups[groupName];
                }
            }
        },
        syncEmptyState: function() {
            var me = this,
                triggers = me.getTriggers(),
                inputMask = me.getInputMask(),
                clearTrigger = triggers && triggers.clear,
                value = me.inputElement.dom.value,
                visible;
            me.toggleCls(me.emptyCls, !value);
            if (clearTrigger) {
                if (me.getClearable()) {
                    if (value !== '' && !me.getDisabled() && !me.getReadOnly()) {
                        visible = true;
                    }
                    if (inputMask) {
                        
                        if (value === inputMask._mask) {
                            visible = false;
                        }
                    }
                }
                if (visible) {
                    clearTrigger.show();
                } else {
                    clearTrigger.hide();
                }
            }
        }
    },
    deprecated: {
        '6.5': {
            configs: {
                
                placeHolder: 'placeholder',
                clearIcon: 'clearable'
            },
            methods: {
                getPlaceHolder: 'getPlaceholder',
                setPlaceHolder: 'setPlaceholder'
            }
        }
    }
}, function() {
    
    if (Ext.os.is.Android) {
        window.addEventListener('resize', function() {
            var el = document.activeElement,
                tag = el && el.tagName;
            if (tag === 'INPUT' || tag === 'TEXTAREA') {
                el.scrollIntoView();
            }
        });
    }
});


Ext.define('Ext.Editor', {
    extend: Ext.Container,
    xtype: 'editor',
    isEditor: true,
    config: {
        
        field: {
            xtype: 'textfield'
        }
    },
    floated: true,
    
    allowBlur: true,
    
    revertInvalid: true,
    
    
    hideEl: true,
    
    value: '',
    
    alignment: 'c-c?',
    
    offset: [
        0,
        0
    ],
    
    shadow: 'frame',
    
    constrain: false,
    
    swallowKeys: true,
    
    completeOnEnter: true,
    
    cancelOnEsc: true,
    
    cancelOnClear: false,
    
    updateEl: false,
    
    
    
    focusOnToFront: false,
    
    baseCls: Ext.baseCSSPrefix + 'editor',
    
    editing: false,
    
    
    
    
    
    
    preventDefaultAlign: true,
    useBoundValue: true,
    specialKeyDelay: 1,
    
    matchFont: false,
    applyField: function(config) {
        return Ext.widget(config);
    },
    updateField: function(newField, oldField) {
        var me = this,
            inputEl;
        if (oldField) {
            me.remove(oldField, true);
            oldField.un({
                specialkey: 'onSpecialKey',
                clearicontap: 'onFieldClear',
                scope: this
            });
            me._fieldSwallower = Ext.destroy(me._fieldSwallower);
        }
        if (newField) {
            inputEl = newField.inputElement;
            me.add(newField);
            newField.on({
                specialkey: 'onSpecialKey',
                clearicontap: 'onFieldClear',
                scope: this
            });
            if (me.swallowKeys) {
                me._fieldSwallower = inputEl.swallowEvent([
                    'keypress',
                    
                    'keydown'
                ]);
            }
        }
    },
    
    onAdded: function(container) {
        
        
        this.ownerCmp = container;
        this.callParent(arguments);
    },
    
    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 (!event.fromBoundList && (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, doFocus) {
        var me = this,
            field = me.getField(),
            dom, font;
        if (!this.allowBlur && this.editing) {
            me.toggleBoundEl(true);
        }
        me.completeEdit(true);
        me.boundEl = el = Ext.get(el);
        dom = me.boundEl.dom;
        if (me.useBoundValue && !Ext.isDefined(value)) {
            value = Ext.String.trim(dom.textContent || dom.innerText || dom.innerHTML);
        }
        if (me.fireEvent('beforestartedit', me, el, value) !== false) {
            if (me.context) {
                
                value = me.context.value;
            }
            if (this.matchFont) {
                font = el.getStyle('font');
                if (!font) {
                    font = el.getStyle('fontWeight') + ' ' + el.getStyle('fontSize') + '/' + el.getStyle('lineHeight') + ' ' + el.getStyle('fontFamily');
                }
                field.inputElement.setStyle('font', font);
            }
            me.startValue = value;
            me.show();
            
            if (!me.getFloated()) {
                me.setTop(0);
            }
            me.realign();
            
            field.suspendEvents();
            field.setValue(value);
            field.resetOriginalValue();
            field.resumeEvents();
            if (doFocus !== false) {
                field.focus(field.selectOnFocus ? true : [
                    Number.MAX_VALUE
                ]);
            }
            me.toggleBoundEl(false);
            me.editing = true;
        }
    },
    
    realign: function() {
        var me = this;
        
        
        me.setConstrainAlign(Ext.getBody().getConstrainRegion());
        me.alignTo(me.boundEl, me.alignment, {
            offset: me.offset
        });
    },
    
    completeEdit: function(remainVisible) {
        var me = this,
            field = me.getField(),
            startValue = me.startValue,
            cancel = me.context && me.context.cancel,
            value;
        if (!me.editing) {
            return;
        }
        value = me.getValue();
        
        
        if (!field.isValid() || !field.validate()) {
            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, cancel);
            me.fireEvent('complete', me, value, startValue);
        }
    },
    afterShow: function() {
        var me = this;
        me.callParent(arguments);
        me.fireEvent('startedit', me, me.boundEl, me.startValue);
    },
    onFieldClear: function() {
        if (this.cancelOnClear) {
            this.cancelEdit();
        }
    },
    
    cancelEdit: function(remainVisible) {
        var me = this,
            startValue = me.startValue,
            field = me.getField(),
            value;
        if (me.editing) {
            if (field) {
                value = me.editedValue = me.getValue();
                
                field.suspendEvents();
                me.setValue(startValue);
                field.resumeEvents();
            }
            me.onEditComplete(remainVisible, true);
            me.fireEvent('canceledit', me, value, startValue);
            delete me.editedValue;
        }
    },
    
    onEditComplete: function(remainVisible, cancelling) {
        var me = this,
            field = me.getField();
        me.editing = false;
        if (remainVisible !== true) {
            me.hide();
            me.toggleBoundEl(true);
        }
        field.inputElement.setStyle('font', null);
    },
    onFocusLeave: function(e) {
        var me = this;
        if (me.allowBlur === true && me.editing) {
            me.completeEdit();
        }
        me.callParent([
            e
        ]);
    },
    updateHidden: function(hidden, oldHidden) {
        var me = this,
            field;
        if (hidden && !me.destroying) {
            field = me.getField();
            if (me.editing) {
                me.completeEdit();
            } else if (field.collapse) {
                field.collapse();
            }
        }
        me.callParent([
            hidden,
            oldHidden
        ]);
    },
    
    getValue: function() {
        var field = this.getField();
        return field.getValue();
    },
    
    setValue: function(value) {
        var field = this.getField();
        field.setValue(value);
    },
    toggleBoundEl: function(visible) {
        if (this.hideEl) {
            this.boundEl.setVisibilityMode(Ext.Element.VISIBILITY);
            this.boundEl.setVisible(visible);
        }
    },
    doDestroy: function() {
        var me = this,
            task = me.specialKeyTask;
        if (task) {
            task.cancel();
        }
        me.callParent();
    }
});




Ext.define('Ext.Img', {
    extend: Ext.Component,
    xtype: [
        'image',
        'img'
    ],
    alternateClassName: 'Ext.Image',
    
    
    
    config: {
        
        src: null,
        
        imageCls: Ext.baseCSSPrefix + 'img-image',
        
        backgroundCls: Ext.baseCSSPrefix + 'img-background',
        
        mode: 'background'
    },
    baseCls: Ext.baseCSSPrefix + 'img',
    beforeInitialize: function() {
        var me = this;
        me.onLoad = me.onLoad.bind(me);
        me.onError = me.onError.bind(me);
    },
    initialize: function() {
        var me = this;
        me.callParent();
        me.relayEvents(me.renderElement, '*');
        me.element.on({
            tap: 'onTap',
            scope: me
        });
    },
    hide: function() {
        var me = this;
        me.callParent(arguments);
        me.hiddenSrc = me.hiddenSrc || me.getSrc();
        if (!me.isDestroying) {
            me.setSrc(null);
        }
    },
    afterShow: function() {
        this.callParent(arguments);
        if (this.hiddenSrc) {
            this.setSrc(this.hiddenSrc);
            delete this.hiddenSrc;
        }
    },
    updateMode: function(mode) {
        var me = this,
            imageCls = me.getImageCls(),
            backgroundCls = me.getBackgroundCls();
        if (mode === 'background') {
            if (me.imageElement) {
                me.imageElement.destroy();
                delete me.imageElement;
                me.updateSrc(me.getSrc());
            }
            me.replaceCls(imageCls, backgroundCls);
        } else {
            me.imageElement = me.element.createChild({
                tag: 'img'
            });
            me.replaceCls(backgroundCls, imageCls);
        }
    },
    updateImageCls: function(newCls, oldCls) {
        this.replaceCls(oldCls, newCls);
    },
    updateBackgroundCls: function(newCls, oldCls) {
        this.replaceCls(oldCls, newCls);
    },
    onTap: function(e) {
        this.fireEvent('tap', this, e);
    },
    applySrc: function(src) {
        return src && Ext.resolveResource(src);
    },
    
    updateSrc: function(newSrc) {
        var me = this,
            dom;
        if (me.getMode() === 'background') {
            dom = this.imageObject || new Image();
        } else {
            dom = me.imageElement.dom;
        }
        this.imageObject = dom;
        dom.setAttribute('src', Ext.isString(newSrc) ? newSrc : '');
        dom.addEventListener('load', me.onLoad, false);
        dom.addEventListener('error', me.onError, false);
    },
    detachListeners: function() {
        var dom = this.imageObject;
        if (dom) {
            dom.removeEventListener('load', this.onLoad, false);
            dom.removeEventListener('error', this.onError, false);
        }
    },
    onLoad: function(e) {
        this.detachListeners();
        if (this.getMode() === 'background') {
            this.element.dom.style.backgroundImage = 'url("' + this.imageObject.src + '")';
        }
        this.fireEvent('load', this, e);
    },
    onError: function(e) {
        this.detachListeners();
        
        if (this.getMode() === 'background') {
            this.element.dom.style.backgroundImage = 'url("' + this.imageObject.src + '")';
        }
        this.fireEvent('error', this, e);
    },
    updateWidth: function(width) {
        var sizingElement = (this.getMode() === 'background') ? this.element : this.imageElement;
        sizingElement.setWidth(width);
        this.callParent(arguments);
    },
    updateHeight: function(height) {
        var sizingElement = (this.getMode() === 'background') ? this.element : this.imageElement;
        sizingElement.setHeight(height);
        this.callParent(arguments);
    },
    doDestroy: function() {
        var me = this;
        me.detachListeners();
        me.imageObject = me.imageElement = Ext.destroy(me.imageObject, me.imageElement);
        me.callParent();
    }
});


Ext.define('Ext.Label', {
    extend: Ext.Component,
    xtype: 'label',
    
    baseCls: Ext.baseCSSPrefix + 'label'
});


Ext.define('Ext.LoadMask', {
    extend: Ext.Mask,
    xtype: 'loadmask',
    config: {
        
        message: 'Loading...',
        
        cls: Ext.baseCSSPrefix + 'loading-mask',
        
        messageCls: Ext.baseCSSPrefix + 'mask-message',
        
        indicator: true
    },
    getTemplate: function() {
        var prefix = Ext.baseCSSPrefix;
        return [
            {
                
                reference: 'innerElement',
                cls: prefix + 'mask-inner',
                children: [
                    
                    {
                        reference: 'indicatorElement',
                        cls: prefix + 'loading-spinner-outer',
                        children: [
                            {
                                cls: prefix + 'loading-spinner',
                                children: [
                                    {
                                        tag: 'span',
                                        cls: prefix + 'loading-top'
                                    },
                                    {
                                        tag: 'span',
                                        cls: prefix + 'loading-right'
                                    },
                                    {
                                        tag: 'span',
                                        cls: prefix + 'loading-bottom'
                                    },
                                    {
                                        tag: 'span',
                                        cls: prefix + 'loading-left'
                                    }
                                ]
                            }
                        ]
                    },
                    
                    {
                        reference: 'messageElement'
                    }
                ]
            }
        ];
    },
    
    updateMessage: function(newMessage) {
        var cls = Ext.baseCSSPrefix + 'has-message';
        if (newMessage) {
            this.addCls(cls);
        } else {
            this.removeCls(cls);
        }
        this.messageElement.setHtml(newMessage);
    },
    
    updateMessageCls: function(newMessageCls, oldMessageCls) {
        this.messageElement.replaceCls(oldMessageCls, newMessageCls);
    },
    
    updateIndicator: function(newIndicator) {
        this[newIndicator ? 'removeCls' : 'addCls'](Ext.baseCSSPrefix + 'indicator-hidden');
    }
});


Ext.define('Ext.field.TextArea', {
    extend: Ext.field.Text,
    xtype: 'textareafield',
    alternateClassName: 'Ext.form.TextArea',
    config: {
        
        autoCapitalize: false,
        
        maxRows: null,
        
        clearable: false
    },
    
    tag: 'textarea',
    
    classCls: Ext.baseCSSPrefix + 'textareafield',
    
    applyMaxRows: function(maxRows) {
        if (maxRows !== null && typeof maxRows !== 'number') {
            throw new Error("Ext.field.TextArea: [applyMaxRows] trying to pass a value which is not a number");
        }
        return maxRows;
    },
    
    updateMaxRows: function(newRows) {
        this.setInputAttribute('rows', newRows);
    },
    doKeyUp: function(me) {
        
        this.syncEmptyState();
    }
});


Ext.define('Ext.layout.VBox', {
    extend: Ext.layout.Box,
    alias: 'layout.vbox',
    config: {
        vertical: true
    }
});


Ext.define('Ext.MessageBox', {
    extend: Ext.Dialog,
    xtype: 'messagebox',
    config: {
        
        iconCls: null,
        
        defaultTextHeight: 75,
        
        
        buttons: null,
        
        message: null,
        
        
        
        prompt: null,
        
        layout: {
            type: 'vbox',
            pack: 'center'
        },
        multiLine: null
    },
    
    classCls: Ext.baseCSSPrefix + 'messagebox',
    
    closeAction: 'hide',
    headerCls: [
        Ext.baseCSSPrefix + 'dialogheader',
        Ext.baseCSSPrefix + 'messageboxheader'
    ],
    titleCls: [
        Ext.baseCSSPrefix + 'dialogtitle',
        Ext.baseCSSPrefix + 'messageboxtitle'
    ],
    toolCls: [
        Ext.baseCSSPrefix + 'paneltool',
        Ext.baseCSSPrefix + 'dialogtool',
        Ext.baseCSSPrefix + 'messageboxtool'
    ],
    statics: {
        INFO: Ext.baseCSSPrefix + 'msgbox-info',
        WARNING: Ext.baseCSSPrefix + 'msgbox-warning',
        QUESTION: Ext.baseCSSPrefix + 'msgbox-question',
        ERROR: Ext.baseCSSPrefix + 'msgbox-error',
        OK: {
            ok: 'me.onClick'
        },
        YES: {
            yes: 'me.onClick'
        },
        NO: {
            no: 'me.onClick'
        },
        CANCEL: {
            cancel: 'me.onClick'
        },
        OKCANCEL: {
            ok: 'me.onClick',
            cancel: 'me.onClick'
        },
        YESNOCANCEL: {
            yes: 'me.onClick',
            no: 'me.onClick',
            cancel: 'me.onClick'
        },
        YESNO: {
            yes: 'me.onClick',
            no: 'me.onClick'
        }
    },
    
    constructor: function(config) {
        config = config || {};
        if (config.hasOwnProperty('multiline') || config.hasOwnProperty('multiLine')) {
            config.prompt = config.prompt || {};
            Ext.applyIf(config.prompt, {
                multiLine: config.multiline || config.multiLine
            });
            delete config.multiline;
            delete config.multiLine;
        }
        this.defaultAllowedConfig = {};
        var allowedConfigs = [
                'ui',
                'showAnimation',
                'hideAnimation',
                'title',
                'message',
                'prompt',
                'iconCls',
                'buttons',
                'defaultTextHeight'
            ],
            ln = allowedConfigs.length,
            i, allowedConfig;
        for (i = 0; i < ln; i++) {
            allowedConfig = allowedConfigs[i];
            this.defaultAllowedConfig[allowedConfig] = this.defaultConfig[allowedConfig];
        }
        this.callParent([
            config
        ]);
    },
    
    applyTitle: function(config) {
        if (typeof config === "string") {
            return config;
        }
        return config.title;
    },
    
    updateTitle: function(newTitle) {
        var header = this.getHeader() || {};
        if (Ext.isSimpleObject(header)) {
            header.title = newTitle;
            this.setHeader(header);
        } else if (Ext.isFunction(header.setTitle)) {
            header.setTitle(newTitle);
        }
    },
    
    applyMessage: function(config) {
        config = {
            html: config,
            cls: this.baseCls + '-text'
        };
        return Ext.factory(config, Ext.Component, this._message);
    },
    
    updateMessage: function(newMessage) {
        if (newMessage) {
            this.add(newMessage);
        }
    },
    getMessage: function() {
        if (this._message) {
            return this._message.getHtml();
        }
        return null;
    },
    
    applyIconCls: function(config) {
        if (config) {
            config = {
                xtype: 'component',
                docked: 'left',
                width: 40,
                height: 40,
                hidden: (config) ? false : true,
                cls: Ext.baseCSSPrefix + 'icon ' + config
            };
            return Ext.factory(config, Ext.Component, this._iconCls);
        }
        return config;
    },
    
    updateIconCls: function(newIconCls, oldIconCls) {
        
        this.getTitle();
        this.getButtons();
        if (newIconCls) {
            this.add(newIconCls);
        } else {
            this.remove(oldIconCls);
        }
    },
    getIconCls: function() {
        var icon = this._iconCls,
            iconCls;
        if (icon) {
            iconCls = icon.getCls();
            return (iconCls) ? iconCls[0] : null;
        }
        return null;
    },
    
    applyPrompt: function(prompt) {
        if (prompt) {
            var config = {
                    label: false
                };
            if (Ext.isObject(prompt)) {
                Ext.apply(config, prompt);
            }
            if (config.multiLine) {
                config.height = Ext.isNumber(config.multiLine) ? parseFloat(config.multiLine) : this.getDefaultTextHeight();
                return Ext.factory(config, Ext.field['TextArea'], this.getPrompt());
            } else {
                return Ext.factory(config, Ext.field['Text'], this.getPrompt());
            }
        }
        return prompt;
    },
    
    updatePrompt: function(newPrompt, oldPrompt) {
        if (newPrompt) {
            this.add(newPrompt);
        }
        if (oldPrompt && !oldPrompt.destroyed) {
            this.remove(oldPrompt);
        }
    },
    
    onClick: function(button) {
        var me = this,
            msgBoxOptions = me.msgBoxOptions,
            prompt = me.getPrompt(),
            fn = msgBoxOptions.fn,
            which;
        if (button) {
            if (typeof fn == 'function') {
                button.disable();
                prompt = prompt ? prompt.getValue() : null;
                which = button.getItemId() || button.getText();
                me.on({
                    single: true,
                    hiddenchange: function() {
                        fn.call(msgBoxOptions.scope || me, which, prompt, msgBoxOptions);
                        button.enable();
                    }
                });
            }
        }
        me.hide();
    },
    
    show: function(msgBoxOptions, options) {
        var me = this,
            buttons, config, prompt;
        Ext.util.InputBlocker.blockInputs();
        if (!msgBoxOptions) {
            return me.callParent(arguments);
        }
        config = Ext.apply({
            buttons: Ext.MessageBox.OK,
            draggable: false,
            prompt: null,
            defaultFocus: null
        }, msgBoxOptions);
        if (config.multiLine) {
            config.prompt = config.prompt || {};
            config.prompt.multiLine = config.multiLine;
            delete config.multiLine;
        }
        delete config.value;
        delete config.fn;
        delete config.scope;
        config = Ext.merge({}, me.defaultAllowedConfig, config);
        me.setConfig(config);
        me.msgBoxOptions = msgBoxOptions;
        buttons = me.getButtons();
        buttons.items.each(function(btn) {
            if (btn.isButton) {
                var value = btn.getScope();
                if (btn.fn && value) {
                    btn.fn = btn.fn.bind(value);
                }
                value = btn.getHandler();
                if (!value || value === 'me.onClick') {
                    btn.setHandler('onClick');
                    btn.setScope(me);
                }
            }
        });
        prompt = me.getPrompt();
        if (prompt) {
            prompt.setValue(msgBoxOptions.value || '');
        }
        me.callParent([
            null,
            options
        ]);
        return me;
    },
    
    alert: function(title, message, fn, scope) {
        return this.show({
            title: title || null,
            message: message || null,
            buttons: Ext.MessageBox.OK,
            defaultFocus: '#ok',
            prompt: false,
            fn: function() {
                if (fn) {
                    Ext.callback(fn, scope, arguments);
                }
            },
            scope: scope
        });
    },
    
    confirm: function(title, message, fn, scope) {
        return this.show({
            title: title || null,
            message: message || null,
            buttons: Ext.MessageBox.YESNO,
            defaultFocus: '#yes',
            prompt: false,
            scope: scope,
            fn: function() {
                if (fn) {
                    Ext.callback(fn, scope, arguments);
                }
            }
        });
    },
    
    prompt: function(title, message, fn, scope, multiLine, value, prompt) {
        return this.show({
            title: title || null,
            message: message || null,
            buttons: Ext.MessageBox.OKCANCEL,
            scope: scope,
            prompt: prompt || true,
            defaultFocus: 'textfield',
            multiLine: multiLine,
            value: value,
            fn: function() {
                if (fn) {
                    Ext.callback(fn, scope, arguments);
                }
            }
        });
    }
}, function(MessageBox) {
    Ext.onInternalReady(function() {
        
        
        Ext.Msg = new Ext.MessageBox({
            id: 'ext-messagebox'
        });
    });
});


Ext.define('Ext.SegmentedButton', {
    extend: Ext.Container,
    xtype: 'segmentedbutton',
    alternateClassName: 'Ext.button.Segmented',
    isSegmentedButton: true,
    
    classCls: Ext.baseCSSPrefix + 'segmentedbutton',
    config: {
        
        allowMultiple: false,
        
        allowDepress: false,
        
        allowToggle: true,
        
        forceSelection: false,
        
        pressedButtons: null,
        
        defaultType: 'button',
        
        defaultUI: 'segmented',
        
        value: undefined,
        
        vertical: false
    },
    
    defaults: {
        flex: '1 1 auto'
    },
    
    autoSize: null,
    
    layout: {
        type: 'box',
        vertical: false,
        align: 'stretch'
    },
    
    defaultBindProperty: 'value',
    
    twoWayBindable: 'value',
    
    publishes: 'value',
    
    
    constructor: function(config) {
        this.valueMap = {};
        this.callParent([
            config
        ]);
    },
    initialize: function() {
        var me = this;
        me.callParent();
        me.onAfter({
            delegate: '> button',
            scope: me,
            hide: 'onButtonHiddenChange',
            show: 'onButtonHiddenChange'
        });
    },
    applyPressedButtons: function(pressedButtons) {
        var buttons = pressedButtons,
            len, i, button;
        pressedButtons = [];
        if (buttons) {
            buttons = Ext.Array.from(buttons);
            for (i = 0; i < len; ++i) {
                button = this.getComponent(buttons[i]);
                if (button) {
                    pressedButtons.push(button);
                }
            }
        }
        return pressedButtons;
    },
    updatePressedButtons: function(pressedButtons) {
        this.setValue(pressedButtons);
    },
    getPressedButtons: function() {
        var items = this.getItems().items,
            len = items.length,
            ret = [],
            i, button;
        for (i = 0; i < len; ++i) {
            button = items[i];
            if (button.getPressed()) {
                ret.push(button);
            }
        }
        return ret;
    },
    applyValue: function(value, oldValue) {
        var me = this,
            buttons = me.getItems(),
            allowMultiple = me.getAllowMultiple(),
            values, i, length, button, buttonValue;
        if (me.isConfiguring) {
            
            if (value == null) {
                values = [];
                length = buttons.length;
                for (i = 0; i < length; i++) {
                    button = buttons.getAt(i);
                    if (button.isPressed()) {
                        buttonValue = me.getButtonValue(button);
                        if (!Ext.Array.contains(values, buttonValue)) {
                            values.push(buttonValue);
                        }
                    }
                }
                value = values;
            } else {
                value = Ext.Array.from(value);
                
                values = value;
                length = values.length;
                for (i = 0; i < length; i++) {
                    button = me.lookupButtonByValue(values[i]);
                    if (!button) {
                        Ext.raise('Invalid value "' + value + '" for segmented button: "' + me.id + '"');
                    }
                }
            }
        } else 
        {
            value = Ext.Array.from(value);
            
            values = value;
            length = values.length;
            for (i = 0; i < length; i++) {
                button = me.lookupButtonByValue(values[i]);
                if (!button) {
                    Ext.raise('Invalid value "' + value + '" for segmented button: "' + me.id + '"');
                }
            }
        }
        
        
        if (value.length > 1 && !allowMultiple) {
            Ext.raise('Cannot set multiple values when allowMultiple is false');
        }
        
        value = allowMultiple ? me.sortValues(value) : value[0];
        if ((allowMultiple ? !value.length : value == null) && me.getForceSelection()) {
            value = me.getButtonValue(buttons.getAt(0));
            if (allowMultiple) {
                value = [
                    value
                ];
            }
        }
        return Ext.isDefined(value) ? value : null;
    },
    updateValue: function(value, oldValue) {
        var me = this,
            changed = [],
            newValues = Ext.Array.from(value),
            oldValues = Ext.Array.from(oldValue),
            button, i, length;
        me.settingValue = true;
        if (oldValues.length) {
            length = oldValues.length;
            for (i = 0; i < length; i++) {
                button = me.lookupButtonByValue(oldValues[i]);
                
                if (!Ext.Array.contains(newValues, oldValues[i])) {
                    button.setPressed(false);
                    changed.push(button);
                }
            }
        }
        if (newValues.length) {
            length = newValues.length;
            for (i = 0; i < length; i++) {
                button = me.lookupButtonByValue(newValues[i]);
                
                if (!Ext.Array.contains(oldValues, newValues[i])) {
                    button.setPressed(true);
                    changed.push(button);
                }
            }
        }
        
        if (!me.isConfiguring) {
            if (me.hasListeners.toggle) {
                length = changed.length;
                me.sortToggleItems(changed);
                for (i = 0; i < length; i++) {
                    button = changed[i];
                    me.fireEvent('toggle', me, button, button.getPressed());
                }
            }
            if (me.hasListeners.change) {
                changed = me.getAllowMultiple() ? !Ext.Array.equals(value, oldValue) : value !== oldValue;
                if (changed) {
                    me.fireEvent('change', me, value, oldValue);
                }
            }
        }
        me.settingValue = false;
    },
    updateAllowMultiple: function(allowMultiple) {
        if (!this.initialized && !this.getInitialConfig().hasOwnProperty('allowDepress') && allowMultiple) {
            this.setAllowDepress(true);
        }
    },
    updateVertical: function(vertical) {
        var me = this,
            vCls = Ext.baseCSSPrefix + 'vertical',
            hCls = Ext.baseCSSPrefix + 'horizontal';
        me.replaceCls(vertical ? hCls : vCls, vertical ? vCls : hCls);
        me.getLayout().setVertical(vertical);
    },
    onItemAdd: function(item, index) {
        var me = this,
            defaultUI = me.getDefaultUI(),
            value = item.getValue();
        
        me.getAllowMultiple();
        if (defaultUI && (item.getUi() == null)) {
            item.setUi(defaultUI);
        }
        if (value !== null) {
            me.valueMap[value] = item;
            me.useValueMap = true;
        }
        item.$segmentedListeners = item.on({
            scope: me,
            beforepressedchange: 'onBeforePressedChange',
            pressedchange: 'onPressedChange',
            order: 'before',
            destroyable: true
        });
        me.callParent([
            item,
            index
        ]);
        me.updateFirstAndLastCls(me.getItems());
        item.$enableToggle = item.getEnableToggle();
        item.$allowDepress = item.getAllowDepress();
        item.setEnableToggle(me.getAllowToggle());
        item.setAllowDepress(me.getAllowDepress());
    },
    onItemRemove: function(item, index, destroying) {
        var me = this,
            value = item.getValue(),
            useValueMap = me.useValueMap,
            listeners = item.$segmentedListeners,
            current, newValue, i;
        me.callParent([
            item,
            index,
            destroying
        ]);
        if (!me.destroying) {
            if (listeners) {
                listeners.destroy();
            }
            me.updateFirstAndLastCls(this.getItems());
            if (useValueMap) {
                value = button.getValue();
                delete me.valueMap[value];
            } else {
                value = index;
            }
            
            
            if (item.getPressed()) {
                current = me.getValue();
                if (me.getAllowMultiple()) {
                    index = current.indexOf(value);
                    newValue = current.slice();
                    
                    
                    
                    if (!useValueMap) {
                        for (i = newValue.length - 1; i > index; --i) {
                            --newValue[i];
                        }
                    }
                    newValue.splice(index, 1);
                } else {
                    newValue = null;
                }
                me.setValue(newValue);
            }
            item.setEnableToggle(item.$enableToggle);
            item.setAllowDepress(item.$allowDepress);
        }
        item.$segmentedListeners = null;
    },
    
    isPressed: function(button) {
        return button.getPressed();
    },
    
    updateDisabled: function(disabled, oldDisabled) {
        var me = this;
        me.items.each(function(item) {
            item.setDisabled(disabled);
        }, me);
        me.callParent([
            disabled,
            oldDisabled
        ]);
    },
    updateDefaultUI: function(defaultUI) {
        var items = this.items && this.items.items,
            len = items.length,
            i, item;
        for (i = 0; i < len; i++) {
            item = items[i];
            if (item.getUi() == null) {
                item.setUi(defaultUI);
            }
        }
    },
    doDestroy: function() {
        this.destroying = true;
        this.valueMap = null;
        this.callParent();
    },
    privates: {
        getAllPressed: function(items) {
            items = items.items;
            var len = items.length,
                map = {},
                pressingItem = this.pressingItem,
                i, button, pressed;
            for (i = 0; i < len; ++i) {
                button = items[i];
                
                
                
                if (button === pressingItem) {
                    pressed = !button.getPressed();
                } else {
                    pressed = button.getPressed();
                }
                if (pressed) {
                    map[button.id] = button;
                }
            }
            return map;
        },
        
        lookupButtonByValue: function(value) {
            return this.useValueMap ? this.valueMap[value] : this.getItems().getAt(value);
        },
        
        getButtonValue: function(button) {
            var value;
            if (this.useValueMap) {
                value = button.getValue();
            }
            if (value == null) {
                value = this.getItems().indexOf(button);
            }
            return value;
        },
        onBeforePressedChange: function(button, pressed) {
            
            
            
            var value = this.getValue(),
                ret;
            if (this.getForceSelection() && !pressed) {
                if (this.getAllowMultiple()) {
                    ret = value.length === 1;
                } else if (value === this.getButtonValue(button)) {
                    ret = false;
                }
            }
            return ret;
        },
        onPressedChange: function(button, pressed) {
            if (this.settingValue) {
                return;
            }
            var me = this,
                Array = Ext.Array,
                allowMultiple = me.getAllowMultiple(),
                buttonValue = me.getButtonValue(button),
                value = me.getValue(),
                valueIndex;
            if (allowMultiple) {
                valueIndex = Array.indexOf(value, buttonValue);
            }
            if (pressed) {
                if (allowMultiple) {
                    if (valueIndex === -1) {
                        
                        value = Array.slice(value);
                        value.push(buttonValue);
                    }
                } else {
                    value = buttonValue;
                }
            } else {
                if (allowMultiple) {
                    if (valueIndex > -1) {
                        
                        value = Array.slice(value);
                        value.splice(valueIndex, 1);
                    }
                } else if (value === buttonValue) {
                    value = null;
                }
            }
            me.pressingItem = button;
            me.setValue(value);
            me.pressingItem = null;
        },
        
        onButtonHiddenChange: function() {
            this.updateFirstAndLastCls(this.getItems());
        },
        sortToggleItems: function(buttons) {
            var items = this.getItems();
            Ext.Array.sort(buttons, function(a, b) {
                var p1 = a.getPressed() ? 1 : 0,
                    p2 = b.getPressed() ? 1 : 0,
                    ret = p1 - p2;
                if (ret === 0) {
                    ret = items.indexOf(a) - items.indexOf(b);
                }
                return ret;
            });
        },
        sortValues: function(values) {
            var fn = Ext.Array.numericSortFn,
                map = this.valueMap,
                items = this.getItems();
            values = Ext.Array.clone(values);
            if (this.useValueMap) {
                fn = function(a, b) {
                    a = items.indexOf(map[a]);
                    b = items.indexOf(map[b]);
                    return a - b;
                };
            }
            Ext.Array.sort(values, fn);
            return values;
        },
        
        updateFirstAndLastCls: function(items) {
            var ln = items.length,
                basePrefix = Ext.baseCSSPrefix,
                firstCls = basePrefix + 'first',
                lastCls = basePrefix + 'last',
                item, i;
            
            for (i = 0; i < ln; i++) {
                item = items.items[i];
                item.removeCls(firstCls);
                item.removeCls(lastCls);
            }
            
            for (i = 0; i < ln; i++) {
                item = items.items[i];
                if (!item.isHidden()) {
                    item.addCls(firstCls);
                    break;
                }
            }
            
            for (i = ln - 1; i >= 0; i--) {
                item = items.items[i];
                if (!item.isHidden()) {
                    item.addCls(lastCls);
                    break;
                }
            }
        }
    }
});


Ext.define('Ext.menu.Item', {
    extend: Ext.Component,
    alias: 'widget.menuitem',
    alternateClassName: 'Ext.menu.TextItem',
    
    isMenuItem: true,
    
    menuExpandDelay: 200,
    
    menuHideDelay: 200,
    
    scope: null,
    
    destroyMenu: true,
    
    clickHideDelay: 0,
    
    hideOnClick: true,
    config: {
        
        href: null,
        
        target: null,
        
        handler: null,
        
        text: null,
        
        menu: {
            lazy: true,
            $value: null
        },
        
        menuAlign: 'tl-tr?',
        
        icon: null,
        
        iconCls: null,
        
        iconAlign: 'left',
        
        indented: null,
        
        separator: null
    },
    inheritUi: true,
    ariaRole: 'menuitem',
    focusable: true,
    classCls: Ext.baseCSSPrefix + 'menuitem',
    activeCls: Ext.baseCSSPrefix + 'active',
    hasLeftIconCls: Ext.baseCSSPrefix + 'has-left-icon',
    hasRightIconCls: Ext.baseCSSPrefix + 'has-right-icon',
    hasArrowCls: Ext.baseCSSPrefix + 'has-arrow',
    hasHrefCls: Ext.baseCSSPrefix + 'has-href',
    isMenuOwner: true,
    template: [
        {
            reference: 'bodyElement',
            tag: 'a',
            href: '#',
            cls: Ext.baseCSSPrefix + 'body-el ' + Ext.baseCSSPrefix + 'unselectable',
            children: [
                {
                    reference: 'leftIconWrapElement',
                    cls: Ext.baseCSSPrefix + 'left-icon-wrap-el ' + Ext.baseCSSPrefix + 'icon-wrap-el',
                    children: [
                        {
                            reference: 'leftIconElement',
                            cls: Ext.baseCSSPrefix + 'left-icon-el ' + Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon'
                        }
                    ]
                },
                {
                    html: '\xa0',
                    reference: 'textElement',
                    cls: Ext.baseCSSPrefix + 'text-el'
                },
                {
                    reference: 'rightIconWrapElement',
                    cls: Ext.baseCSSPrefix + 'right-icon-wrap-el ' + Ext.baseCSSPrefix + 'icon-wrap-el',
                    children: [
                        {
                            reference: 'rightIconElement',
                            cls: Ext.baseCSSPrefix + 'right-icon-el ' + Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon'
                        }
                    ]
                },
                {
                    reference: 'arrowElement',
                    cls: Ext.baseCSSPrefix + 'arrow-el ' + Ext.baseCSSPrefix + 'font-icon'
                }
            ]
        }
    ],
    ariaEl: 'bodyElement',
    focusEl: 'bodyElement',
    initialize: function() {
        this.callParent();
        this.syncHasIconCls();
    },
    getFocusClsEl: function() {
        return this.el;
    },
    
    expandMenu: function(event) {
        var me = this,
            menu = me.getMenu();
        
        
        
        
        if (!me.getDisabled() && menu) {
            
            menu.parentMenu = me.parentMenu;
            
            me.hideOnClick = false;
            if (menu.isVisible()) {
                
                if (event && event.type === 'keydown') {
                    menu.focus();
                }
            } else {
                
                menu.autoFocus = !event || !event.pointerType;
                menu.showBy(me, me.getMenuAlign(), {
                    axisLock: true
                });
            }
        }
    },
    
    
    getRefItems: function(deep) {
        
        
        var menu = this.getMenu(),
            items;
        if (menu) {
            items = menu.getRefItems(deep);
            items.unshift(menu);
        }
        return items || [];
    },
    onFocusEnter: function(e) {
        var me = this;
        me.callParent([
            e
        ]);
        
        
        
        me.addCls(me.activeCls);
        me.activated = true;
        if (me.hasListeners.activate) {
            me.fireEvent('activate', me);
        }
        if (me.parentMenu) {
            me.parentMenu.setActiveItem(me);
        }
    },
    onFocusLeave: function(e) {
        var me = this,
            
            
            menu = me.menu;
        me.callParent([
            e
        ]);
        me.removeCls(me.activeCls);
        if (menu) {
            menu.hide();
        }
        me.activated = false;
        if (me.hasListeners.deactivate) {
            me.fireEvent('deactivate', me);
        }
        if (me.parentMenu) {
            me.parentMenu.setActiveItem(null);
        }
    },
    onRemoved: function(destroying) {
        this.callParent([
            destroying
        ]);
        this.parentMenu = null;
    },
    doDestroy: function() {
        var me = this;
        me.separatorElement = Ext.destroy(me.separatorElement);
        me.setMenu(null);
        me.callParent();
    },
    updateText: function(text) {
        if (text == null || text === '') {
            text = '\xa0';
        }
        this.textElement.dom.firstChild.data = text;
    },
    applyMenu: function(menu) {
        var me = this,
            ariaDom = me.ariaEl.dom;
        if (menu) {
            if (menu.isMenu) {
                menu.setConstrainAlign(Ext.getBody());
                menu.ownerCmp = me;
            } else {
                menu = Ext.menu.Menu.create(menu, {
                    ownerCmp: me,
                    $initParent: me,
                    constrainAlign: Ext.getBody()
                });
            }
            ariaDom.setAttribute('aria-haspopup', true);
            ariaDom.setAttribute('aria-owns', menu.id);
        } else {
            ariaDom.removeAttribute('aria-haspopup');
            ariaDom.removeAttribute('aria-owns');
        }
        me.toggleCls(me.hasArrowCls, !!menu);
        return menu;
    },
    updateMenu: function(menu, oldMenu) {
        if (oldMenu) {
            if (this.destroyMenu) {
                Ext.destroy(oldMenu);
            } else {
                oldMenu.parentMenu = null;
            }
        }
        
        this.menu = menu;
    },
    updateHref: function(href) {
        this.bodyElement.dom.href = href;
        this.toggleCls(this.hasHrefCls, !!href);
    },
    updateTarget: function(target) {
        this.bodyElement.dom.target = target;
    },
    updateIcon: function(icon) {
        var me = this,
            iconElement = (me.getIconAlign() === 'left') ? this.leftIconElement : this.rightIconElement;
        if (icon) {
            iconElement.setStyle('background-image', 'url(' + icon + ')');
        } else {
            iconElement.setStyle('background-image', '');
        }
        if (!me.isConfiguring) {
            me.syncHasIconCls();
        }
    },
    updateIconCls: function(iconCls, oldIconCls) {
        var me = this,
            iconElement = (me.getIconAlign() === 'left') ? this.leftIconElement : this.rightIconElement;
        if (iconCls) {
            iconElement.replaceCls(oldIconCls, iconCls);
        } else {
            iconElement.removeCls(oldIconCls);
        }
        if (!me.isConfiguring) {
            me.syncHasIconCls();
        }
    },
    updateIconAlign: function(iconAlign) {
        if (!this.isConfiguring) {
            this.syncHasIconCls();
        }
    },
    updateSeparator: function(separator) {
        var me = this,
            separatorElement = me.separatorElement;
        if (separator) {
            if (separatorElement) {
                separatorElement.show();
            } else {
                me.separatorElement = separatorElement = Ext.Element.create({
                    cls: Ext.baseCSSPrefix + 'menuseparator'
                });
                me.el.dom.insertBefore(separatorElement.dom, me.el.dom.firstChild);
            }
        } else if (separatorElement) {
            separatorElement.hide();
        }
    },
    privates: {
        onSpace: function(e) {
            return this.onClick(e);
        },
        onClick: function(e) {
            var me = this,
                href = me.getHref(),
                clickHideDelay = me.clickHideDelay,
                browserEvent = e.browserEvent,
                handler = me.getHandler(),
                clickResult;
            
            if ((!href || me.getDisabled()) && me.bodyElement.dom === e.getTarget('a')) {
                e.stopEvent();
                if (me.getDisabled()) {
                    return false;
                }
            }
            if (me.getDisabled() || me.handlingClick) {
                return;
            }
            if (me.hideOnClick && !me.getMenu()) {
                if (!clickHideDelay) {
                    me.hideParentMenus();
                } else {
                    me.hideParentMenusTimer = Ext.defer(me.hideParentMenus, clickHideDelay, me);
                }
            }
            
            clickResult = me.fireEvent('click', me, e);
            
            if (me.destroyed) {
                return;
            }
            if (clickResult !== false && handler) {
                Ext.callback(handler, me.scope, [
                    me,
                    e
                ], 0, me);
            }
            
            
            
            if (me.destroyed) {
                return;
            }
            
            if (href && e.type !== 'click' && !browserEvent.defaultPrevented) {
                me.handlingClick = true;
                me.bodyElement.dom.click();
                me.handlingClick = false;
            }
            return clickResult;
        },
        
        hideParentMenus: function() {
            for (var menu = this.getRefOwner(); menu && ((menu.isMenu && menu.getFloated()) || menu.isMenuItem); menu = menu.getRefOwner()) {
                if (menu.isMenu) {
                    menu.hide();
                }
            }
        },
        hasIcon: function() {
            return !!(this.getIconCls() || this.getIcon());
        },
        syncHasIconCls: function() {
            var me = this,
                rightCls = me.hasRightIconCls,
                leftCls = me.hasLeftIconCls,
                iconAlign = me.getIconAlign();
            if (me.hasIcon()) {
                if (iconAlign === 'left') {
                    me.replaceCls(rightCls, leftCls);
                } else if (iconAlign === 'right') {
                    me.replaceCls(leftCls, rightCls);
                }
            } else {
                me.removeCls([
                    leftCls,
                    rightCls
                ]);
            }
        }
    }
});


Ext.define('Ext.menu.Manager', {
    singleton: true,
    alternateClassName: 'Ext.menu.MenuMgr',
    
    hideAll: function() {
        var allMenus = this.visible,
            len = allMenus.length,
            result = false,
            i;
        if (len) {
            
            allMenus = allMenus.slice();
            for (i = 0; i < len; i++) {
                allMenus[i].hide();
                result = true;
            }
        }
        return result;
    },
    privates: {
        groups: {},
        visible: [],
        
        constructor: function() {
            var me = this;
            
            me.onShow = function() {
                
                me.registerGlobalListeners();
                return me.onShow.apply(me, arguments);
            };
        },
        
        onGlobalScroll: function(scroller) {
            var allMenus = this.visible,
                len = allMenus.length,
                i, menu;
            
            
            if (len && scroller !== Ext.scroll.Scroller.viewport) {
                
                allMenus = allMenus.slice();
                for (i = 0; i < len; ++i) {
                    menu = allMenus[i];
                    
                    
                    if (scroller.contains(menu)) {
                        menu.hide();
                    }
                }
            }
        },
        checkActiveMenus: function(e) {
            var allMenus = this.visible,
                len = allMenus.length,
                i, menu,
                mousedownCmp = Ext.Component.from(e);
            if (len) {
                
                allMenus = allMenus.slice();
                for (i = 0; i < len; ++i) {
                    menu = allMenus[i];
                    
                    
                    
                    
                    if (!(menu.owns(e) || (mousedownCmp && mousedownCmp.isMenuOwner && mousedownCmp.getMenu() === menu))) {
                        menu.hide();
                    }
                }
            }
        },
        
        onShow: function(menu) {
            if (menu.getFloated()) {
                Ext.Array.include(this.visible, menu);
            }
        },
        
        onHide: function(menu) {
            if (menu.getFloated()) {
                Ext.Array.remove(this.visible, menu);
            }
        },
        registerGlobalListeners: function() {
            var me = this;
            delete me.onShow;
            
            
            Ext.on({
                mousedown: me.checkActiveMenus,
                scrollstart: me.onGlobalScroll,
                scope: me
            });
            
            
            if (window.jasmine && jasmine.addAllowedListener) {
                jasmine.addAllowedListener('mousedown');
                jasmine.addAllowedListener('scrollstart');
            }
        }
    }
});



Ext.define('Ext.menu.Menu', {
    extend: Ext.Panel,
    xtype: 'menu',
    
    isMenu: true,
    config: {
        
        align: 'tl-bl?',
        
        
        indented: true,
        
        separator: null,
        
        autoHide: null,
        
        groups: null
    },
    
    allowOtherMenus: false,
    
    ignoreParentClicks: false,
    
    mouseLeaveDelay: 50,
    defaultType: 'menuitem',
    autoSize: null,
    keyMap: {
        scope: 'this',
        
        SPACE: 'onSpaceKey',
        
        ESC: 'onEscKey'
    },
    layout: {
        type: 'vbox',
        align: 'stretch'
    },
    classCls: Ext.baseCSSPrefix + 'menu',
    indentedCls: Ext.baseCSSPrefix + 'indented',
    hasSeparatorCls: Ext.baseCSSPrefix + 'has-separator',
    nonMenuItemCls: Ext.baseCSSPrefix + 'non-menuitem',
    
    
    allowFocusingDisabledChildren: true,
    border: true,
    
    
    
    
    defaultFocus: ':focusable',
    floated: true,
    
    focusable: true,
    focusableContainer: true,
    nameHolder: true,
    weighted: true,
    
    initialize: function() {
        var me = this,
            listeners = {
                click: me.onClick,
                mouseover: me.onMouseOver,
                scope: me
            };
        me.callParent();
        if (Ext.supports.Touch) {
            listeners.pointerdown = me.onMouseOver;
        }
        me.element.on(listeners);
        
        
        
        me.itemOverTask = new Ext.util.DelayedTask(me.handleItemOver, me);
        me.mouseMonitor = me.el.monitorMouseLeave(me.mouseLeaveDelay, me.onMouseLeave, me);
    },
    doDestroy: function() {
        var me = this;
        
        me.itemOverTask.cancel();
        
        
        Ext.menu.Manager.onHide(me);
        me.parentMenu = me.ownerCmp = null;
        if (me.rendered) {
            me.el.un(me.mouseMonitor);
        }
        me.callParent();
    },
    showBy: function(component, alignment, options) {
        this.callParent([
            component,
            alignment || this.getAlign(),
            options
        ]);
    },
    onFocusEnter: function(e) {
        var me = this,
            hierarchyState;
        me.callParent([
            e
        ]);
        me.mixins.focusablecontainer.onFocusEnter.call(me, e);
        if (me.getFloated()) {
            hierarchyState = me.getInherited();
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (!hierarchyState.topmostFocusEvent) {
                hierarchyState.topmostFocusEvent = e;
            }
        }
    },
    onFocusLeave: function(e) {
        this.callParent([
            e
        ]);
        if (this.getAutoHide() !== false) {
            this.hide();
        }
    },
    onItemAdd: function(item, index) {
        this.callParent([
            item,
            index
        ]);
        this.syncItemIndentedCls(item);
        if (!item.isMenuItem && !item.isMenuSeparator) {
            item.addCls(this.nonMenuItemCls);
        }
    },
    onItemRemove: function(item, index, destroying) {
        this.callParent([
            item,
            index,
            destroying
        ]);
        item.removeCls(this.indentedCls, this.nonMenuItemCls);
    },
    beforeShow: function() {
        var me = this,
            parent;
        
        
        if (me.getFloated()) {
            parent = me.hasFloatMenuParent();
            if (!parent && !me.allowOtherMenus) {
                Ext.menu.Manager.hideAll();
            }
        }
        me.callParent(arguments);
    },
    afterShow: function() {
        var me = this,
            ariaDom = me.ariaEl.dom;
        me.callParent(arguments);
        Ext.menu.Manager.onShow(me);
        if (me.getFloated() && ariaDom) {
            ariaDom.setAttribute('aria-expanded', true);
        }
        
        if (me.getFloated()) {
            me.maxHeight = me.savedMaxHeight;
        }
        if (me.autoFocus) {
            me.focus();
        }
    },
    afterHide: function() {
        var me = this,
            ariaDom = me.ariaEl.dom;
        me.callParent();
        me.lastHide = Ext.Date.now();
        Ext.menu.Manager.onHide(me);
        if (me.getFloated() && ariaDom) {
            ariaDom.setAttribute('aria-expanded', false);
        }
        
        delete me.getInherited().topmostFocusEvent;
    },
    factoryItem: function(cfg) {
        var result;
        if (typeof cfg === 'string' && cfg[0] !== '@') {
            if (cfg === '-') {
                cfg = {
                    xtype: 'menuseparator'
                };
            } else {
                cfg = {};
            }
        }
        result = this.callParent([
            cfg
        ]);
        if (result.isMenuItem) {
            result.parentMenu = this;
        }
        return result;
    },
    updateIndented: function(indented) {
        var me = this;
        if (!me.isConfiguring) {
            me.bodyElement.toggleCls(me.hasSeparatorCls, !!(indented && me.getSeparator()));
            me.items.each(me.syncItemIndentedCls, me);
        }
    },
    updateSeparator: function(separator) {
        this.bodyElement.toggleCls(this.hasSeparatorCls, !!(separator && this.getIndented()));
    },
    privates: {
        applyItemDefaults: function(item) {
            item = this.callParent([
                item
            ]);
            if (!item.isComponent && !item.xtype && !item.xclass) {
                
                if (item.group || item.name) {
                    item.xtype = 'menuradioitem';
                }
                
                else if ('checked' in item) {
                    item.xtype = 'menucheckitem';
                }
            }
            return item;
        },
        applyGroups: function(groups, oldGroups) {
            var me = this,
                currentGroups = Ext.apply({}, oldGroups),
                isConfiguring = me.isConfiguring,
                groupName, members, len, i, item, value, oldValue;
            if (groups) {
                for (groupName in groups) {
                    oldValue = currentGroups[groupName];
                    currentGroups[groupName] = value = groups[groupName];
                    if (!isConfiguring) {
                        members = me.lookupName(groupName);
                        for (i = 0 , len = members.length; i < len; i++) {
                            item = members[i];
                            
                            item.setChecked(item.getValue() === value);
                        }
                        me.fireEvent('groupchange', me, groupName, value, oldValue);
                    }
                }
                
                
                if (!isConfiguring) {
                    me.addBindableUpdater('groups');
                }
            }
            return currentGroups;
        },
        processFocusableContainerKeyEvent: function(e) {
            var keyCode = e.keyCode,
                item;
            
            
            if (keyCode === e.ESC || (Ext.fly(e.target).is('input[type=checkbox]') && (keyCode === e.LEFT || keyCode === e.RIGHT || keyCode === e.UP || keyCode === e.DOWN))) {
                e.preventDefault();
                
                item = this.getItemFromEvent(e);
                e.target = item && item.focusEl.dom;
            }
            
            else if (keyCode === e.TAB && Ext.fly(e.target).is('input[type=text],textarea')) {
                e.preventDefault();
                
                item = this.getItemFromEvent(e);
                e.target = item && item.focusEl.dom;
                if (e.shiftKey) {
                    e.shiftKey = false;
                    e.keyCode = e.UP;
                } else {
                    e.keyCode = e.DOWN;
                }
            } else {
                return this.callParent([
                    e
                ]);
            }
            return e;
        },
        onEscKey: function(e) {
            if (this.getFloated()) {
                this.hide();
            }
        },
        onSpaceKey: function(e) {
            var clickedItem = this.getItemFromEvent(e);
            if (clickedItem && clickedItem.isMenuItem) {
                clickedItem.onSpace(e);
            }
        },
        onFocusableContainerLeftKey: function(e) {
            
            e.preventDefault();
            
            if (this.parentMenu) {
                this.hide();
            }
        },
        onFocusableContainerRightKey: function(e) {
            var clickedItem = this.getItemFromEvent(e);
            
            e.preventDefault();
            if (clickedItem) {
                clickedItem.expandMenu(e);
            }
        },
        onClick: function(e) {
            var me = this,
                type = e.type,
                clickedItem, clickResult,
                isKeyEvent = type === 'keydown',
                isTouchEvent = e.pointerType === 'touch';
            if (me.getDisabled()) {
                return e.stopEvent();
            }
            clickedItem = me.getItemFromEvent(e);
            if (clickedItem && clickedItem.isMenuItem) {
                if (!clickedItem.getMenu() || !me.ignoreParentClicks) {
                    clickResult = clickedItem.onClick(e);
                } else {
                    e.stopEvent();
                }
                
                if (me.destroyed) {
                    return;
                }
                
                if (clickedItem.getMenu() && clickResult !== false && (isKeyEvent || isTouchEvent)) {
                    clickedItem.expandMenu(e);
                }
            }
            
            if (!clickedItem || clickedItem.getDisabled()) {
                clickedItem = undefined;
            }
            me.fireEvent('click', me, clickedItem, e);
        },
        onMouseLeave: function(e) {
            var me = this;
            if (me.itemOverTask) {
                me.itemOverTask.cancel();
            }
            if (me.getDisabled()) {
                return;
            }
            me.fireEvent('mouseleave', me, e);
        },
        
        onMouseOver: function(e) {
            var me = this,
                activeItem = me.getActiveItem(),
                activeItemMenu = activeItem && activeItem.getMenu && activeItem.getMenu(),
                activeItemExpanded = activeItemMenu && activeItemMenu.isVisible(),
                isTouch = e.pointerType === 'touch',
                mouseEnter, overItem, el;
            if (!me.getDisabled()) {
                
                
                if (isTouch) {
                    mouseEnter = !me.el.contains(document.activeElement);
                } else {
                    mouseEnter = !me.el.contains(e.getRelatedTarget());
                }
                overItem = me.getItemFromEvent(e);
                
                
                if (overItem) {
                    
                    if (isTouch) {
                        me.handleItemOver(e, overItem);
                    } else {
                        
                        
                        
                        
                        el = overItem.isMenuItem ? overItem.bodyElement : overItem.el;
                        if (!el.contains(e.getRelatedTarget())) {
                            me.itemOverTask.delay(activeItemExpanded ? me.mouseLeaveDelay : 0, null, null, [
                                e,
                                overItem
                            ]);
                        }
                    }
                }
                if (mouseEnter) {
                    me.fireEvent('mouseenter', me, e);
                }
                me.fireEvent('mouseover', me, overItem, e);
            }
        },
        
        handleItemOver: function(e, item) {
            var isMouseover = e.pointerType === 'mouse';
            
            
            if (!item.containsFocus && (isMouseover || item.isMenuItem)) {
                item.focus();
            }
            
            if (item.expandMenu && isMouseover) {
                item.expandMenu(e);
            }
        },
        
        getItemFromEvent: function(e) {
            var bodyDom = this.bodyElement.dom,
                toEl = e.getTarget(),
                component;
            
            
            while (toEl && toEl.parentNode !== bodyDom) {
                toEl = toEl.parentNode;
            }
            component = toEl && Ext.getCmp(toEl.id);
            if (component && component.isMenuItem && !e.within(component.bodyElement)) {
                
                
                
                
                component = null;
            }
            return component;
        },
        hasFloatMenuParent: function() {
            return this.parentMenu || this.up('menu[_floated=true]');
        },
        syncItemIndentedCls: function(item) {
            
            
            
            var indented = item.isMenuItem ? item.getIndented() : item.indented;
            item.toggleCls(this.indentedCls, !!(indented || (this.getIndented() && (indented !== false))));
        }
    },
    statics: {
        
        create: function(menu, config) {
            if (Ext.isArray(menu)) {
                
                menu = Ext.apply({
                    xtype: 'menu',
                    items: menu
                }, config);
            } else {
                menu = Ext.apply({
                    xtype: 'menu'
                }, menu, config);
            }
            return Ext.create(menu);
        }
    },
    deprecated: {
        '6.5': {
            configs: {
                plain: {
                    message: 'To achieve classic toolkit "plain" effect, use "indented".'
                },
                showSeparator: {
                    message: 'To achieve classic toolkit "showSeparator" effect, use "separator".'
                }
            }
        }
    }
});


Ext.define('Ext.SplitButton', {
    extend: Ext.Button,
    xtype: 'splitbutton',
    isSplitButton: true,
    baseCls: Ext.baseCSSPrefix + 'splitButton',
    
    config: {
        
        arrowHandler: null
    },
    
    arrowCls: 'split',
    initialize: function() {
        var me = this,
            el = me.el;
        me.callParent();
        this.arrowElement.addClsOnOver(this.hoveredCls, this.isEnabled, this);
        this.splitInnerElement.addClsOnOver(this.hoveredCls, this.isEnabled, this);
    },
    getTemplate: function() {
        return [
            {
                reference: 'innerElement',
                cls: Ext.baseCSSPrefix + 'splitBody-el',
                children: [
                    {
                        reference: 'splitInnerElement',
                        cls: Ext.baseCSSPrefix + 'splitInner-el',
                        children: [
                            {
                                reference: 'bodyElement',
                                cls: Ext.baseCSSPrefix + 'body-el',
                                children: [
                                    {
                                        cls: Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon',
                                        reference: 'iconElement'
                                    },
                                    {
                                        reference: 'textElement',
                                        cls: Ext.baseCSSPrefix + 'text-el'
                                    }
                                ]
                            },
                            this.getButtonTemplate()
                        ]
                    },
                    {
                        reference: 'arrowElement',
                        cls: Ext.baseCSSPrefix + 'splitArrow-el',
                        children: [
                            {
                                reference: 'splitArrowElement',
                                cls: Ext.baseCSSPrefix + 'arrow-el ' + Ext.baseCSSPrefix + 'font-icon'
                            },
                            this.getArrowButtonTemplate()
                        ]
                    }
                ]
            }
        ];
    },
    getArrowButtonTemplate: function() {
        return {
            tag: 'button',
            reference: 'splitArrowCoverElement',
            cls: Ext.baseCSSPrefix + 'button-el',
            onfocus: 'return Ext.doEv(this, event);',
            onblur: 'return Ext.doEv(this, event);'
        };
    },
    
    doTap: function(me, e) {
        var arrowEl = this.splitArrowCoverElement,
            arrowKeydown = (e.type === 'keydown' || e.type === 'click') && (e.target === arrowEl.dom);
        
        
        if (e && e.preventDefault && me.preventDefaultAction) {
            e.preventDefault();
        }
        if (!me.getDisabled()) {
            if (arrowKeydown) {
                me.toggleMenu(e, me.getMenu());
                me.fireEvent("arrowclick", me, e);
                if (me.getArrowHandler()) {
                    Ext.callback(me.getArrowHandler(), me.getScope(), [
                        me,
                        e
                    ], 0, me);
                }
            } else {
                if (me.getMenu().isVisible()) {
                    me.hideMenu(e, me.getMenu());
                }
                Ext.callback(me.getHandler(), me.getScope(), [
                    me,
                    e
                ], 0, me);
            }
        }
    },
    onDownKey: function(e) {
        var arrowEl = this.splitArrowCoverElement;
        if (e.target === arrowEl.dom) {
            this.callParent([
                e
            ]);
        }
    },
    updatePressed: function(pressed) {
        this.callParent([
            pressed
        ]);
        this.arrowElement.toggleCls(this.pressedCls, pressed);
    },
    findEventTarget: function(e) {
        return e.target === this.buttonElement.dom ? this.splitInnerElement : this.arrowElement;
    },
    shouldRipple: function(e) {
        var arrowEl = this.splitArrowCoverElement,
            ripple = (arrowEl && e.target === arrowEl.dom) ? this.getArrowRipple() : this.getSplitRipple();
        this.setRipple(ripple);
        return this.callParent([
            e
        ]);
    },
    enableFocusable: function() {
        this.splitArrowCoverElement.dom.disabled = false;
        this.callParent();
    },
    disableFocusable: function() {
        this.callParent();
        this.splitArrowCoverElement.dom.disabled = true;
    },
    privates: {
        onButtonFocus: function(e) {
            this.splitInnerElement.addCls(this.focusCls);
        },
        onButtonBlur: function(e) {
            this.splitInnerElement.removeCls(this.focusCls);
        },
        onArrowFocus: function(e) {
            this.arrowElement.addCls(this.focusCls);
        },
        onArrowBlur: function(e) {
            this.arrowElement.removeCls(this.focusCls);
        },
        handleFocusEvent: function(e) {
            var arrowEl = this.splitArrowCoverElement;
            this.callParent([
                e
            ]);
            if (e.target === arrowEl.dom) {
                this.onArrowFocus([
                    e
                ]);
            } else if (e.target === this.buttonElement.dom) {
                this.onButtonFocus([
                    e
                ]);
            }
        },
        handleBlurEvent: function(e) {
            var arrowEl = this.splitArrowCoverElement;
            this.callParent([
                e
            ]);
            if (e.target === arrowEl.dom) {
                this.onArrowBlur([
                    e
                ]);
            } else if (e.target === this.buttonElement.dom) {
                this.onButtonBlur([
                    e
                ]);
            }
        }
    }
});


Ext.define('Ext.TitleBar', {
    extend: Ext.Container,
    xtype: 'titlebar',
    
    defaultBindProperty: 'title',
    
    isToolbar: true,
    
    classCls: Ext.baseCSSPrefix + 'titlebar',
    
    inheritUi: true,
    config: {
        
        cls: Ext.baseCSSPrefix + 'navigation-bar',
        
        title: null,
        
        titleAlign: 'center',
        
        defaultType: 'button',
        
        defaultButtonUI: null,
        
        minHeight: null,
        
        layout: {
            type: 'hbox',
            align: 'center'
        },
        
        items: [],
        
        maxButtonWidth: '40%'
    },
    
    autoSize: null,
    
    border: false,
    beforeInitialize: function() {
        this.applyItems = this.applyInitialItems;
    },
    initialize: function() {
        var me = this;
        me.callParent();
        delete me.applyItems;
        me.add(me.initialItems);
        delete me.initialItems;
        me.on({
            scope: me,
            painted: 'refreshTitlePosition',
            single: true
        });
    },
    applyInitialItems: function(items) {
        var me = this,
            titleAlign = me.getTitleAlign(),
            defaults = me.getDefaults() || {};
        me.initialItems = items;
        me.leftBox = me.add({
            xtype: 'container',
            style: 'position: relative',
            cls: Ext.baseCSSPrefix + 'titlebar-left',
            autoSize: null,
            layout: {
                type: 'hbox',
                align: 'center'
            },
            listeners: {
                resize: 'refreshTitlePosition',
                scope: me
            }
        });
        me.spacer = me.add({
            xtype: 'component',
            style: 'position: relative',
            cls: Ext.baseCSSPrefix + 'titlebar-center',
            flex: 1,
            listeners: {
                resize: 'refreshTitlePosition',
                scope: me
            }
        });
        me.rightBox = me.add({
            xtype: 'container',
            style: 'position: relative',
            cls: Ext.baseCSSPrefix + 'titlebar-right',
            autoSize: null,
            layout: {
                type: 'hbox',
                align: 'center'
            },
            listeners: {
                resize: 'refreshTitlePosition',
                scope: me
            }
        });
        switch (titleAlign) {
            case 'left':
                me.titleComponent = me.leftBox.add({
                    xtype: 'title',
                    cls: Ext.baseCSSPrefix + 'title-align-left',
                    hidden: defaults.hidden
                });
                me.refreshTitlePosition = Ext.emptyFn;
                break;
            case 'right':
                me.titleComponent = me.rightBox.add({
                    xtype: 'title',
                    cls: Ext.baseCSSPrefix + 'title-align-right',
                    hidden: defaults.hidden
                });
                me.refreshTitlePosition = Ext.emptyFn;
                break;
            default:
                me.titleComponent = me.add({
                    xtype: 'title',
                    hidden: defaults.hidden,
                    centered: true
                });
                break;
        }
        me.doAdd = me.doBoxAdd;
        me.remove = me.doBoxRemove;
        me.doInsert = me.doBoxInsert;
    },
    doBoxAdd: function(item) {
        var me = this,
            titleAlign = me.getTitleAlign();
        me.addDefaultButtonUI(item);
        if (item.config.align == 'right') {
            me.rightBox.add(item);
        } else if (me.titleComponent && titleAlign === 'left') {
            me.leftBox.insertBefore(item, me.titleComponent);
        } else {
            me.leftBox.add(item);
        }
    },
    doBoxRemove: function(item, destroy) {
        if (item.config.align == 'right') {
            this.rightBox.remove(item, destroy);
        } else {
            this.leftBox.remove(item, destroy);
        }
    },
    doBoxInsert: function(index, item) {
        var me = this;
        me.addDefaultButtonUI(item);
        if (item.config.align == 'right') {
            me.rightBox.insert(index, item);
        } else {
            me.leftBox.insert(index, item);
        }
    },
    addDefaultButtonUI: function(item) {
        var defaultButtonUI = this.getDefaultButtonUI();
        if (defaultButtonUI) {
            if (item.isSegmentedButton) {
                if (item.getDefaultUI() == null) {
                    item.setDefaultUI(defaultButtonUI);
                }
            } else if (item.isButton && (item.getUi() == null)) {
                item.setUi(defaultButtonUI);
            }
        }
    },
    calculateMaxButtonWidth: function() {
        var maxButtonWidth = this.getMaxButtonWidth();
        
        if (Ext.isString(maxButtonWidth)) {
            maxButtonWidth = parseInt(maxButtonWidth.replace('%', ''), 10);
        }
        maxButtonWidth = Math.round((this.element.getWidth() / 100) * maxButtonWidth);
        return maxButtonWidth;
    },
    refreshTitlePosition: function() {
        if (this.destroyed) {
            return;
        }
        var titleElement = this.titleComponent.renderElement;
        titleElement.setWidth(null);
        titleElement.setLeft(null);
        
        var leftBox = this.leftBox,
            leftButton = leftBox.down('button'),
            singleButton = leftBox.getItems().getCount() == 1,
            leftBoxWidth, maxButtonWidth;
        if (leftButton && singleButton) {
            if (leftButton.getWidth() == null) {
                leftButton.renderElement.setWidth('auto');
            }
            leftBoxWidth = leftBox.renderElement.getWidth();
            maxButtonWidth = this.calculateMaxButtonWidth();
            if (leftBoxWidth > maxButtonWidth) {
                leftButton.renderElement.setWidth(maxButtonWidth);
            }
        }
        var spacerBox = this.spacer.renderElement.getBox();
        if (Ext.browser.is.IE) {
            titleElement.setWidth(spacerBox.width);
        }
        var titleBox = titleElement.getBox(),
            widthDiff = titleBox.width - spacerBox.width,
            titleLeft = titleBox.left,
            titleRight = titleBox.right,
            halfWidthDiff, leftDiff, rightDiff;
        if (widthDiff > 0) {
            halfWidthDiff = widthDiff / 2;
            titleLeft += halfWidthDiff;
            titleRight -= halfWidthDiff;
            titleElement.setWidth(spacerBox.width);
        }
        leftDiff = spacerBox.left - titleLeft;
        rightDiff = titleRight - spacerBox.right;
        if (leftDiff > 0) {
            titleElement.setLeft(leftDiff);
        } else if (rightDiff > 0) {
            titleElement.setLeft(-rightDiff);
        }
        titleElement.repaint();
    },
    
    updateTitle: function(newTitle) {
        
        this.getItems();
        this.titleComponent.setTitle(newTitle);
        if (this.isPainted()) {
            this.refreshTitlePosition();
        }
    }
});


Ext.define('Ext.Toast', {
    extend: Ext.Sheet,
    config: {
        
        centered: false,
        
        showAnimation: {
            type: 'popIn',
            duration: 250,
            easing: 'ease-out'
        },
        
        hideAnimation: {
            type: 'popOut',
            duration: 250,
            easing: 'ease-out'
        },
        
        zIndex: 999,
        
        message: '',
        
        timeout: 1000,
        
        maxQueue: 3,
        
        messageAnimation: true,
        
        hideOnMaskTap: true,
        
        modal: false,
        
        layout: {
            type: 'vbox',
            pack: 'center'
        }
    },
    
    classCls: Ext.baseCSSPrefix + 'toast',
    
    applyMessage: function(value) {
        var config = {
                html: value,
                cls: this.baseCls + '-text'
            };
        return Ext.factory(config, Ext.Component, this._message);
    },
    
    updateMessage: function(newMessage) {
        if (newMessage) {
            this.add(newMessage);
        }
    },
    
    startTimer: function() {
        var timeout = this.getTimeout();
        if (this._timeoutID) {
            Ext.undefer(this._timeoutID);
        }
        if (!Ext.isEmpty(timeout)) {
            this._timeoutID = Ext.defer(this.onTimeout.bind(this), timeout);
        } else {
            this.onTimeout();
        }
    },
    stopTimer: function() {
        Ext.undefer(this._timeoutID);
        this._timeoutID = null;
    },
    
    next: Ext.emptyFn,
    getIsAnimating: function() {
        var messageContainer = this.getMessage();
        return (messageContainer && Ext.Animator.hasRunningAnimations(messageContainer)) || Ext.Animator.hasRunningAnimations(this);
    },
    
    showToast: function(config) {
        var me = this,
            message = config.message,
            timeout = config.timeout,
            messageContainer = me.getMessage(),
            msgAnimation = me.getMessageAnimation();
        
        if (me.isRendered() && me.isHidden() === false) {
            messageContainer.onAfter({
                
                hiddenchange: function() {
                    me.setMessage(message);
                    me.setTimeout(timeout);
                    messageContainer.onAfter({
                        scope: me,
                        
                        hiddenchange: function() {
                            me.startTimer();
                        },
                        single: true
                    });
                    messageContainer.show(msgAnimation);
                },
                scope: me,
                single: true
            });
            messageContainer.hide(msgAnimation);
        } else {
            Ext.util.InputBlocker.blockInputs();
            
            if (!me.getParent() && Ext.Viewport) {
                Ext.Viewport.add(me);
            }
            me.setMessage(message);
            me.setTimeout(timeout);
            me.startTimer();
            me.show({
                animation: null,
                alignment: {
                    component: document.body,
                    alignment: 't-t',
                    options: {
                        offset: [
                            0,
                            20
                        ]
                    }
                }
            });
        }
    },
    
    beforeHide: function(animation) {
        this.callParent(arguments);
        
        if (this.getIsAnimating()) {
            return false;
        }
        this.stopTimer();
        if (!this.next()) {
            return false;
        }
    },
    
    onTimeout: function() {
        if (this._timeoutID !== null) {
            this.hide();
        }
    },
    doDestroy: function() {
        this.stopTimer();
        this.callParent();
    }
}, function(Toast) {
    var _queue = [];
    function getInstance() {
        if (!Ext.Toast._instance) {
            Ext.Toast._instance = Ext.create('Ext.Toast');
        }
        return Ext.Toast._instance;
    }
    
    
    Toast.prototype.getQueueCount = function() {
        return _queue.length;
    };
    
    Toast.prototype.next = function() {
        var config = _queue.shift();
        if (config) {
            this.showToast(config);
        }
        return !config;
    };
    
    Ext.Toast.destroy = function() {
        if (Ext.Toast._instance) {
            Ext.Toast._instance.destroy();
            Ext.Toast._instance = null;
        }
    };
    Ext.toast = function(message, timeout) {
        var toast = getInstance(),
            maxQueue = Ext.Toast.prototype.config.maxQueue,
            config = message;
        if (Ext.isString(message)) {
            config = {
                message: message,
                timeout: timeout
            };
        }
        
        if (!config) {
            throw new Error("Toast requires a message");
        }
        
        if (config.timeout === undefined) {
            config.timeout = Ext.Toast.prototype.config.timeout;
        }
        _queue.push(config);
        if (_queue.length > maxQueue) {
            _queue.shift();
        }
        if (!toast.isRendered() || toast.isHidden()) {
            toast.next();
        }
        return toast;
    };
});


Ext.define('Ext.Video', {
    extend: Ext.Media,
    xtype: 'video',
    config: {
        
        
        posterUrl: null,
        
        showPosterOnPause: false
    },
    baseCls: Ext.baseCSSPrefix + 'video',
    template: [
        {
            
            reference: 'ghost',
            classList: [
                Ext.baseCSSPrefix + 'video-ghost'
            ]
        },
        {
            tag: 'video',
            reference: 'media',
            classList: [
                Ext.baseCSSPrefix + 'media'
            ]
        }
    ],
    initialize: function() {
        var me = this;
        me.callParent();
        me.media.hide();
        me.ghost.on({
            tap: 'onGhostTap',
            scope: me
        });
        me.media.on({
            pause: 'onPause',
            scope: me
        });
        if (Ext.os.is.Android4 || Ext.os.is.iPad) {
            this.isInlineVideo = true;
        }
    },
    applyUrl: function(url) {
        return [].concat(url);
    },
    updateUrl: function(newUrl) {
        var me = this,
            media = me.media,
            newLn = newUrl.length,
            existingSources = media.query('source'),
            oldLn = existingSources.length,
            i;
        for (i = 0; i < oldLn; i++) {
            Ext.fly(existingSources[i]).destroy();
        }
        for (i = 0; i < newLn; i++) {
            media.appendChild(Ext.Element.create({
                tag: 'source',
                src: newUrl[i]
            }));
        }
        if (me.isPlaying()) {
            me.play();
        }
    },
    onActivate: function() {
        this.media.show();
    },
    onDeactivate: function() {
        this.pause();
        this.media.hide();
        this.ghost.show();
    },
    
    onGhostTap: function() {
        var me = this,
            media = this.media,
            ghost = this.ghost;
        media.show();
        ghost.hide();
        me.play();
    },
    
    onPause: function(e) {
        this.callParent([
            e
        ]);
        
        
        if (!this.isInlineVideo && !e.target.seeking && this.getShowPosterOnPause()) {
            this.media.hide();
            this.ghost.show();
        }
    },
    
    onPlay: function(e) {
        this.callParent([
            e
        ]);
        this.media.show();
    },
    
    updatePosterUrl: function(newUrl) {
        var ghost = this.ghost;
        if (ghost) {
            ghost.setStyle('background-image', 'url(' + newUrl + ')');
        }
    }
});


Ext.define('Ext.behavior.Behavior', {
    constructor: function(component) {
        this.component = component;
        component.on('destroy', 'onComponentDestroy', this);
    },
    onComponentDestroy: Ext.emptyFn
});



Ext.define('Ext.carousel.Item', {
    extend: Ext.Decorator,
    config: {
        component: null,
        translatable: true
    },
    baseCls: Ext.baseCSSPrefix + 'carousel-item'
});


Ext.define('Ext.util.TranslatableGroup', {
    extend: Ext.util.translatable.Abstract,
    config: {
        items: [],
        activeIndex: 0,
        itemLength: {
            x: 0,
            y: 0
        }
    },
    applyItems: function(items) {
        return Ext.Array.from(items);
    },
    doTranslate: function(x, y) {
        var items = this.getItems(),
            activeIndex = this.getActiveIndex(),
            itemLength = this.getItemLength(),
            itemLengthX = itemLength.x,
            itemLengthY = itemLength.y,
            useX = Ext.isNumber(x),
            useY = Ext.isNumber(y),
            offset, i, ln, item, translateX, translateY;
        for (i = 0 , ln = items.length; i < ln; i++) {
            item = items[i];
            if (item) {
                offset = (i - activeIndex);
                if (useX) {
                    translateX = x + offset * itemLengthX;
                }
                if (useY) {
                    translateY = y + offset * itemLengthY;
                }
                item.translate(translateX, translateY);
            }
        }
    }
});


Ext.define('Ext.carousel.Carousel', {
    extend: Ext.Container,
    alternateClassName: 'Ext.Carousel',
    xtype: 'carousel',
    config: {
        
        
        direction: 'horizontal',
        animation: {
            duration: 250,
            easing: {
                type: 'ease-out'
            }
        },
        
        
        indicator: true,
        
        ui: 'dark',
        itemConfig: {
            translatable: {
                type: 'csstransform'
            }
        },
        bufferSize: 1,
        itemLength: null
    },
    baseCls: Ext.baseCSSPrefix + 'carousel',
    itemLength: 0,
    offset: 0,
    flickStartOffset: 0,
    flickStartTime: 0,
    dragDirection: 0,
    count: 0,
    painted: false,
    activeIndex: -1,
    beforeInitialize: function() {
        var me = this;
        me.element.on({
            resize: 'onSizeChange',
            dragstart: 'onDragStart',
            drag: 'onDrag',
            dragend: 'onDragEnd',
            dragcancel: 'onDragEnd',
            scope: me
        });
        me.carouselItems = [];
        me.orderedCarouselItems = [];
        me.inactiveCarouselItems = [];
        me.hiddenTranslation = 0;
    },
    updateBufferSize: function(size) {
        var ItemClass = Ext.carousel.Item,
            total = size * 2 + 1,
            isRendered = this.isRendered(),
            bodyElement = this.bodyElement,
            items = this.carouselItems,
            ln = items.length,
            itemConfig = Ext.apply({
                ownerCmp: this
            }, this.getItemConfig()),
            itemLength = this.getItemLength(),
            direction = this.getDirection(),
            setterName = direction === 'horizontal' ? 'setWidth' : 'setHeight',
            i, item;
        for (i = ln; i < total; i++) {
            item = Ext.factory(itemConfig, ItemClass);
            if (itemLength) {
                item[setterName].call(item, itemLength);
            }
            items.push(item);
            bodyElement.append(item.renderElement);
            if (isRendered && item.setRendered(true)) {
                item.fireEvent('renderedchange', this, item, true);
            }
        }
        this.getTranslatable().setActiveIndex(size);
    },
    onSizeChange: function() {
        this.refreshSizing();
        this.refreshCarouselItems();
        this.refreshActiveItem();
    },
    onItemAdd: function(item, index) {
        this.callParent([
            item,
            index
        ]);
        var innerIndex = this.getInnerItems().indexOf(item),
            indicator = this.getIndicator();
        if (indicator && item.isInnerItem()) {
            indicator.add();
        }
        if (innerIndex <= this.getActiveIndex()) {
            this.refreshActiveIndex();
        }
        if (this.isIndexDirty(innerIndex) && !this.isItemsInitializing) {
            this.refreshActiveItem();
        }
    },
    doItemLayoutAdd: function(item, index, destroying) {
        if (item.isInnerItem()) {
            return;
        }
        this.callParent(arguments);
    },
    onItemRemove: function(item, index, destroying) {
        this.callParent(arguments);
        var innerIndex = this.getInnerItems().indexOf(item),
            indicator = this.getIndicator(),
            carouselItems = this.carouselItems,
            i, ln, carouselItem;
        if (item.isInnerItem() && indicator) {
            indicator.remove();
        }
        if (innerIndex <= this.getActiveIndex()) {
            this.refreshActiveIndex();
        }
        if (this.isIndexDirty(innerIndex)) {
            for (i = 0 , ln = carouselItems.length; i < ln; i++) {
                carouselItem = carouselItems[i];
                if (carouselItem.getComponent() === item) {
                    carouselItem.setComponent(null);
                    break;
                }
            }
            this.refreshActiveItem();
        }
    },
    doItemLayoutRemove: function(item) {
        if (item.isInnerItem()) {
            return;
        }
        this.callParent(arguments);
    },
    onInnerItemMove: function(item, toIndex, fromIndex) {
        if ((this.isIndexDirty(toIndex) || this.isIndexDirty(fromIndex))) {
            this.refreshActiveItem();
        }
    },
    doItemLayoutMove: function(item) {
        if (item.isInnerItem()) {
            return;
        }
        this.callParent(arguments);
    },
    isIndexDirty: function(index) {
        var activeIndex = this.getActiveIndex(),
            bufferSize = this.getBufferSize();
        return (index >= activeIndex - bufferSize && index <= activeIndex + bufferSize);
    },
    getTranslatable: function() {
        var me = this,
            translatable = me.translatable;
        if (!translatable) {
            me.translatable = translatable = new Ext.util.TranslatableGroup();
            translatable.setItems(me.orderedCarouselItems);
            translatable.on('animationend', 'onAnimationEnd', me);
        }
        return translatable;
    },
    onDragStart: function(e) {
        var direction = this.getDirection(),
            absDeltaX = e.absDeltaX,
            absDeltaY = e.absDeltaY;
        this.isDragging = true;
        if ((direction === 'horizontal' && absDeltaX > absDeltaY) || (direction === 'vertical' && absDeltaY > absDeltaX)) {
            e.stopPropagation();
        } else {
            this.isDragging = false;
            return;
        }
        this.getTranslatable().stopAnimation();
        this.dragStartOffset = this.offset;
        this.dragDirection = 0;
    },
    onDrag: function(e) {
        if (!this.isDragging) {
            return;
        }
        var startOffset = this.dragStartOffset,
            direction = this.getDirection(),
            delta = direction === 'horizontal' ? e.deltaX : e.deltaY,
            lastOffset = this.offset,
            flickStartTime = this.flickStartTime,
            dragDirection = this.dragDirection,
            now = Ext.Date.now(),
            currentActiveIndex = this.getActiveIndex(),
            maxIndex = this.getMaxItemIndex(),
            lastDragDirection = dragDirection,
            offset;
        if ((currentActiveIndex === 0 && delta > 0) || (currentActiveIndex === maxIndex && delta < 0)) {
            delta *= 0.5;
        }
        offset = startOffset + delta;
        if (offset > lastOffset) {
            dragDirection = 1;
        } else if (offset < lastOffset) {
            dragDirection = -1;
        }
        if (dragDirection !== lastDragDirection || (now - flickStartTime) > 300) {
            this.flickStartOffset = lastOffset;
            this.flickStartTime = now;
        }
        this.dragDirection = dragDirection;
        this.setOffset(offset);
    },
    onDragEnd: function(e) {
        if (!this.isDragging) {
            return;
        }
        this.onDrag(e);
        this.isDragging = false;
        var now = Ext.Date.now(),
            itemLength = this.itemLength,
            threshold = itemLength / 2,
            offset = this.offset,
            activeIndex = this.getActiveIndex(),
            maxIndex = this.getMaxItemIndex(),
            animationDirection = 0,
            flickDistance = offset - this.flickStartOffset,
            flickDuration = now - this.flickStartTime,
            indicator = this.getIndicator(),
            velocity;
        if (flickDuration > 0 && Math.abs(flickDistance) >= 10) {
            velocity = flickDistance / flickDuration;
            if (Math.abs(velocity) >= 1) {
                if (velocity < 0 && activeIndex < maxIndex) {
                    animationDirection = -1;
                } else if (velocity > 0 && activeIndex > 0) {
                    animationDirection = 1;
                }
            }
        }
        if (animationDirection === 0) {
            if (activeIndex < maxIndex && offset < -threshold) {
                animationDirection = -1;
            } else if (activeIndex > 0 && offset > threshold) {
                animationDirection = 1;
            }
        }
        if (indicator) {
            indicator.setActiveIndex(activeIndex - animationDirection);
        }
        this.animationDirection = animationDirection;
        this.setOffsetAnimated(animationDirection * itemLength);
    },
    onRender: function() {
        this.callParent();
        this.refresh();
    },
    applyAnimation: function(animation) {
        animation.easing = Ext.factory(animation.easing, Ext.fx.easing.EaseOut);
        return animation;
    },
    updateDirection: function(direction) {
        var indicator = this.getIndicator(),
            vertical = (direction === 'vertical');
        this.currentAxis = vertical ? 'y' : 'x';
        this.setTouchAction(vertical ? {
            panY: false
        } : {
            panX: false
        });
        if (indicator) {
            indicator.setDirection(direction);
        }
    },
    
    setOffset: function(offset) {
        this.offset = offset;
        if (Ext.isNumber(this.itemOffset)) {
            this.getTranslatable().translateAxis(this.currentAxis, offset + this.itemOffset);
        }
        return this;
    },
    
    setOffsetAnimated: function(offset) {
        var indicator = this.getIndicator();
        if (indicator) {
            indicator.setActiveIndex(this.getActiveIndex() - this.animationDirection);
        }
        this.offset = offset;
        this.getTranslatable().translateAxis(this.currentAxis, offset + this.itemOffset, this.getAnimation());
        return this;
    },
    onAnimationEnd: function(translatable) {
        if (this.destroyed) {
            return;
        }
        var currentActiveIndex = this.getActiveIndex(),
            animationDirection = this.animationDirection,
            axis = this.currentAxis,
            currentOffset = translatable[axis],
            itemLength = this.itemLength,
            offset;
        if (animationDirection === -1) {
            offset = itemLength + currentOffset;
        } else if (animationDirection === 1) {
            offset = currentOffset - itemLength;
        } else {
            offset = currentOffset;
        }
        offset -= this.itemOffset;
        this.offset = offset;
        this.setActiveItem(currentActiveIndex - animationDirection);
    },
    refresh: function() {
        this.refreshSizing();
        this.refreshActiveItem();
    },
    refreshSizing: function() {
        var element = this.element,
            itemLength = this.getItemLength(),
            translatableItemLength = {
                x: 0,
                y: 0
            },
            itemOffset, containerSize;
        if (this.getDirection() === 'horizontal') {
            containerSize = element.getWidth();
        } else {
            containerSize = element.getHeight();
        }
        this.hiddenTranslation = -containerSize;
        if (itemLength === null) {
            itemLength = containerSize;
            itemOffset = 0;
        } else {
            itemOffset = (containerSize - itemLength) / 2;
        }
        this.itemLength = itemLength;
        this.itemOffset = itemOffset;
        translatableItemLength[this.currentAxis] = itemLength;
        this.getTranslatable().setItemLength(translatableItemLength);
    },
    refreshOffset: function() {
        this.setOffset(this.offset);
    },
    refreshActiveItem: function() {
        this.updateActiveItem(this.getActiveItem());
    },
    
    getActiveIndex: function() {
        return this.activeIndex;
    },
    refreshActiveIndex: function() {
        this.activeIndex = this.getInnerItemIndex(this.getActiveItem());
    },
    refreshCarouselItems: function() {
        var items = this.carouselItems,
            i, ln, item;
        for (i = 0 , ln = items.length; i < ln; i++) {
            item = items[i];
            item.getTranslatable().refresh();
        }
        this.refreshInactiveCarouselItems();
    },
    refreshInactiveCarouselItems: function() {
        var items = this.inactiveCarouselItems,
            hiddenTranslation = this.hiddenTranslation,
            axis = this.currentAxis,
            i, ln, item;
        for (i = 0 , ln = items.length; i < ln; i++) {
            item = items[i];
            item.translateAxis(axis, hiddenTranslation);
        }
    },
    
    getMaxItemIndex: function() {
        return this.innerItems.length - 1;
    },
    
    getInnerItemIndex: function(item) {
        return this.innerItems.indexOf(item);
    },
    
    getInnerItemAt: function(index) {
        return this.innerItems[index];
    },
    
    applyActiveItem: function(activeItem, oldActiveItem) {
        var activeIndex;
        activeItem = this.callParent([
            activeItem,
            oldActiveItem
        ]);
        if (activeItem) {
            activeIndex = this.getInnerItemIndex(activeItem);
            if (activeIndex !== -1) {
                this.activeIndex = activeIndex;
                return activeItem;
            }
        }
    },
    updateActiveItem: function(activeItem, oldActiveItem) {
        var me = this,
            activeIndex = me.getActiveIndex(),
            maxIndex = me.getMaxItemIndex(),
            indicator = me.getIndicator(),
            bufferSize = me.getBufferSize(),
            carouselItems = me.carouselItems.slice(),
            orderedCarouselItems = this.orderedCarouselItems,
            visibleIndexes = {},
            visibleItems = {},
            visibleItem, component, id, i, index, ln, carouselItem;
        if (carouselItems.length === 0) {
            return;
        }
        me.callParent([
            activeItem,
            oldActiveItem
        ]);
        orderedCarouselItems.length = 0;
        if (activeItem) {
            id = activeItem.getId();
            visibleItems[id] = activeItem;
            visibleIndexes[id] = bufferSize;
            if (activeIndex > 0) {
                for (i = 1; i <= bufferSize; i++) {
                    index = activeIndex - i;
                    if (index >= 0) {
                        visibleItem = me.getInnerItemAt(index);
                        id = visibleItem.getId();
                        visibleItems[id] = visibleItem;
                        visibleIndexes[id] = bufferSize - i;
                    } else {
                        break;
                    }
                }
            }
            if (activeIndex < maxIndex) {
                for (i = 1; i <= bufferSize; i++) {
                    index = activeIndex + i;
                    if (index <= maxIndex) {
                        visibleItem = me.getInnerItemAt(index);
                        id = visibleItem.getId();
                        visibleItems[id] = visibleItem;
                        visibleIndexes[id] = bufferSize + i;
                    } else {
                        break;
                    }
                }
            }
            for (i = 0 , ln = carouselItems.length; i < ln; i++) {
                carouselItem = carouselItems[i];
                component = carouselItem.getComponent();
                if (component) {
                    id = component.getId();
                    if (visibleIndexes.hasOwnProperty(id)) {
                        carouselItems.splice(i, 1);
                        i--;
                        ln--;
                        delete visibleItems[id];
                        orderedCarouselItems[visibleIndexes[id]] = carouselItem;
                    }
                }
            }
            for (id in visibleItems) {
                if (visibleItems.hasOwnProperty(id)) {
                    visibleItem = visibleItems[id];
                    carouselItem = carouselItems.pop();
                    carouselItem.setComponent(visibleItem);
                    orderedCarouselItems[visibleIndexes[id]] = carouselItem;
                }
            }
        }
        me.inactiveCarouselItems.length = 0;
        me.inactiveCarouselItems = carouselItems;
        me.refreshOffset();
        me.refreshInactiveCarouselItems();
        if (indicator && !indicator.isDestroying && activeIndex !== -1) {
            indicator.sync(me.getInnerItems().length, activeIndex);
        }
    },
    
    next: function() {
        this.setOffset(0);
        if (this.activeIndex === this.getMaxItemIndex()) {
            return this;
        }
        this.animationDirection = -1;
        this.setOffsetAnimated(-this.itemLength);
        return this;
    },
    
    previous: function() {
        this.setOffset(0);
        if (this.activeIndex === 0) {
            return this;
        }
        this.animationDirection = 1;
        this.setOffsetAnimated(this.itemLength);
        return this;
    },
    
    applyIndicator: function(indicator, currentIndicator) {
        return Ext.factory(indicator, Ext.Indicator, currentIndicator);
    },
    
    updateIndicator: function(indicator) {
        var me = this,
            bottom, right;
        if (indicator) {
            if (me.getDirection() === 'horizontal') {
                bottom = 0;
                right = null;
            } else {
                bottom = null;
                right = 0;
            }
            indicator.
            setRight(right).setBottom(bottom).setUi(me.getUi()).on({
                indicatortap: 'onIndicatorTap',
                next: 'next',
                previous: 'previous',
                scope: me
            });
            me.insertFirst(indicator);
        }
    },
    onIndicatorTap: function(indicator, index) {
        this.setActiveItem(index);
    },
    doDestroy: function() {
        var me = this,
            carouselItems = me.carouselItems.slice();
        me.carouselItems.length = 0;
        Ext.destroy(carouselItems, me.getIndicator(), me.translatable);
        me.callParent();
    }
});


Ext.define('Ext.carousel.Infinite', {
    extend: Ext.carousel.Carousel,
    config: {
        indicator: null,
        maxItemIndex: Infinity,
        innerItemConfig: {}
    },
    applyIndicator: function(indicator) {
        
        if (indicator) {
            Ext.Logger.error("'indicator' in Infinite Carousel implementation is not currently supported", this);
        }
        
        return;
    },
    updateBufferSize: function(size) {
        this.callParent(arguments);
        var total = size * 2 + 1,
            ln = this.innerItems.length,
            innerItemConfig = this.getInnerItemConfig(),
            i;
        this.isItemsInitializing = true;
        for (i = ln; i < total; i++) {
            this.doAdd(this.factoryItem(innerItemConfig));
        }
        this.isItemsInitializing = false;
        this.rebuildInnerIndexes();
        this.refreshActiveItem();
    },
    updateMaxItemIndex: function(maxIndex, oldMaxIndex) {
        if (oldMaxIndex !== undefined) {
            var activeIndex = this.getActiveIndex();
            if (activeIndex > maxIndex) {
                this.setActiveItem(maxIndex);
            } else {
                this.rebuildInnerIndexes(activeIndex);
                this.refreshActiveItem();
            }
        }
    },
    rebuildInnerIndexes: function(activeIndex) {
        var indexToItem = this.innerIndexToItem,
            idToIndex = this.innerIdToIndex,
            items = this.innerItems.slice(),
            ln = items.length,
            bufferSize = this.getBufferSize(),
            maxIndex = this.getMaxItemIndex(),
            changedIndexes = [],
            i, oldIndex, index, id, item;
        if (activeIndex === undefined) {
            this.innerIndexToItem = indexToItem = {};
            this.innerIdToIndex = idToIndex = {};
            for (i = 0; i < ln; i++) {
                item = items[i];
                id = item.getId();
                idToIndex[id] = i;
                indexToItem[i] = item;
                this.fireEvent('itemindexchange', this, item, i, -1);
            }
        } else {
            for (i = activeIndex - bufferSize; i <= activeIndex + bufferSize; i++) {
                if (i >= 0 && i <= maxIndex) {
                    if (indexToItem.hasOwnProperty(i)) {
                        Ext.Array.remove(items, indexToItem[i]);
                        
                        continue;
                    }
                    changedIndexes.push(i);
                }
            }
            for (i = 0 , ln = changedIndexes.length; i < ln; i++) {
                item = items[i];
                id = item.getId();
                index = changedIndexes[i];
                oldIndex = idToIndex[id];
                delete indexToItem[oldIndex];
                idToIndex[id] = index;
                indexToItem[index] = item;
                this.fireEvent('itemindexchange', this, item, index, oldIndex);
            }
        }
    },
    reset: function() {
        this.rebuildInnerIndexes();
        this.setActiveItem(0);
    },
    refreshItems: function() {
        var items = this.innerItems,
            idToIndex = this.innerIdToIndex,
            index, item, i, ln;
        for (i = 0 , ln = items.length; i < ln; i++) {
            item = items[i];
            index = idToIndex[item.getId()];
            this.fireEvent('itemindexchange', this, item, index, -1);
        }
    },
    getInnerItemIndex: function(item) {
        var index = this.innerIdToIndex[item.getId()];
        return (typeof index == 'number') ? index : -1;
    },
    getInnerItemAt: function(index) {
        return this.innerIndexToItem[index];
    },
    applyActiveItem: function(activeItem) {
        this.getItems();
        this.getBufferSize();
        var maxIndex = this.getMaxItemIndex(),
            currentActiveIndex = this.getActiveIndex();
        if (typeof activeItem == 'number') {
            activeItem = Math.max(0, Math.min(activeItem, maxIndex));
            if (activeItem === currentActiveIndex) {
                return;
            }
            this.activeIndex = activeItem;
            this.rebuildInnerIndexes(activeItem);
            activeItem = this.getInnerItemAt(activeItem);
        }
        if (activeItem) {
            return this.callParent([
                activeItem
            ]);
        }
    }
});


Ext.define('Ext.dataview.Location', {
    
    isDataViewLocation: true,
    
    child: null,
    
    event: null,
    
    item: null,
    
    record: null,
    
    recordIndex: -1,
    
    sourceElement: null,
    
    view: null,
    
    viewIndex: -1,
    
    constructor: function(view, source) {
        this.view = view;
        if (source != null) {
            this.attach(source);
        }
    },
    attach: function(source) {
        var me = this,
            view = me.view,
            store = view.store,
            record, child, sourceElement;
        
        if (me.source) {
            Ext.raise('DataView Locations cannot be modified');
        }
        
        if (source.isEvent) {
            me.event = source;
            sourceElement = source.target;
        }
        if (source.isElement || source.nodeType === 1) {
            sourceElement = source;
        }
        me.source = source;
        if (source.isWidget) {
            sourceElement = source.getFocusEl();
            source = source.element;
        }
        if (typeof source === 'number') {
            child = view.itemFromRecord(source);
            me.recordIndex = source;
            
            record = store && store.getAt(source);
        } else {
            if (source.isModel) {
                record = source;
            } else {
                record = view.mapToRecord(source);
            }
            child = view.mapToItem(source);
            
            me.recordIndex = store ? store.indexOf(record) : -1;
        }
        if (child && !sourceElement) {
            sourceElement = child.isWidget ? child.getFocusEl() : child;
        }
        me.child = child;
        me.record = record;
        
        if (record && child) {
            me.item = child;
        }
        if (child) {
            me.viewIndex = view.mapToViewIndex(child);
        }
        me.sourceElement = Ext.getDom(sourceElement);
    },
    
    clone: function() {
        var me = this,
            ret = new this.self(me.view);
        ret.event = me.event;
        ret.sourceElement = me.sourceElement;
        ret.item = me.item;
        ret.record = me.record;
        ret.recordIndex = me.recordIndex;
        ret.viewIndex = me.viewIndex;
        return ret;
    },
    equals: function(other) {
        
        
        
        return other && other.view === this.view && other.isDataViewLocation && other.sourceElement === this.sourceElement;
    },
    
    getFocusEl: function(as) {
        var item = this.get(),
            ret = null;
        if (item && item.isWidget) {
            item = item.element;
        }
        if (item) {
            ret = (as === 'dom' || as === true) ? Ext.getDom(item) : Ext.get(item);
        }
        return ret;
    },
    
    get: function() {
        return this.child;
    },
    isFirstDataItem: function() {
        return this.recordIndex === 0;
    },
    isFirstViewItem: function() {
        var view = this.view;
        if (view.infinite) {
            return view.previous(this.child == null);
        }
        return this.viewIndex === 0;
    },
    isLastDataItem: function() {
        return this.recordIndex === this.view.store.getCount() - 1;
    },
    isLastViewItem: function() {
        var view = this.view;
        if (view.infinite) {
            return view.next(this.child == null);
        }
        return this.viewIndex === view.innerItems.length - 1;
    },
    
    refresh: function() {
        var me = this,
            view = me.view,
            item = me.child,
            
            
            
            newSource = view.mapToItem(me.record) || (view.items.contains(item) ? item : view.mapToItem(Math.min(me.viewIndex, view.dataItems.length - 1)));
        return new this.self(view, newSource);
    },
    isFirst: function() {
        return this.view.isFirstItem(this.child);
    },
    isLast: function() {
        return this.view.isLastItem(this.child);
    },
    
    next: function(options) {
        var me = this,
            candidate = me.nextItem(options),
            item = candidate && candidate.get();
        while (candidate && (!item || !item.el.isFocusable())) {
            
            if (candidate.equals(me)) {
                return me;
            }
            candidate = candidate.nextItem(options);
            item = candidate && candidate.get();
        }
        return candidate || me;
    },
    
    previous: function(options) {
        var me = this,
            candidate = me.previousItem(options),
            item = candidate && candidate.get();
        while (candidate && (!item || !item.el.isFocusable())) {
            
            if (candidate.equals(me)) {
                return me;
            }
            candidate = candidate.previousItem(options);
            item = candidate && candidate.get();
        }
        return candidate || me;
    },
    
    nextItem: function(options) {
        var view = this.view,
            item = this.child,
            wrap = (typeof options === 'boolean') ? options : !!(options && options.wrap),
            nextItem;
        if (view.isLastItem(item)) {
            if (wrap) {
                nextItem = view.getFirstItem();
            } else {
                return null;
            }
        } else {
            nextItem = view.nextItem(item);
        }
        return new this.self(view, nextItem);
    },
    
    previousItem: function(options) {
        var view = this.view,
            item = this.child,
            wrap = (typeof options === 'boolean') ? options : !!(options && options.wrap),
            prevItem;
        if (view.isFirstItem(item)) {
            if (wrap) {
                prevItem = view.getLastItem();
            } else {
                return null;
            }
        } else {
            prevItem = view.previousItem(item);
        }
        return new this.self(view, prevItem);
    }
});


Ext.define('Ext.dataview.NavigationModel', {
    extend: Ext.Evented,
    alias: 'navmodel.dataview',
    mixins: [
        Ext.mixin.Factoryable,
        Ext.mixin.Bufferable
    ],
    factoryConfig: {
        type: 'navmodel',
        defaultType: 'dataview',
        instanceProp: 'isNavigationModel'
    },
    isNavigationModel: true,
    config: {
        view: null,
        disabled: false
    },
    bufferableMethods: {
        
        
        handleChildTrigger: 1
    },
    
    locationClass: 'Ext.dataview.Location',
    
    
    
    setLocation: function(location, options) {
        var me = this,
            view = me.getView(),
            oldLocation = me.location,
            animation = options && options.animation,
            scroller, child, record, itemContainer, childFloatStyle;
        if (location == null) {
            return me.clearLocation();
        }
        if (!location.isDataViewLocation) {
            location = this.createLocation(location);
        }
        
        
        
        if (!location.equals(oldLocation)) {
            record = location.record;
            child = location.child;
            
            if (record && !child) {
                
                return view.ensureVisible(record, {
                    animation: animation
                }).then(function() {
                    if (!me.destroyed) {
                        me.setLocation({
                            record: record,
                            column: location.column
                        }, options);
                    }
                });
            }
            
            
            if (child && me.floatingItems == null) {
                child = child.isComponent ? child.el : Ext.fly(child);
                itemContainer = child.up();
                childFloatStyle = child.getStyleValue('float');
                me.floatingItems = (view.getInline && view.getInline()) || child.isStyle('display', 'inline-block') || childFloatStyle === 'left' || childFloatStyle === 'right' || (itemContainer.isStyle('display', 'flex') && itemContainer.isStyle('flex-direction', 'row'));
            }
            
            
            scroller = view.getScrollable();
            if (scroller) {
                scroller.ensureVisible(location.sourceElement, {
                    animation: options && options.animation
                });
            }
            
            
            me.handleLocationChange(location, options);
            
            if (!me.destroyed) {
                me.doFocus();
            }
        }
    },
    clearLocation: function() {
        var me = this,
            targetElement;
        if (me.location) {
            me.previousLocation = me.location;
            targetElement = me.location.getFocusEl();
            if (targetElement && !targetElement.destroyed) {
                Ext.fly(targetElement).removeCls(me.focusedCls);
            }
            me.location = null;
        }
    },
    getLocation: function() {
        return this.location;
    },
    getPreviousLocation: function() {
        var result = this.previousLocation;
        if (result && (!result.sourceElement || !result.sourceElement.destroyed)) {
            result.refresh();
        }
        return result;
    },
    disable: function() {
        this.setDisabled(true);
    },
    enable: function() {
        this.setDisabled(false);
    },
    privates: {
        createLocation: function(source, options) {
            return Ext.create(this.locationClass, this.getView(), source, options);
        },
        getKeyNavCfg: function(view) {
            var me = this;
            return {
                target: view.getFocusEl(),
                processEvent: me.processViewEvent,
                processEventScope: me,
                eventName: 'keydown',
                defaultEventAction: 'stopEvent',
                esc: me.onKeyEsc,
                f2: me.onKeyF2,
                up: me.onKeyUp,
                down: me.onKeyDown,
                right: me.onKeyRight,
                left: me.onKeyLeft,
                pageDown: me.onKeyPageDown,
                pageUp: me.onKeyPageUp,
                home: me.onKeyHome,
                end: me.onKeyEnd,
                space: me.onKeySpace,
                enter: me.onKeyEnter,
                tab: me.onKeyTab,
                A: {
                    ctrl: true,
                    
                    
                    handler: me.onSelectAllKeyPress
                },
                scope: me
            };
        },
        updateView: function(view) {
            var me = this,
                keyNavCfg = me.getKeyNavCfg(view);
            me.focusedCls = view.focusedCls;
            
            
            
            if (keyNavCfg) {
                me.keyNav = new Ext.util.KeyNav(keyNavCfg);
            }
            me.viewListeners = view.on(me.getViewListeners(view));
        },
        getViewListeners: function(view) {
            var result = {
                    scope: this
                };
            result[view.getTriggerEvent()] = 'onChildTrigger';
            return result;
        },
        
        processViewEvent: function(e) {
            var location = this.getLocation(),
                component;
            if (location && e.keyCode) {
                component = Ext.fly(e.target).component;
                
                
                
                e.navigationMode = component && component.parent === this.getView();
                e.setCurrentTarget(location.sourceElement);
                if (!Ext.fly(e.target).isInputField()) {
                    return e;
                }
            }
        },
        
        doFocus: function(location) {
            location = location || this.location;
            
            
            
            
            if (location && location.getFocusEl()) {
                location.getFocusEl().focus();
            }
        },
        
        
        onFocusMove: function(e) {
            var location = this.createLocation(e);
            
            
            if (!location.equals(this.location)) {
                this.handleLocationChange(location, {
                    event: e,
                    navigate: false
                });
            }
        },
        
        handleLocationChange: function(location, options) {
            var me = this,
                oldLocation = me.location,
                view = me.getView(),
                target, item;
            
            
            
            
            
            
            
            
            me.previousLocation = oldLocation;
            if (oldLocation) {
                me.lastLocation = oldLocation;
                
                
                
                
                
                target = oldLocation.getFocusEl();
                if (target && !target.destroyed) {
                    Ext.fly(target).removeCls(me.focusedCls);
                }
            }
            me.location = location;
            
            target = location && location.getFocusEl('dom');
            if (target) {
                item = location.get();
                if (item) {
                    if (item.isWidget) {
                        item = item.el;
                    } else {
                        item = Ext.get(item);
                    }
                    if (item && target === item.dom) {
                        item.addCls(me.focusedCls);
                    }
                    if (options && (options.event || options.select) && options.navigate !== false) {
                        me.onNavigate(options.event);
                    }
                }
            }
            
            if (!view.destroyed) {
                view.fireEvent('navigate', view, location, oldLocation);
            }
        },
        onKeyUp: function(e) {
            
            e.preventDefault();
            if (this.location) {
                if (this.floatingItems) {
                    this.moveUp(e);
                } else {
                    this.movePrevious({
                        event: e
                    });
                }
            } else {
                this.setLocation(0);
            }
        },
        onKeyDown: function(e) {
            
            e.preventDefault();
            if (this.location) {
                if (this.floatingItems) {
                    this.moveDown(e);
                } else {
                    this.moveNext({
                        event: e
                    });
                }
            } else {
                this.setLocation(0);
            }
        },
        onKeyLeft: function(e) {
            
            e.preventDefault();
            this.movePrevious({
                event: e
            });
        },
        onKeyRight: function(e) {
            
            e.preventDefault();
            this.moveNext({
                event: e
            });
        },
        onKeyF2: function(e) {
            return false;
        },
        onKeyEsc: function(e) {
            return false;
        },
        onKeyTab: function(e) {
            return !this.location.actionable;
        },
        onKeyPageDown: function(e) {
            
            e.preventDefault();
            if (!this.location.actionable && !this.floatingItems) {
                var me = this,
                    view = me.getView(),
                    y = (view.infinite ? view.getItemTop(me.location.child) : me.location.child.el.dom.offsetTop) + view.el.getClientRegion().height,
                    candidate = me.createLocation(view.getItemFromPoint(0, y));
                
                
                if (!(candidate.child && candidate.child.el.isFocusable())) {
                    candidate = candidate.previous();
                }
                
                me.setLocation(candidate, {
                    event: e
                });
            }
        },
        onKeyPageUp: function(e) {
            
            e.preventDefault();
            if (!this.location.actionable && !this.floatingItems) {
                var me = this,
                    view = me.getView(),
                    y = (view.infinite ? view.getItemTop(me.location.child) : me.location.child.el.dom.offsetTop) - view.el.getClientRegion().height,
                    candidate = me.createLocation(view.getItemFromPoint(0, y));
                
                
                if (!(candidate.child && candidate.child.el.isFocusable())) {
                    candidate = candidate.next();
                }
                
                me.setLocation(candidate, {
                    event: e
                });
            }
        },
        onKeyHome: function(e) {
            this.setLocation(0, {
                event: e
            });
        },
        onKeyEnd: function(e) {
            this.setLocation(this.getView().getStore().last(), {
                event: e
            });
        },
        onKeySpace: function(e) {
            this.onNavigate(e);
        },
        
        onKeyEnter: function(e) {
            
            
            e.stopEvent();
            this.getView()._onChildTap(e);
        },
        onSelectAllKeyPress: function(e) {
            var view = this.getView(),
                selModel = view.getSelectable();
            
            
            if (selModel && view.getStore().getCount()) {
                selModel[selModel.allSelected ? 'deselectAll' : 'selectAll']();
                e.preventDefault();
                return false;
            }
        },
        
        
        moveUp: function(e) {
            var view = this.getView(),
                location = this.location,
                el = this.location.sourceElement,
                topCentre = Ext.fly(el).getAnchorXY('t'),
                item;
            
            
            topCentre[1] -= (Ext.fly(el).getMargin('tb') + 10);
            item = this.getView().getItemFromPagePoint(topCentre[0], topCentre[1], true);
            
            
            if (!item || !item.isFocusable()) {
                item = location.isFirst() ? view.getLastItem() : view.getFirstItem();
            }
            if (item) {
                this.setLocation(item, {
                    event: e
                });
            }
        },
        
        
        moveDown: function(e) {
            var view = this.getView(),
                location = this.location,
                el = location.sourceElement,
                bottomCentre = Ext.fly(el).getAnchorXY('b'),
                item;
            
            
            bottomCentre[1] += Ext.fly(el).getMargin('tb') + 10;
            item = view.getItemFromPagePoint(bottomCentre[0], bottomCentre[1]);
            
            if (!item || !item.isFocusable()) {
                item = location.isLast() ? view.getFirstItem() : view.getLastItem();
            }
            if (item) {
                this.setLocation(item, {
                    event: e
                });
            }
        },
        moveNext: function(options) {
            var location = this.getLocation();
            if (location) {
                location = location.next(options);
                if (location) {
                    this.setLocation(location, options);
                }
            }
        },
        movePrevious: function(options) {
            var location = this.getLocation();
            if (location) {
                location = location.previous(options);
                if (location) {
                    this.setLocation(location, options);
                }
            }
        },
        onChildTrigger: function(view, location) {
            var e = location.event,
                isFocusingEvent = (e.pointerType === 'touch') ? e.type === 'tap' : e.type === 'touchstart';
            
            
            
            
            
            
            
            if (isFocusingEvent) {
                this.handleChildTrigger(view, location);
            } else {
                this.doHandleChildTrigger(view, location);
            }
        },
        doHandleChildTrigger: function(view, location) {
            var myLocation = this.location,
                event = location.event,
                compareMethod = location.isGridLocation ? 'equalCell' : 'equals';
            
            
            
            
            
            
            
            if (myLocation && myLocation[compareMethod](location)) {
                this.onNavigate(event);
            } else 
            
            {
                this.setLocation(location, {
                    event: event
                });
            }
        },
        onNavigate: function(event) {
            var me = this,
                location = me.location;
            
            if (!event) {
                event = new Ext.event.Event({
                    target: location.sourceElement
                });
            }
            Ext.apply(event, {
                navigationModel: me,
                from: me.previousLocation,
                to: location
            });
            me.getView().onNavigate(event);
        },
        updateDisabled: function(disabled) {
            
            
            
            if (this.keyNav) {
                if (disabled) {
                    this.keyNav.disable();
                } else {
                    this.keyNav.enable();
                }
            }
        }
    }
});


Ext.define('Ext.dataview.selection.Selection', {
    mixins: [
        Ext.mixin.Factoryable
    ],
    factoryConfig: {
        type: 'selection',
        defaultType: 'records',
        instanceProp: 'isSelection'
    },
    
    isSelection: true,
    config: {
        
        selectionModel: null
    },
    constructor: function(config) {
        
        if (config.isDataView) {
            config = {
                selectionModel: config.getSelectionModel()
            };
        }
        this.initConfig(config);
        
        if (!this.getSelectionModel()) {
            Ext.raise('Selection must be configured with a SelectionModel');
        }
    },
    
    destroy: function() {
        this.clear();
        this.callParent();
    },
    
    
    
    
    
    
    privates: {
        applySelectionModel: function(selectionModel) {
            var view;
            
            
            this.view = view = selectionModel.getView();
            
            if (view.isGrid) {
                this.view = view.ownerGrid;
            }
            return selectionModel;
        }
    }
});


Ext.define('Ext.dataview.selection.Rows', {
    extend: Ext.dataview.selection.Selection,
    alias: 'selection.rows',
    
    isRows: true,
    config: {
        
        selected: true
    },
    
    
    clone: function() {
        return new this.self({
            selectionModel: this.getSelectionModel(),
            selected: new Ext.util.Spans().unstash(this.getSelected().stash())
        });
    },
    
    
    add: function(range, keepExisting, suppressEvent) {
        var me = this,
            view = me.view,
            rowIdx;
        
        
        
        if (range.length === 1) {
            range = range[0];
        }
        
        if (range.isEntity) {
            range = view.mapToRecordIndex(range);
        }
        
        if (typeof range === 'number') {
            range = [
                range,
                range + 1
            ];
        }
        
        if (range.length !== 2 || (typeof range[0] !== 'number') || (typeof range[1] !== 'number')) {
            Ext.raise('add must be called with a [start, end] row index *EXCLUSIVE* range');
        }
        
        if (range[0] > range[1]) {
            var tmp = range[1];
            range[1] = range[0];
            range[0] = tmp;
        }
        me.lastSelected = range[1];
        if (!keepExisting) {
            me.clear();
        }
        me.getSelected().add(range);
        for (rowIdx = range[0]; rowIdx < range[1]; rowIdx++) {
            view.onItemSelect(rowIdx);
        }
        if (!suppressEvent) {
            me.getSelectionModel().fireSelectionChange();
        }
    },
    remove: function(range, suppressEvent) {
        var me = this,
            selModel = me.getSelectionModel(),
            view = me.view,
            rowIdx;
        
        
        if (!selModel.getDeselectable() && me.getCount() === 1) {
            return;
        }
        
        
        
        if (range.length === 1) {
            range = range[0];
        }
        
        if (range.isEntity) {
            range = view.mapToRecordIndex(range);
        }
        
        if (typeof range === 'number') {
            range = [
                range,
                range + 1
            ];
        }
        
        if (!range.length === 2 && (typeof range[0] === 'number') && (typeof range[1] === 'number')) {
            Ext.raise('remove must be called with a [start, end] record *EXCLUSIVE* range');
        }
        if (range[0] > range[1]) {
            Ext.raise('A range MUST have the start index first, and the exclusive end index second');
        }
        
        me.getSelected().remove(range);
        for (rowIdx = range[0]; rowIdx < range[1]; rowIdx++) {
            view.onItemDeselect(rowIdx);
        }
        if (!suppressEvent) {
            selModel.fireSelectionChange();
        }
    },
    
    isSelected: function(record) {
        var me = this,
            ranges = me.getSelected().spans,
            len = ranges.length,
            recIndex, range, i;
        recIndex = record.isEntity ? me.view.getStore().indexOf(record) : record;
        for (i = 0; i < len; i++) {
            range = ranges[i];
            if (recIndex >= range[0] && recIndex < range[1]) {
                return true;
            }
        }
        return false;
    },
    
    getCount: function() {
        return this.getSelected().getCount();
    },
    selectAll: function() {
        var view = this.view,
            items = view.dataItems,
            len = items.length,
            i;
        
        
        
        for (i = 0; i < len; i++) {
            view.onItemSelect(i);
        }
        
        
        this.getSelected().add(0, view.store.getTotalCount() || view.store.getCount());
        this.getSelectionModel().fireSelectionChange();
    },
    
    getFirstRowIndex: function() {
        var ranges = this.getSelected().spans;
        return ranges.length ? this.getSelected().spans[0][0] : 0;
    },
    
    getLastRowIndex: function() {
        var ranges = this.getSelected().spans;
        return ranges.length ? ranges[ranges.length - 1][1] - 1 : 0;
    },
    eachRow: function(fn, scope) {
        var me = this,
            ranges = me.getSelected().spans,
            len = ranges && ranges.length,
            result, range, i, j;
        for (i = 0; i < len; i++) {
            range = ranges[i];
            for (j = range[0]; result !== false && j < range[1]; j++) {
                result = fn.call(this || scope, j);
            }
        }
    },
    eachColumn: function(fn, scope) {
        var columns = this.view.getHeaderContainer().getVisibleColumns(),
            len = columns.length,
            i;
        
        if (this.getCount()) {
            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.getSelected(),
            view = me.view,
            columns = view.ownerGrid.getVisibleColumnManager().getColumns(),
            range = me.dragRange,
            colCount, i, j, location, recCount,
            abort = false;
        if (columns) {
            colCount = columns.length;
            location = new Ext.grid.Location(view);
            
            if (selection) {
                me.eachRow(function(recordIndex) {
                    location.setItem(recordIndex);
                    for (i = 0; i < colCount; i++) {
                        location.setColumn(columns[i]);
                        if (fn.call(scope || me, location, location.columnIndex, location.recordIndex) === false) {
                            abort = true;
                            return false;
                        }
                    }
                });
            }
            
            if (!abort && range != null) {
                me.view.getStore().getRange(range[0], range[1], {
                    forRender: false,
                    callback: function(records) {
                        recCount = records.length;
                        for (i = 0; !abort && i < recCount; i++) {
                            location.setItem(records[i]);
                            for (j = 0; !abort && j < colCount; j++) {
                                location.setColumn(columns[j]);
                                if (fn.call(scope || me, location, location.columnIndex, location.recordIndex) === false) {
                                    abort = true;
                                }
                            }
                        }
                    }
                });
            }
        }
    },
    
    privates: {
        applySelected: function(spans) {
            if (!spans.isSpans) {
                spans = new Ext.util.Spans();
            }
            return spans;
        },
        compareRanges: function(lhs, rhs) {
            return lhs[0] - rhs[0];
        },
        
        clear: function(suppressEvent) {
            var me = this,
                selModel = me.getSelectionModel(),
                view = me.view,
                items = view.dataItems,
                len = items.length,
                i;
            
            
            
            for (i = 0; i < len; i++) {
                view.onItemDeselect(i);
            }
            me.getSelected().clear();
            
            
            if (!selModel.getDeselectable() && me.lastSelected) {
                me.add(me.lastSelected, true, true);
            }
            if (!suppressEvent) {
                selModel.fireSelectionChange();
            }
        },
        addRecordRange: function(start, end) {
            return this.add([
                start,
                end + 1
            ], true);
        },
        removeRecordRange: function(start, end) {
            return this.remove([
                start,
                end + 1
            ]);
        },
        
        isAllSelected: function() {
            return this.getCount() === (this.view.getStore().getTotalCount() || this.view.store.getCount());
        },
        
        setRangeStart: function(start) {
            if (start == null) {
                this.dragRange = null;
            } else {
                this.dragRange = [
                    start,
                    start
                ];
                
                
                this.view.onItemSelect(start);
            }
        },
        
        setRangeEnd: function(end) {
            var me = this,
                dragRange = me.dragRange,
                oldEnd = dragRange[1] || 0,
                start = dragRange[0],
                view = me.view,
                renderInfo = view.renderInfo,
                tmp = dragRange[1] = end,
                rowIdx, limit;
            
            
            if (start > end) {
                end = start;
                start = tmp;
            }
            
            for (rowIdx = Math.max(Math.min(dragRange[0], start, oldEnd, end), renderInfo.indexTop) , limit = Math.min(Math.max(dragRange[0], start, oldEnd, end), renderInfo.indexBottom - 1); rowIdx <= limit; rowIdx++) {
                
                if (rowIdx < start || rowIdx > end) {
                    
                    me.removeRowRange(rowIdx, rowIdx);
                    
                    
                    view.onItemDeselect(rowIdx);
                } else {
                    view.onItemSelect(rowIdx);
                }
            }
            me.lastSelectedIndex = end;
        },
        
        addRange: function(keep) {
            var range = this.dragRange;
            if (range) {
                
                
                
                this.addRecordRange(range[0], range[1]);
                if (!keep) {
                    this.dragRange = null;
                }
            }
        },
        extendRange: function(extensionVector) {
            
            
            
            this.addRecordRange(extensionVector.start, extensionVector.end);
        },
        
        getRange: function() {
            var range = this.dragRange;
            if (range == null) {
                return [
                    0,
                    -1
                ];
            }
            if (range[0] <= range[1]) {
                return range;
            }
            return [
                range[1],
                range[0]
            ];
        },
        
        getRangeSize: function() {
            var range = this.getRange();
            return range[1] - range[0] + 1;
        },
        onSelectionFinish: function() {
            var me = this,
                range = me.getContiguousSelection();
            if (range) {
                me.getSelectionModel().onSelectionFinish(me, new Ext.grid.Location(me.view, {
                    record: range[0],
                    column: 0
                }), new Ext.grid.Location(me.view, {
                    record: range[1],
                    column: me.view.getHeaderContainer().getVisibleColumns().length - 1
                }));
            } else {
                me.getSelectionModel().onSelectionFinish(me);
            }
        },
        getContiguousSelection: function() {
            var selected = this.getSelected(),
                store = this.view.store,
                spans = selected.spans;
            
            
            if (spans === 1 && store.getAt(spans[0][0]) && store.getAt(spans[0][1])) {
                return selected.spans[0];
            }
        }
    }
});


Ext.define('Ext.dataview.selection.Records', {
    extend: Ext.dataview.selection.Rows,
    alias: 'selection.records',
    
    isRecords: true,
    config: {
        
        selected: null
    },
    
    constructor: function(config) {
        this.callParent([
            config
        ]);
        var selected = this.getSelected();
        if (!(selected && selected.isCollection)) {
            Ext.raise('An Ext.dataview.selection.Records object MUST be configured with a "selected" Collection');
        }
    },
    
    
    
    clone: function() {
        return new this.self({
            selectionModel: this.getSelectionModel(),
            selected: this.getSelected()
        });
    },
    
    
    addRowRange: function(start, end, keepExisting, suppressEvent) {
        
        if (typeof start !== 'number' || typeof end !== 'number') {
            Ext.raise('addRange must be called with a [start, end] row index *EXCLUSIVE* range');
        }
        
        
        if (start > end) {
            var tmp = end;
            end = start;
            start = tmp;
        }
        
        this.add(this.getSelectionModel().getStore().getRange(start, end - 1), keepExisting, suppressEvent);
    },
    removeRowRange: function(start, end, suppressEvent) {
        
        if (typeof start !== 'number' || typeof end !== 'number') {
            Ext.raise('addRange must be called with a [start, end] row index *EXCLUSIVE* range');
        }
        
        
        if (start > end) {
            var tmp = end;
            end = start;
            start = tmp;
        }
        
        this.remove(this.getSelectionModel().getStore().getRange(start, end - 1), suppressEvent);
    },
    add: function(records, keepExisting, suppressEvent) {
        records = Ext.Array.from(records);
        
        for (var i = 0,
            ln = records.length; i < ln; i++) {
            if (!records[i].isEntity) {
                Ext.raise('add must be called with records or an array of records');
            }
        }
        
        var me = this,
            selected = me.getSelected(),
            selectionCount = selected.getCount(),
            args = [
                keepExisting ? selectionCount : 0,
                keepExisting ? 0 : selectionCount,
                records
            ];
        
        
        
        selected.suppressEvent = suppressEvent;
        selected.splice.apply(selected, args);
        selected.suppressEvent = false;
    },
    remove: function(records, suppressEvent) {
        records = Ext.Array.from(records);
        
        for (var i = 0,
            ln = records.length; i < ln; i++) {
            if (!records[i].isEntity) {
                Ext.raise('add must be called with records or an array of records');
            }
        }
        
        var selected = this.getSelected();
        
        
        if (!this.getSelectionModel().getDeselectable() && selected.getCount() === 1) {
            Ext.Array.remove(records, selected.first());
        }
        if (records.length) {
            selected.suppressEvent = suppressEvent;
            selected.remove(records);
            selected.suppressEvent = false;
        }
    },
    
    isSelected: function(record) {
        if (!record || !record.isModel) {
            return false;
        }
        return !!this.getSelected().byInternalId.get(record.internalId);
    },
    
    getRecords: function() {
        return this.getSelected().getRange();
    },
    selectAll: function(suppressEvent) {
        var selected = this.getSelected();
        selected.suppressEvent = suppressEvent;
        selected.add(this.getSelectionModel().getStore().getRange());
        selected.suppressEvent = false;
    },
    
    getFirstRowIndex: function() {
        return this.getCount() ? this.view.getStore().indexOf(this.getSelected().first()) : 0;
    },
    
    getLastRowIndex: function() {
        return this.getCount() ? this.view.getStore().indexOf(this.getSelected().last()) : -1;
    },
    eachRow: function(fn, scope) {
        var selectedRecords = this.getSelected();
        if (selectedRecords) {
            selectedRecords.each(fn, scope || this);
        }
    },
    eachColumn: function(fn, scope) {
        var columns = this.view.getHeaderContainer().getVisibleColumns(),
            len = columns.length,
            i;
        
        if (this.getSelected().getCount()) {
            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.getSelected(),
            view = me.view,
            columns = view.getHeaderContainer().getVisibleColumns(),
            colCount, i, j, baseLocation, location,
            abort = false;
        if (columns) {
            colCount = columns.length;
            baseLocation = new Ext.grid.Location(view);
            
            if (selection) {
                selection.each(function(record) {
                    location = baseLocation.clone({
                        record: record
                    });
                    for (i = 0; i < colCount; i++) {
                        location = location.cloneForColumn(columns[i]);
                        if (fn.call(scope || me, location, location.columnIndex, location.recordIndex) === false) {
                            abort = true;
                            return false;
                        }
                    }
                });
            }
        }
    },
    
    beginUpdate: function() {
        this.getSelected().beginUpdate();
    },
    
    endUpdate: function() {
        this.getSelected().endUpdate();
    },
    
    privates: {
        
        clear: function(suppressEvent) {
            var selected = this.getSelected(),
                spliceArgs;
            if (selected) {
                spliceArgs = [
                    0,
                    selected.getCount()
                ];
                
                if (!this.getSelectionModel().getDeselectable()) {
                    spliceArgs[2] = selected.last();
                }
                
                selected.suppressEvent = suppressEvent;
                selected.splice.apply(selected, spliceArgs);
                selected.suppressEvent = false;
            }
        },
        addRecordRange: function(start, end) {
            var tmp = end;
            if (start > end) {
                end = start;
                start = tmp;
            }
            this.getSelected().add(this.view.getStore().getRange(start, end || start));
        },
        removeRecordRange: function(start, end) {
            var tmp = end;
            if (start > end) {
                end = start;
                start = tmp;
            }
            this.getSelected().remove(this.view.getStore().getRange(start, end || start));
        },
        onSelectionFinish: function() {
            var me = this,
                range = me.getContiguousSelection();
            if (range) {
                me.getSelectionModel().onSelectionFinish(me, new Ext.grid.Location(me.view, {
                    record: range[0],
                    column: 0
                }), new Ext.grid.Location(me.view, {
                    record: range[1],
                    column: me.view.getHeaderContainer().getVisibleColumns().length - 1
                }));
            } else {
                me.getSelectionModel().onSelectionFinish(me);
            }
        },
        
        getContiguousSelection: function() {
            var store = this.view.getStore(),
                selection, len, i;
            selection = Ext.Array.sort(this.getSelected().getRange(), function(r1, r2) {
                return store.indexOf(r1) - store.indexOf(r2);
            });
            len = selection.length;
            if (len) {
                if (len === 1 && store.indexOf(selection[0]) === -1) {
                    return false;
                }
                for (i = 1; i < len; i++) {
                    if (store.indexOf(selection[i]) !== store.indexOf(selection[i - 1]) + 1) {
                        return false;
                    }
                }
                return [
                    store.indexOf(selection[0]),
                    store.indexOf(selection[len - 1])
                ];
            }
        },
        
        
        
        applySelected: function(selected) {
            
            if (!selected) {
                Ext.raise("The selection model's own selected Collection must always be passed into a Records Selection");
            }
            
            return selected;
        },
        
        refresh: function() {
            var me = this,
                view = me.view,
                selModel = me.getSelectionModel(),
                storeCollection = view.getStore().getData(),
                filterFn = storeCollection.getFilters().getFilterFn(),
                ignoredFilter = selModel.ignoredFilter,
                selected = me.getSelected(),
                lastSelected = selModel.getLastSelected(),
                newLastSelected, selections,
                toDeselect = [],
                toReselect = [],
                selectionLength, i, rec, matchingSelection;
            
            
            
            if (ignoredFilter) {
                if (ignoredFilter.getDisabled()) {
                    ignoredFilter = null;
                } else {
                    ignoredFilter.setDisabled(true);
                    storeCollection = storeCollection.getSource() || storeCollection;
                }
            }
            
            if (lastSelected) {
                newLastSelected = storeCollection.get(storeCollection.getKey(lastSelected));
                
                
                
                
                if (newLastSelected && ignoredFilter && !filterFn(newLastSelected)) {
                    newLastSelected = null;
                }
            }
            
            if (me.getCount()) {
                selections = selected.getRange();
                selectionLength = selections.length;
                for (i = 0; i < selectionLength; i++) {
                    rec = selections[i];
                    matchingSelection = storeCollection.get(storeCollection.getKey(rec));
                    
                    
                    
                    if (matchingSelection && ignoredFilter && !filterFn(matchingSelection)) {
                        matchingSelection = null;
                    }
                    if (matchingSelection) {
                        if (matchingSelection !== rec) {
                            toDeselect.push(rec);
                            toReselect.push(matchingSelection);
                        }
                    } else {
                        toDeselect.push(rec);
                    }
                }
                
                
                
                if (view.beforeSelectionRefresh) {
                    view.beforeSelectionRefresh(toDeselect, toReselect);
                }
                
                
                
                
                
                selected.suppressEvent = true;
                selected.splice(selected.getCount(), toDeselect, toReselect);
                selected.suppressEvent = false;
            }
            if (ignoredFilter) {
                ignoredFilter.setDisabled(false);
            }
            
            selModel.setLastSelected(newLastSelected || toReselect[toReselect.length - 1] || null);
        }
    }
});


Ext.define('Ext.dataview.selection.Model', {
    extend: Ext.Evented,
    alias: 'selmodel.dataview',
    mixins: [
        Ext.mixin.Factoryable
    ],
    factoryConfig: {
        type: 'selmodel',
        defaultType: 'dataview',
        aliasPrefix: 'selmodel.'
    },
    isSelectionModel: true,
    
    
    config: {
        view: null,
        store: null,
        
        disabled: null,
        
        mode: 'single',
        
        deselectable: true,
        
        lastSelected: null,
        
        selected: true,
        
        selectedRecord: undefined,
        
        selection: {
            type: 'records'
        }
    },
    
    modes: {
        single: true,
        simple: true,
        multi: true
    },
    onNavigate: function(e) {
        if (!this.getDisabled()) {
            this.selectWithEvent(e.to.record, e);
        }
    },
    
    getSelectedRecords: function() {
        var selection = this.getSelection();
        return selection && selection.isRecords ? selection.getRecords() : Ext.emptyArray;
    },
    getStoreListeners: function() {
        return {
            add: 'onSelectionStoreAdd',
            remove: 'onSelectionStoreRemove',
            update: 'onSelectionStoreUpdate',
            clear: {
                fn: 'onSelectionStoreClear',
                priority: 1000
            },
            load: 'onSelectionStoreLoad',
            refresh: 'refreshSelection',
            idchanged: 'onIdChanged'
        };
    },
    applySelected: function(selected, oldSelected) {
        var me = this,
            store = me.getStore(),
            collectionConfig = {
                rootProperty: 'data',
                extraKeys: {
                    byInternalId: {
                        rootProperty: false,
                        property: 'internalId'
                    }
                },
                sorters: [
                    function(r1, r2) {
                        return store.indexOf(r1) - store.indexOf(r2);
                    }
                ],
                autoSort: false
            };
        if (oldSelected) {
            
            oldSelected.removeObserver(me);
            if (me.destroySelected) {
                oldSelected.destroy();
                me.destroySelected = false;
            }
        }
        
        
        if (selected && selected.isCollection) {
            me.destroySelected = false;
            selected.setConfig(collectionConfig);
        } else {
            
            me.destroySelected = true;
            selected = new Ext.util.Collection(Ext.apply(collectionConfig, selected));
        }
        
        
        me.observerPriority = 1000;
        selected.addObserver(me);
        return selected;
    },
    updateSelected: function(selected) {
        this.setSelectedRecord(selected.last() || null);
    },
    
    applyMode: function(mode) {
        var view = this.getView(),
            el = view.getRenderTarget();
        mode = mode ? mode.toLowerCase() : 'single';
        el.toggleCls(view.multiSelectCls, mode === 'multi');
        
        
        return this.modes[mode] ? mode : 'single';
    },
    updateView: function(view) {
        this.setStore(view ? view.getStore() : null);
    },
    
    applyStore: function(store) {
        return store ? Ext.data.StoreManager.lookup(store) : null;
    },
    updateStore: function(newStore, oldStore) {
        var me = this,
            bindEvents = Ext.apply({
                scope: me
            }, me.getStoreListeners());
        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            oldStore.un(bindEvents);
        }
        if (newStore) {
            newStore.on(bindEvents);
            if (oldStore) {
                me.refreshSelection();
            }
        }
    },
    selectByLocation: function(location) {
        
        if (!location.isDataViewLocation) {
            Ext.raise('selectByLocation MUST be passed an Ext.dataview.Location');
        }
        
        this.select(location.record);
    },
    
    selectAll: function(suppressEvent) {
        this.select(this.getStore().getRange(), true, suppressEvent);
    },
    
    deselectAll: function(suppressEvent) {
        var selected = this.getSelected();
        selected.suppressEvent = suppressEvent;
        selected.removeAll();
        selected.suppressEvent = false;
    },
    applySelectedRecord: function(selectedRecord) {
        if (selectedRecord === false) {
            selectedRecord = null;
        }
        return selectedRecord;
    },
    updateSelectedRecord: function(selectedRecord) {
        var me = this,
            selected = me.getSelected(),
            selectionCount = selected.getCount();
        if (selectedRecord) {
            
            
            
            if (selected.last() !== selectedRecord) {
                if (me.getMode() === 'single') {
                    selected.splice(0, selectionCount, selectedRecord);
                } else {
                    selected.add(selectedRecord);
                }
            }
            me.setLastSelected(selectedRecord);
        } else {
            if (!me.isConfiguring && selectionCount) {
                me.deselectAll();
            }
        }
        me.getView().publishState('selection', selectedRecord);
    },
    
    
    selectWithEvent: function(record, e) {
        var me = this,
            mode = me.getMode(),
            isSelected = me.isSelected(record);
        if (mode === 'multi') {
            me.selectWithEventMulti(record, e, isSelected);
        } else {
            if (isSelected) {
                
                me.deselect(record, mode === 'simple' && e.ctrlKey);
            } else {
                me.select(record, false);
            }
        }
        
        if (!e.shiftKey && me.isSelected(record)) {
            me.selectionStart = record;
        }
    },
    selectWithEventMulti: function(record, e, isSelected) {
        var me = this,
            shift = e.shiftKey,
            ctrl = e.ctrlKey,
            start = shift ? me.selectionStart : null;
        
        if (shift && start) {
            me.selectRange(start, record, ctrl);
        } else {
            me[isSelected ? 'deselect' : 'select'](record, true);
        }
    },
    
    selectRange: function(startRecord, endRecord, keepExisting) {
        var store = this.getStore();
        startRecord = (typeof startRecord === 'number') ? startRecord : store.indexOf(startRecord);
        endRecord = (typeof endRecord === 'number') ? endRecord : store.indexOf(endRecord);
        if (startRecord > endRecord) {
            var tmp = startRecord;
            startRecord = endRecord;
            endRecord = tmp;
        }
        this.getSelection().addRowRange(startRecord, 
        endRecord + 1, keepExisting);
    },
    
    select: function(records, keepExisting, suppressEvent) {
        var me = this,
            record;
        if (me.getDisabled()) {
            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) {
        this.doMultiSelect([
            record
        ], false, suppressEvent);
    },
    
    doMultiSelect: function(records, keepExisting, suppressEvent) {
        if (records === null || this.getDisabled()) {
            return;
        }
        this.getSelection().add(records, keepExisting, suppressEvent);
    },
    
    deselect: function(records, suppressEvent) {
        var me = this;
        if (me.getDisabled()) {
            return;
        }
        records = Ext.isArray(records) ? records : [
            records
        ];
        var selection = me.getSelection(),
            store = me.getStore(),
            len = records.length,
            i, record;
        
        for (i = 0; i < len; i++) {
            record = records[i];
            if (typeof record === 'number') {
                records[i] = store.getAt(record);
            }
        }
        
        
        
        selection.remove(records, suppressEvent);
    },
    
    onCollectionRemove: function(selectedCollection, chunk) {
        var me = this,
            view = me.getView(),
            records = chunk.items;
        me.getSelection().allSelected = this.allSelected = false;
        
        
        if (!chunk.next && !chunk.replacement) {
            me.setSelectedRecord(selectedCollection.last() || null);
        }
        view.onItemDeselect(records, selectedCollection.suppressEvent);
        if (!selectedCollection.suppressEvent) {
            me.fireSelectionChange(records, false);
        }
    },
    
    onCollectionAdd: function(selectedCollection, adds) {
        var me = this,
            view = me.getView(),
            selection = me.getSelection(),
            records = adds.items;
        if (view.destroyed) {
            return;
        }
        selection.allSelected = this.allSelected = selection.getCount() === view.getStore().getCount();
        
        me.setSelectedRecord(selectedCollection.last() || null);
        view.onItemSelect(me.getMode() === 'single' ? records[0] : records, selectedCollection.suppressEvent);
        if (!selectedCollection.suppressEvent) {
            me.fireSelectionChange(records, true);
        }
    },
    fireSelectionChange: function(records, selected) {
        this.fireEvent('selectionchange', this.getView(), records, selected);
    },
    
    getSelections: function() {
        if (this.getSelection().isRecords) {
            return this.getSelected().getRange();
        }
    },
    
    isRowSelected: function(record) {
        var me = this,
            sel = me.getSelection();
        if (sel && (sel.isRows || sel.isRecords)) {
            record = Ext.isNumber(record) ? me.getStore().getAt(record) : record;
            return sel.isSelected(record);
        } else {
            return false;
        }
    },
    
    isSelected: function(record) {
        return this.getSelection().isSelected(record);
    },
    
    hasSelection: function() {
        return this.getSelection().getCount() > 0;
    },
    
    refreshSelection: function() {
        if (this.getSelection().isRecords) {
            this.getSelection().refresh();
        }
    },
    
    
    
    onSelectionStoreRemove: function(store, records) {
        var selection = this.getSelection();
        if (selection.isRecords) {
            
            selection.remove(records);
        }
    },
    onSelectionStoreClear: function(store) {
        this.getSelection().clear();
    },
    
    getSelectionCount: function() {
        return this.getSelection().getCount();
    },
    destroy: function() {
        var me = this;
        me.setView(null);
        
        Ext.destroy(me.selection, me.destroySelected ? me.selected : null);
    },
    onIdChanged: function(store, rec, oldId, newId) {
        var selected = this.getSelected();
        if (selected) {
            selected.updateKey(rec, oldId);
        }
    },
    onSelectionStoreAdd: Ext.emptyFn,
    onSelectionStoreLoad: Ext.emptyFn,
    onSelectionStoreUpdate: Ext.emptyFn,
    onItemSelect: Ext.emptyFn,
    onItemDeselect: Ext.emptyFn,
    onEditorKey: Ext.emptyFn,
    privates: {
        
        onSelectionFinish: Ext.privateFn,
        applySelection: function(selection, oldSelection) {
            if (oldSelection) {
                
                if (oldSelection.type === selection.type) {
                    oldSelection.setConfig(selection);
                    return oldSelection;
                }
                Ext.destroy(oldSelection);
            }
            if (selection) {
                var store = this.getStore();
                selection = Ext.Factory.selection(Ext.apply({
                    selectionModel: this,
                    type: (store && store.isVirtualStore) ? 'rows' : 'records',
                    selected: this.getSelected()
                }, selection));
            }
            return selection;
        }
    }
});


Ext.define('Ext.dataview.EmptyText', {
    extend: Ext.Component,
    xtype: 'emptytext',
    classCls: Ext.baseCSSPrefix + 'emptytext',
    inheritUi: true,
    html: 'No data to display',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0
});


Ext.define('Ext.dataview.Abstract', {
    extend: Ext.Container,
    mixins: [
        Ext.mixin.ConfigProxy,
        Ext.mixin.ItemRippler
    ],
    
    isDataView: true,
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    cachedConfig: {
        
        associatedData: null,
        
        deferEmptyText: true,
        
        deselectOnContainerClick: true,
        
        disableSelection: false,
        
        emptyTextDefaults: {
            xtype: 'emptytext'
        },
        
        emptyItemText: '\xa0',
        
        itemsFocusable: true,
        
        itemTpl: '<div>{text:htmlEncode}</div>',
        
        loadingText: 'Loading...',
        
        pressedDelay: 100,
        
        scrollToTopOnRefresh: true,
        storeEventListeners: {
            add: 'onStoreAdd',
            beforeload: 'onStoreBeforeLoad',
            clear: 'onStoreClear',
            load: 'onStoreLoad',
            refresh: 'onStoreRefresh',
            remove: 'onStoreRemove',
            update: 'onStoreUpdate'
        },
        
        
        triggerEvent: 'childtap',
        
        triggerCtEvent: 'tap'
    },
    
    config: {
        
        itemButtonMode: false,
        
        data: null,
        
        emptyState: null,
        
        emptyText: null,
        
        enableTextSelection: null,
        
        inline: null,
        
        itemCls: null,
        
        loadingHeight: null,
        
        markDirty: null,
        navigationModel: {
            type: 'dataview'
        },
        
        selectable: true
    },
    
    autoSize: null,
    
    publishes: {
        selection: 1
    },
    
    twoWayBindable: {
        selection: 1
    },
    eventedConfig: {
        
        store: undefined
    },
    
    
    proxyConfig: {
        selectable: {
            configs: [
                'mode',
                'deselectable',
                'lastSelected',
                'selected'
            ],
            methods: [
                'isSelected',
                'select',
                'selectAll',
                'deselectAll',
                'getSelections',
                'hasSelection',
                'getSelectionCount'
            ]
        }
    },
    
    emptyTextProperty: 'html',
    
    restoreFocus: true,
    
    refreshCounter: 0,
    
    selectionModel: 'dataview',
    
    defaultBindProperty: 'store',
    
    focusable: true,
    
    scrollable: true,
    
    tabIndex: 0,
    
    classCls: Ext.baseCSSPrefix + 'dataview',
    focusedCls: Ext.baseCSSPrefix + 'focused',
    hoveredCls: Ext.baseCSSPrefix + 'hovered',
    inlineCls: Ext.baseCSSPrefix + 'inline',
    noWrapCls: Ext.baseCSSPrefix + 'nowrap',
    pressedCls: Ext.baseCSSPrefix + 'pressed',
    scrollDockCls: Ext.baseCSSPrefix + 'scrolldock',
    selectedCls: Ext.baseCSSPrefix + 'selected',
    hasLoadedStore: false,
    scrollDockedItems: null,
    beforeInitialize: function(config) {
        
        this.dataItems = [];
        this.callParent([
            config
        ]);
    },
    initialize: function() {
        var me = this;
        me.generateSelectorFunctions();
        me.callParent();
        
        
        me.bodyElement.on({
            touchstart: '_onChildTouchStart',
            touchend: '_onChildTouchEnd',
            touchcancel: '_onChildTouchCancel',
            tap: '_onChildTap',
            tapcancel: '_onChildTapCancel',
            longpress: '_onChildLongPress',
            taphold: '_onChildTapHold',
            singletap: '_onChildSingleTap',
            doubletap: '_onChildDoubleTap',
            swipe: '_onChildSwipe',
            mouseover: '_onChildMouseOver',
            mouseout: '_onChildMouseOut',
            contextmenu: '_onChildContextMenu',
            delegate: me.eventDelegate,
            scope: me
        });
        
        
        if (Ext.getScrollbarSize().width) {
            me.bodyElement.on('touchstart', '_onContainerTouchStart', me);
        }
        me.on(me.getTriggerCtEvent(), 'onContainerTrigger', me);
    },
    onRender: function() {
        var me = this;
        me.callParent();
        if (me.forceRefreshOnRender) {
            me.runRefresh();
        } else {
            me.refresh();
        }
    },
    doDestroy: function() {
        var me = this;
        me.destroyAllRipples();
        me.clearPressedTimer();
        me.setStore(null);
        me.setNavigationModel(null);
        me.setSelectable(null);
        me.lastPressedLocation = null;
        me.callParent();
    },
    createEmptyText: function(emptyText) {
        var ret = Ext.apply({}, this.getEmptyTextDefaults());
        if (typeof emptyText === 'string') {
            ret[this.emptyTextProperty] = emptyText;
        } else if (emptyText) {
            Ext.apply(ret, emptyText);
        }
        ret.isEmptyText = ret.hidden = true;
        ret.showInEmptyState = null;
        return ret;
    },
    
    ensureVisible: function(record, options) {
        var me = this,
            plan = me.ensureVisiblePlan(record, options),
            step;
        
        for (; ; ) {
            if (!(step = plan.steps.pop())) {
                break;
            }
            me[step](plan);
        }
        return plan.promise;
    },
    gatherData: function(record, recordIndex) {
        var me = this,
            data = record && record.getData(me.associatedData);
        if (data) {
            if (recordIndex === undefined) {
                recordIndex = me.store.indexOf(record);
            }
            data = me.prepareData(data, recordIndex, record);
        }
        return data || null;
    },
    getFirstDataItem: function() {
        return this.dataItems[0] || null;
    },
    getFirstItem: function() {
        return this.getFastItems()[0] || null;
    },
    
    getItemAt: function(index) {
        var items = this.getFastItems();
        if (index < 0) {
            index += items.length;
        }
        return items[index] || null;
    },
    
    getItemIndex: function(item) {
        return this.mapToRecordIndex(item);
    },
    getItem: function(record) {
        var ret = null,
            idx;
        if (record) {
            idx = record.isEntity ? this.store.indexOf(record) : record;
            if (idx > -1) {
                ret = this.getItemAt(idx);
            }
        }
        return ret;
    },
    getLastDataItem: function() {
        var dataItems = this.dataItems;
        return dataItems[dataItems.length - 1] || null;
    },
    getLastItem: function() {
        var items = this.getFastItems();
        return items[items.length - 1];
    },
    
    getScrollDockedItems: function(which) {
        var scrollDock = this.scrollDockedItems;
        if (scrollDock) {
            if (which) {
                which = this.scrollDockAliases[which] || which;
                scrollDock = scrollDock[which].slice();
            } else {
                scrollDock = scrollDock.start.items.concat(scrollDock.end.items);
            }
        }
        return scrollDock || [];
    },
    
    isItemSelected: function(item) {
        var record = this.mapToRecord(item);
        return record ? this.isSelected(record) : false;
    },
    isFirstItem: function(item) {
        return Ext.getDom(item) === this.getFirstItem();
    },
    isFirstDataItem: function(item) {
        return Ext.getDom(item) === this.getFirstDataItem();
    },
    isLastItem: function(item) {
        return Ext.getDom(item) === this.getLastItem();
    },
    isLastDataItem: function(item) {
        return Ext.getDom(item) === this.getLastDataItem();
    },
    
    mapToItem: function(value, as) {
        var me = this,
            el = me.element,
            item, items;
        if (value && value.isEvent) {
            item = value.getTarget(me.itemSelector, el);
        } else if (value && (value.isElement || value.nodeType === 1)) {
            item = Ext.fly(value).findParent(me.itemSelector, el);
        } else if (value && value.isEntity) {
            item = me.itemFromRecord(value);
        } else {
            if (value && value.isComponent && me.items.contains(value)) {
                item = value;
            } else {
                
                items = me.getFastItems();
                if (value < 0) {
                    value += items.length;
                }
                
                item = items[value || 0];
            }
        }
        if (item) {
            item = me.itemAs(item, as || (me.isElementDataView ? 'el' : 'cmp'));
        }
        return item || null;
    },
    
    mapToRecord: function(value) {
        var me = this,
            item = value,
            el = me.element,
            dom, rec;
        if (item && item.isEvent) {
            item = item.getTarget(me.itemSelector, el);
        } else if (item && (item.isElement || item.nodeType === 1)) {
            item = Ext.fly(item).findParent(me.itemSelector, el);
        } else if (typeof item === 'number') {
            item = me.mapToItem(item);
        }
        if (item) {
            
            dom = item.isWidget ? item.el : item;
            dom = dom.dom || dom;
            
            if (this.itemSelector(dom)) {
                rec = dom.getAttribute('data-recordid');
                rec = rec && me.store.getByInternalId(+rec);
            }
        }
        return rec || null;
    },
    
    mapToRecordIndex: function(value) {
        var me = this,
            item = value,
            index = -1,
            el = me.element,
            dom;
        if (item && item.isEntity) {
            index = me.store.indexOf(item);
        } else {
            if (item && item.isEvent) {
                item = item.getTarget(me.itemSelector, el);
            } else if (item && (item.isElement || item.nodeType === 1)) {
                item = Ext.fly(item).findParent(me.itemSelector, el);
            } else if (typeof item === 'number') {
                item = me.mapToItem(item);
            }
            if (item) {
                
                dom = item.isWidget ? item.el : item;
                dom = dom.dom || dom;
                
                
                if (me.itemSelector(dom)) {
                    index = dom.getAttribute('data-recordindex');
                    index = index ? +index : -1;
                }
            }
        }
        return index;
    },
    
    mapToViewIndex: function(value, indexOffset) {
        var me = this,
            index = -1,
            item = value,
            el = me.element,
            items = me.getFastItems(),
            dom;
        if (typeof item === 'number') {
            indexOffset = indexOffset || 0;
            
            
            
            for (; item < items.length; ++item) {
                dom = items[item];
                if (dom.isWidget) {
                    dom = dom.el.dom;
                }
                
                
                
                
                
                if (+dom.getAttribute('data-recordindex') === item + indexOffset) {
                    index = item;
                    break;
                }
            }
        } else if (item) {
            if (item.isEntity) {
                item = me.itemFromRecord(item);
            } else if (item.isEvent) {
                item = item.getTarget(me.itemSelector, el);
            } else if (item.isElement || item.nodeType === 1) {
                item = Ext.fly(item).findParent(me.itemSelector, el);
            }
            if (item && items.length) {
                if (items[0].isWidget) {
                    if (!item.isWidget) {
                        item = Ext.Component.from(item);
                    }
                } else {
                    
                    item = item.nodeType ? item : item.el.dom;
                }
                
                
                
                
                
                index = Array.prototype.indexOf.call(items, item);
            }
        }
        return index;
    },
    
    nextItem: function(item, as) {
        var next = this.traverseItem(item, 1);
        return as ? this.itemAs(next, as) : next;
    },
    
    previousItem: function(item, as) {
        var prev = this.traverseItem(item, -1);
        return as ? this.itemAs(prev, as) : prev;
    },
    
    prepareData: function(data, index, record) {
        return data;
    },
    
    refresh: function() {
        this.whenVisible('runRefresh');
    },
    
    
    onFocusEnter: function(e) {
        var me = this;
        me.callParent([
            e
        ]);
        
        
        if (!(e.within(me.getRenderTarget()) || e.target === me.getFocusEl().dom)) {
            return;
        }
        
        return me.onInnerFocusEnter(e);
    },
    onInnerFocusEnter: function(e) {
        var me = this,
            navigationModel = me.getNavigationModel(),
            focusPosition, itemCount;
        
        
        
        
        
        if (navigationModel.lastLocation === 'scrollbar') {
            if (e.relatedTarget) {
                e.relatedTarget.focus();
            }
            return;
        }
        
        if (e.target === me.getFocusEl().dom) {
            focusPosition = me.restoreFocus && navigationModel.getPreviousLocation();
            if (focusPosition) {
                
                
                
                
                
                
                focusPosition = focusPosition.refresh();
            }
            
            
            else if (e.backwards) {
                focusPosition = me.getLastDataItem();
            } else 
            
            {
                focusPosition = me.getFirstDataItem();
            }
        } else 
        
        {
            focusPosition = e;
        }
        
        me.toggleChildrenTabbability(false);
        itemCount = me.getFastItems().length;
        
        if (itemCount) {
            
            
            
            if (focusPosition.isWidget) {
                focusPosition = focusPosition.getFocusEl() || focusPosition.el;
            }
            
            navigationModel.setLocation(focusPosition, {
                event: e,
                navigate: false
            });
        }
        
        
        
        
        if (navigationModel.getLocation()) {
            me.el.dom.setAttribute('tabIndex', -1);
        }
    },
    onFocusLeave: function(e) {
        var me = this,
            navModel = me.getNavigationModel();
        
        
        if (navModel.getLocation()) {
            
            navModel.setLocation(null, {
                event: e
            });
            me.el.dom.setAttribute('tabIndex', 0);
        }
        me.callParent([
            e
        ]);
    },
    
    onInnerFocusLeave: function(e) {
        
        this.getNavigationModel().setLocation(null, {
            event: e
        });
    },
    onFocusMove: function(e) {
        var me = this,
            el = me.el,
            renderTarget = me.getRenderTarget(),
            toComponent = e.event.toComponent,
            fromComponent = e.event.fromComponent;
        
        
        
        if (!el.contains(e.toElement)) {
            return me.callParent([
                e
            ]);
        }
        
        
        
        if (el.contains(e.toElement) && !renderTarget.contains(e.toElement) && renderTarget.contains(e.fromElement)) {
            return me.onInnerFocusLeave(e.event);
        }
        
        if (el.contains(e.fromElement) && !renderTarget.contains(e.fromElement) && renderTarget.contains(e.toElement)) {
            return me.onInnerFocusEnter(e.event);
        }
        
        if (!renderTarget.contains(e.fromElement) && !renderTarget.contains(e.toElement)) {
            return me.callParent([
                e
            ]);
        }
        
        
        
        if ((toComponent === me || toComponent.up('dataview,componentdataview') === me) && (fromComponent === me || fromComponent.up('dataview,componentdataview') === me)) {
            me.getNavigationModel().onFocusMove(e.event);
        }
        return me.callParent([
            e
        ]);
    },
    onItemAdd: function(item, index) {
        var me = this,
            scrollDock = item.scrollDock,
            scrollDockCls = me.scrollDockCls,
            scrollDockedItems;
        if (!item.$dataItem && item.isInner) {
            if (scrollDock !== null) {
                scrollDock = scrollDock || 'end';
            }
            if (scrollDock) {
                if (!(scrollDockedItems = me.scrollDockedItems)) {
                    me.scrollDockedItems = scrollDockedItems = {
                        start: {
                            items: [],
                            height: 0,
                            filter: me.filterScrollDockStart,
                            name: scrollDock
                        },
                        end: {
                            items: [],
                            height: 0,
                            filter: me.filterScrollDockEnd,
                            name: scrollDock
                        }
                    };
                }
                scrollDock = me.scrollDockAliases[scrollDock] || scrollDock;
                
                if (!scrollDockedItems[scrollDock]) {
                    Ext.raise('Invalid value for scrollDock: ' + item.scrollDock);
                }
                
                item.scrollDock = scrollDock;
                
                scrollDock = scrollDockedItems[scrollDock];
                scrollDock.items = me.innerItems.filter(scrollDock.filter);
                if (item.showInEmptyState === undefined) {
                    item.showInEmptyState = false;
                }
                item.addCls(scrollDockCls + ' ' + scrollDockCls + '-' + scrollDock.name);
                if (me.getItemsFocusable()) {
                    item.el.set({
                        tabIndex: -1
                    });
                }
                if (me.addScrollDockedItem) {
                    me.addScrollDockedItem(item);
                }
            }
        }
        me.callParent([
            item,
            index
        ]);
    },
    
    onItemDeselect: function(records, suppressEvent) {
        var me = this;
        if (!me.isConfiguring && !me.destroyed) {
            if (suppressEvent) {
                me.setItemSelection(records, false);
            } else {
                me.fireEventedAction('deselect', [
                    me,
                    records
                ], 'setItemSelection', me, [
                    records,
                    false
                ]);
            }
        }
    },
    
    onItemSelect: function(records, suppressEvent) {
        var me = this;
        if (suppressEvent) {
            me.setItemSelection(records, true);
        } else {
            me.fireEventedAction('select', [
                me,
                records
            ], 'setItemSelection', me, [
                records,
                true
            ]);
        }
    },
    onChildTouchStart: function(location) {
        var me = this,
            child = location.item,
            e = location.event,
            hasListeners = me.hasListeners,
            curLocation = me.getNavigationModel().getLocation(),
            actionable = curLocation && curLocation.actionable,
            name, skip;
        
        
        if (!location.actionable && !(location.equalCell || location.equals)(curLocation)) {
            me.rippleItem(child, e);
        }
        
        name = 'beforechildtouchstart';
        skip = hasListeners[name] && me.fireEvent(name, me, location) === false;
        if (!skip) {
            name = 'beforeitemtouchstart';
            skip = hasListeners[name] && me.fireEvent(name, me, location.viewIndex, child, location.record, e) === false;
        }
        if (!skip) {
            
            if (!actionable) {
                me.doChildTouchStart(location);
            }
            me.fireChildEvent('touchstart', location);
        }
    },
    onChildTouchEnd: function(location) {
        var me = this,
            child = location.item,
            curLocation = me.getNavigationModel().getLocation(),
            e = location.event;
        
        if (!(curLocation && curLocation.actionable)) {
            me.rippleItem(child, e);
        }
        this.clearPressedCls('touchend', location);
    },
    onChildTouchCancel: function(location) {
        this.clearPressedCls('touchcancel', location);
    },
    onChildTouchMove: function(location) {
        this.fireChildEvent('touchmove', location);
    },
    onChildTap: function(location) {
        this.fireChildEvent('tap', location);
    },
    onChildTapCancel: function(location) {
        var me = this,
            itemButtonMode = me.getItemButtonMode();
        if (!itemButtonMode) {
            this.clearPressedCls('tapcancel', location);
        }
    },
    onChildContextMenu: function(location) {
        this.fireChildEvent('contextmenu', location);
    },
    onChildLongPress: function(location) {
        this.fireChildEvent('longpress', location);
    },
    onChildTapHold: function(location) {
        this.fireChildEvent('taphold', location);
    },
    onChildSingleTap: function(location) {
        this.fireChildEvent('singletap', location);
    },
    onChildDoubleTap: function(location) {
        this.fireChildEvent('doubletap', location);
    },
    onChildSwipe: function(location) {
        this.fireChildEvent('swipe', location);
    },
    onChildMouseOver: function(location) {
        var me = this,
            child = location.item;
        if (me.mouseOverItem !== child) {
            me.mouseOverItem = child;
            if (me.doHover) {
                me.toggleHoverCls(true);
            }
            me.fireChildEvent('mouseenter', location);
        }
    },
    onChildMouseOut: function(location) {
        var me = this,
            itemButtonMode = me.getItemButtonMode(),
            child = location.item,
            relatedTarget = location.event.getRelatedTarget(me.itemSelector);
        if (child && child.dom !== relatedTarget) {
            if (me.doHover) {
                me.toggleHoverCls(false);
            }
            if (!itemButtonMode) {
                this.clearPressedCls('mouseleave', location);
            } else {
                me.fireChildEvent('mouseleave', location);
            }
            me.mouseOverItem = null;
        }
    },
    
    onNavigate: function(e) {
        var me = this,
            selectable = !me.destroyed && me.getSelectable();
        if (selectable && me.shouldSelectItem(e)) {
            selectable.onNavigate(e);
        }
    },
    shouldSelectItem: function(e) {
        var me = this,
            selectable = me.getSelectable(),
            no = e.stopSelection || !selectable || selectable.getDisabled(),
            target = !no && e.getTarget('.' + Ext.baseCSSPrefix + 'item-no-select,.' + Ext.baseCSSPrefix + 'item-no-tap', this.element);
        if (target) {
            no = me.el.contains(target);
        }
        return !no;
    },
    
    onStoreAdd: function() {
        this.syncEmptyState();
    },
    onStoreBeforeLoad: function() {
        this.handleBeforeLoad();
    },
    onStoreClear: function() {
        this.doClear();
    },
    onStoreLoad: function() {
        this.hasLoadedStore = true;
        this.clearMask();
        this.syncEmptyState();
    },
    onStoreRefresh: function() {
        this.refresh();
    },
    onStoreRemove: function() {
        this.syncEmptyState();
    },
    onStoreUpdate: function(store, record, type, modifiedFieldNames, info) {
        var me = this,
            item;
        
        
        if (!info || !(info.indexChanged || info.filtered)) {
            
            
            item = me.itemFromRecord(record);
            if (item) {
                me.syncItemRecord({
                    item: item,
                    modified: me.indexModifiedFields(modifiedFieldNames),
                    record: record
                });
            }
        }
        if (me.isSelected(record)) {
            me.setItemSelection(record, true);
        }
    },
    
    
    
    updateAssociatedData: function(assocData) {
        this.associatedData = {
            associated: assocData
        };
    },
    
    updateData: function(data) {
        var store = this.store;
        if (!store) {
            this.settingStoreFromData = true;
            this.setStore({
                data: data,
                autoDestroy: true
            });
            this.settingStoreFromData = false;
        } else {
            store.loadData(data);
        }
    },
    
    updateDisableSelection: function(value) {
        var el = this.getRenderTarget();
        el.toggleCls(this.showSelectionCls, !value);
    },
    
    updateEmptyText: function(emptyText) {
        var me = this,
            config = emptyText,
            emptyTextCmp = me.emptyTextCmp;
        if (emptyTextCmp) {
            if (!emptyText || typeof emptyText === 'string') {
                config = {};
                config[me.emptyTextProperty] = emptyText || '\xa0';
            }
            emptyTextCmp.setConfig(config);
        }
        if (!me.isConfiguring) {
            me.syncEmptyState();
        }
    },
    
    updateEnableTextSelection: function(enableTextSelection) {
        this.setUserSelectable({
            bodyElement: !!enableTextSelection
        });
    },
    
    updateInline: function(inline) {
        var me = this;
        me.toggleCls(me.inlineCls, !!inline);
        me.toggleCls(me.noWrapCls, inline && inline.wrap === false);
    },
    
    updateItemCls: function(newCls, oldCls) {
        if (!this.isConfiguring) {
            var items = this.dataItems,
                
                len = items.length,
                i, item;
            for (i = 0; i < len; i++) {
                item = items[i];
                item = item.isWidget ? item.el : Ext.fly(item);
                item.replaceCls(oldCls, newCls);
            }
        }
    },
    
    applyItemTpl: function(config) {
        return Ext.XTemplate.get(config);
    },
    updateItemTpl: function() {
        if (!this.isConfiguring) {
            this.refresh();
        }
    },
    
    updateMarkDirty: function(markDirty) {
        var dataItems = this.dataItems,
            i, ln, dataItem;
        markDirty = !!markDirty;
        for (i = 0 , ln = dataItems.length; i < ln; i++) {
            dataItem = dataItems[i];
            (dataItem.el || Ext.fly(dataItem)).toggleCls(this.markDirtyCls, markDirty);
        }
    },
    
    updateMasked: function(masked) {
        var me = this,
            loadingHeight = me.getLoadingHeight();
        if (masked) {
            if (loadingHeight && loadingHeight > me.el.getHeight()) {
                me.hasLoadingHeight = true;
                me.oldMinHeight = me.getMinHeight();
                me.setMinHeight(loadingHeight);
            }
        } else {
            if (!me.destroying && me.hasLoadingHeight) {
                me.setMinHeight(me.oldMinHeight);
                delete me.hasLoadingHeight;
            }
        }
    },
    
    applySelectable: function(selectable, oldSelectable) {
        var me = this,
            record = me.selection;
        if (selectable === false) {
            selectable = {
                disabled: true
            };
        }
        if (selectable) {
            if (typeof selectable === 'string') {
                selectable = {
                    type: me.selectionModel,
                    mode: selectable.toLowerCase(),
                    view: me
                };
            } else {
                selectable = Ext.apply({
                    type: me.selectionModel,
                    view: me
                }, selectable);
            }
            
            if (oldSelectable) {
                
                if (selectable.isSelectionModel || selectable.type !== oldSelectable.type) {
                    Ext.raise('Switching out selectables dynamically is not supported');
                }
                
                selectable = oldSelectable.setConfig(selectable);
            } else 
            {
                selectable = Ext.Factory.selmodel(me.mergeProxiedConfigs('selectable', selectable));
            }
            
            if (record) {
                
                delete me.selection;
                
                if (!record.isEntity) {
                    Ext.raise('DataView selection config must be single record');
                }
                if (selectable.getRecords && !selectable.getRecords()) {
                    Ext.raise('DataView configured with selection when selectable not configured to accept records');
                }
                
                selectable.select(record);
            }
        }
        return selectable;
    },
    
    applyStore: function(store) {
        return store ? Ext.data.StoreManager.lookup(store) : null;
    },
    updateStore: function(newStore, oldStore) {
        var me = this,
            storeEvents = Ext.apply({
                scope: me
            }, me.getStoreEventListeners()),
            mask = me.autoMask,
            newLoad;
        if (oldStore) {
            if (!oldStore.destroyed) {
                if (oldStore.getAutoDestroy()) {
                    oldStore.destroy();
                } else {
                    oldStore.un(storeEvents);
                }
            }
            me.dataRange = me.store = Ext.destroy(me.dataRange);
            
            if (!me.destroying && !me.destroyed && !newStore) {
                me.doClear();
            }
        }
        if (newStore) {
            me.store = newStore;
            if (me.destroying) {
                return;
            }
            newStore.on(storeEvents);
            if (newStore.isLoaded()) {
                me.hasLoadedStore = true;
            }
            
            
            newLoad = !newStore.isTreeStore && newStore.hasPendingLoad();
            me.bindStore(newStore);
            if (me.initialized) {
                me.refresh();
            }
        }
        
        if (!me.isConfiguring || me.settingStoreFromData) {
            me.getSelectable().setStore(newStore);
        }
        if (mask && !newLoad) {
            me.setMasked(false);
            me.autoMask = false;
        } else if (!mask && newLoad) {
            me.handleBeforeLoad();
        }
    },
    updateHidden: function(hidden, oldHidden) {
        this.callParent([
            hidden,
            oldHidden
        ]);
        this.destroyAllRipples();
    },
    
    privates: {
        
        associatedData: true,
        doHover: true,
        showSelectionCls: Ext.baseCSSPrefix + 'show-selection',
        multiSelectCls: Ext.baseCSSPrefix + 'multi-select',
        markDirtyCls: Ext.baseCSSPrefix + 'mark-dirty',
        scrollDockAliases: {
            top: 'start',
            bottom: 'end'
        },
        getSelection: function() {
            
            
            
            
            return this.getSelectable().getSelectedRecord();
        },
        setSelection: function(record) {
            
            
            
            
            return this.getSelectable().setSelectedRecord(record);
        },
        generateSelectorFunctions: function() {
            var renderTarget = this.getRenderTarget(),
                bodyElement = this.bodyElement;
            
            
            
            
            
            
            
            this.eventDelegate = this.itemSelector = function(candidate) {
                return candidate && (candidate.parentNode === bodyElement.dom || candidate.parentNode === renderTarget.dom);
            };
        },
        bindStore: function(store) {
            this.dataRange = store.createActiveRange();
        },
        clearMask: function() {
            this.setMasked(false);
            this.autoMask = false;
        },
        clearPressedCls: function(type, location) {
            var me = this,
                record = location.record,
                child = location.child,
                el;
            me.clearPressedTimer();
            if (record && child) {
                el = child.isWidget ? child.element : Ext.fly(child);
                el.removeCls(me.pressedCls);
            }
            me.fireChildEvent(type, location);
        },
        clearPressedTimer: function() {
            var timeout = this.pressedTimeout;
            if (timeout) {
                Ext.undefer(timeout);
                delete this.pressedTimeout;
            }
        },
        doAddPressedCls: function(record) {
            var me = this,
                item = me.itemFromRecord(record);
            if (item) {
                item = item.isWidget ? item.element : Ext.fly(item);
                item.addCls(me.pressedCls);
            }
        },
        doClear: function() {
            this.syncEmptyState();
        },
        doChildTouchStart: function(location) {
            var me = this,
                record = location.record,
                itemButtonMode = me.getItemButtonMode(),
                pressedDelay = me.getPressedDelay();
            me.clearPressedTimer();
            if (record) {
                if (pressedDelay > 0) {
                    me.pressedTimeout = Ext.defer(me.doAddPressedCls, pressedDelay, me, [
                        record
                    ]);
                } else {
                    me.doAddPressedCls(record);
                }
                if (itemButtonMode) {
                    me.lastPressedLocation = location;
                    Ext.GlobalEvents.setPressedComponent(me, location);
                }
            }
        },
        
        onRelease: function() {
            var me = this;
            if (me.lastPressedLocation) {
                me.clearPressedCls('release', me.lastPressedLocation);
            }
            me.lastPressedLocation = null;
        },
        
        ensureVisiblePlan: function(record, plan) {
            var store = this.store,
                recIndex;
            
            if (record.record) {
                plan = Ext.apply({}, record);
                record = plan.record;
                delete plan.record;
            } else {
                plan = Ext.apply({}, plan);
            }
            if (record.isEntity) {
                recIndex = store.indexOf(record);
            } else if (typeof record === 'number') {
                recIndex = record;
                record = store.getAt(record);
            } else 
            {
                Ext.raise('ensureVisible first parameter must be record or recordIndex ' + 'or an options object with a record property');
            }
            
            plan.record = record;
            plan.recordIndex = recIndex;
            plan.animation = plan.animation || plan.animate;
            
            plan.async = !!plan.animation;
            plan.steps = [];
            
            
            if (recIndex < 0 || recIndex >= store.getCount()) {
                
                Ext.raise('Invalid record passed to List#ensureVisible');
                
                plan.promise = Ext.Deferred.getCachedRejected();
            } else {
                
                
                plan.steps.push('ensureVisibleFocus', 'ensureVisibleSelect', 'ensureVisiblePrep');
            }
            return plan;
        },
        ensureVisibleFocus: function(plan) {
            if (plan.focus) {
                var item = plan.item;
                if (plan.async) {
                    plan.promise = plan.promise.then(function(o) {
                        item = o.item;
                        if (item) {
                            item.focus();
                        }
                        return o;
                    });
                } else if (item) {
                    item.focus();
                }
            }
        },
        ensureVisiblePrep: function(plan) {
            var me = this,
                dataRange = me.dataRange,
                cleanup = function() {
                    delete dataRange.goto;
                    if (args) {
                        dataRange.goto(args[0], args[1]);
                    }
                },
                args, promise;
            if (plan.async) {
                
                
                
                dataRange.goto = function(begin, end) {
                    if (args) {
                        args[0] = begin;
                        args[1] = end;
                    } else {
                        args = [
                            begin,
                            end
                        ];
                    }
                };
                promise = me.ensureVisibleScroll(plan);
                
                
                
                promise = promise.then(function(v) {
                    cleanup();
                    return v;
                }, function(ex) {
                    cleanup();
                    throw ex;
                });
            } else {
                promise = me.ensureVisibleScroll(plan);
            }
            plan.promise = promise;
        },
        ensureVisibleScroll: function(plan) {
            var item = plan.item || (plan.item = this.itemFromRecord(plan.recIndex));
            return this.getScrollable().ensureVisbile(item.el, {
                animation: plan.animation
            });
        },
        ensureVisibleSelect: function(plan) {
            if (plan.select) {
                var me = this;
                if (plan.async) {
                    plan.promise = plan.promise.then(function(o) {
                        
                        
                        
                        
                        return o;
                    });
                } else {}
            }
        },
        
        filterScrollDockStart: function(item) {
            var scrollDock = item.scrollDock;
            return scrollDock === 'start' || scrollDock === 'top';
        },
        filterScrollDockEnd: function(item) {
            var scrollDock = item.scrollDock;
            return scrollDock === 'end' || scrollDock === 'bottom';
        },
        findTailItem: function(rawElements) {
            var me = this,
                items = rawElements ? me.innerItems : me.items.items,
                at = -1,
                tail = null,
                i, item, scrollDock;
            for (i = items.length; i-- > 0; ) {
                item = items[i];
                scrollDock = item.scrollDock;
                if (scrollDock === 'end') {
                    tail = items[at = i];
                } else {
                    break;
                }
            }
            return rawElements ? tail : at;
        },
        fireChildEvent: function(type, location) {
            var me = this,
                deprecatedName = 'item' + type,
                name = 'child' + type,
                hasListeners = me.hasListeners;
            if (hasListeners[name]) {
                me.fireEvent(name, me, location);
            }
            
            if (hasListeners[deprecatedName] && location.record) {
                me.fireEvent(deprecatedName, me, location.viewIndex, location.item, location.record, location.event);
            }
        },
        getEmptyTextCmp: function() {
            var me = this,
                cmp = me.emptyTextCmp;
            if (!cmp) {
                me.emptyTextCmp = cmp = me.add(me.createEmptyText(me.getEmptyText()));
            }
            return cmp;
        },
        getRecordIndexFromPoint: function(x, y) {
            var item = this.getItemFromPoint(x, y);
            return item ? this.mapToRecordIndex(item) : -1;
        },
        getItemFromPoint: function(x, y) {
            var me = this,
                scroller = me.getScrollable(),
                scrollPosition = scroller.getPosition(),
                scrollSize = scroller.getSize(),
                offset = me.getScrollerTarget().getXY();
            return me.getItemFromPagePoint(Math.max(Math.min(x, scrollSize.x), 0) + offset[0] - scrollPosition.x, Math.max(Math.min(y, scrollSize.y), 0) + offset[1] - scrollPosition.y);
        },
        getItemFromPagePoint: function(x, y) {
            var items = this.getFastItems(),
                len = items.length,
                point = new Ext.util.Point(x, y),
                ret = null,
                i, item, el;
            for (i = 0; i < len; i++) {
                item = items[i];
                el = item.isWidget ? item.element : Ext.fly(item);
                if (el.getRegion().contains(point)) {
                    ret = item;
                    break;
                }
            }
            return ret;
        },
        handleBeforeLoad: function() {
            var me = this,
                loadingText = me.getLoadingText();
            if (loadingText) {
                me.autoMask = true;
                me.setMasked({
                    xtype: 'loadmask',
                    message: loadingText
                });
            }
            me.hideEmptyText();
        },
        hideEmptyText: function() {
            var cmp = this.emptyTextCmp;
            if (cmp) {
                cmp.hide();
            }
        },
        
        indexModifiedFields: function(modified) {
            return modified;
        },
        
        itemAs: function(item, as) {
            var ret = item;
            
            if (as !== 'cmp' && as !== 'dom' && as !== 'el') {
                Ext.raise('Invalid "as" value "' + as + '" to mapToItem()');
            }
            
            if (typeof ret === 'number') {
                
                ret = null;
            } else if (ret) {
                if (as === 'cmp') {
                    if (!ret.isWidget) {
                        ret = Ext.getCmp(ret.id);
                    }
                } else {
                    if (ret.isWidget) {
                        ret = ret.el;
                    }
                    
                    if (ret) {
                        if (ret.isElement) {
                            if (as === 'dom') {
                                ret = ret.dom;
                            }
                        } else if (as === 'el') {
                            ret = Ext.fly(ret);
                        }
                    }
                }
            }
            return ret;
        },
        itemFromRecord: function(rec) {
            var index = rec.isEntity ? this.store.indexOf(rec) : rec;
            
            return ((index > -1) && this.dataItems[index]) || null;
        },
        onContainerTrigger: function(e) {
            var me = this;
            if (e.target === me.element.dom) {
                if (me.getDeselectOnContainerClick() && me.store) {
                    me.getSelectable().deselectAll();
                }
            }
        },
        runRefresh: function() {
            var me = this,
                store = me.store;
            me.syncEmptyState();
            
            
            if (store && !me.isConfiguring && (store.isTreeStore || !store.hasPendingLoad())) {
                me.fireEventedAction('refresh', [
                    me
                ], 'doRefresh', me, [
                    me.getScrollToTopOnRefresh()
                ]);
            }
        },
        
        saveFocusState: function() {
            var me = this,
                navModel = me.getNavigationModel(),
                location = navModel.location,
                lastFocusedViewIndex, lastFocusedRecord, itemCount, focusItem;
            
            if (location) {
                lastFocusedRecord = location.record;
                lastFocusedViewIndex = location.viewIndex;
                
                
                return function() {
                    itemCount = me.getFastItems().length;
                    
                    
                    if (itemCount) {
                        
                        
                        
                        if (lastFocusedRecord) {
                            focusItem = me.mapToItem(lastFocusedRecord);
                        }
                        if (!focusItem) {
                            focusItem = me.mapToItem(Math.min(lastFocusedViewIndex || 0, itemCount - 1));
                        }
                        navModel.setLocation(null);
                        navModel.setLocation(focusItem);
                    }
                };
            }
            return Ext.emptyFn;
        },
        setItemHidden: function(item, hide) {
            if (hide) {
                if (!item.$hidden) {
                    item.hide();
                    item.$hidden = true;
                }
            } else if (item.$hidden) {
                item.$hidden = false;
                item.show();
            }
        },
        setItemSelection: function(records, selected) {
            
            records = Ext.Array.from(records);
            var me = this,
                len = records.length,
                pressedCls = me.pressedCls,
                selectedCls = me.selectedCls,
                toRemove = pressedCls,
                i, record, item, toAdd;
            if (!selected) {
                toRemove = [
                    pressedCls,
                    selectedCls
                ];
            } else {
                toAdd = selectedCls;
            }
            if (!me.isConfiguring && !me.destroyed) {
                for (i = 0; i < len; i++) {
                    record = records[i];
                    item = me.itemFromRecord(record);
                    if (item) {
                        item = item.isWidget ? item.element : Ext.fly(item);
                        item.removeCls(toRemove);
                        if (toAdd) {
                            item.addCls(toAdd);
                        }
                    }
                }
            }
        },
        shouldRippleItem: function(item, e) {
            var disableSelection = this.getDisableSelection();
            if (!disableSelection && this.isItemSelected(item)) {
                return false;
            }
            return this.mixins.itemrippler.shouldRippleItem.call(this, item, e);
        },
        syncEmptyState: function() {
            var me = this,
                store = me.store,
                empty = !store || !store.getCount() && me.getEmptyText(),
                emptyTextCmp = me.emptyTextCmp;
            if (!empty) {
                if (emptyTextCmp) {
                    emptyTextCmp.hide();
                }
            } else if ((me.hasLoadedStore || !me.getDeferEmptyText()) && !(store && store.hasPendingLoad())) {
                emptyTextCmp = emptyTextCmp || me.getEmptyTextCmp();
                emptyTextCmp.show();
            }
            me.setEmptyState(empty);
            return empty;
        },
        toggleChildrenTabbability: function(enableTabbing) {
            var focusEl = this.getRenderTarget();
            if (enableTabbing) {
                focusEl.restoreTabbableState({
                    skipSelf: true
                });
            } else {
                
                
                focusEl.saveTabbableState({
                    skipSelf: true,
                    includeSaved: false
                });
            }
        },
        toggleHoverCls: function(on) {
            var target = this.mouseOverItem,
                el;
            if (target) {
                el = target.isWidget ? target.element : Ext.fly(target);
                el.toggleCls(this.hoveredCls, on);
            }
        },
        _onChildEvent: function(fn, e) {
            var me = this,
                last = me.lastPressedLocation,
                location = me.getNavigationModel().createLocation(e);
            if (location.child) {
                location.pressing = !!(last && last.child === location.child);
                me[fn](location);
            }
            return location;
        },
        _onChildTouchStart: function(e) {
            var child = this._onChildEvent('onChildTouchStart', e).child,
                el = child && (child.element || Ext.get(child));
            if (el) {
                el.on('touchmove', '_onChildTouchMove', this);
            }
        },
        _onChildTouchMove: function(e) {
            this._onChildEvent('onChildTouchMove', e);
        },
        _onChildTouchEnd: function(e) {
            var child = this._onChildEvent('onChildTouchEnd', e).child,
                el = child && (child.element || Ext.get(child));
            if (el) {
                el.un('touchmove', '_onChildTouchMove', this);
            }
        },
        _onChildTouchCancel: function(e) {
            var child = this._onChildEvent('onChildTouchCancel', e).child,
                el = child && (child.element || Ext.get(child));
            if (el) {
                el.un('touchmove', '_onChildTouchMove', this);
            }
        },
        _onChildTap: function(e) {
            var target = e.getTarget('.' + Ext.baseCSSPrefix + 'item-no-tap', this.element);
            if (!target) {
                this._onChildEvent('onChildTap', e);
            }
        },
        _onChildTapCancel: function(e) {
            this._onChildEvent('onChildTapCancel', e);
        },
        _onChildContextMenu: function(e) {
            this._onChildEvent('onChildContextMenu', e);
        },
        _onChildLongPress: function(e) {
            this._onChildEvent('onChildLongPress', e);
        },
        _onChildTapHold: function(e) {
            this._onChildEvent('onChildTapHold', e);
        },
        _onChildSingleTap: function(e) {
            this._onChildEvent('onChildSingleTap', e);
        },
        _onChildDoubleTap: function(e) {
            this._onChildEvent('onChildDoubleTap', e);
        },
        _onChildSwipe: function(e) {
            this._onChildEvent('onChildSwipe', e);
        },
        _onChildMouseOver: function(e) {
            var fromItem = e.getRelatedTarget(this.itemSelector),
                toItem = e.getTarget(this.itemSelector);
            if (toItem !== fromItem) {
                this._onChildEvent('onChildMouseOver', e);
            }
        },
        _onChildMouseOut: function(e) {
            var toItem = e.getRelatedTarget(this.itemSelector),
                fromItem = e.getTarget(this.itemSelector);
            if (toItem !== fromItem || !e.getRelatedTarget(this.eventDelegate)) {
                this._onChildEvent('onChildMouseOut', e);
            }
        },
        _onContainerTouchStart: function(e) {
            
            if (!e.getTarget(this.itemSelector)) {
                e.preventDefault();
                if (!this.bodyElement.getClientRegion().contains(e.getPoint())) {
                    this.getNavigationModel().lastLocation = 'scrollbar';
                }
            }
        },
        setupChildEvent: Ext.privateFn,
        
        
        
        updateEmptyState: function(empty) {
            var me = this,
                items = me.items.items,
                showInEmptyState, hide, i, item, show;
            for (i = 0; i < items.length; ++i) {
                item = items[i];
                showInEmptyState = item.showInEmptyState;
                hide = show = false;
                if (showInEmptyState === false) {
                    
                    hide = !(show = !empty);
                } else if (showInEmptyState) {
                    if (typeof showInEmptyState === 'function') {
                        hide = !(show = item.showInEmptyState(empty));
                        if (show == null) {
                            
                            continue;
                        }
                    } else {
                        hide = !(show = empty);
                    }
                }
                if (hide) {
                    if (item.isInner) {
                        me.setItemHidden(item, true);
                    } else {
                        item.hide();
                    }
                } else if (show) {
                    if (item.isInner) {
                        me.setItemHidden(item, false);
                    } else {
                        item.show();
                    }
                }
            }
        },
        
        applyNavigationModel: function(navigationModel) {
            if (navigationModel) {
                if (typeof navigationModel === 'string') {
                    navigationModel = {
                        type: navigationModel
                    };
                }
                navigationModel = Ext.Factory.navmodel(Ext.apply({
                    view: this
                }, navigationModel));
            }
            return navigationModel;
        },
        updateNavigationModel: function(navigationModel, oldNavigationModel) {
            Ext.destroy(oldNavigationModel);
        },
        getUseComponents: function() {
            return this.isComponentDataView;
        }
    }
});




Ext.define('Ext.dataview.GenericItem', {
    mixinId: 'dataviewitem',
    isDataViewItem: true,
    config: {
        innerCls: null,
        contentCls: null,
        
        recordIndex: null
    },
    updateRecordIndex: function(value) {
        this.el.dom.setAttribute('data-recordindex', value);
    },
    getDataview: function() {
        return this.parent;
    },
    
    updateInnerCls: function(cls, old) {
        this.innerElement.replaceCls(old, cls);
    },
    updateContentCls: function(cls, old) {
        this.getInnerHtmlElement().replaceCls(old, cls);
    },
    privates: {
        $dirty: false,
        dirtyCls: Ext.baseCSSPrefix + 'dirty',
        augmentToolHandler: function(tool, args) {
            
            var me = this;
            args[1] = {
                event: args.pop(),
                item: me,
                list: me.parent,
                record: me.getRecord(),
                tool: args[1]
            };
        },
        handleEmptyText: function(html) {
            var parent;
            if (!html) {
                parent = this.parent;
                if (parent && parent.getEmptyItemText) {
                    html = parent.getEmptyItemText();
                }
            }
            return html;
        },
        syncDirty: function(record) {
            var me = this,
                dirty = record.dirty;
            if (dirty !== me.$dirty) {
                me.toggleCls(me.dirtyCls, dirty);
                me.$dirty = dirty;
            }
        }
    }
});


Ext.define('Ext.dataview.DataItem', function(DataItem) {
    return {
        extend: Ext.Container,
        alternateClassName: 'Ext.dataview.component.DataItem',
        xtype: 'dataitem',
        mixins: [
            Ext.dataview.GenericItem
        ],
        config: {
            
            itemCls: null,
            
            dataMap: {
                cached: true,
                $value: null
            }
        },
        html: '\xa0',
        classCls: Ext.baseCSSPrefix + 'dataitem',
        inheritUi: true,
        autoSize: null,
        defaultType: 'component',
        
        
        
        referenceHolder: true,
        template: [
            {
                reference: 'bodyElement',
                cls: Ext.baseCSSPrefix + 'body-el',
                uiCls: 'body-el',
                children: [
                    {
                        reference: 'innerElement',
                        cls: Ext.baseCSSPrefix + 'inner-el',
                        uiCls: 'inner-el'
                    }
                ]
            }
        ],
        updateItemCls: function(newCls, oldCls) {
            this.el.replaceCls(oldCls, newCls);
        },
        
        updateRecord: function(record) {
            if (this.destroying || this.destroyed) {
                return;
            }
            var me = this,
                dataMap = me.getDataMap(),
                tpl = me.getTpl(),
                data;
            if (dataMap) {
                DataItem.executeDataMap(record, me, dataMap);
            }
            me.syncDirty(record);
            if (tpl || !dataMap || me.hasListeners.updatedata) {
                data = me.parent.gatherData(record);
                if (tpl) {
                    me.updateData(data);
                }
                
                if (me.hasListeners.updatedata) {
                    me.fireEvent('updatedata', me, data);
                }
            }
        },
        updateHtml: function(html, oldHtml) {
            this.callParent([
                this.handleEmptyText(html),
                oldHtml
            ]);
        },
        privates: {
            applyDataMap: function(dataMap) {
                return DataItem.parseDataMap(dataMap);
            },
            getRenderTarget: function() {
                return this.innerElement;
            },
            statics: {
                assignDataToItem: function(record, target, mappings, legacy) {
                    var configMap = Ext.Config.map,
                        cfg, dataPath, i, n, name, s, value;
                    for (name in mappings) {
                        s = legacy ? name : ((cfg = configMap[name]) && cfg.names.set);
                        if (!target[s]) {
                            
                            if (legacy) {
                                Ext.raise('No method "' + name + '" on ' + target.$className);
                            } else {
                                Ext.raise('No config "' + name + '" on ' + target.$className);
                            }
                            
                            
                            continue;
                        }
                        
                        
                        
                        dataPath = mappings[name];
                        value = record;
                        for (i = 0 , n = dataPath.length; value && i < n; ++i) {
                            value = value.interpret(dataPath[i]);
                        }
                        target[s]((i < n) ? null : value);
                    }
                },
                executeDataMap: function(record, item, dataMap) {
                    var reference, legacy, target, mappings;
                    for (reference in dataMap) {
                        if (!(mappings = dataMap[reference])) {
                            
                            continue;
                        }
                        legacy = false;
                        if (!(target = (reference === '#') ? item : item.lookup(reference))) {
                            
                            
                            if (typeof item[reference] === 'function') {
                                target = item[reference]();
                                legacy = true;
                                
                                if (!item.$dataMapWarning) {
                                    item.$dataMapWarning = true;
                                    Ext.log.warn('Using getters in dataMaps is deprecated (for ' + item.getId() + '); support will be removed in 7.0');
                                }
                            }
                            
                            if (!target) {
                                
                                continue;
                            }
                        }
                        DataItem.assignDataToItem(record, target, mappings, legacy);
                    }
                },
                parseDataMap: function(dataMap) {
                    var map = {},
                        inner, innerSrc, key1, key2;
                    for (key1 in dataMap) {
                        map[key1] = inner = {};
                        innerSrc = dataMap[key1];
                        for (key2 in innerSrc) {
                            inner[key2] = innerSrc[key2].split('.');
                        }
                    }
                    return map;
                }
            }
        }
    };
});




Ext.define('Ext.dataview.Component', {
    extend: Ext.dataview.Abstract,
    xtype: 'componentdataview',
    isComponentDataView: true,
    config: {
        
        itemInnerCls: null,
        
        itemConfig: {
            xtype: 'dataitem'
        },
        
        itemContentCls: null,
        
        itemDataMap: null,
        
        maxItemCache: 20,
        
        striped: null,
        
        
        itemCount: 0
    },
    
    
    
    
    
    
    
    
    
    
    
    
    firstCls: Ext.baseCSSPrefix + 'first',
    lastCls: Ext.baseCSSPrefix + 'last',
    oddCls: Ext.baseCSSPrefix + 'odd',
    beforeInitialize: function(config) {
        
        this.itemCache = [];
        this.callParent([
            config
        ]);
    },
    isFirstItem: function(item) {
        return item === this.getFirstItem();
    },
    isFirstDataItem: function(item) {
        return item === this.getFirstDataItem();
    },
    isLastItem: function(item) {
        return item === this.getLastItem();
    },
    isLastDataItem: function(item) {
        return item === this.getLastDataItem();
    },
    doDestroy: function() {
        
        Ext.destroy(this.itemCache, this.dataRange);
        this.callParent();
    },
    onRender: function() {
        var me = this,
            itemConfig = me.getItemConfig();
        
        
        if (itemConfig.viewModel) {
            me.hasItemVm = true;
            if (!me.lookupViewModel()) {
                me.setViewModel(true);
            }
        }
        me.callParent();
    },
    getViewItems: function() {
        return this.getInnerItems().slice();
    },
    onStoreAdd: function(store, records, index) {
        var me = this;
        me.callParent(arguments);
        me.setItemCount(store.getCount());
        me.syncItemRange(me.getStoreChangeSyncIndex(index));
    },
    onStoreRemove: function(store, records, index) {
        var me = this,
            len = records.length,
            dataItems = me.dataItems.splice(index, len),
            itemCount = me.getItemCount(),
            i;
        me.callParent(arguments);
        if (!dataItems.length) {
            return;
        }
        for (i = len; i-- > 0; ) {
            me.removeDataItem(dataItems[i]);
        }
        
        
        me.setItemCount(itemCount - len);
        me.syncItemRange(me.getStoreChangeSyncIndex(index));
    },
    
    
    
    updateItemInnerCls: function(cls) {
        if (!this.isConfiguring) {
            var items = this.dataItems,
                len = items.length,
                i, item;
            for (i = 0; i < len; i++) {
                item = items[i];
                if (item.setInnerCls) {
                    item.setInnerCls(cls);
                }
            }
        }
    },
    
    applyItemConfig: function(itemConfig, oldItemConfig) {
        
        
        itemConfig = itemConfig || {};
        if (oldItemConfig && !itemConfig.xtype && !itemConfig.xclass) {
            var xtype = oldItemConfig.xtype,
                xclass = oldItemConfig.xclass;
            if (xtype || xclass) {
                itemConfig = Ext.apply({}, itemConfig);
                itemConfig[xclass ? 'xclass' : 'xtype'] = xclass || xtype;
            }
        }
        return itemConfig;
    },
    updateItemConfig: function() {
        if (!this.isConfiguring) {
            this.clearItems();
            this.refresh();
        }
    },
    
    updateItemContentCls: function(cls) {
        if (!this.isConfiguring) {
            var items = this.dataItems,
                len = items.length,
                i, item;
            for (i = 0; i < len; i++) {
                item = items[i];
                if (item.setContentCls) {
                    item.setContentCls(cls);
                }
            }
        }
    },
    
    applyItemDataMap: function(dataMap) {
        return Ext.dataview.DataItem.parseDataMap(dataMap);
    },
    
    updateStriped: function(striped) {
        var me = this,
            dataItems = me.dataItems,
            oddCls = me.oddCls,
            i, el, odd;
        me.striped = !!striped;
        if (!me.isConfiguring) {
            for (i = 0; i < dataItems.length; ++i) {
                el = dataItems[i].el;
                odd = striped ? +el.dom.getAttribute('data-recordindex') : 0;
                el.toggleCls(oddCls, odd % 2);
            }
        }
    },
    
    privates: {
        dataRange: null,
        infinite: false,
        
        striped: false,
        _itemChangeHandlers: [
            'changeItemRecordIndex',
            'changeItemRecord',
            'changeItemIsFirst',
            'changeItemIsLast'
        ],
        acquireItem: function(cfg, itemsFocusable) {
            var me = this,
                at = null,
                el, item;
            if (typeof cfg === 'number') {
                at = cfg;
                cfg = null;
            }
            if (!cfg) {
                cfg = me.getItemConfig();
                itemsFocusable = me.getItemsFocusable();
            }
            
            if (!(item = me.itemCache.pop())) {
                
                item = me.createDataItem(cfg);
                item = me.addDataItem(item, at);
                el = item.element;
                
                
                
                
                
                if (itemsFocusable) {
                    (item.getFocusEl() || el).setTabIndex(-1);
                }
                
                el.dom.setAttribute('data-viewid', me.id);
            } else {
                item.removeCls(me._cachedRemoveClasses);
                
                me.addDataItem(item, at);
            }
            return item;
        },
        addDataItem: function(item, at) {
            var me = this;
            if (at === null) {
                at = me.findTailItem(
                false);
            }
            item = (at < 0) ? me.add(item) : me.insert(at, item);
            me.dataItems.push(item);
            
            return item;
        },
        
        changeItem: function(itemIndex, recordIndex) {
            var me = this,
                store = me.store,
                page = store.currentPage,
                datasetIndex = recordIndex + (page ? ((page - 1) * store.pageSize) : 0),
                dataItems = me.dataItems,
                realIndex = (itemIndex < 0) ? dataItems.length + itemIndex : itemIndex,
                item = dataItems[realIndex],
                storeCount = store.getCount(),
                handlers = me._itemChangeHandlers,
                options = {
                    isFirst: !recordIndex,
                    isLast: recordIndex === storeCount - 1,
                    item: item,
                    itemIndex: realIndex,
                    record: me.dataRange.records[recordIndex],
                    recordIndex: recordIndex,
                    datasetIndex: datasetIndex
                },
                i, itemEl;
            
            
            options.afterEl = options.beforeEl = options.itemEl = itemEl = item.renderElement;
            options.itemClasses = itemEl.getClassMap(
            false);
            options.isFirstChanged = item.isFirst !== options.isFirst;
            options.isLastChanged = item.isLast !== options.isLast;
            for (i = 0; i < handlers.length; ++i) {
                me[handlers[i]](options);
            }
            itemEl.setClassMap(options.itemClasses, 
            true);
            return options;
        },
        changeItemIsFirst: function(options) {
            if (!options.isFirstChanged) {
                return;
            }
            var me = this,
                firstCls = me.firstCls,
                item = options.item,
                itemClasses = options.itemClasses,
                items = me.scrollDockedItems,
                i, len;
            if (!(item.isFirst = options.isFirst)) {
                delete itemClasses[firstCls];
            } else {
                itemClasses[firstCls] = 1;
                if (items && !me.infinite) {
                    
                    
                    items = items.start.items;
                    len = items.length;
                    for (i = 0; i < len; ++i) {
                        items[i].renderElement.insertBefore(options.beforeEl);
                    }
                }
            }
        },
        changeItemIsLast: function(options) {
            if (!options.isLastChanged) {
                return;
            }
            var me = this,
                item = options.item,
                itemClasses = options.itemClasses,
                lastCls = me.lastCls,
                items = me.scrollDockedItems,
                i, len;
            if (!(item.isLast = options.isLast)) {
                delete itemClasses[lastCls];
            } else {
                itemClasses[lastCls] = 1;
                if (items && !me.infinite) {
                    
                    
                    items = items.end.items;
                    len = items.length;
                    for (i = 0; i < len; ++i) {
                        items[i].renderElement.insertAfter(options.afterEl);
                    }
                }
            }
        },
        changeItemRecord: function(options) {
            this.syncItemRecord(options);
        },
        changeItemRecordIndex: function(options) {
            var item = options.item,
                recordIndex = options.recordIndex,
                itemClasses = options.itemClasses,
                oddCls = this.oddCls;
            
            
            item.$datasetIndex = options.datasetIndex;
            if (item.isDataViewItem) {
                if (item.getRecordIndex() !== recordIndex) {
                    item.setRecordIndex(recordIndex);
                }
            } else {
                item.el.dom.setAttribute('data-recordindex', recordIndex);
            }
            if (this.striped && options.recordIndex % 2) {
                itemClasses[oddCls] = 1;
            } else {
                delete itemClasses[oddCls];
            }
        },
        clearItemCaches: function() {
            var cache = this.itemCache;
            Ext.destroy(cache);
            cache.length = 0;
        },
        clearItems: function() {
            var me = this,
                dataItems = me.dataItems,
                len = dataItems.length,
                itemCache = me.itemCache,
                i;
            for (i = 0; i < len; ++i) {
                me.removeDataItem(dataItems[i], true);
            }
            Ext.destroy(itemCache);
            dataItems.length = itemCache.length = 0;
            me.setItemCount(0);
        },
        createDataItem: function(cfg) {
            var me = this,
                markDirty = me.getMarkDirty(),
                cls = markDirty ? me.markDirtyCls : '',
                itemCls = me.getItemCls(),
                config;
            if (itemCls) {
                if (markDirty) {
                    cls += ' ';
                }
                cls += itemCls;
            }
            config = {
                xtype: me.getDefaultType(),
                cls: cls,
                tpl: me.getItemTpl(),
                $dataItem: 'record'
            };
            cls = me.getItemInnerCls();
            if (cls) {
                config.innerCls = cls;
            }
            cls = me.getItemContentCls();
            if (cls) {
                config.contentCls = cls;
            }
            return Ext.apply(config, cfg || me.getItemConfig());
        },
        doClear: function() {
            this.setItemCount(0);
            this.callParent();
        },
        doRefresh: function(scrollToTop) {
            var me = this,
                storeCount = me.dataRange.records.length,
                scroller = me.getScrollable(),
                restoreFocus;
            ++me.refreshCounter;
            if (scroller && scrollToTop) {
                scroller.scrollTo(0, 0);
            }
            if (storeCount) {
                
                restoreFocus = me.saveFocusState();
                me.hideEmptyText();
                me.setItemCount(storeCount);
                me.syncItemRange();
                if (me.hasSelection()) {
                    me.setItemSelection(me.getSelections(), true);
                }
                restoreFocus();
            } else {
                me.doClear();
            }
        },
        getFastItems: function() {
            return this.getInnerItems();
        },
        getStoreChangeSyncIndex: function(index) {
            return index;
        },
        removeCachedItem: function(item, preventCache, cache, max, preventRemoval) {
            var me = this,
                ret = false;
            if (!preventCache && cache.length < max) {
                
                
                if (preventRemoval) {
                    me.setItemHidden(item, true);
                } else {
                    me.remove(item, 
                    false);
                }
                cache.push(item);
            } else {
                item.destroy();
                ret = true;
            }
            return ret;
        },
        removeDataItem: function(item, preventCache) {
            return this.removeCachedItem(item, preventCache, this.itemCache, this.getMaxItemCache());
        },
        syncItemRange: function(start, end) {
            var count = this.store.getCount(),
                i;
            if (end == null) {
                end = count;
            }
            for (i = start || 0; i < end; ++i) {
                this.changeItem(i, i);
            }
        },
        syncItemRecord: function(options, tombstoneRec) {
            var me = this,
                item = options.item,
                itemClasses = options && options.itemClasses,
                oldRecord = item.getRecord(),
                record = tombstoneRec || options.record,
                dataMap = me.getItemDataMap(),
                el = item.el,
                viewModel = item.getViewModel(),
                selectedCls = me.selectedCls;
            if (oldRecord === record) {
                if (!tombstoneRec) {
                    if (item.isRecordRefreshable) {
                        item.refresh(options);
                    } else {
                        item.updateRecord(record, oldRecord);
                    }
                }
            } else {
                
                if (me.getSelectable().isRowSelected(record)) {
                    if (itemClasses) {
                        itemClasses[selectedCls] = true;
                    } else {
                        el.addCls(selectedCls);
                    }
                } else if (itemClasses) {
                    delete itemClasses[selectedCls];
                } else {
                    el.removeCls(selectedCls);
                }
                item.setRecord(record);
                item.el.dom.setAttribute('data-recordid', record.internalId);
            }
            if (dataMap) {
                Ext.dataview.DataItem.executeDataMap(record, item, dataMap);
            }
            if (viewModel) {
                viewModel.setData({
                    record: options.record
                });
            }
        },
        
        traverseItem: function(item, delta) {
            var me = this,
                items = me.innerItems,
                next = null,
                cmp = item,
                i;
            if (item) {
                if (item.isElement) {
                    cmp = Ext.getCmp(item.id);
                }
                i = items.indexOf(cmp);
                if (i > -1) {
                    next = items[i + delta] || null;
                }
            }
            return next;
        },
        
        
        
        updateItemCount: function(count) {
            var me = this,
                items = me.dataItems,
                cfg, itemsFocusable;
            if (items.length < count) {
                cfg = me.getItemConfig();
                itemsFocusable = me.getItemsFocusable();
                while (items.length < count) {
                    me.acquireItem(cfg, itemsFocusable);
                }
            }
            while (items.length > count) {
                me.removeDataItem(items.pop());
            }
        }
    }
}, 
function(ComponentDataView) {
    var proto = ComponentDataView.prototype;
    proto._cachedRemoveClasses = [
        proto.pressedCls,
        proto.selectedCls
    ];
});


Ext.define('Ext.dataview.Pinnable', {
    mixinId: 'dataviewpinnable',
    isDataViewPinnable: true,
    config: {
        
        pinned: null
    },
    pinnedCls: Ext.baseCSSPrefix + 'pinned',
    updatePinned: function(value) {
        var me = this,
            el = me.el,
            pinnedCls = me.pinnedCls,
            pinnedClsMap = me._pinnedClsMap,
            classes = el.getClassMap(
            false);
        delete classes[pinnedClsMap.top];
        delete classes[pinnedClsMap.bottom];
        if (value) {
            classes[pinnedCls] = true;
            pinnedCls = pinnedClsMap[value];
            if (pinnedCls) {
                classes[pinnedCls] = true;
            }
        } else {
            delete classes[pinnedCls];
        }
        el.setClassMap(classes, 
        true);
    },
    _pinnedClsMap: {
        top: Ext.baseCSSPrefix + 'pinned-top',
        bottom: Ext.baseCSSPrefix + 'pinned-bottom'
    }
});


Ext.define('Ext.dataview.ItemHeader', {
    extend: Ext.Component,
    xtype: 'itemheader',
    mixins: [
        Ext.mixin.Toolable,
        Ext.dataview.Pinnable
    ],
    isItemHeader: true,
    config: {
        
        group: null,
        contentWidth: null
    },
    
    html: '\xa0',
    classCls: Ext.baseCSSPrefix + 'itemheader',
    inheritUi: true,
    toolDefaults: {
        ui: 'itemheader'
    },
    template: [
        {
            reference: 'bodyElement',
            cls: Ext.baseCSSPrefix + 'body-el',
            uiCls: 'body-el'
        }
    ],
    setGroup: function(group) {
        var me = this,
            was = me._group;
        
        
        me._group = group;
        me.updateGroup(group, was);
        return me;
    },
    updateGroup: function(group) {
        var me = this,
            data, grouper, html, list, tpl;
        if (group) {
            list = me.parent;
            grouper = list.getStore().getGrouper();
            
            
            tpl = (grouper && grouper.owner === list && grouper.headerTpl) || me.getTpl();
            if (tpl) {
                data = me.getGroupHeaderTplData();
                html = tpl.apply(data);
            }
        }
        me.setHtml(html || '\xa0');
    },
    getScrollerTarget: function() {
        return this.el;
    },
    doDestroy: function() {
        this.mixins.toolable.doDestroy.call(this);
        this.callParent();
    },
    privates: {
        augmentToolHandler: function(tool, args) {
            
            var info = args[1] = {
                    event: args.pop(),
                    group: this.getGroup(),
                    itemHeader: args[0],
                    tool: args[1]
                };
            args[0] = info.list = this.parent;
        },
        getGroupHeaderTplData: function(skipHtml) {
            var group = this.getGroup(),
                list = this.parent,
                data = group && {
                    name: group.getGroupKey(),
                    group: group,
                    groupField: list.getStore().getGrouper().getProperty(),
                    children: group.items,
                    count: group.length
                };
            if (data) {
                data.value = group.items[0].data[data.groupField];
            }
            if (!skipHtml) {
                data.html = Ext.htmlEncode(data.name);
            }
            
            data.groupValue = data.value;
            return data;
        },
        getList: function() {
            return this.parent;
        },
        
        updateContentWidth: function(width) {
            var el = this._toolDockWrap || this.bodyElement;
            if (el) {
                el.setWidth(width ? width : null);
            }
        }
    }
});


Ext.define('Ext.dataview.Disclosable', {
    mixinId: 'disclosable',
    isListItem: true,
    toolDefaults: {
        ui: 'listitem'
    },
    toolAnchorName: 'innerElement',
    getDisclosure: function() {
        return this.lookupTool('disclosure');
    },
    privates: {
        invokeDisclosure: function(tool, handler, e) {
            var parent = this.parent;
            if (tool.type === 'disclosure' && !handler) {
                if (parent && parent.onItemDisclosureTap) {
                    parent.onItemDisclosureTap(this, e);
                    return true;
                }
            }
        },
        syncDisclosure: function(record) {
            var me = this,
                disclosure = me.getDisclosure(),
                parent = me.parent;
            if (disclosure) {
                disclosure.setHidden(parent.shouldHideDisclosure(record));
            }
        }
    }
});


Ext.define('Ext.dataview.SimpleListItem', {
    extend: Ext.Component,
    alternateClassName: 'Ext.dataview.component.SimpleListItem',
    xtype: 'simplelistitem',
    mixins: [
        Ext.dataview.Disclosable,
        Ext.mixin.Toolable,
        Ext.dataview.GenericItem,
        Ext.dataview.Pinnable
    ],
    classCls: Ext.baseCSSPrefix + 'listitem',
    inheritUi: true,
    html: '\xa0',
    template: [
        {
            reference: 'bodyElement',
            cls: Ext.baseCSSPrefix + 'body-el',
            uiCls: 'body-el',
            children: [
                {
                    reference: 'innerElement',
                    cls: Ext.baseCSSPrefix + 'inner-el',
                    uiCls: 'inner-el'
                }
            ]
        }
    ],
    doDestroy: function() {
        this.mixins.toolable.doDestroy.call(this);
        this.callParent();
    },
    
    
    handleFocusEvent: Ext.emptyFn,
    updateRecord: function(record) {
        if (this.destroying || this.destroyed) {
            return;
        }
        var me = this,
            dataview = me.parent,
            data = dataview && dataview.gatherData(record);
        me.updateData(data);
        me.syncDisclosure(record);
    },
    updateHtml: function(html, oldHtml) {
        this.callParent([
            this.handleEmptyText(html),
            oldHtml
        ]);
    },
    privates: {
        getRenderTarget: function() {
            return this.innerElement;
        },
        invokeToolHandler: function(tool, handler, scope, args, e) {
            if (this.invokeDisclosure(tool, handler, e)) {
                return false;
            }
            return tool.invokeToolHandler(tool, handler, scope, args, e);
        }
    }
});


Ext.define('Ext.dataview.List', {
    extend: Ext.dataview.Component,
    alternateClassName: 'Ext.List',
    xtype: 'list',
    isList: true,
    mixins: [
        Ext.mixin.Bufferable
    ],
    config: {
        
        bufferSize: 20,
        
        disclosureProperty: 'disclosure',
        
        grouped: null,
        
        groupFooter: {
            cached: true,
            $value: null
        },
        
        groupHeader: {
            cached: true,
            $value: {
                xtype: 'itemheader',
                tpl: '{html} ({count})'
            }
        },
        
        indexBar: null,
        
        infinite: null,
        
        minimumBufferDistance: 5,
        
        onItemDisclosure: {
            $value: null,
            merge: function(value, oldValue, target) {
                
                
                var t = value && target && target.$isClass && typeof value;
                if (t === 'string' || t === 'function') {
                    return {
                        handler: value,
                        scope: 'self'
                    };
                }
                return value;
            }
        },
        
        pinFooters: false,
        
        pinHeaders: true,
        
        pinnedFooter: {
            lazy: true,
            $value: null
        },
        
        pinnedHeader: {
            lazy: true,
            $value: {
                xtype: 'itemheader'
            }
        },
        
        preventSelectionOnDisclose: true,
        
        preventSelectionOnTool: true,
        
        rowLines: null,
        
        useSimpleItems: null,
        
        variableHeights: null,
        
        
        
        horizontalOverflow: null,
        
        innerCtHeight: null,
        
        innerWidth: null,
        
        pinnedFooterHeight: null,
        
        pinnedHeaderHeight: null,
        
        topRenderedIndex: null,
        
        verticalOverflow: null,
        
        visibleHeight: null,
        
        visibleLeft: null,
        
        visibleTop: null,
        
        visibleWidth: null
    },
    
    
    
    
    
    
    
    
    
    
    
    bufferableMethods: {
        syncVerticalOverflow: 1
    },
    
    classCls: Ext.baseCSSPrefix + 'list',
    
    itemConfig: {
        xtype: 'simplelistitem'
    },
    
    maintainChildNodes: false,
    
    rowHeight: 0,
    
    scrollable: {
        x: false,
        y: true
    },
    storeEventListeners: {
        groupchange: 'onStoreGroupChange',
        totalcountchange: 'onStoreTotalCountChange'
    },
    
    template: [
        {
            reference: 'bodyElement',
            cls: Ext.baseCSSPrefix + 'body-el',
            uiCls: 'body-el',
            children: [
                {
                    
                    reference: 'outerCt',
                    className: Ext.baseCSSPrefix + 'list-outer-ct',
                    children: [
                        {
                            
                            reference: 'innerCt',
                            className: Ext.baseCSSPrefix + 'list-inner-ct'
                        }
                    ]
                }
            ]
        }
    ],
    
    beforeInitialize: function(config) {
        var me = this,
            
            
            infinite = me.getInfinite();
        
        if (!infinite) {
            me.innerCt.on('resize', 'onInnerCtResize', me);
        }
        me.gapMap = {};
        
        if (!me.itemTranslationMethod) {
            me.itemTranslationMethod = Ext.supports.TranslateYCausesHorizontalScroll ? 'cssposition' : 'csstransform';
        }
        
        
        
        me.groupingInfo = {
            headers: {
                config: me.getGroupHeader(),
                creator: 'createGroupHeader',
                name: '$header',
                offset: 0,
                unused: []
            },
            footers: {
                config: me.getGroupFooter(),
                creator: 'createGroupFooter',
                name: '$footer',
                offset: 1,
                unused: []
            }
        };
        
        
        me.bodyElement.on({
            resize: 'onBodyResize',
            scope: me
        });
        me.stickyItems = [];
        me.stickyItemsByRecordId = {};
        
        me.callParent([
            config
        ]);
    },
    doDestroy: function() {
        var me = this,
            groupingInfo = me.groupingInfo,
            scrollInfo = me.scrollInfo;
        
        
        
        if (scrollInfo) {
            Ext.unraf(scrollInfo.timer);
        }
        Ext.destroy(me.resyncListener, groupingInfo.headers.unused, groupingInfo.footers.unused);
        me.callParent();
    },
    createIndexBar: function(config) {
        return Ext.apply({
            xtype: 'indexbar',
            $initParent: this,
            parent: this,
            hidden: true
        }, config);
    },
    createPinnedFooter: function(config) {
        var ret = this.createPinnedHeaderFooter(config);
        ret.bottom = 0;
        ret.pinned = 'bottom';
        return ret;
    },
    createPinnedHeader: function(config) {
        var me = this,
            groupedHeader = me.getGroupHeader(),
            ret = me.createPinnedHeaderFooter(config),
            tools;
        ret.top = 0;
        ret.pinned = 'top';
        if (!ret.tpl && groupedHeader.tpl) {
            ret.tpl = groupedHeader.tpl;
        }
        if (!('tools' in ret)) {
            tools = groupedHeader && groupedHeader.tools;
            if (tools) {
                ret.tools = tools;
            }
        }
        return ret;
    },
    isGrouping: function() {
        var store = this.getGrouped() && this.store,
            grouper = store && store.getGrouper();
        return !!grouper;
    },
    
    isRecordRendered: function(recordIndex) {
        if (!this.infinite) {
            return true;
        }
        var renderInfo = this.renderInfo;
        if (recordIndex.isEntity) {
            recordIndex = this.store.indexOf(recordIndex);
        }
        return recordIndex >= renderInfo.indexTop && recordIndex < renderInfo.indexBottom;
    },
    mapToViewIndex: function(value) {
        var me = this,
            indexOffset;
        if (me.infinite && typeof value === 'number') {
            
            indexOffset = me.renderInfo.indexTop;
            value -= indexOffset;
        }
        return me.callParent([
            value,
            indexOffset
        ]);
    },
    
    scrollToRecord: function(record, animation) {
        return this.ensureVisible({
            record: record,
            animation: animation
        });
    },
    shouldSelectItem: function(e) {
        var me = this,
            no = !me.callParent([
                e
            ]),
            cmp;
        if (!no) {
            cmp = e.getTarget(me.toolSelector);
            cmp = cmp && me.el.contains(cmp) && Ext.Component.from(cmp);
            if (cmp) {
                no = cmp.preventSelection;
                if (no == null) {
                    if (cmp.type === 'disclosure') {
                        no = me.getPreventSelectionOnDisclose();
                    } else {
                        no = me.getPreventSelectionOnTool();
                    }
                }
            }
        }
        return !no;
    },
    
    onBodyResize: function(el, info) {
        var me = this,
            height = info.height,
            width = info.width;
        if (width === me.getVisibleWidth()) {
            me.setVisibleHeight(height);
        } else {
            
            
            me.suspendSync = true;
            me.setVisibleHeight(me.outerCt.measure('h'));
            me.suspendSync = false;
            me.setVisibleWidth(width);
        }
    },
    onItemAdd: function(item, index) {
        var me = this;
        if (me.infinite) {
            if (item.$dataItem && me.variableHeights) {
                item.on('resize', 'onDataItemResize', me);
            }
            if (item.isInner) {
                item.setTranslatable({
                    type: me.itemTranslationMethod
                });
            }
        }
        return me.callParent([
            item,
            index
        ]);
    },
    onItemRemove: function(item, index, destroying) {
        var me = this,
            height = item.$height,
            scrollDock = item.scrollDock;
        me.callParent([
            item,
            index,
            destroying
        ]);
        if (scrollDock && height) {
            Ext.Array.remove(me.scrollDockedItems[scrollDock].items, item);
            me.adjustScrollDockHeight(scrollDock, -height);
        }
    },
    onStoreAdd: function(store, records, index) {
        var me = this;
        me.syncEmptyState();
        if (me.infinite) {
            if (me.getVisibleHeight()) {
                me.refreshGrouping();
                me.resyncOnPaint();
            }
        } else {
            me.refreshGroupIndices();
            me.setItemCount(store.getCount());
            
            
            me.syncItemRange(Math.max(0, index - 1));
        }
    },
    onStoreRemove: function(store, records, index) {
        var me = this;
        if (me.infinite) {
            if (me.getVisibleHeight()) {
                me.refreshGrouping();
                me.resyncOnPaint();
            }
            me.syncEmptyState();
        } else {
            me.refreshGroupIndices();
            me.callParent([
                store,
                records,
                index
            ]);
        }
    },
    onStoreUpdate: function(store, record, type, modifiedFieldNames, info) {
        var me = this;
        if (info && info.groupChanged && me.isGrouping()) {
            me.refreshGrouping();
            me.syncRows();
        } else {
            me.callParent([
                store,
                record,
                type,
                modifiedFieldNames,
                info
            ]);
        }
    },
    
    
    
    updateGrouped: function() {
        var me = this;
        if (me.initialized) {
            me.refreshGrouping();
            me.syncRows();
        }
    },
    
    updateGroupFooter: function(footer) {
        var groupingInfo = this.groupingInfo;
        if (groupingInfo) {
            
            
            groupingInfo.footers.config = footer;
        }
    },
    
    applyGroupHeader: function(header) {
        var tpl = header && header.tpl;
        
        
        if (tpl != null) {
            header = Ext.apply({}, header);
            header.tpl = Ext.XTemplate.get(tpl);
        }
        return header;
    },
    updateGroupHeader: function(header) {
        var groupingInfo = this.groupingInfo;
        if (groupingInfo) {
            
            
            groupingInfo.headers.config = header;
        }
    },
    
    applyIndexBar: function(config, existing) {
        return Ext.updateWidget(existing, config, this, 'createIndexBar');
    },
    updateIndexBar: function(indexBar) {
        if (indexBar) {
            this.add(indexBar);
        }
    },
    
    applyItemConfig: function(itemConfig, oldItemConfig) {
        var ret = this.callParent([
                itemConfig,
                oldItemConfig
            ]),
            disclosure, tools;
        if (this.getOnItemDisclosure()) {
            disclosure = {
                disclosure: true
            };
            tools = ret.tools;
            ret.tools = tools ? Ext.merge(disclosure, tools) : disclosure;
        }
        return ret;
    },
    
    updateInfinite: function(infinite) {
        var me = this;
        me.infinite = infinite;
        
        me.freezeConfig('infinite');
        
        if (infinite) {
            me.setItemHidden = me.setItemHiddenInfinite;
            me.el.addCls(me.infiniteCls);
            me.innerCt.addCls(me.infiniteCls);
            
            me.renderInfo = {
                
                
                
                
                
                
                
                
                
                
                
                
                
                atBegin: false,
                atEnd: false,
                bottom: 0,
                height: 0,
                top: 0,
                indexBottom: 0,
                indexTop: 0
            };
            me.scrollInfo = {};
            me.getScrollable().on({
                scope: me,
                scroll: 'onContainerScroll',
                scrollstart: 'onContainerScrollStart',
                scrollend: 'onContainerScrollEnd'
            });
        }
    },
    
    updatePinFooters: function(pinFooters) {
        var me = this,
            pinnedFooter = me.pinnedFooter;
        me.pinFooters = pinFooters;
        if (me.isConfiguring) {
            return;
        }
        if (me.setupFooterPinning()) {
            if (me.infinite) {
                me.syncPinnedFooter();
            }
        } else if (pinnedFooter) {
            me.setItemHidden(pinnedFooter, true);
        }
    },
    
    applyPinnedFooter: function(config, existing) {
        var me = this,
            ret = Ext.updateWidget(existing, config, me, 'createPinnedFooter'),
            index;
        if (!existing) {
            index = me.getIndexBar();
            if (index) {
                index = me.indexOf(index);
                me.insert(index, ret);
            } else {
                me.add(ret);
            }
            me.setItemHidden(ret, true);
        }
        return ret;
    },
    updatePinnedFooter: function(pinnedFooter) {
        var me = this;
        
        
        me.pinnedFooter = pinnedFooter;
        me.setupFooterPinning();
        if (pinnedFooter) {
            pinnedFooter.$pinnedFooter = true;
        }
    },
    
    updatePinHeaders: function(pinHeaders) {
        var me = this,
            pinnedHeader = me.pinnedHeader;
        me.pinHeaders = pinHeaders;
        if (me.isConfiguring) {
            return;
        }
        if (me.setupHeaderPinning()) {
            if (me.infinite) {
                me.syncPinnedHeader();
            }
        } else if (pinnedHeader) {
            me.setItemHidden(pinnedHeader, true);
        }
    },
    
    applyPinnedHeader: function(config, existing) {
        var me = this,
            ret = Ext.updateWidget(existing, config, me, 'createPinnedHeader');
        if (!existing && ret) {
            me.insert(0, ret);
            me.setItemHidden(ret, true);
        }
        return ret;
    },
    updatePinnedHeader: function(pinnedHeader) {
        var me = this;
        
        
        me.pinnedHeader = pinnedHeader;
        me.setupHeaderPinning();
        if (pinnedHeader) {
            pinnedHeader.$pinnedHeader = true;
        }
    },
    
    updateRowLines: function(rowLines) {
        this.innerCt.toggleCls(this.noRowLinesCls, rowLines === false);
    },
    
    updateUseSimpleItems: function(useSimpleItems) {
        
        if (!this.self._updateUseSimpleItemsWarning) {
            this.self._updateUseSimpleItemsWarning = true;
            Ext.log.warn('The Ext.List#useSimpleItems config is deprecated; ' + 'use itemConfig.xtype instead');
        }
        
        var itemConfig = this.getItemConfig();
        itemConfig = Ext.applyIf({
            xtype: useSimpleItems ? 'simplelistitem' : 'listitem'
        }, itemConfig);
        this.setItemConfig(itemConfig);
    },
    
    updateVariableHeights: function(variableHeights) {
        this.variableHeights = variableHeights;
    },
    updateStore: function(store, oldStore) {
        var me = this;
        me.callParent([
            store,
            oldStore
        ]);
        if (store) {
            
            if (me.isConfiguring && this.getGrouped() != null) {
                return;
            }
            this.setGrouped(store.getGrouper());
        }
    },
    privates: {
        
        
        
        
        toolSelector: '.' + Ext.baseCSSPrefix + 'tool:not(.' + Ext.baseCSSPrefix + 'passive)',
        infiniteCls: Ext.baseCSSPrefix + 'infinite',
        groupFirstCls: Ext.baseCSSPrefix + 'group-first',
        groupLastCls: Ext.baseCSSPrefix + 'group-last',
        groupedCls: Ext.baseCSSPrefix + 'grouped',
        hasPinnedFooterCls: Ext.baseCSSPrefix + 'has-pinned-footer',
        hasPinnedHeaderCls: Ext.baseCSSPrefix + 'has-pinned-header',
        noRowLinesCls: Ext.baseCSSPrefix + 'no-row-lines',
        stickyCls: Ext.baseCSSPrefix + 'sticky',
        tombstoneCls: Ext.baseCSSPrefix + 'tombstone',
        blockingScroll: 0,
        discardMeasureRow: false,
        gapAfter: 0,
        groupingInfo: null,
        measuredFirstRow: false,
        pinnedFooter: null,
        pinnedHeader: null,
        lastAdjustedPosition: null,
        measuredHeight: null,
        renderInfo: null,
        
        suspendSync: false,
        
        
        onAnimationFrame: function() {
            var me = this,
                info = me.scrollInfo,
                x = info.x,
                y = info.y;
            if (x !== null) {
                me.setVisibleLeft(x);
            }
            if (y !== null) {
                me.setVisibleTop(y);
            }
            info.timer = null;
        },
        onContainerScroll: function(scroller, x, y, dx, dy) {
            var me = this,
                info = me.scrollInfo;
            if (!me.blockingScroll) {
                info.x = dx ? x : null;
                info.y = dy ? y : null;
                if (!info.timer) {
                    info.timer = Ext.raf(me.onAnimationFrame, me);
                }
            }
        },
        onContainerScrollStart: function() {
            this.toggleHoverCls(false);
            this.doHover = false;
        },
        onContainerScrollEnd: function() {
            this.doHover = true;
            this.toggleHoverCls(true);
        },
        onDataItemResize: function(item, width, height) {
            var me = this,
                dataItems = me.dataItems,
                renderInfo = me.renderInfo,
                row = item.$dataRow || item,
                
                bottomUp, count, index, y;
            height += item.el.getMargin('tb');
            
            
            
            if (item.$height !== height) {
                
                item.$height = height;
                
                
                
                
                index = dataItems.indexOf(row);
                if (index > -1) {
                    if (renderInfo.indexTop && renderInfo.indexBottom >= me.store.getCount()) {
                        
                        
                        
                        bottomUp = true;
                        count = index + 1;
                        y = row.$y1;
                    } else {
                        
                        
                        
                        count = dataItems.length - index;
                        y = row.$y0;
                    }
                    me.positionItems(y, bottomUp, count);
                }
            }
        },
        onItemDisclosureTap: function(item, e) {
            var me = this,
                record = item.getRecord(),
                index = me.store.indexOf(record);
            me.fireAction('disclose', [
                me,
                record,
                item,
                index,
                e
            ], 'doDisclose');
        },
        _onChildTouchCancel: function(e) {
            if (!e.getTarget(this.toolSelector)) {
                this.callParent([
                    e
                ]);
            }
        },
        _onChildTouchEnd: function(e) {
            if (!e.getTarget(this.toolSelector)) {
                this.callParent([
                    e
                ]);
            }
        },
        _onChildTouchStart: function(e) {
            if (!e.getTarget(this.toolSelector)) {
                this.callParent([
                    e
                ]);
            }
        },
        onRangeAvailable: function() {
            
            
            this.syncRows();
        },
        onScrollDockItemHide: function(item) {
            var height = item.$height;
            if (height) {
                this.adjustScrollDockHeight(item.scrollDock, -height);
                
                
                item.$height = null;
            }
        },
        onScrollDockItemResize: function(item, width, height) {
            var was = item.$height;
            if (was !== height) {
                item.$height = height;
                this.adjustScrollDockHeight(item.scrollDock, height - was);
            }
        },
        onScrollDockItemShow: function(item) {
            var height = item.$height;
            if (height == null) {
                height = this.measureItem(item);
            }
            this.adjustScrollDockHeight(item.scrollDock, height);
        },
        onStoreGroupChange: function(store, grouper) {
            if (this.initialized) {
                this.refreshGrouping();
                this.syncRows();
            }
            this.setGrouped(!!grouper);
        },
        onStoreTotalCountChange: function() {
            if (this.getVisibleHeight()) {
                this.syncRowsToHeight();
            }
            this.syncEmptyState();
        },
        
        
        addDataItem: function(item, at) {
            var me = this,
                ret;
            ret = me.callParent([
                item,
                at
            ]);
            ret.$height = me.variableHeights ? null : me.rowHeight;
            return ret;
        },
        addScrollDockedItem: function(item) {
            var me = this;
            if (me.infinite) {
                item.on({
                    hide: 'onScrollDockItemHide',
                    resize: 'onScrollDockItemResize',
                    show: 'onScrollDockItemShow',
                    scope: me
                });
                item.$height = null;
                me.setItemHidden(item, true);
            }
        },
        adjustContentTop: function(adjust) {
            var me = this,
                rows = this.dataItems,
                len = rows.length,
                renderInfo = me.renderInfo,
                scrollDock = me.scrollDockedItems,
                i, row, decoration, item, items;
            for (i = 0; i < len; ++i) {
                row = rows[i];
                me.setItemPosition(row, row.$position + adjust);
                decoration = row.$header;
                if (decoration) {
                    me.setItemPosition(decoration, decoration.$position + adjust);
                }
                decoration = row.$footer;
                if (decoration) {
                    me.setItemPosition(decoration, decoration.$position + adjust);
                }
                row.$y0 += adjust;
                row.$y1 += adjust;
            }
            if (scrollDock) {
                if (renderInfo.atBegin) {
                    items = scrollDock.start.items;
                    len = items.length;
                    for (i = 0; i < len; ++i) {
                        item = items[i];
                        if (!item.getHidden()) {
                            me.setItemPosition(item, item.$position + adjust);
                        }
                    }
                }
                if (renderInfo.atEnd) {
                    items = scrollDock.end.items;
                    len = items.length;
                    for (i = 0; i < len; ++i) {
                        item = items[i];
                        if (!item.getHidden()) {
                            me.setItemPosition(item, item.$position + adjust);
                        }
                    }
                }
            }
        },
        adjustScrollDockHeight: function(which, amount) {
            var me = this,
                scrollDock = me.scrollDockedItems;
            scrollDock = scrollDock && scrollDock[which];
            if (scrollDock) {
                scrollDock.height += amount;
                me.resyncOnPaint();
            }
        },
        adjustRenderedRows: function(y, oldY) {
            
            var me = this,
                bufferSize = me.getBufferSize(),
                minimumBufferDistance = me.getMinimumBufferDistance(),
                renderInfo = me.renderInfo,
                indexTop = renderInfo.indexTop,
                indexBottom = renderInfo.indexBottom,
                rows = me.dataItems,
                rowCount = rows.length,
                height = me.getVisibleHeight(),
                storeCount = me.store.getCount(),
                
                visibleTopIndex = me.recordIndexByPosition(y),
                visibleBottomIndex = me.recordIndexByPosition(y + height),
                newIndexTop, delta;
            
            if (oldY < y) {
                
                if (indexBottom - visibleBottomIndex >= minimumBufferDistance) {
                    return;
                }
            } else 
            {
                
                if (visibleTopIndex - indexTop >= minimumBufferDistance) {
                    return;
                }
            }
            
            
            
            newIndexTop = visibleTopIndex - (bufferSize >>> 1);
            
            newIndexTop = Math.max(0, Math.min(newIndexTop, storeCount - rowCount));
            delta = newIndexTop - indexTop;
            if (delta > 0 && delta < rowCount) {
                me.rollDown(delta);
            } else if (delta < 0 && -delta < rowCount) {
                me.rollUp(-delta);
            } else if (delta || me.refreshing) {
                
                me.teleport(y);
            }
        },
        bindStore: function(store) {
            var me = this,
                Model = store.getModel(),
                tombstoneRec = new Model();
            
            if (store.isBufferedStore) {
                Ext.raise('Did you mean to use Ext.data.virtual.Store? ' + '(Ext.data.BufferedStore is not supported)');
            }
            if (store.isVirtualStore && !me.infinite) {
                Ext.raise('Virtual stores require infinite:true');
            }
            
            me.dataRange = store.createActiveRange({
                prefetch: true,
                callback: 'onRangeAvailable',
                scope: me
            });
            me.tombstoneRec = tombstoneRec;
            tombstoneRec.tombstone = true;
            if (me.getVisibleHeight()) {
                me.syncRowsToHeight();
            }
        },
        bisectPosition: function(y) {
            var rows = this.dataItems,
                begin = 0,
                end = rows.length - 1,
                middle, midVal;
            if (y < rows[0].$y0) {
                return -1;
            }
            while (begin <= end) {
                middle = (begin + end) >>> 1;
                
                midVal = rows[middle].$y0;
                if (y === midVal) {
                    return middle;
                }
                if (midVal < y) {
                    begin = middle + 1;
                } else {
                    end = middle - 1;
                }
            }
            if (begin && y < rows[begin - 1].$y1) {
                --begin;
            }
            return begin;
        },
        blockAndScrollTo: function(y, anim) {
            var me = this,
                scroller = me.getScrollable();
            if (scroller.getPosition().y !== y) {
                
                
                scroller.on({
                    single: true,
                    priority: -1000,
                    scroll: function() {
                        --me.blockingScroll;
                    }
                });
                ++me.blockingScroll;
            }
            return scroller.scrollTo(null, y, anim);
        },
        changeHeaderFooter: function(item, recordIndex, def, enabled) {
            var me = this,
                property = def.name,
                decoration = item[property] || null,
                infinite = me.infinite,
                group, destroyed;
            
            
            enabled = enabled && def.config;
            
            group = enabled && def.map[recordIndex];
            if (group) {
                if (!decoration) {
                    if (!(decoration = def.unused.pop())) {
                        decoration = me[def.creator]();
                    }
                    decoration = me.reorderItem(decoration, item, def.offset);
                }
                decoration.$dataRow = item;
                decoration.setGroup(group);
            } else if (decoration) {
                destroyed = me.removeGroupItem(decoration, def.unused, !enabled);
                if (!destroyed && infinite) {
                    
                    
                    me.setItemHidden(decoration, true);
                    
                    
                    me.reorderItem(decoration);
                }
                
                decoration = null;
            }
            item[property] = decoration;
        },
        changeItem: function(itemIndex, recordIndex) {
            var me = this,
                options = me.callParent([
                    itemIndex,
                    recordIndex
                ]),
                item = options.item;
            if (me.infinite && me.variableHeights) {
                item.$height = null;
            }
            
            return options;
        },
        changeItemGrouping: function(options) {
            var me = this,
                enabled = me.isGrouping(),
                groupingInfo = me.groupingInfo,
                item = options.item,
                recordIndex = options.recordIndex;
            me.changeHeaderFooter(item, recordIndex, groupingInfo.headers, enabled);
            me.changeHeaderFooter(item, recordIndex, groupingInfo.footers, enabled);
        },
        changeItemIsFirst: function(options) {
            if (options.isFirstChanged) {
                var me = this,
                    items = me.scrollDockedItems,
                    i, len;
                me.callParent([
                    options
                ]);
                
                if (items && !options.isFirst && me.infinite) {
                    items = items.start.items;
                    len = items.length;
                    for (i = 0; i < len; ++i) {
                        me.setItemHidden(items[i], true);
                    }
                }
            }
        },
        changeItemIsLast: function(options) {
            if (options.isLastChanged) {
                var me = this,
                    items = me.scrollDockedItems,
                    i, len;
                me.callParent([
                    options
                ]);
                
                if (items && !options.isLast && me.infinite) {
                    items = items.end.items;
                    len = items.length;
                    for (i = 0; i < len; ++i) {
                        me.setItemHidden(items[i], true);
                    }
                }
            }
        },
        changeItemRecord: function(options) {
            var me = this,
                itemClasses = options.itemClasses,
                tombstoneCls = me.tombstoneCls;
            if (options.record) {
                delete itemClasses[tombstoneCls];
                
                
                me.syncItemRecord(options);
            } else {
                itemClasses[tombstoneCls] = 1;
                me.syncItemRecord(options, me.tombstoneRec);
            }
        },
        changeItemStuck: function(options) {
            
            
            var me = this,
                item = options.item,
                record = options.record,
                stickyItem = record && me.stickyItemsByRecordId[record.internalId] || null;
            if (item.$sticky) {
                if (record !== item.getRecord()) {
                    
                    
                    
                    
                    
                    me.dislodgeItem(item, options, stickyItem);
                }
            } else if (stickyItem) {
                
                
                
                me.dislodgeItem(item, options, stickyItem);
                me.removeDataItem(item);
            }
        },
        clearItemCaches: function() {
            var info = this.groupingInfo,
                headers = info.headers.unused,
                footers = info.footers.unused;
            this.callParent();
            Ext.destroy(headers, footers);
            headers.length = footers.length = 0;
        },
        constrainStickyItem: function(item) {
            var me = this,
                pinnedFooter = me.pinnedFooter,
                pinnedHeader = me.pinnedHeader,
                pinned = false,
                renderInfo = me.renderInfo,
                recordIndex = item.$recordIndex,
                h = me.measureItem(item, me),
                options = item.$sticky,
                y = options.pos,
                y0 = me.getVisibleTop(),
                y1 = y0 + me.getVisibleHeight() - h,
                ret = y,
                hide;
            if (options.floated) {
                me.setItemHidden(item, false);
                return null;
            }
            if (pinnedHeader) {
                y0 += me.measureItem(pinnedHeader);
            }
            if (pinnedFooter) {
                y1 -= me.measureItem(pinnedFooter);
            }
            if (recordIndex < renderInfo.indexTop) {
                hide = true;
                y = y0;
                pinned = 'top';
            } else if (recordIndex >= renderInfo.indexBottom) {
                hide = true;
                y = y1;
                pinned = 'bottom';
            } else if (y < y0) {
                y = y0;
                pinned = 'top';
            } else if (y > y1) {
                y = y1;
                pinned = 'bottom';
            }
            if (options.autoPin) {
                ret = y;
                if (item.isDataViewPinnable) {
                    item.setPinned(pinned);
                }
            } else if (hide) {
                me.setItemHidden(item, true);
            }
            return ret;
        },
        createGroupFooter: function() {
            var me = this,
                footer = me.getGroupFooter();
            if (typeof footer === 'string') {
                footer = {
                    xtype: footer
                };
            }
            footer = Ext.apply({
                $dataItem: 'footer'
            }, footer);
            footer.$initParent = footer.ownerCmp = footer.list = me;
            return footer;
        },
        createGroupHeader: function() {
            var me = this,
                header = me.getGroupHeader();
            if (typeof header === 'string') {
                header = {
                    xtype: header
                };
            }
            header = Ext.apply({
                $dataItem: 'header'
            }, header);
            header.$initParent = header.ownerCmp = header.list = me;
            return header;
        },
        createPinnedHeaderFooter: function(config) {
            return Ext.merge({
                translatable: {
                    type: 'csstransform'
                },
                isPinnedItem: true,
                list: this
            }, config);
        },
        dislodgeItem: function(item, options, replacement) {
            var me = this,
                dataItems = me.dataItems,
                sticky = item.$sticky;
            if (!replacement) {
                replacement = me.acquireItem(me.indexOf(item));
                dataItems.pop();
            }
            
            else if (replacement.$sticky && !replacement.$sticky.dislodged) {
                
                
                
                
                
                
                
                me.dislodgeItem(replacement, {
                    itemIndex: dataItems.indexOf(replacement)
                });
            }
            me.dataItems[options.itemIndex] = options.item = replacement;
            replacement.$footer = item.$footer;
            replacement.$header = item.$header;
            replacement.$position = null;
            item.$footer = item.$header = null;
            if (sticky) {
                sticky.dislodged = true;
            }
            sticky = replacement.$sticky;
            if (sticky) {
                sticky.dislodged = false;
            }
        },
        doClear: function() {
            var me = this,
                groupingInfo = me.groupingInfo,
                headers = groupingInfo.headers.unused,
                footers = groupingInfo.footers.unused,
                scroller;
            Ext.destroy(headers, footers);
            footers.length = headers.length = 0;
            if (me.infinite) {
                
                
                
                
                
                
                
                
                
                
                me.setItemCount(0);
                me.lastAdjustedPosition = null;
                me.setVisibleTop(0);
                scroller = me.getScrollable();
                scroller.scrollTo(null, 0);
                me.refreshScrollerSize();
                me.syncEmptyState();
            } else {
                
                me.callParent();
            }
        },
        doDisclose: function(me, record, item, index, e) {
            var onItemDisclosure = me.getOnItemDisclosure(),
                handler = onItemDisclosure,
                scope;
            if (handler && handler !== true) {
                if (handler.handler) {
                    scope = handler.scope;
                    handler = handler.handler;
                }
                Ext.callback(handler, scope, [
                    record,
                    item,
                    index,
                    e
                ], 0, me);
            }
        },
        doRefresh: function(scrollToTop) {
            var me = this,
                scroller = me.getScrollable(),
                store = me.store,
                storeCount = store.getCount(),
                preventSync, count, restoreFocus;
            if (me.infinite) {
                count = ++me.refreshCounter;
                me.refreshGrouping();
                if (storeCount) {
                    me.hideEmptyText();
                    if (count > 1 && scroller && scrollToTop) {
                        
                        restoreFocus = me.saveFocusState();
                        me.blockAndScrollTo(0, false);
                        me.lastAdjustedPosition = null;
                        me.refreshing = true;
                        me.syncRowsToHeight(false);
                        
                        
                        me.resetVisibleTop();
                        me.setVisibleTop(0);
                        preventSync = true;
                        me.refreshing = false;
                        restoreFocus();
                    }
                } else if (me.dataItems.length && !store.hasPendingLoad()) {
                    me.doClear();
                }
                if (!preventSync) {
                    me.resync(true);
                }
            } else {
                me.refreshGroupIndices();
                me.callParent([
                    scrollToTop
                ]);
            }
        },
        ensureVisibleScroll: function(plan) {
            var me = this,
                recIndex = plan.recordIndex,
                item = plan.item || (plan.item = me.itemFromRecord(recIndex)),
                scroller = me.getScrollable(),
                promise, y;
            if (item) {
                return scroller.ensureVisible(item.el, {
                    align: plan.align,
                    animation: plan.animation,
                    highlight: plan.highlight,
                    x: false
                });
            }
            
            
            y = Math.floor(scroller.getSize().y * (recIndex / me.store.getCount()));
            
            plan.animation = false;
            me.nextTeleportTopIndex = recIndex;
            me.setVisibleTop(y);
            delete me.nextTeleportTopIndex;
            promise = me.blockAndScrollTo(y, false);
            plan.item = me.itemFromRecord(recIndex);
            return promise.then(function() {
                if (!me.destroyed) {
                    
                    
                    plan.item = me.itemFromRecord(recIndex);
                }
                return plan;
            });
        },
        
        getScrollableClientRegion: function() {
            return this.callParent().adjust(this.getPinnedHeaderHeight() || 0, 0, -(this.getPinnedFooterHeight() || 0), 0);
        },
        getItemTop: function(item) {
            var y;
            item = item.$header || item;
            if (this.infinite) {
                y = item.$y0;
            } else {
                y = this.getScrollable().getEnsureVisibleXY(item.element, {
                    align: {
                        y: 'start?'
                    }
                }).y;
            }
            return y;
        },
        getPositionedItemTarget: function(item) {
            if (item && item.layer === 'inner') {
                return this.callParent([
                    item
                ]);
            }
            return this.bodyElement;
        },
        getRenderTarget: function() {
            return this.innerCt;
        },
        getScrollerTarget: function() {
            return this.outerCt;
        },
        getStoreChangeSyncIndex: function(index) {
            
            
            
            
            
            return this.isGrouping() ? Math.max(0, index - 1) : index;
        },
        itemFromRecord: function(rec) {
            var me = this,
                store = me.store,
                index, item;
            if (store) {
                if (me.infinite) {
                    index = rec.isEntity ? store.indexOf(rec) : rec;
                    item = me.dataItems[index - me.renderInfo.indexTop];
                } else {
                    item = me.callParent([
                        rec
                    ]);
                }
            }
            return item || null;
        },
        measureItem: function(item, heightCache) {
            var height = item.$height;
            if (height == null) {
                if (this.variableHeights || !heightCache || !(height = heightCache.rowHeight)) {
                    height = item.el.measure('h') + item.el.getMargin('tb');
                    if (heightCache) {
                        heightCache.rowHeight = height;
                    }
                }
                item.$height = height;
                if (item.$pinnedFooter) {
                    this.setPinnedFooterHeight(height);
                } else if (item.$pinnedHeader) {
                    this.setPinnedHeaderHeight(height);
                }
            }
            return height;
        },
        measureItems: function() {
            var me = this,
                scrollDock = me.scrollDockedItems,
                rows = me.dataItems,
                i = rows.length,
                decoration, h, item, items, row, rowHeight,
                hasItemVm = me.hasItemVm;
            if (me.variableHeights) {
                if (hasItemVm) {
                    me.lookupViewModel().notify();
                }
                while (i-- > 0) {
                    row = rows[i];
                    if (row.$height == null) {
                        row.$height = me.measureItem(row);
                    }
                    decoration = row.$header;
                    if (decoration && decoration.$height == null) {
                        decoration.$height = me.measureItem(decoration);
                    }
                    decoration = row.$footer;
                    if (decoration) {
                        decoration.$height = me.measureItem(decoration);
                    }
                }
            } else if (i && !me.measuredFirstRow) {
                if (hasItemVm) {
                    me.lookupViewModel().notify();
                }
                me.measuredFirstRow = true;
                row = rows[0];
                row.$height = null;
                me.rowHeight = rowHeight = me.measureItem(row);
                while (i-- > 0) {
                    rows[i].$height = rowHeight;
                }
            }
            if (scrollDock) {
                
                
                for (h = 0 , items = scrollDock.start.items , i = items.length; i-- > 0; ) {
                    item = items[i];
                    if (!item.getHidden()) {
                        h += item.$height || me.measureItem(item);
                    }
                }
                scrollDock.start.height = h;
                for (h = 0 , items = scrollDock.end.items , i = items.length; i-- > 0; ) {
                    item = items[i];
                    if (!item.getHidden()) {
                        h += item.$height || me.measureItem(item);
                    }
                }
                scrollDock.end.height = h;
            }
        },
        onInnerCtResize: function(innerCt) {
            this.syncVerticalOverflow();
        },
        positionItems: function(position, bottomUp, count) {
            var me = this,
                renderInfo = me.renderInfo,
                rows = me.dataItems,
                len = rows.length,
                scrollDock = me.scrollDockedItems,
                i, item, items, y;
            if (bottomUp) {
                me.positionItemsBottomUp(position, count);
            } else {
                me.positionItemsTopDown(position, count);
            }
            if (len) {
                renderInfo.top = rows[0].$y0;
                renderInfo.bottom = rows[len - 1].$y1;
            } else if (scrollDock) {
                renderInfo.top = renderInfo.bottom = scrollDock.start.height || 0;
            }
            renderInfo.height = renderInfo.bottom - renderInfo.top;
            if (renderInfo.atEnd) {
                y = renderInfo.bottom;
                y += me.gapAfter;
                if (scrollDock) {
                    items = scrollDock.end.items;
                    len = items.length;
                    for (i = 0; i < len; ++i) {
                        item = items[i];
                        if (!item.getHidden()) {
                            y += me.setItemPosition(item, y);
                        }
                    }
                }
            }
            me.refreshScrollerSize();
        },
        positionItemsBottomUp: function(position, count) {
            var me = this,
                groupingInfo = me.groupingInfo,
                footers = groupingInfo.footers,
                headers = groupingInfo.headers,
                renderInfo = me.renderInfo,
                rows = me.dataItems,
                scrollDock = me.scrollDockedItems,
                y = position,
                indexTop = renderInfo.indexTop,
                decoration, ht, i, item, row, y1, stickyPos;
            
            
            for (i = count; i-- > 0; ) {
                row = rows[i];
                y1 = y;
                decoration = row.$footer;
                if (decoration) {
                    if ((ht = decoration.$height) == null) {
                        
                        
                        
                        ht = me.measureItem(decoration, footers);
                    }
                    y -= ht;
                    me.setItemPosition(decoration, y);
                }
                y -= row.$height;
                if (row.$sticky) {
                    row.$sticky.pos = y;
                    stickyPos = me.constrainStickyItem(row);
                    if (stickyPos !== null) {
                        me.setItemPosition(row, stickyPos);
                    }
                } else {
                    me.setItemPosition(row, y);
                }
                decoration = row.$header;
                if (decoration) {
                    if ((ht = decoration.$height) == null) {
                        
                        
                        
                        ht = me.measureItem(decoration, headers);
                    }
                    y -= ht;
                    me.setItemPosition(decoration, y);
                }
                y -= me.gapMap[i + indexTop] || 0;
                row.$y0 = y;
                row.$y1 = y1;
            }
            
            
            
            if (renderInfo.atBegin && scrollDock) {
                scrollDock = scrollDock.start.items;
                for (i = scrollDock.length; i-- > 0; ) {
                    item = scrollDock[i];
                    if (!item.getHidden()) {
                        y -= item.$height;
                        me.setItemPosition(item, y);
                    }
                }
            }
            if (y < 0 || (y > 0 && renderInfo.indexTop === 0)) {
                me.adjustContentTop(-y);
            }
        },
        positionItemsTopDown: function(position, count) {
            var me = this,
                groupingInfo = me.groupingInfo,
                footers = groupingInfo.footers,
                headers = groupingInfo.headers,
                rows = me.dataItems,
                len = rows.length,
                scrollDock = me.scrollDockedItems,
                indexTop = me.renderInfo.indexTop,
                y = position,
                decoration, i, item, row, y0, stickyPos;
            count = count || len;
            
            
            if (me.renderInfo.atBegin && count === len) {
                if (scrollDock) {
                    scrollDock = scrollDock.start.items;
                    y = 0;
                    for (i = 0; i < scrollDock.length; ++i) {
                        item = scrollDock[i];
                        if (!item.getHidden()) {
                            y += me.setItemPosition(item, y);
                        }
                    }
                }
                
                else if (y && !Object.keys(me.gapMap).length) {
                    
                    Ext.raise('Top-most item should be positioned at 0 not ' + y);
                }
            }
            
            for (i = len - count; i < len; ++i) {
                row = rows[i];
                y0 = y;
                y += me.gapMap[indexTop + i] || 0;
                decoration = row.$header;
                if (decoration) {
                    if (decoration.$height == null) {
                        
                        
                        
                        me.measureItem(decoration, headers);
                    }
                    y += me.setItemPosition(decoration, y);
                }
                if (row.$sticky) {
                    row.$sticky.pos = y;
                    stickyPos = me.constrainStickyItem(row);
                    if (stickyPos !== null) {
                        y += me.setItemPosition(row, stickyPos);
                    }
                } else {
                    y += me.setItemPosition(row, y);
                }
                decoration = row.$footer;
                if (decoration) {
                    if (decoration.$height == null) {
                        
                        
                        
                        me.measureItem(decoration, footers);
                    }
                    y += me.setItemPosition(decoration, y);
                }
                row.$y0 = y0;
                row.$y1 = y;
            }
        },
        refreshGrouping: function() {
            var me = this,
                grouped = me.isGrouping(),
                infinite = me.infinite,
                item;
            me.toggleCls(me.groupedCls, grouped);
            if (infinite) {
                
                
                
                item = (grouped && me.getPinFooters()) ? me.getPinnedFooter() : me.pinnedFooter;
                if (item) {
                    me.setItemHidden(item, true);
                }
                item = (grouped && me.getPinHeaders()) ? me.getPinnedHeader() : me.pinnedHeader;
                if (item) {
                    me.setItemHidden(item, true);
                }
            }
            me.refreshGroupIndices();
            me.syncIndexBar();
        },
        refreshGroupIndices: function() {
            var me = this,
                store = me.store,
                groups = me.isGrouping() ? store.getGroups() : null,
                groupingInfo = me.groupingInfo,
                footers = groupingInfo.footers,
                headers = groupingInfo.headers,
                groupCount = groups && groups.length,
                firstRecordIndex, footerIndices, footerMap, group, headerIndices, headerMap, i, previous;
            me.groups = groups;
            if (groupCount) {
                headers.map = headerMap = {};
                headers.indices = headerIndices = [];
                footers.map = footerMap = {};
                footers.indices = footerIndices = [];
                for (i = 0; i < groupCount; ++i) {
                    group = groups.getAt(i);
                    firstRecordIndex = store.indexOf(group.first());
                    headerIndices.push(firstRecordIndex);
                    headerMap[firstRecordIndex] = group;
                    if (previous) {
                        footerIndices.push(firstRecordIndex - 1);
                        footerMap[firstRecordIndex - 1] = previous;
                    }
                    previous = group;
                }
                i = store.getCount() - 1;
                footerIndices.push(i);
                footerMap[i] = group;
            } else {
                headers.map = headers.indices = footers.map = footers.indices = null;
            }
        },
        refreshScrollerSize: function() {
            var me = this,
                store = me.store,
                h, renderInfo, scrollDock, storeCount;
            if (store && me.infinite) {
                me.syncContentTop();
                renderInfo = me.renderInfo;
                scrollDock = me.scrollDockedItems;
                storeCount = store.getCount();
                h = renderInfo.bottom + (storeCount - renderInfo.indexBottom) * me.rowHeight;
                scrollDock = scrollDock && scrollDock.end;
                if (scrollDock) {
                    h += scrollDock.height;
                }
                me.getScrollable().setSize({
                    x: null,
                    y: h
                });
                me.setVerticalOverflow(h > me.getVisibleHeight());
            }
        },
        
        reorderItem: function(item, ref, offset) {
            offset = offset || 0;
            var me = this,
                innerItems = me.innerItems,
                innerCount = innerItems.length,
                innerIndex = (ref == null) ? innerCount : (
                ref.isWidget ? innerItems.indexOf(ref) + offset : ref),
                items = me.items,
                index = (innerIndex < innerCount) ? items.indexOf(innerItems[innerIndex]) : items.length;
            if (!item.isWidget || !me.infinite || me.maintainChildNodes || item.parent !== me) {
                item = me.insert(index, item);
            } else {
                
                
                
                items.insert(index, item);
                index = innerItems.indexOf(item);
                if (index > -1) {
                    innerItems.splice(index, 1);
                    if (index < innerIndex) {
                        --innerIndex;
                    }
                }
                if (innerIndex < innerCount) {
                    innerItems.splice(innerIndex, 0, item);
                } else {
                    innerItems.push(item);
                }
            }
            return item;
        },
        getRecordIndexFromPoint: function(x, y) {
            if (this.infinite) {
                return this.recordIndexByPosition(Math.max(0, Math.min(y, this.getScrollable().getSize().y)));
            } else {
                return this.callParent([
                    x,
                    y
                ]);
            }
        },
        getItemFromPoint: function(x, y) {
            if (this.infinite) {
                return this.dataItems[this.recordIndexByPosition(Math.max(0, Math.min(y, this.getScrollable().getSize().y))) - this.renderInfo.indexTop];
            } else {
                return this.callParent([
                    x,
                    y
                ]);
            }
        },
        recordIndexByPosition: function(y) {
            var me = this,
                renderInfo = me.renderInfo,
                renderTop = renderInfo.top,
                renderBottom = renderInfo.bottom,
                indexTop = renderInfo.indexTop,
                ret;
            if (y < renderTop) {
                ret = Math.floor(y / renderTop * indexTop);
            } else if (y < renderBottom) {
                ret = indexTop + me.bisectPosition(y);
            } else {
                y -= renderBottom;
                ret = Math.min(renderInfo.indexBottom + Math.floor(y / me.rowHeight), me.store.getCount() - 1);
            }
            return ret;
        },
        removeDataItem: function(item, preventCache) {
            var me = this,
                header = item.$header,
                footer = item.$footer,
                groupingInfo = me.groupingInfo;
            if (header) {
                me.removeGroupItem(header, groupingInfo.headers.unused, preventCache);
            }
            if (footer) {
                me.removeGroupItem(footer, groupingInfo.footers.unused, preventCache);
            }
            item.$header = item.$footer = null;
            return me.callParent([
                item,
                preventCache
            ]);
        },
        removeGroupItem: function(item, cache, preventCache) {
            var destroyed = this.removeCachedItem(item, preventCache, cache, this.getMaxItemCache(), this.infinite);
            if (!destroyed) {
                item.$dataRow = null;
                item.setGroup(null);
            }
            return destroyed;
        },
        resync: function(force) {
            var me = this,
                height = me.outerCt.measure('h');
            me.resyncListener = null;
            if (height) {
                if (height === me.getVisibleHeight()) {
                    me.syncRowsToHeight(force);
                } else {
                    me.setVisibleHeight(height);
                }
            }
        },
        resyncOnPaint: function() {
            this.whenVisible('resync', [
                true
            ]);
        },
        rollDown: function(count) {
            var me = this,
                dataItems = me.dataItems,
                renderInfo = me.renderInfo,
                indexBottom = renderInfo.indexBottom,
                tailItem = dataItems[dataItems.length - 1],
                innerTailIndex = me.innerItems.indexOf(tailItem) + 1,
                adjust, decoration, i, row;
            if (tailItem.$footer) {
                ++innerTailIndex;
            }
            
            me.setTopRenderedIndex(renderInfo.indexTop + count);
            for (i = 0; i < count; i++) {
                row = dataItems.shift();
                dataItems.push(row);
                adjust = (row.$header ? 1 : 0) + (row.$footer ? 1 : 0);
                
                me.changeItem(-1, indexBottom + i);
                
                
                
                adjust -= (row.$header ? 1 : 0) + (row.$footer ? 1 : 0);
                
                innerTailIndex -= adjust;
                decoration = row.$header;
                if (decoration) {
                    me.reorderItem(decoration, innerTailIndex);
                }
                me.reorderItem(row, innerTailIndex);
                decoration = row.$footer;
                if (decoration) {
                    me.reorderItem(decoration, innerTailIndex);
                }
            }
            me.measureItems();
            me.positionItems(renderInfo.bottom, 
            false, count);
        },
        rollUp: function(count) {
            var me = this,
                dataItems = me.dataItems,
                innerItems = me.innerItems,
                renderInfo = me.renderInfo,
                indexTop = renderInfo.indexTop,
                headItem = dataItems[0],
                innerHeadIndex = innerItems.indexOf(headItem),
                decoration, i, row;
            if (headItem.$header) {
                --innerHeadIndex;
            }
            
            me.setTopRenderedIndex(indexTop - count);
            --indexTop;
            
            for (i = 0; i < count; i++) {
                row = dataItems.pop();
                dataItems.unshift(row);
                me.changeItem(0, indexTop - i);
                decoration = row.$footer;
                if (decoration) {
                    me.reorderItem(decoration, innerHeadIndex);
                }
                me.reorderItem(row, innerHeadIndex);
                decoration = row.$header;
                if (decoration) {
                    me.reorderItem(decoration, innerHeadIndex);
                }
            }
            me.measureItems();
            me.positionItems(renderInfo.top, 
            true, count);
        },
        setGaps: function(gaps) {
            var me = this;
            gaps = gaps || {};
            if (!Ext.Object.equals(me.gapMap, gaps)) {
                me.gapMap = gaps;
                me.gapAfter = me.gapMap[me.store.getCount()] || 0;
                me.syncRowsToHeight(true);
            }
        },
        setItemHiddenInfinite: function(item, hide) {
            
            if (!hide) {
                item.$hidden = false;
            } else if (!item.$hidden) {
                item.$hidden = true;
                item.$position = null;
                item.translate(0, -10000);
            }
        },
        
        setItemPosition: function(item, y) {
            if (item.$hidden) {
                this.setItemHidden(item, false);
            }
            if (item.$position !== y) {
                item.$position = y;
                item.translate(null, y);
            }
            return item.$height;
        },
        
        setupFooterPinning: function() {
            var me = this;
            return me.setupGroupPinning(me.getPinFooters(), me.pinnedFooter, me.hasPinnedFooterCls, 'setPinnedFooterHeight');
        },
        setupGroupPinning: function(pin, item, cls, setter) {
            var isPinning = pin && !!item;
            if (!isPinning) {
                this[setter](0);
            }
            this.el.toggleCls(cls, isPinning);
            return isPinning;
        },
        setupHeaderPinning: function() {
            var me = this;
            return me.setupGroupPinning(me.getPinHeaders(), me.pinnedHeader, me.hasPinnedHeaderCls, 'setPinnedHeaderHeight');
        },
        shouldHideDisclosure: function(record) {
            var name, show;
            if (this.getOnItemDisclosure()) {
                name = this.getDisclosureProperty();
                show = !name || record.data[name] !== false;
            }
            return !show;
        },
        stickItem: function(item, options) {
            var me = this,
                opt = item.$sticky,
                record = item.getRecord(),
                stickyCls = me.stickyCls,
                stickyItems = me.stickyItems,
                stickyItemsByRecordId = me.stickyItemsByRecordId,
                stickyPos;
            
            if (!me.infinite) {
                Ext.raise('Only infinite lists support sticky items');
            }
            
            if (options) {
                if (!opt) {
                    stickyItems.push(item);
                    stickyItemsByRecordId[record.internalId] = item;
                    item.addCls(stickyCls);
                    item.$sticky = opt = {
                        pos: item.$position
                    };
                }
                Ext.apply(opt, options);
                stickyPos = me.constrainStickyItem(item);
                if (stickyPos !== null) {
                    me.setItemPosition(item, stickyPos);
                }
            } else if (opt) {
                Ext.Array.remove(stickyItems, item);
                delete stickyItemsByRecordId[record.internalId];
                item.removeCls(stickyCls);
                item.$sticky = null;
                if (opt.autoPin && item.isDataViewPinnable) {
                    item.setPinned(false);
                }
                if (opt.floated) {
                    delete item.$position;
                }
                if (opt.dislodged) {
                    me.removeDataItem(item);
                } else {
                    
                    
                    me.setItemPosition(item, opt.pos);
                }
            }
        },
        syncContentTop: function() {
            var me = this,
                renderInfo = me.renderInfo,
                visibleTop = me.getVisibleTop(),
                visibleHeight = me.getVisibleHeight(),
                bufferZone = me.getMinimumBufferDistance() * me.rowHeight,
                adjust;
            if (renderInfo.atEnd) {
                return;
            }
            adjust = visibleTop + visibleHeight - (renderInfo.bottom - bufferZone);
            
            if (adjust < 0) {
                return;
            }
            me.getScrollable().scrollBy(null, -adjust, false);
        },
        syncIndexBar: function() {
            var me = this,
                indexBar = me.getIndexBar(),
                store = me.store;
            if (indexBar) {
                indexBar.setHidden(me.getEmptyState() || !store || !store.getGrouper());
            }
        },
        syncPinnedFooter: function(visibleTop) {
            var me = this,
                dataItems = me.dataItems,
                len = dataItems.length,
                pinnedFooter = me.pinnedFooter,
                renderInfo = me.renderInfo,
                
                
                grouping = me.pinFooters && pinnedFooter && len && me.isGrouping(),
                hide = pinnedFooter,
                indexTop = renderInfo.indexTop,
                scrollDock = me.scrollDockedItems,
                bottom, footerIndices, footers, height, index, totalHeight, visibleBottomIndex, y, gap;
            visibleTop = visibleTop || me.getVisibleTop();
            
            if (grouping) {
                
                
                totalHeight = me.getScrollable().getSize().y;
                bottom = visibleTop + me.getVisibleHeight();
                hide = bottom <= me.renderInfo.top || bottom >= totalHeight - (scrollDock ? scrollDock.end.height : 0) - me.gapAfter;
                if (!hide) {
                    
                    visibleBottomIndex = me.bisectPosition(bottom - 1) + indexTop;
                    footers = me.groupingInfo.footers;
                    footerIndices = footers.indices;
                    
                    
                    index = Ext.Number.binarySearch(footerIndices, visibleBottomIndex);
                    pinnedFooter.setGroup(footers.map[footerIndices[index]]);
                    if (visibleBottomIndex === footerIndices[index] && dataItems[visibleBottomIndex - indexTop].$y1 === bottom) {
                        
                        
                        hide = true;
                    }
                    
                    else if (index) {
                        
                        
                        index = footerIndices[index - 1];
                        if (index < indexTop) {
                            
                            
                            y = 0;
                        } else {
                            
                            y = dataItems[index - indexTop].$y1;
                            gap = me.gapMap[index + 1] || 0;
                            if (gap) {
                                
                                
                                
                                if (!(hide = bottom - y < gap)) {
                                    y += gap;
                                }
                            }
                        }
                    } else {
                        
                        
                        y = scrollDock ? scrollDock.start.height : 0;
                    }
                    if (!hide) {
                        height = me.measureItem(pinnedFooter);
                        
                        
                        
                        
                        y = bottom - y;
                        y = (y < height) ? height - y : 0;
                        me.setItemPosition(pinnedFooter, y);
                    }
                }
            }
            if (hide) {
                me.setItemHidden(pinnedFooter, true);
            } else if (pinnedFooter) {
                me.syncPinnedHorz(pinnedFooter);
            }
        },
        syncPinnedHeader: function(visibleTop) {
            var me = this,
                dataItems = me.dataItems,
                len = dataItems.length,
                pinnedHeader = me.pinnedHeader,
                renderInfo = me.renderInfo,
                
                
                grouping = me.pinHeaders && pinnedHeader && len && me.isGrouping(),
                hide = pinnedHeader,
                indexTop = renderInfo.indexTop,
                scrollDock = me.scrollDockedItems,
                headerIndices, headers, height, index, visibleTopIndex, y, headerIndex, gap, item;
            visibleTop = visibleTop || me.getVisibleTop();
            
            if (grouping) {
                
                
                hide = (scrollDock && visibleTop <= scrollDock.start.height) || (visibleTopIndex = me.bisectPosition(visibleTop)) < 0 || visibleTopIndex >= len;
                if (!hide) {
                    visibleTopIndex += indexTop;
                    headers = me.groupingInfo.headers;
                    headerIndices = headers.indices;
                    
                    
                    
                    
                    index = Ext.Number.binarySearch(headerIndices, visibleTopIndex);
                    
                    
                    if (headerIndices[index] !== visibleTopIndex) {
                        --index;
                    }
                    headerIndex = headerIndices[index];
                    pinnedHeader.setGroup(headers.map[headerIndex]);
                    
                    
                    
                    if (headerIndex > indexTop) {
                        item = dataItems[headerIndex - indexTop];
                        gap = me.gapMap[headerIndex] || 0;
                        if (gap) {
                            
                            
                            
                            
                            
                            
                            
                            
                            hide = visibleTop - item.$y0 < gap;
                        }
                    }
                    
                    
                    
                    
                    
                    if (!hide) {
                        
                        
                        ++index;
                        if (index < headerIndices.length) {
                            
                            index = headerIndices[index] - indexTop;
                            
                            
                            
                            y = (index < len) ? dataItems[index].$y0 - visibleTop : 0;
                        } else {
                            
                            
                            y = renderInfo.bottom - visibleTop;
                            hide = y <= 0;
                        }
                        if (!hide) {
                            height = me.measureItem(pinnedHeader);
                            
                            
                            
                            
                            
                            
                            
                            y = (y && y < height) ? y - height : 0;
                            me.setItemPosition(pinnedHeader, y || 0);
                        }
                    }
                }
            }
            if (hide) {
                me.setItemHidden(pinnedHeader, true);
            } else if (pinnedHeader) {
                me.syncPinnedHorz(pinnedHeader);
            }
        },
        syncPinnedHorz: function(item) {
            var me = this,
                scroller = item.getScrollable();
            if (!scroller) {
                item.setScrollable({
                    x: false,
                    y: false
                });
                scroller = item.getScrollable();
            }
            if (item.isItemHeader) {
                item.setContentWidth(me.getInnerWidth());
            }
            scroller.scrollTo(me.getVisibleLeft(), null);
        },
        syncRows: function(bottomUp) {
            var me = this,
                renderInfo = me.renderInfo,
                scrollDock = me.scrollDockedItems,
                maxHeight = me.getMaxHeight(),
                i, position, indexTop, len, innerCt, contentHeight, height;
            if (!me.infinite) {
                me.syncItemRange();
                return;
            }
            len = me.dataItems.length;
            indexTop = renderInfo.indexTop;
            if (len) {
                if (bottomUp) {
                    position = renderInfo.bottom;
                } else {
                    position = renderInfo.top;
                    if (!indexTop && scrollDock) {
                        position = scrollDock.start.height;
                    }
                }
                for (i = 0; i < len; ++i) {
                    me.changeItem(i, indexTop + i);
                }
            }
            me.measureItems();
            me.positionItems(position, bottomUp, len);
            if (me.pinnedHeader) {
                me.syncPinnedHeader();
            }
            if (me.pinnedFooter) {
                me.syncPinnedFooter();
            }
            if (me.stickyItems.length) {
                me.syncStickyItems();
            }
            if (maxHeight) {
                innerCt = me.innerCt;
                contentHeight = renderInfo.bottom + me.gapAfter;
                if (scrollDock) {
                    contentHeight += scrollDock.end.height;
                }
                height = innerCt.measure('h');
                
                height = me.el.measure('h') - height + me.el.getBorderWidth('tb');
                
                height = Math.min(maxHeight - height, contentHeight);
                me.setInnerCtHeight(height);
            }
        },
        syncRowsToHeight: function(force) {
            var me = this,
                bufferZone = me.getBufferSize(),
                infinite = me.infinite,
                rowCountWas = me.getItemCount(),
                rowHeight = me.rowHeight,
                firstTime = !rowHeight,
                renderInfo = me.renderInfo,
                oldIndexBottom = renderInfo && renderInfo.indexBottom,
                storeCount = me.store.getCount(),
                
                
                
                visibleHeight = me.getMaxHeight() || me.getVisibleHeight(),
                indexTop, row, rowCount;
            if (firstTime) {
                
                
                
                if (!rowCountWas) {
                    me.setItemCount(1);
                }
                row = me.dataItems[0];
                row.$height = null;
                
                me.rowHeight = rowHeight = me.measureItem(row);
                if (!rowCountWas && me.discardMeasureRow) {
                    row.destroy();
                    me.dataItems.length = 0;
                    me.setItemCount(0);
                }
            }
            if (infinite) {
                
                
                rowCount = Math.ceil(visibleHeight / rowHeight) + bufferZone;
                rowCount = Math.min(rowCount, storeCount);
            } else {
                rowCount = storeCount;
            }
            me.setItemCount(rowCount);
            
            if ((firstTime && me.store.isVirtualStore) || rowCountWas !== rowCount || storeCount < oldIndexBottom) {
                if (infinite) {
                    indexTop = Math.min(storeCount - rowCount, renderInfo.indexTop);
                    indexTop = Math.max(0, indexTop);
                    if (indexTop === me.getTopRenderedIndex()) {
                        
                        me.updateTopRenderedIndex(indexTop);
                    } else {
                        me.setTopRenderedIndex(indexTop);
                    }
                }
                if (firstTime) {
                    me.refreshGrouping();
                }
                force = force !== false;
                if (force && storeCount < oldIndexBottom) {
                    
                    
                    renderInfo.top = renderInfo.indexTop * me.rowHeight;
                }
            }
            if (force) {
                me.syncRows();
            }
        },
        syncStickyItems: function() {
            var me = this,
                stickyItems = me.stickyItems,
                n = stickyItems.length,
                i, stickyItem, stickyPos;
            for (i = 0; i < n; ++i) {
                stickyPos = me.constrainStickyItem(stickyItem = stickyItems[i]);
                if (stickyPos !== null) {
                    me.setItemPosition(stickyItem, stickyPos);
                }
            }
        },
        doSyncVerticalOverflow: function() {
            var scroller = this.getScrollable();
            this.setVerticalOverflow(scroller.getSize().y > scroller.getClientSize().y);
        },
        resetVisibleTop: function() {
            this.lastAdjustedPosition = this._visibleTop = null;
        },
        teleport: function(y) {
            var me = this,
                scrollSize = me.getScrollable().getSize(),
                renderInfo = me.renderInfo,
                rowCount = me.dataItems.length,
                storeCount = me.store.getCount(),
                indexMax = storeCount - rowCount,
                backOff = me.getBufferSize(),
                scrollDock = me.scrollDockedItems,
                nextTeleportTopIndex = me.nextTeleportTopIndex,
                bottomUp, indexTop;
            
            
            if (nextTeleportTopIndex !== undefined) {
                indexTop = nextTeleportTopIndex;
            } else {
                indexTop = Math.floor(y / scrollSize.y * storeCount);
            }
            if (indexTop < indexMax) {
                
                
                
                
                backOff = Math.min(indexTop, backOff >>> 1);
                
                indexTop -= backOff;
                
                if (indexTop) {
                    renderInfo.top = Math.max(0, y - me.rowHeight * backOff);
                } else {
                    renderInfo.top = scrollDock ? scrollDock.start.height : 0;
                }
            } else {
                bottomUp = true;
                indexTop = indexMax;
                renderInfo.bottom = scrollSize.y - me.gapAfter;
                if (scrollDock) {
                    renderInfo.bottom -= scrollDock.end.height;
                }
            }
            me.setTopRenderedIndex(indexTop);
            me.syncRows(bottomUp);
        },
        traverseItem: function(item, delta) {
            var me = this,
                dataItems = me.dataItems,
                renderInfo = me.renderInfo,
                next;
            
            
            if (item && me.infinite) {
                if (delta < 0) {
                    if (item === dataItems[0] && !item.isFirst) {
                        
                        
                        next = renderInfo.indexTop;
                    }
                }
                
                else if (item === dataItems[dataItems.length - 1] && !item.isLast) {
                    next = renderInfo.indexBottom + 1;
                }
            }
            
            return next ? next - 1 : this.callParent([
                item,
                delta
            ]);
        },
        
        
        
        updateEmptyState: function(empty, was) {
            this.callParent([
                empty,
                was
            ]);
            this.syncIndexBar();
        },
        
        updateHorizontalOverflow: function(overflow) {
            
            
            var scroller = this.getScrollable();
            scroller.setX(overflow);
            if (!overflow) {
                
                scroller.scrollTo(0, null);
            }
        },
        
        updateInnerCtHeight: function(height) {
            this.innerCt.setHeight(height);
        },
        
        updateInnerWidth: function(innerWidth) {
            var me = this,
                innerCt = me.innerCt,
                pinnedHeader = me.pinnedHeader,
                pinnedFooter = me.pinnedFooter,
                width;
            if (innerWidth == null) {
                innerCt.setStyle('width', '');
                me.setHorizontalOverflow(false);
            } else {
                innerCt.setStyle('width', innerWidth + 'px');
                width = me.getVisibleWidth();
                if (width != null) {
                    
                    me.setHorizontalOverflow(width < innerWidth);
                }
            }
            if (pinnedHeader) {
                me.syncPinnedHorz(pinnedHeader);
            }
            if (pinnedFooter) {
                me.syncPinnedHorz(pinnedFooter);
            }
        },
        
        updatePinnedFooterHeight: function(height) {
            var me = this;
            if (!me.destroyed && !me.destroying) {
                me.fireEvent('pinnedfooterheightchange', me, height);
            }
        },
        
        updatePinnedHeaderHeight: function(height) {
            var me = this;
            if (!me.destroyed && !me.destroying) {
                me.fireEvent('pinnedheaderheightchange', me, height);
            }
        },
        
        updateTopRenderedIndex: function(top) {
            var me = this,
                store = me.store,
                renderInfo = me.renderInfo,
                bottom = top + me.dataItems.length;
            renderInfo.atBegin = !top;
            renderInfo.atEnd = bottom === me.store.getCount();
            renderInfo.indexTop = top;
            renderInfo.indexBottom = bottom;
            if (top === bottom && store.isVirtualStore) {
                bottom = top + store.getPageSize();
            }
            me.dataRange.goto(top, bottom);
        },
        
        updateVerticalOverflow: function(overflow) {
            var me = this,
                items = me.items.items,
                n = items.length,
                i, item, width;
            if (me.infinite) {
                width = overflow ? Ext.getScrollbarSize().reservedWidth : null;
                for (i = 0; i < n; ++i) {
                    item = items[i];
                    if (item.isPinnedItem) {
                        item.el.setStyle('width', width);
                    }
                }
                me.syncPinnedHeader();
                me.syncPinnedFooter();
            }
            me.fireEvent('verticaloverflowchange', me, overflow);
        },
        
        updateVisibleHeight: function() {
            var me = this;
            if (me.infinite) {
                if (me.store) {
                    me.syncRowsToHeight();
                }
            } else {
                me.syncVerticalOverflow();
            }
        },
        
        updateVisibleLeft: function() {
            var me = this;
            if (me.infinite && !me.suspendSync) {
                me.syncPinnedHeader();
                me.syncPinnedFooter();
                if (me.stickyItems.length) {
                    me.syncStickyItems();
                }
            }
        },
        
        updateVisibleTop: function(y) {
            var me = this,
                oldY = me.lastAdjustedPosition;
            if (me.infinite) {
                if (me.dataItems.length && (oldY == null || Math.abs(y - oldY) > me.rowHeight)) {
                    me.lastAdjustedPosition = y;
                    me.adjustRenderedRows(y, oldY);
                }
                me.syncPinnedHeader(y);
                me.syncPinnedFooter(y);
                if (me.stickyItems.length) {
                    me.syncStickyItems();
                }
            }
        },
        
        updateVisibleWidth: function(width) {
            var me = this,
                innerWidth = me.getInnerWidth();
            if (innerWidth != null) {
                me.setHorizontalOverflow(width < innerWidth);
            }
        }
    }
}, 
function(List) {
    var proto = List.prototype,
        handlers = proto._itemChangeHandlers = proto._itemChangeHandlers.slice();
    handlers.unshift('changeItemStuck');
    handlers.push('changeItemGrouping');
});

Ext.define('Ext.dataview.BoundListLocation', {
    extend: Ext.dataview.Location,
    next: function(options) {
        var me = this,
            candidate = me.nextItem(options),
            item = candidate && candidate.get();
        
        while (candidate && (!item || !candidate.record)) {
            
            if (candidate.equals(me)) {
                return;
            }
            candidate = candidate.nextItem(options);
            item = candidate && candidate.get();
        }
        return candidate;
    },
    previous: function(options) {
        var me = this,
            candidate = me.previousItem(options),
            item = candidate && candidate.get();
        
        while (candidate && (!item || !candidate.record)) {
            
            if (candidate.equals(me)) {
                return;
            }
            candidate = candidate.previousItem(options);
            item = candidate && candidate.get();
        }
        return candidate;
    }
});

Ext.define('Ext.dataview.BoundListNavigationModel', {
    extend: Ext.dataview.NavigationModel,
    alias: 'navmodel.boundlist',
    config: {
        navigateOnSpace: true
    },
    locationClass: 'Ext.dataview.BoundListLocation',
    privates: {
        getKeyNavCfg: function(view) {
            if (this.keyboard !== false) {
                
                
                
                var eventEl = (view.ownerField || view).getFocusEl();
                
                if (eventEl) {
                    var me = this;
                    return {
                        target: eventEl,
                        eventName: 'keydown',
                        defaultEventAction: 'stopEvent',
                        esc: me.onKeyEsc,
                        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
                        },
                        
                        
                        priority: 1001,
                        scope: me
                    };
                }
            }
        },
        getViewListeners: function(view) {
            var result = this.callParent([
                    view
                ]);
            result.childtouchstart = 'onChildTouchStart';
            result.childTap = 'onChildTap';
            return result;
        },
        
        
        
        
        doFocus: Ext.privateFn,
        handleLocationChange: function(location, options) {
            var target = location.sourceElement,
                ownerField = this.getView().ownerField;
            if (target && ownerField) {
                ownerField.inputElement.dom.setAttribute('aria-activedescendant', target.id);
            }
            this.callParent([
                location,
                options
            ]);
        },
        onChildTouchStart: function(view, location) {
            var e = location.event;
            
            
            if (e.pointerType !== 'touch') {
                e.preventDefault();
            }
        },
        onChildTap: function(view, location) {
            var e = location.event,
                newLocation;
            if (!view.destroyed) {
                
                
                if (e.pointerType === 'touch') {
                    e.preventDefault();
                }
                
                
                
                
                newLocation = this.createLocation(location.item);
                
                if (this.location && this.location.equals(newLocation)) {
                    this.onNavigate(e);
                } else {
                    
                    
                    this.setLocation(newLocation, {
                        event: location.event,
                        animation: true
                    });
                }
            }
            
            if (e.pointerType === 'touch') {
                e.stopEvent();
            }
        },
        onChildTrigger: Ext.privateFn,
        onKeyLeft: function() {
            return true;
        },
        onKeyRight: function() {
            return true;
        },
        onKeySpace: function(e) {
            if (this.getNavigateOnSpace()) {
                e.preventDefault();
                this.onNavigate(e);
            }
            
            return true;
        },
        onKeyEsc: function() {
            var view = this.getView(),
                field = view.ownerField;
            
            if (field && view.isVisible()) {
                field.collapse();
            } else 
            
            {
                return true;
            }
        },
        onKeyTab: function(e) {
            var view = this.getView(),
                field = view.ownerField;
            if (view.isVisible()) {
                
                
                
                
                if (field.getSelectOnTab()) {
                    this.selectHighlighted(e);
                }
                if (field.collapse) {
                    field.collapse();
                }
            }
            
            return true;
        },
        
        onKeyEnter: function(e) {
            var view = this.getView(),
                selectable = view.getSelectable(),
                field = view.ownerField;
            
            
            e.stopEvent();
            
            
            if (selectable.isSelected(this.location.record) && field.collapse) {
                field.collapse();
            } else {
                this.selectHighlighted(e);
            }
            
            
            
            e.fromBoundList = true;
            field.fireEvent('specialkey', field, e);
            return false;
        },
        onNavigate: function(event) {
            var doNavigate = event && (event.pointerType || (this.getNavigateOnSpace() && event.keyCode === event.SPACE)),
                view = this.getView(),
                field = view.getRefOwner();
            
            
            if (doNavigate) {
                this.callParent([
                    event
                ]);
                if (field && field.maybeCollapse) {
                    field.maybeCollapse(event);
                }
            }
        },
        
        selectHighlighted: function(e) {
            var me = this,
                view = me.getView(),
                store = view.getStore(),
                selectable = view.getSelectable(),
                location = me.location,
                highlightedRec, index;
            
            if (view.getViewItems().length) {
                highlightedRec = location.record;
                if (highlightedRec) {
                    
                    
                    if (e.getKey() === e.ENTER || !selectable.isSelected(highlightedRec)) {
                        selectable.selectWithEvent(highlightedRec, e);
                        
                        
                        if (!view.getStore().contains(highlightedRec)) {
                            index = Math.min(location.recordIndex, store.getCount() - 1);
                            me.setLocation(store.getAt(index));
                        }
                    }
                }
            }
        }
    }
});


Ext.define('Ext.dataview.BoundList', {
    extend: Ext.dataview.List,
    xtype: 'boundlist',
    tabIndex: null,
    focusEl: null,
    itemsFocusable: false,
    navigationModel: {
        type: 'boundlist'
    },
    itemConfig: {
        cls: Ext.baseCSSPrefix + 'boundlistitem',
        tools: {
            selected: {
                zone: 'start',
                passive: true,
                cls: Ext.baseCSSPrefix + 'selected-icon',
                iconCls: Ext.baseCSSPrefix + 'fa fa-check'
            }
        }
    },
    
    onFocusEnter: Ext.emptyFn,
    onFocusLeave: Ext.emptyFn,
    privates: {
        
        beforeSelectionRefresh: function(toDeselect, toReselect) {
            var len = toDeselect.length,
                i, rec;
            for (i = 0; i < len; ) {
                rec = toDeselect[i];
                
                
                if (rec.isEntered) {
                    toDeselect.splice(i, 1);
                    len--;
                } else {
                    i++;
                }
            }
        }
    }
});


Ext.define('Ext.dataview.DataView', {
    extend: Ext.dataview.Abstract,
    alternateClassName: 'Ext.DataView',
    xtype: 'dataview',
    isElementDataView: true,
    
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        if (config && config.useComponents) {
            
            if (this.self !== Ext.dataview.DataView) {
                Ext.raise('The useComponents config has been replaced by Ext.dataview.Component');
            }
            Ext.log.warn('The useComponents config has been replaced by Ext.dataview.Component');
            
            
            
            return new Ext.dataview['Component'](config);
        }
        
        
        if (this.useComponents) {
            Ext.raise('The useComponents config has been replaced by Ext.dataview.Component');
        }
        
        this.callParent([
            config
        ]);
    },
    getViewItems: function() {
        return Array.prototype.slice.call(this.getFastItems());
    },
    onStoreAdd: function(store, records, index) {
        this.callParent(arguments);
        this.renderItems(index, index + records.length);
    },
    onStoreRemove: function(store, records, index) {
        this.removeItems(index, index + records.length);
    },
    privates: {
        dirtyCls: Ext.baseCSSPrefix + 'dirty',
        changeItem: function(recordIndex) {
            var me = this,
                dataItems = me.dataItems,
                item = dataItems[recordIndex],
                record = me.dataRange.records[recordIndex],
                storeCount = me.store.getCount(),
                options = {
                    isFirst: !recordIndex,
                    isLast: recordIndex === storeCount - 1,
                    item: item,
                    record: record,
                    recordIndex: recordIndex
                };
            me.syncItemRecord(options);
        },
        clearItems: function() {
            var elements = this.dataItems,
                dom;
            while (elements.length) {
                dom = elements.pop();
                Ext.fly(dom).destroy();
            }
        },
        createDataItem: function(index, record) {
            var me = this,
                store = me.store,
                data = me.gatherData(record, index),
                markDirty = me.getMarkDirty(),
                dom, itemEl;
            itemEl = Ext.Element.create(me.getItemElementConfig(index, data, store));
            dom = itemEl.dom;
            if (markDirty) {
                itemEl.addCls(me.markDirtyCls);
            }
            dom.setAttribute('data-viewid', me.id);
            dom.setAttribute('data-recordid', record.internalId);
            dom.setAttribute('data-recordindex', index);
            return itemEl;
        },
        doClear: function() {
            this.clearItems();
            this.callParent();
        },
        resetSelection: function(records) {
            this.setItemSelection(records, false);
        },
        doRefresh: function(scrollToTop) {
            var me = this,
                records = me.dataRange.records,
                storeCount = records.length,
                itemCount = me.dataItems.length,
                scroller = me.getScrollable(),
                restoreFocus, i;
            if (scroller && scrollToTop) {
                scroller.scrollTo(0, 0);
            }
            ++me.refreshCounter;
            
            if (!storeCount) {
                me.doClear();
            } else {
                
                restoreFocus = me.saveFocusState();
                me.hideEmptyText();
                
                me.resetSelection(records);
                if (itemCount > storeCount) {
                    me.removeItems(storeCount, itemCount);
                    
                    
                    itemCount = storeCount;
                } else if (itemCount < storeCount) {
                    me.renderItems(itemCount, storeCount);
                }
                
                
                for (i = 0; i < itemCount; ++i) {
                    me.changeItem(i);
                }
                if (me.hasSelection()) {
                    me.setItemSelection(me.getSelections(), true);
                }
                restoreFocus();
            }
        },
        getFastItems: function() {
            return this.getRenderTarget().dom.childNodes;
        },
        getItemElementConfig: function(index, data, store) {
            var me = this,
                result = {
                    cls: me.baseCls + '-item ' + (me.getItemCls() || ''),
                    html: me.renderItemTpl(index, data, store)
                };
            
            
            
            
            
            if (me.getItemsFocusable()) {
                result.tabIndex = -1;
            }
            return result;
        },
        removeItems: function(from, to) {
            var me = this,
                items = me.dataItems.splice(from, to - from),
                i;
            for (i = 0; i < items.length; ++i) {
                Ext.fly(items[i]).destroy();
            }
        },
        renderItems: function(from, to) {
            var me = this,
                dataItems = me.dataItems,
                records = me.dataRange.records,
                parentNode = me.getRenderTarget().dom,
                args = [
                    from,
                    0
                ],
                before = me.dataItems[from] || null,
                dom, i;
            if (records.length) {
                me.hideEmptyText();
            }
            if (!before) {
                
                
                
                before = me.findTailItem(
                true);
                before = before && before.el.dom;
            }
            for (i = from; i < to; ++i) {
                args.push(dom = me.createDataItem(i, records[i]).dom);
                parentNode.insertBefore(dom, before);
            }
            dataItems.splice.apply(dataItems, args);
        },
        renderItemTpl: function(index, data, store) {
            var itemTpl = this.getItemTpl(),
                parent = store.getData().items,
                value;
            data.xcount = typeof data.xcount === 'number' ? data.xcount : store.getCount();
            data.xindex = typeof data.xindex === 'number' ? data.xindex : index;
            value = itemTpl.apply(data, parent, index + 1, parent.length);
            value = (value == null) ? '' : String(value);
            return value || this.getEmptyItemText();
        },
        syncItemRecord: function(options) {
            var me = this,
                item = options.item,
                record = options.record,
                store = me.store,
                recordIndex = options ? options.recordIndex : store.indexOf(record),
                data = me.gatherData(record, recordIndex),
                dirtyCls = me.$dirty;
            item.innerHTML = me.renderItemTpl(recordIndex, data, store);
            item.setAttribute('data-recordid', record.internalId);
            item.setAttribute('data-recordindex', recordIndex);
            Ext.fly(item).toggleCls(me.dirtyCls, record.dirty);
        },
        traverseItem: function(item, delta) {
            var me = this,
                items = me.getRenderTarget().dom.childNodes,
                next = null,
                dom, i;
            if (item) {
                if (item.isElement) {
                    dom = item.dom;
                } else if (item.isWidget) {
                    dom = item.el.dom;
                }
                i = Array.prototype.indexOf.call(items, dom);
                if (i > -1) {
                    next = items[i + delta] || null;
                    if (next) {
                        next = Ext.getCmp(next.id) || next;
                    }
                }
            }
            return next;
        }
    }
});


Ext.define('Ext.dataview.IndexBar', {
    extend: Ext.Component,
    alternateClassName: 'Ext.IndexBar',
    xtype: 'indexbar',
    
    cachedConfig: {
        
        letters: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    },
    config: {
        
        animation: true,
        
        autoHide: false,
        
        dynamic: false,
        
        listPrefix: null,
        
        indicator: true
    },
    eventedConfig: {
        
        direction: 'vertical'
    },
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
    inheritUi: true,
    autoHideCls: Ext.baseCSSPrefix + 'autohide',
    classCls: Ext.baseCSSPrefix + 'indexbar',
    horizontalCls: Ext.baseCSSPrefix + 'horizontal',
    indexedCls: Ext.baseCSSPrefix + 'indexed',
    indexedHorizontalCls: Ext.baseCSSPrefix + 'indexed-horizontal',
    indexedVerticalCls: Ext.baseCSSPrefix + 'indexed-vertical',
    indexedNoAutoHideCls: Ext.baseCSSPrefix + 'indexed-no-autohide',
    indicatorCls: Ext.baseCSSPrefix + 'indexbar-indicator',
    pressedCls: Ext.baseCSSPrefix + 'pressed',
    verticalCls: Ext.baseCSSPrefix + 'vertical',
    element: {
        reference: 'element',
        cls: Ext.baseCSSPrefix + 'unselectable',
        children: [
            {
                reference: 'bodyElement',
                cls: Ext.baseCSSPrefix + 'body-el'
            }
        ]
    },
    initialize: function() {
        var me = this,
            bodyElement = me.bodyElement;
        me.callParent();
        bodyElement.addClsOnClick(me.pressedCls);
        bodyElement.on({
            touchstart: 'onTouchStart',
            touchend: 'onTouchEnd',
            mouseover: 'onMouseOver',
            mouseout: 'onMouseOut',
            drag: 'onDrag',
            dragEnd: 'onDragEnd',
            scope: me
        });
    },
    getVertical: function() {
        return this.getDirection() === 'vertical';
    },
    setVertical: function(vertical) {
        return this.setDirection(vertical ? 'vertical' : 'horizontal');
    },
    
    
    onAdded: function(parent, instanced) {
        var me = this;
        parent.el.addCls(me.indexedCls);
        me.parentListeners = parent.on({
            pinnedfooterheightchange: 'onPinnedFooterHeightChange',
            pinnedheaderheightchange: 'onPinnedHeaderHeightChange',
            verticaloverflowchange: 'onVerticalOverflowChange',
            destroyable: true,
            scope: me
        });
        me.callParent([
            parent,
            instanced
        ]);
    },
    onRemoved: function(destroying) {
        var me = this,
            parent = me.parent;
        Ext.destroy(me.parentListeners);
        if (parent && !parent.destroying && !parent.destroyed) {
            parent.el.removeCls(me.indexedCls);
        }
        me.callParent([
            destroying
        ]);
    },
    
    privates: {
        parentListeners: null,
        onDrag: function(e) {
            this.trackMove(e, false);
        },
        onDragEnd: function(e) {
            var me = this,
                indicator = me.getIndicator();
            me.trackMove(e, true);
            if (indicator && me.indicator) {
                me.indicator.hide();
            }
        },
        onMouseOver: function() {
            var me = this;
            me.$isMouseOver = true;
            if (me.shouldAutoHide('over')) {
                me.bodyElement.show();
            }
        },
        onMouseOut: function() {
            var me = this;
            me.$isMouseOver = false;
            if (me.shouldAutoHide('out')) {
                me.bodyElement.hide();
            }
        },
        onPinnedFooterHeightChange: function(list, height) {
            this.setBottom(height);
        },
        onPinnedHeaderHeightChange: function(list, height) {
            this.setTop(height);
        },
        onTouchStart: function(e) {
            var me = this;
            me.$isPressing = true;
            me.pageBox = me.bodyElement.getBox();
            me.onDrag(e);
            if (me.shouldAutoHide('press')) {
                me.bodyElement.show();
            }
        },
        onTouchEnd: function(e) {
            var me = this;
            me.$isPressing = false;
            if (me.shouldAutoHide('release')) {
                me.bodyElement.hide();
            }
            me.onDragEnd(e);
        },
        onVerticalOverflowChange: function(list, verticalOverflow) {
            this.setRight(verticalOverflow ? Ext.getScrollbarSize().width : 0);
        },
        scrollToClosestByIndex: function(index) {
            var me = this,
                list = me.parent,
                key = index.toLowerCase(),
                store = list.getStore(),
                groups = store.getGroups(),
                ln = groups.length,
                group, groupKey, i, closest, item, record;
            for (i = 0; i < ln; i++) {
                group = groups.getAt(i);
                groupKey = group.getGroupKey().toLowerCase();
                if (groupKey >= key) {
                    closest = group;
                    break;
                }
                closest = group;
            }
            if (closest) {
                record = closest.first();
                
                
                
                
                
                
                if (!list.getInfinite()) {
                    item = list.itemFromRecord(record).$header;
                }
                list.ensureVisible(record, {
                    animation: me.getAnimation(),
                    item: item,
                    align: {
                        y: 'start'
                    }
                });
            }
        },
        
        shouldAutoHide: function(trigger) {
            var me = this,
                autoHide = me.getAutoHide(),
                ret = false;
            
            
            
            if (autoHide) {
                
                if (autoHide === 'pressed' || !Ext.os.is.Desktop) {
                    ret = trigger === 'press' || trigger === 'release';
                } else 
                {
                    
                    
                    
                    ret = trigger === 'over' || (trigger === 'release' && !me.$isMouseOver) || (trigger === 'out' && !me.$isPressing);
                }
            }
            return ret;
        },
        syncIndicatorPosition: function(point, target, isValidTarget) {
            var me = this,
                isUsingIndicator = me.getIndicator(),
                direction = me.getDirection(),
                renderElement = me.renderElement,
                bodyElement = me.bodyElement,
                indicator = me.indicator,
                indicatorInner = me.indicatorInner,
                first = bodyElement.getFirstChild(),
                last = bodyElement.getLastChild(),
                indexbarWidth, indexbarHeight, indicatorSpacing, firstPosition, lastPosition, indicatorSize;
            if (isUsingIndicator && indicator) {
                indicator.show();
                if (direction === 'vertical') {
                    indicatorSize = indicator.getHeight();
                    indexbarWidth = bodyElement.getWidth();
                    indicatorSpacing = bodyElement.getMargin('lr');
                    firstPosition = first.getY();
                    lastPosition = last.getY();
                    if (point.y < firstPosition) {
                        target = first;
                    } else if (point.y > lastPosition) {
                        target = last;
                    }
                    if (isValidTarget) {
                        indicatorInner.setHtml(target.getHtml().toUpperCase());
                    }
                    indicator.setTop(target.getY() - renderElement.getY() - (indicatorSize / 2) + (target.getHeight() / 2));
                    indicator.setRight(indicatorSpacing + indexbarWidth);
                } else {
                    indicatorSize = indicator.getWidth();
                    indicatorSpacing = bodyElement.getMargin('tb');
                    indexbarHeight = bodyElement.getHeight();
                    firstPosition = first.getX();
                    lastPosition = last.getX();
                    if (point.x < firstPosition) {
                        target = first;
                    } else if (point.x > lastPosition) {
                        target = last;
                    }
                    indicator.setLeft(target.getX() - renderElement.getX() - (indicatorSize / 2) + (target.getWidth() / 2));
                    indicator.setBottom(indicatorSpacing + indexbarHeight);
                }
                indicatorInner.setHtml(target.getHtml().toUpperCase());
            }
        },
        trackMove: function(event, drop) {
            var me = this,
                el = me.bodyElement,
                pageBox = me.pageBox || (me.pageBox = me.el.getBox()),
                point = Ext.util.Point.fromEvent(event),
                target, isValidTarget;
            if (me.getDirection() === 'vertical') {
                if (point.y > pageBox.bottom || point.y < pageBox.top) {
                    return;
                }
                target = Ext.Element.fromPoint(pageBox.left + (pageBox.width / 2), point.y);
                isValidTarget = target && target.getParent() === el;
            } else {
                if (point.x > pageBox.right || point.x < pageBox.left) {
                    return;
                }
                target = Ext.Element.fromPoint(point.x, pageBox.top + (pageBox.height / 2));
                isValidTarget = target && target.getParent() === el;
            }
            if (target && isValidTarget) {
                if (me.getIndicator()) {
                    me.syncIndicatorPosition(point, target, isValidTarget);
                }
                if (drop || me.getDynamic()) {
                    me.scrollToClosestByIndex(target.dom.innerHTML);
                }
            }
        },
        
        
        
        updateAutoHide: function(autoHide) {
            var me = this,
                parentEl = me.parent.el,
                autoHideCls = me.autoHideCls,
                indexedNoAutoHideCls = me.indexedNoAutoHideCls;
            
            me.bodyElement.setVisibilityMode(Ext.Element.OPACITY);
            if (autoHide) {
                
                me.addCls(autoHideCls);
                me.bodyElement.hide();
                parentEl.removeCls(indexedNoAutoHideCls);
            } else {
                me.removeCls(autoHideCls);
                me.bodyElement.show();
                parentEl.addCls(indexedNoAutoHideCls);
            }
        },
        
        updateDirection: function(direction) {
            var me = this,
                verticalCls = me.verticalCls,
                horizontalCls = me.horizontalCls,
                indexedVerticalCls = me.indexedVerticalCls,
                indexedHorizontalCls = me.indexedHorizontalCls,
                oldCls, newCls, oldIndexedCls, newIndexedCls;
            if (direction === 'vertical') {
                oldCls = horizontalCls;
                newCls = verticalCls;
                oldIndexedCls = indexedHorizontalCls;
                newIndexedCls = indexedVerticalCls;
            } else {
                oldCls = verticalCls;
                newCls = horizontalCls;
                oldIndexedCls = indexedVerticalCls;
                newIndexedCls = indexedHorizontalCls;
            }
            me.element.replaceCls(oldCls, newCls);
            me.bodyElement.replaceCls(oldCls, newCls);
            me.parent.element.replaceCls(oldIndexedCls, newIndexedCls);
        },
        
        updateIndicator: function(indicator) {
            var me = this,
                config = {
                    cls: me.indicatorCls
                };
            if (indicator && indicator !== true) {
                config = Ext.apply(config, indicator);
            }
            if (indicator) {
                me.indicator = me.el.appendChild(config);
                me.indicatorInner = me.indicator.appendChild({
                    cls: me.indicatorCls + '-inner'
                });
                me.indicator.hide(false);
            } else if (me.indicator) {
                me.indicator.destroy();
                me.indicatorInner.destroy();
                me.indicator = me.indicatorInner = null;
            }
        },
        
        updateLetters: function(letters) {
            var bodyElement = this.bodyElement,
                len = letters.length,
                i;
            bodyElement.setHtml('');
            if (letters) {
                
                for (i = 0; i < len; i++) {
                    bodyElement.createChild({
                        cls: Ext.baseCSSPrefix + 'indexbar-item',
                        html: letters[i]
                    });
                }
            }
        },
        
        updateListPrefix: function(listPrefix) {
            if (listPrefix && listPrefix.length) {
                this.bodyElement.createChild({
                    html: listPrefix
                }, 0);
            }
        },
        
        updateUi: function(ui, oldUi) {
            var me = this,
                list = me.parent,
                listElement = list.element,
                indexedCls = me.indexedCls;
            
            
            if (oldUi) {
                listElement.removeCls(oldUi, indexedCls);
            }
            if (ui) {
                listElement.addCls(ui, indexedCls);
            }
            me.callParent([
                ui,
                oldUi
            ]);
        }
    }
});



Ext.define('Ext.dataview.ListItem', {
    extend: Ext.dataview.DataItem,
    alternateClassName: 'Ext.dataview.component.ListItem',
    xtype: 'listitem',
    mixins: [
        Ext.dataview.Disclosable,
        Ext.mixin.Toolable,
        Ext.dataview.Pinnable
    ],
    classCls: [
        
        
        Ext.baseCSSPrefix + 'listitem',
        Ext.baseCSSPrefix + 'container',
        Ext.baseCSSPrefix + 'component'
    ],
    classClsRoot: true,
    inheritUi: true,
    items: null,
    
    updateRecord: function(record) {
        var me = this;
        if (!me.destroying && !me.destroyed) {
            me.callParent([
                record
            ]);
            me.syncDisclosure(record);
        }
    },
    doDestroy: function() {
        this.mixins.toolable.doDestroy.call(this);
        this.callParent();
    },
    privates: {
        invokeToolHandler: function(tool, handler, scope, args, e) {
            if (this.invokeDisclosure(tool, handler, e)) {
                return false;
            }
            return tool.invokeToolHandler(tool, handler, scope, args, e);
        }
    }
});


Ext.define('Ext.layout.HBox', {
    extend: Ext.layout.Box,
    alias: 'layout.hbox',
    config: {
        vertical: false
    }
});


Ext.define('Ext.dataview.NestedList', {
    alternateClassName: 'Ext.NestedList',
    extend: Ext.Container,
    xtype: 'nestedlist',
    config: {
        
        
        backText: 'Back',
        
        useTitleAsBackText: true,
        
        updateTitleText: true,
        
        displayField: 'text',
        
        loadingText: 'Loading...',
        
        emptyText: 'No items available.',
        
        onItemDisclosure: false,
        
        allowDeselect: false,
        
        useToolbar: null,
        
        toolbar: {
            docked: 'top',
            xtype: 'titlebar',
            ui: 'light',
            inline: true
        },
        
        title: '',
        
        layout: {
            type: 'card',
            animation: {
                type: 'slide',
                duration: 250,
                direction: 'left'
            }
        },
        
        store: null,
        
        detailContainer: undefined,
        
        detailCard: null,
        
        backButton: {
            hidden: true
        },
        
        listConfig: null,
        
        variableHeights: false,
        
        lastNode: null,
        
        lastActiveList: null,
        ui: null,
        clearSelectionOnListChange: true
    },
    baseCls: Ext.baseCSSPrefix + 'nested-list',
    
    listMode: 'title',
    
    
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        if (Ext.isObject(config)) {
            if (config.getTitleTextTpl) {
                this.getTitleTextTpl = config.getTitleTextTpl;
            }
            if (config.getItemTextTpl) {
                this.getItemTextTpl = config.getItemTextTpl;
            }
        }
        this.callParent([
            config
        ]);
    },
    changeListMode: function(node) {
        var me = this,
            store = me.getStore(),
            rootNode = store && store.getRoot();
        if (node === rootNode) {
            me.listMode = 'title';
        } else if (node.parentNode === rootNode) {
            me.listMode = 'node';
        } else {
            me.listMode = 'deep';
        }
    },
    onChildInteraction: function() {
        if (this.isGoingTo) {
            return false;
        }
    },
    applyDetailContainer: function(config) {
        if (!config) {
            config = this;
        }
        return config;
    },
    updateDetailContainer: function(newContainer, oldContainer) {
        if (newContainer) {
            newContainer.on('beforeactiveitemchange', 'onBeforeDetailContainerChange', this);
            newContainer.on('activeitemchange', 'onDetailContainerChange', this);
        }
    },
    onBeforeDetailContainerChange: function() {
        this.isGoingTo = true;
    },
    onDetailContainerChange: function() {
        this.isGoingTo = false;
    },
    updateLayout: function(layout, oldLayout) {
        this.callParent([
            layout,
            oldLayout
        ]);
        if (oldLayout) {
            oldLayout.un({
                beforeactiveitemchange: 'beforeLayoutActiveItemChange',
                activeitemchange: 'onLayoutActiveItemChange',
                scope: this
            });
        }
        if (layout) {
            layout.on({
                beforeactiveitemchange: 'beforeLayoutActiveItemChange',
                activeitemchange: 'onLayoutActiveItemChange',
                scope: this
            });
        }
    },
    
    onChildTap: function(list, location) {
        var me = this,
            hasListeners = me.hasListeners,
            record = location.record;
        if (me.onChildInteraction(list, location) === false) {
            return false;
        }
        if (hasListeners.childtap) {
            location.list = list;
            me.fireEvent('childtap', me, location);
        }
        if (hasListeners.itemtap) {
            me.fireEvent('itemtap', me, list, location.viewIndex, location.child, record, location.event);
        }
        if (record.isLeaf()) {
            if (hasListeners.leafchildtap) {
                location.list = list;
                me.fireEvent('leafchildtap', me, location);
            }
            if (hasListeners.leafitemtap) {
                me.fireEvent('leafitemtap', me, list, location.viewIndex, location.child, record, location.event);
            }
            me.goToLeaf(record);
        } else {
            this.goToNode(record);
        }
    },
    onBeforeSelect: function() {
        this.fireEvent.apply(this, [].concat('beforeselect', this, Array.prototype.slice.call(arguments)));
    },
    onContainerTap: function() {
        this.fireEvent.apply(this, [].concat('containertap', this, Array.prototype.slice.call(arguments)));
    },
    onSelect: function() {
        var args = Array.prototype.slice.call(arguments);
        this.fireEvent.apply(this, [].concat('select', this, args));
        this.onSelectionChange(args);
    },
    onDeselect: function() {
        var args = Array.prototype.slice.call(arguments);
        this.fireEvent.apply(this, [].concat('deselect', this, args));
        this.onSelectionChange(args);
    },
    onSelectionChange: function(args) {
        this.fireEvent.apply(this, [].concat('selectionchange', this, args));
    },
    onChildDoubleTap: function(list, location) {
        var me = this,
            hasListeners = me.hasListeners;
        if (hasListeners.childdoubletap) {
            location.list = list;
            me.fireEvent('childdoubletap', me, location);
        }
        if (hasListeners.itemdoubletap) {
            me.fireEvent('itemdoubletap', me, list, location.viewIndex, location.child, location.record, location.event);
        }
    },
    onStoreBeforeLoad: function() {
        var loadingText = this.getLoadingText();
        if (loadingText) {
            this.setMasked({
                xtype: 'loadmask',
                message: loadingText
            });
        }
        this.fireEvent.apply(this, [].concat('beforeload', this, Array.prototype.slice.call(arguments)));
    },
    onStoreLoad: function(store, records, successful, operation, parentNode) {
        this.setMasked(false);
        this.fireEvent.apply(this, [].concat('load', this, Array.prototype.slice.call(arguments)));
        if (store.indexOf(this.getLastNode()) === -1) {
            this.goToNode(store.getRoot());
        }
    },
    
    onBackTap: function() {
        var me = this,
            node = me.getLastNode(),
            detailCard = me.getDetailCard(),
            detailCardActive = detailCard && me.getActiveItem() == detailCard,
            lastActiveList = me.getLastActiveList();
        this.fireAction('back', [
            this,
            node,
            lastActiveList,
            detailCardActive
        ], 'doBack', null, null, 'after');
    },
    doBack: function(me, node, lastActiveList, detailCardActive) {
        var layout = me.getLayout(),
            animation = layout ? layout.getAnimation() : null;
        if (detailCardActive && lastActiveList) {
            if (animation) {
                animation.setReverse(true);
            }
            me.setActiveItem(lastActiveList);
            me.setLastNode(node.parentNode);
            me.syncToolbar();
        } else {
            me.goToNode(node.parentNode);
        }
    },
    updateData: function(data) {
        if (!this.getStore()) {
            this.setStore(new Ext.data.TreeStore({
                root: data
            }));
        }
    },
    applyStore: function(store) {
        if (store) {
            if (Ext.isString(store)) {
                
                store = Ext.data.StoreManager.get(store);
            } else {
                
                if (!(store instanceof Ext.data.TreeStore)) {
                    store = Ext.factory(store, Ext.data.TreeStore, null);
                }
            }
            
            if (!store) {
                Ext.Logger.warn("The specified Store cannot be found", this);
            }
        }
        
        return store;
    },
    storeListeners: {
        rootchange: 'onStoreRootChange',
        load: 'onStoreLoad',
        beforeload: 'onStoreBeforeLoad'
    },
    updateStore: function(newStore, oldStore) {
        var me = this,
            listeners = this.storeListeners;
        listeners.scope = me;
        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            if (oldStore.autoDestroy) {
                oldStore.destroy();
            }
            oldStore.un(listeners);
        }
        if (newStore) {
            newStore.on(listeners);
            me.goToNode(newStore.getRoot());
        }
    },
    onStoreRootChange: function(store, node) {
        this.goToNode(node);
    },
    applyDetailCard: function(detailCard, oldDetailCard) {
        return Ext.factory(detailCard, Ext.Component, detailCard === null ? oldDetailCard : undefined);
    },
    applyBackButton: function(config) {
        var toolbar = this.getToolbar();
        return !toolbar ? false : Ext.factory(config, Ext.Button, this.getBackButton());
    },
    updateBackButton: function(newButton, oldButton) {
        if (newButton) {
            var me = this;
            newButton.on('tap', me.onBackTap, me);
            newButton.setText(me.getBackText());
            if (me.$backButtonContainer) {
                me.$backButtonContainer.insert(0, newButton);
            } else {
                me.getToolbar().insert(0, newButton);
            }
        } else if (oldButton) {
            oldButton.destroy();
        }
    },
    applyToolbar: function(config) {
        if (config && config.splitNavigation) {
            Ext.apply(config, {
                docked: 'top',
                xtype: 'titlebar',
                ui: 'light'
            });
            var containerConfig = (config.splitNavigation === true) ? {} : config.splitNavigation;
            this.$backButtonContainer = this.add(Ext.apply({
                xtype: 'toolbar',
                docked: 'bottom',
                hidden: true,
                ui: 'dark'
            }, containerConfig));
        }
        return Ext.factory(config, Ext.TitleBar, this.getToolbar());
    },
    updateToolbar: function(newToolbar, oldToolbar) {
        var me = this;
        if (newToolbar) {
            newToolbar.setTitle(me.getTitle());
            if (!newToolbar.getParent()) {
                me.add(newToolbar);
            }
        } else if (oldToolbar) {
            oldToolbar.destroy();
        }
    },
    updateUseToolbar: function(newUseToolbar, oldUseToolbar) {
        if (!newUseToolbar) {
            this.setToolbar(false);
        }
    },
    updateTitle: function(newTitle) {
        var me = this,
            backButton = me.getBackButton();
        if (me.getUpdateTitleText()) {
            if (me.listMode === 'title') {
                me.setToolbarTitle(newTitle);
            } else if (backButton && me.getUseTitleAsBackText() && me.listMode === 'node') {
                backButton.setText(newTitle);
            }
        } else {
            me.setToolbarTitle(newTitle);
        }
    },
    
    getItemTextTpl: function(node) {
        return '{' + this.getDisplayField() + '}';
    },
    
    getTitleTextTpl: function(node) {
        return '{' + this.getDisplayField() + '}';
    },
    
    renderTitleText: function(node, forBackButton) {
        if (!node.titleTpl) {
            node.titleTpl = Ext.create('Ext.XTemplate', this.getTitleTextTpl(node));
        }
        if (node.isRoot()) {
            var initialTitle = this.getTitle();
            return (forBackButton && initialTitle === '') ? this.getInitialConfig('backText') : initialTitle;
        }
        return node.titleTpl.applyTemplate(node.data);
    },
    
    goToNode: function(node) {
        if (!node) {
            return;
        }
        var me = this,
            activeItem = me.getActiveItem(),
            detailCard = me.getDetailCard(),
            detailCardActive = detailCard && me.getActiveItem() == detailCard,
            reverse = me.goToNodeReverseAnimation(node),
            firstList = me.firstList,
            secondList = me.secondList,
            layout = me.getLayout(),
            animation = layout ? layout.getAnimation() : null,
            list;
        
        if (node.isLeaf()) {
            throw new Error('goToNode: passed a node which is a leaf.');
        }
        
        if (node === me.getLastNode() && !detailCardActive) {
            return;
        }
        if (detailCardActive) {
            if (animation) {
                animation.setReverse(true);
            }
            list = me.getLastActiveList();
            list.getStore().setNode(node);
            node.expand();
            me.setActiveItem(list);
        } else {
            if (animation) {
                animation.setReverse(reverse);
            }
            if (firstList && secondList) {
                
                activeItem = me.getActiveItem();
                me.setLastActiveList(activeItem);
                list = (activeItem == firstList) ? secondList : firstList;
                list.getStore().setNode(node);
                node.expand();
                me.setActiveItem(list);
                if (me.getClearSelectionOnListChange()) {
                    list.deselectAll();
                }
            } else if (firstList) {
                
                me.setLastActiveList(me.getActiveItem());
                me.setActiveItem(me.getList(node));
                me.secondList = me.getActiveItem();
            } else {
                
                me.setActiveItem(me.getList(node));
                me.firstList = me.getActiveItem();
            }
        }
        me.fireEvent('listchange', me, me.getActiveItem());
        me.setLastNode(node);
        me.changeListMode(node);
        me.syncToolbar();
    },
    
    goToLeaf: function(node) {
        if (!node.isLeaf()) {
            throw new Error('goToLeaf: passed a node which is not a leaf.');
        }
        var me = this,
            card = me.getDetailCard(),
            container = me.getDetailContainer(),
            sharedContainer = container === me,
            layout = me.getLayout(),
            animation = layout ? layout.getAnimation() : false,
            activeItem;
        if (card) {
            if (container.getItems().indexOf(card) === -1) {
                container.add(card);
            }
            if (sharedContainer) {
                activeItem = me.getActiveItem();
                if (activeItem instanceof Ext.dataview.List) {
                    me.setLastActiveList(activeItem);
                }
                me.setLastNode(node);
            }
            if (animation) {
                animation.setReverse(false);
            }
            container.setActiveItem(card);
            me.syncToolbar();
        }
    },
    
    syncToolbar: function(forceDetail) {
        var me = this,
            detailCard = me.getDetailCard(),
            node = me.getLastNode(),
            detailActive = forceDetail || (detailCard && (me.getActiveItem() == detailCard)),
            parentNode = (detailActive) ? node : node.parentNode,
            backButton = me.getBackButton(),
            toolbar = me.getToolbar();
        if (!toolbar) {
            return;
        }
        
        if (backButton) {
            var splitNavigation = toolbar.getInitialConfig('splitNavigation');
            if (splitNavigation) {
                me.$backButtonContainer[parentNode ? 'show' : 'hide']();
            }
            backButton[parentNode ? 'show' : 'hide']();
            if (parentNode && me.getUseTitleAsBackText()) {
                backButton.setText(me.renderTitleText(node.parentNode, true));
            }
        }
        if (node) {
            me.setToolbarTitle(me.renderTitleText(node));
        }
    },
    updateBackText: function(newText) {
        var btn = this.getBackButton();
        if (btn) {
            btn.setText(newText);
        }
    },
    
    goToNodeReverseAnimation: function(node) {
        var lastNode = this.getLastNode();
        if (!lastNode) {
            return false;
        }
        return (!lastNode.contains(node) && lastNode.isAncestor(node)) ? true : false;
    },
    beforeLayoutActiveItemChange: function() {
        var backButton = this.getBackButton();
        if (backButton) {
            backButton.disable();
        }
    },
    onLayoutActiveItemChange: function() {
        var backButton = this.getBackButton();
        if (backButton) {
            backButton.enable();
        }
    },
    
    getList: function(node) {
        var me = this,
            treeStore = new Ext.data.NodeStore({
                recursive: false,
                node: node,
                rootVisible: false,
                model: me.getStore().getModel(),
                proxy: 'memory'
            }),
            list;
        node.expand();
        list = Ext.create(Ext.Object.merge({
            xtype: 'list',
            pressedDelay: 250,
            autoDestroy: true,
            store: treeStore,
            onItemDisclosure: me.getOnItemDisclosure(),
            variableHeights: me.getVariableHeights(),
            emptyText: me.getEmptyText(),
            selectable: {
                deselectable: me.getAllowDeselect()
            },
            listeners: {
                scope: me,
                childdoubletap: 'onChildDoubleTap',
                beforeselectionchange: 'onBeforeSelect',
                containertap: 'onContainerTap',
                select: 'onSelect',
                deselect: 'onDeselect',
                childtap: {
                    fn: 'onChildTap',
                    priority: 1000
                },
                childtouchstart: {
                    fn: 'onChildInteraction',
                    priority: 1000
                }
            },
            itemTpl: '<span<tpl if="leaf == true"> class="x-list-item-leaf"</tpl>>' + me.getItemTextTpl(node) + '</span>'
        }, me.getListConfig()));
        me.relayEvents(list, [
            'activate'
        ]);
        return list;
    },
    privates: {
        
        setToolbarTitle: function(newTitle) {
            var me = this,
                toolbar = me.getToolbar();
            if (toolbar) {
                toolbar.setTitle(newTitle);
            }
        }
    }
});


Ext.define('Ext.dataview.listswiper.Item', {
    extend: Ext.Container,
    xtype: 'listswiperitem',
    classCls: Ext.baseCSSPrefix + 'listswiperitem',
    config: {
        leftActions: null,
        rightActions: null,
        
        undo: {
            lazy: true,
            $value: {
                xtype: 'button',
                text: 'Undo',
                touchAction: {
                    panX: false,
                    panY: false
                }
            }
        },
        
        action: null,
        
        state: null,
        
        translationTarget: null
    },
    autoSize: null,
    initialize: function() {
        this.callParent();
        this.ownerCmp.on({
            scope: this,
            destroy: 'onItemDestroy',
            removed: 'onItemDestroy'
        });
    },
    applyUndo: function(config) {
        var action = this.getAction();
        return Ext.apply({}, action && action.undoable, config);
    },
    onItemDestroy: function(item) {
        var me = this,
            plugin = me.owner;
        plugin.destroyItem(item);
    },
    invokeAction: function(action, type) {
        var me = this,
            plugin = me.owner,
            list = plugin.cmp,
            item = me.ownerCmp,
            fn = action[type],
            obj = {
                item: item,
                record: item && item.getRecord(),
                action: action
            };
        list.fireEvent('itemaction' + type, list, obj);
        return Ext.callback(fn, action.getScope && action.getScope() || action.scope, [
            list,
            obj
        ], 0, me);
    },
    updateState: function(state, oldState) {
        if (oldState) {
            this.removeCls(Ext.baseCSSPrefix + oldState);
        }
        if (state) {
            this.addCls(Ext.baseCSSPrefix + state);
        }
    },
    onDragStart: Ext.emptyFn,
    onDragMove: Ext.emptyFn,
    onDragEnd: Ext.emptyFn
});

Ext.define('Ext.dataview.listswiper.Accordion', {
    extend: Ext.dataview.listswiper.Item,
    xtype: 'listswiperaccordion',
    classCls: Ext.baseCSSPrefix + 'listswiperaccordion',
    cls: Ext.baseCSSPrefix + 'item-no-tap',
    config: {
        bodyOffset: null,
        actionDefaults: {
            cls: Ext.baseCSSPrefix + 'listswiperaction',
            xtype: 'button',
            iconAlign: 'top'
        },
        actionUI: 'square',
        singleActionDefaults: {},
        multiActionDefaults: {},
        undo: {
            cls: Ext.baseCSSPrefix + 'listswiperundoable',
            ui: 'undo',
            docked: 'right',
            ignoreDefaults: true
        },
        thresholds: null,
        
        scaleDrag: true,
        
        swipeToCommit: true,
        
        
        side: null
    },
    layout: {
        type: 'hbox',
        align: 'stretch'
    },
    template: [
        {
            reference: 'bodyElement',
            cls: Ext.baseCSSPrefix + 'body-el',
            uiCls: 'body-el',
            children: [
                {
                    reference: 'leftElement',
                    cls: Ext.baseCSSPrefix + 'listswiperaccordion-wrapper ' + Ext.baseCSSPrefix + 'listswiperaccordion-wrapper-left'
                },
                {
                    reference: 'rightElement',
                    cls: Ext.baseCSSPrefix + 'listswiperaccordion-wrapper ' + Ext.baseCSSPrefix + 'listswiperaccordion-wrapper-right'
                }
            ]
        }
    ],
    scrollDock: null,
    constructor: function(config) {
        var me = this;
        me.left = {
            name: 'left',
            isLeft: true,
            items: []
        };
        me.right = {
            name: 'right',
            isLeft: false,
            items: []
        };
        me.callParent([
            config
        ]);
    },
    initialize: function() {
        var me = this,
            target = me.getTranslationTarget();
        me.callParent();
        target.on({
            scope: me,
            tap: 'onDismissTap'
        });
    },
    destroy: function() {
        var me = this,
            target = me.getTranslationTarget();
        
        if (me.thresholdEl) {
            me.thresholdEl.destroy();
        }
        
        target.un({
            scope: me,
            tap: 'onDismissTap'
        });
        me.callParent();
    },
    applyLeftActions: function(items) {
        this.addActions('left', items);
    },
    applyRightActions: function(items) {
        this.addActions('right', items);
    },
    applySide: function(side) {
        this.side = side && this[side];
        return side;
    },
    getButtonBackgroundColor: function(button) {
        var action = button.$action,
            backgroundColorEl = button[action.backgroundColorEl || 'innerElement'];
        return backgroundColorEl.getStyle('backgroundColor');
    },
    addActions: function(side, items) {
        var me = this,
            i, config, action, button;
        side = me[side];
        side.el = side.isLeft ? me.leftElement : me.rightElement;
        side.multiple = items.length > 1;
        config = side.multiple ? me.getMultiActionDefaults() : me.getSingleActionDefaults();
        side.el.toggleCls(me.baseCls + '-multiple', side.multiple);
        side.el.toggleCls(me.baseCls + '-single', !side.multiple);
        for (i = 0; i < items.length; i++) {
            action = me.createActionItem(Ext.apply({}, items[i], config));
            action.$side = side;
            button = me.add(action);
            button.addUi(this.getActionUI());
            button.$action = action;
            button.$originalHandler = button.getHandler();
            button.setHandler(me.onActionTap.bind(me, action));
            side.items.push(button);
        }
    },
    createActionItem: function(config) {
        return Ext.apply({}, config, this.getActionDefaults());
    },
    getSwipeRange: function() {
        var me = this,
            side = me.side,
            plugin = me.owner,
            swipeMax = plugin.getSwipeMax();
        return me.itemWidth * (swipeMax[side.multiple ? 'multiple' : 'single'] / 100);
    },
    onActionTap: function(action, button, e) {
        var me = this,
            state = me.getState();
        if (state !== 'dragpeek') {
            e.stopPropagation();
            this.commit(e, action, button);
        }
    },
    onDismissTap: function() {
        var me = this,
            plugin = me.owner,
            dimissOnTap = plugin.getDismissOnTap();
        if (dimissOnTap) {
            me.dismiss();
        }
    },
    onRender: function() {
        var me = this,
            item = me.ownerCmp;
        me.itemWidth = item.el.measure('w');
        me.syncSides();
    },
    updateSide: function(side, oldSide) {
        var me = this,
            layout = me.getLayout();
        me.el.replaceCls(oldSide, side, me.baseCls + '-side');
        if (side === 'left') {
            layout.setPack('start');
        } else {
            layout.setPack('end');
        }
    },
    updateState: function(state, oldState) {
        var me = this,
            side = me.side,
            defaultButton = me.getDefaultButton();
        me.callParent([
            state,
            oldState
        ]);
        if (side.multiple) {
            me.el.toggleCls(me.baseCls + '-collapsed', state === 'dragcommit');
            if (state === 'dragcommit') {
                defaultButton.el.setStyle({
                    'flex-basis': side.maxActionWidth + 'px'
                });
            } else {
                defaultButton.setStyle({
                    'flex-basis': null
                });
            }
            if (oldState === 'dragcommit' && me.isDragging) {
                me.el.addCls(me.baseCls + '-was-collapsed');
            } else {
                me.el.removeCls(me.baseCls + '-was-collapsed');
            }
        }
    },
    privates: {
        destroyItem: function() {
            var me = this,
                plugin = me.owner,
                item = me.ownerCmp;
            if (!me.destroyed) {
                me.animating = false;
                me.el.removeCls(me.baseCls + '-was-collapsed');
                plugin.destroyItem(item);
            }
        },
        animateItem: function(offset, config) {
            config = config || {};
            var me = this,
                side = me.side,
                target = this.getTranslationTarget(),
                duration = config.duration || 150,
                completeFn;
            return new Ext.Promise(function(resolve) {
                me.animating = true;
                me.offset = side.isLeft ? offset : -offset;
                completeFn = function() {
                    if (!me.destroyed) {
                        me.animating = false;
                        me.el.removeCls(me.baseCls + '-was-collapsed');
                    }
                    resolve();
                };
                if (target.dom) {
                    if (side.el.dom) {
                        side.el.animate({
                            preserveEndState: true,
                            duration: duration,
                            to: {
                                width: offset
                            }
                        });
                    }
                    target.animate({
                        preserveEndState: true,
                        duration: duration,
                        to: {
                            transform: {
                                translateX: me.offset
                            }
                        },
                        callback: completeFn
                    });
                } else {
                    completeFn();
                }
            });
        },
        commit: function(e, action, button) {
            var me = this,
                plugin = me.owner,
                undoable, handler, delay, precommitResult, undo, backgroundColor;
            action = action || me.getDefaultAction();
            button = button || me.getDefaultButton();
            undoable = action.undoable;
            handler = button.$originalHandler;
            me.setAction(action);
            me.$precommitResult = precommitResult = me.invokeAction(action, 'precommit');
            if (handler) {
                me.snapback().then(function() {
                    Ext.callback(handler, button.getScope(), [
                        action,
                        e
                    ], 0, button);
                }).then(function() {
                    me.destroyItem();
                });
            } else {
                if (!undoable) {
                    me.dismiss();
                } else {
                    undo = me.add(me.getUndo());
                    undo.addUi(button.getUi());
                    me.bodyElement.on({
                        scope: me,
                        tap: 'onDismissTap'
                    });
                    Ext.raf(function() {
                        me.setState('undo');
                        backgroundColor = me.getButtonBackgroundColor(button);
                        if (backgroundColor) {
                            me.el.setStyle('backgroundColor', backgroundColor);
                        }
                        undo.setHandler(me.onUndoTap.bind(me));
                        delay = plugin.getCommitDelay();
                        if (delay) {
                            if (precommitResult && precommitResult.then) {
                                precommitResult.then(function() {
                                    plugin.dismissAllTask.delay(delay);
                                });
                            } else {
                                plugin.dismissAllTask.delay(delay);
                            }
                        }
                    });
                }
            }
        },
        onUndoTap: function() {
            this.undo();
        },
        undo: function() {
            var me = this,
                action = me.getAction(),
                precommitResult = me.$precommitResult;
            me.setState('open');
            if (precommitResult && precommitResult.then) {
                precommitResult.then(function() {
                    me.$precommitResult = null;
                    me.undo();
                });
                return;
            }
            me.snapback().then(function() {
                me.invokeAction(action, 'revert');
            }).then(function() {
                me.destroyItem();
            });
        },
        
        createThresholds: function() {
            var me = this,
                item = me.ownerCmp,
                plugin = this.owner,
                side = me.side,
                swipeRange = me.getSwipeRange(),
                commitThreshold = side.commitThreshold,
                openThreshold = side.openThreshold;
            if (plugin.showThresholds && !me.thresholdEl) {
                me.thresholdEl = item.el.append({
                    style: {
                        display: 'flex',
                        flexDirection: 'row',
                        justifyContent: 'flex-end',
                        position: 'absolute',
                        height: 'auto',
                        top: 0,
                        left: 0,
                        right: 0
                    },
                    children: [
                        {
                            style: {
                                width: (swipeRange - (commitThreshold - openThreshold) - openThreshold) + 'px',
                                height: '6px',
                                opacity: 0.8,
                                backgroundColor: '#EF5350'
                            }
                        },
                        {
                            style: {
                                width: (commitThreshold - openThreshold) + 'px',
                                height: '6px',
                                opacity: 0.8,
                                backgroundColor: '#FFEE58'
                            }
                        },
                        {
                            style: {
                                width: openThreshold + 'px',
                                height: '6px',
                                opacity: 0.8,
                                backgroundColor: '#66BB6A'
                            }
                        }
                    ]
                });
            }
            if (me.thresholdEl) {
                me.thresholdEl.setStyle({
                    flexDirection: !side.isLeft ? 'row' : 'row-reverse'
                });
            }
        },
        
        dismiss: function() {
            var me = this,
                action = me.getAction(),
                precommitResult = me.$precommitResult;
            if (precommitResult && precommitResult.then) {
                precommitResult.then(function() {
                    me.$precommitResult = null;
                    me.dismiss();
                });
                return;
            }
            if (action) {
                me.snapback().then(function() {
                    me.invokeAction(action, 'commit');
                }).then(function() {
                    me.destroyItem();
                });
            } else {
                me.snapback(true);
            }
        },
        onDragStart: function(e) {
            var me = this,
                state = me.getState();
            if (me.animating || state === 'undo') {
                return;
            }
            e.claimGesture();
            me.initialOffset = me.offset || 0;
            me.startX = e.getX() - me.el.getX() - me.initialOffset;
            me.isDragging = true;
            me.syncState(e.deltaX);
        },
        onDragMove: function(e) {
            
            if ((this.getState()) === 'undo') {
                return;
            }
            e.preventDefault();
            this.syncState(e.deltaX);
        },
        onDragEnd: function(e) {
            var me = this,
                state = me.getState();
            e.preventDefault();
            me.isDragging = false;
            if (state === 'dragcommit') {
                me.commit(e);
            } else if (state === 'dragopen') {
                me.open();
            } else if (state === 'undo') {
                
                return;
            } else {
                me.snapback(true);
            }
        },
        getDefaultButton: function(side) {
            side = side || this.side;
            var items = side.items;
            return items[side.isLeft ? 0 : items.length - 1];
        },
        getDefaultAction: function(side) {
            var button = this.getDefaultButton(side);
            return button && button.$action;
        },
        getRenderTarget: function(item) {
            var side = item && item.$side;
            if (side) {
                return side.el;
            }
            return this.callParent(arguments);
        },
        open: function() {
            return this.animateItem(this.side.naturalWidth);
        },
        snapback: function(destroy) {
            var me = this,
                anim = me.animateItem(0);
            return destroy ? anim.then(function() {
                me.destroyItem();
            }) : anim;
        },
        syncSides: function() {
            var me = this;
            me.syncSide('left');
            me.syncSide('right');
        },
        syncSide: function(side) {
            var me = this,
                thresholds = me.getThresholds(),
                itemWidth = me.itemWidth,
                element = side === 'left' ? me.leftElement : me.rightElement,
                children = element.dom.childNodes,
                maxActionWidth = 0,
                backgroundColor, defaultButton, childWidth, i, child, naturalWidth;
            side = me[side];
            defaultButton = this.getDefaultButton(side);
            element.addCls(me.baseCls + '-measure');
            for (i = 0; i < children.length; i++) {
                child = Ext.get(children[i]);
                childWidth = child.measure('w');
                if (childWidth > maxActionWidth) {
                    maxActionWidth = childWidth;
                }
            }
            naturalWidth = side.naturalWidth = maxActionWidth * children.length;
            side.maxActionWidth = maxActionWidth;
            if (thresholds && thresholds.open) {
                side.openThreshold = (thresholds.open / 100) * itemWidth;
            } else {
                side.openThreshold = maxActionWidth;
            }
            if (thresholds && thresholds.commit) {
                side.commitThreshold = (thresholds.commit / 100) * itemWidth;
            } else {
                side.commitThreshold = Math.min(0.95 * itemWidth, naturalWidth * 1.4);
            }
            element.removeCls(me.baseCls + '-measure');
            if (side.multiple) {
                backgroundColor = me.getButtonBackgroundColor(defaultButton);
                if (backgroundColor) {
                    side.el.setStyle('backgroundColor', backgroundColor);
                }
            }
        },
        syncState: function(deltaX) {
            var me = this,
                plugin = me.owner,
                itemWidth = me.itemWidth,
                swipeToCommit = me.getSwipeToCommit(),
                scaleDrag = me.getScaleDrag(),
                testOffset = me.initialOffset + deltaX,
                side = this[(testOffset < 0 ? 'right' : 'left')],
                
                
                scaler = scaleDrag ? Math.max(1, Math.min(3, Math.abs((side.isLeft ? 0 : 1) - (me.startX / itemWidth)) * 3)) : 1,
                offset = me.offset = me.initialOffset + (deltaX * scaler),
                currentSide = me.side,
                directionLock = plugin.getDirectionLock(),
                positiveOffset, swipeRange, openThreshold, commitThreshold;
            if (this.left.items.length === 0 || this.right.items.length === 0) {
                directionLock = false;
            }
            
            if (side.items.length === 0 || (currentSide && (side.name !== currentSide.name && directionLock))) {
                offset = me.offset = me.initialOffset + (deltaX * 0.1);
                
                if (side.items.length === 0) {
                    me.setSide(side.name);
                }
                me.setState('draglocked');
            } else {
                me.setSide(side.name);
                
                swipeRange = me.getSwipeRange();
                openThreshold = side.openThreshold;
                commitThreshold = side.commitThreshold;
                positiveOffset = (side.isLeft ? Math.abs(Math.max(0, offset)) : Math.abs(Math.min(0, offset)));
                
                me.createThresholds();
                
                if (positiveOffset <= openThreshold) {
                    me.setState('dragpeek');
                } else if (positiveOffset <= commitThreshold || !swipeToCommit) {
                    me.setState('dragopen');
                } else {
                    me.setState('dragcommit');
                }
                if (side.isLeft) {
                    offset = Math.min(offset, swipeRange);
                } else {
                    offset = Math.max(offset, -swipeRange);
                }
            }
            me.setBodyOffset(offset);
        },
        updateBodyOffset: function(offset) {
            var me = this,
                side = me.side,
                target = me.getTranslationTarget();
            target.setStyle('transform', 'translateX(' + offset + 'px)');
            side.el.setWidth(Math.abs(offset));
        }
    }
});


Ext.define('Ext.dataview.listswiper.ListSwiper', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.listswiper',
    
    config: {
        left: [],
        right: [],
        
        dismissOnTap: true,
        
        dismissOnScroll: true,
        
        commitDelay: 0,
        
        widget: {
            xtype: 'listswiperaccordion'
        },
        swipeMax: {
            single: 50,
            multiple: 90
        },
        directionLock: true,
        
        target: null
    },
    shadowCls: Ext.baseCSSPrefix + 'listswiper-shadow',
    init: function(list) {
        var me = this,
            scrollable = list.getScrollable();
        
        me.items = [];
        list.on({
            scope: this,
            add: 'onItemAdd'
        });
        list.el.on({
            scope: this,
            dragstart: 'onDragStart',
            drag: 'onDragMove',
            dragend: 'onDragEnd'
        });
        if (scrollable) {
            scrollable.setX(false);
        }
        me.dismissAllTask = new Ext.util.DelayedTask(me.dismissAll, me);
        me.updateDismissOnScroll(me.getDismissOnScroll());
    },
    destroy: function() {
        var list = this.cmp;
        list.un({
            scope: this,
            add: 'onItemAdd'
        });
        list.el.un({
            scope: this,
            dragstart: 'onDragStart',
            drag: 'onDragMove',
            dragend: 'onDragEnd'
        });
        this.callParent();
    },
    createWidget: function(config) {
        var me = this,
            leftItems = me.getLeft(),
            rightItems = me.getRight();
        return Ext.apply({
            owner: me,
            defaults: me.defaults,
            leftActions: leftItems,
            rightActions: rightItems
        }, config);
    },
    onScrollStart: function() {
        if (this.getDismissOnScroll()) {
            this.dismissAll();
        }
    },
    onItemAdd: function(list, item) {
        item.setTouchAction({
            panX: false
        });
    },
    onItemUpdateData: function(item) {
        
        
        
        Ext.asap(this.resyncItem, this, [
            item
        ]);
    },
    onDragStart: function(evt) {
        var me = this,
            list = me.cmp,
            record = list.mapToRecord(evt),
            target, translationTarget, renderTarget, item, widget;
        if (!me.hasActions() || (evt.absDeltaX < evt.absDeltaY)) {
            return;
        }
        if (record) {
            item = list.mapToItem(record);
            if (item) {
                widget = item.$swiperWidget;
                if (!widget) {
                    widget = me.createWidget(me.getWidget());
                    widget.ownerCmp = item;
                    target = me.getTarget();
                    if (item.isGridRow || (target === 'outer')) {
                        renderTarget = item.el;
                        
                        
                        translationTarget = item.el.first();
                    } else {
                        renderTarget = item.bodyElement;
                        translationTarget = item.hasToolZones ? renderTarget.child('.' + Ext.baseCSSPrefix + 'tool-dock') : item.innerElement;
                    }
                    translationTarget.addCls(me.shadowCls);
                    widget.translationTarget = translationTarget;
                    renderTarget = translationTarget.parent();
                    item.$swiperWidget = widget = Ext.create(widget);
                    renderTarget.insertFirst(widget.el);
                    widget.setRendered(true);
                    if (list.infinite) {
                        list.stickItem(item, true);
                    }
                    this.items.push(item);
                }
                widget.onDragStart(evt);
            }
        }
    },
    onDragMove: function(evt) {
        var me = this,
            list = me.cmp,
            item = list.mapToItem(evt),
            swiperItem;
        if (item) {
            swiperItem = item.$swiperWidget;
            if (!me.hasActions() || !swiperItem) {
                return;
            }
            swiperItem.onDragMove(evt);
        }
    },
    onDragEnd: function(evt) {
        var me = this,
            list = me.cmp,
            item = list.mapToItem(evt),
            swiperItem;
        if (item) {
            swiperItem = item.$swiperWidget;
            if (!me.hasActions() || !swiperItem) {
                return;
            }
            swiperItem.onDragEnd(evt);
        }
    },
    updateDismissOnScroll: function(value) {
        var list = this.getCmp(),
            scrollable, listeners;
        if (this.isConfiguring || !list) {
            return;
        }
        scrollable = list.getScrollable();
        if (!scrollable) {
            return;
        }
        listeners = {
            scrollstart: 'onScrollStart',
            scope: this
        };
        if (value === true) {
            scrollable.on(listeners);
        } else {
            scrollable.un(listeners);
        }
    },
    
    updateTarget: function(target, oldTarget) {
        if (target != null && target !== 'inner' && target !== 'outer') {
            Ext.raise('Invalid target for ListSwiper: ' + target);
        }
    },
    
    hasActions: function() {
        return this.getLeft() || this.getRight();
    },
    privates: {
        destroyItem: function(item) {
            var me = this,
                list = me.cmp,
                swiperWidget = item.$swiperWidget,
                i = me.items.indexOf(item);
            if (i !== -1) {
                me.items.splice(i, 1);
            }
            if (swiperWidget) {
                swiperWidget.destroy();
            }
            item.$swiperWidget = null;
            if (list.infinite && !item.destroyed) {
                list.stickItem(item, null);
            }
        },
        dismissAll: function() {
            var me = this;
            me.items.map(function(item) {
                return item.$swiperWidget;
            }).forEach(function(swiperItem) {
                swiperItem.dismiss();
            });
        }
    }
});


Ext.define('Ext.dataview.listswiper.Stepper', {
    extend: Ext.dataview.listswiper.Item,
    xtype: 'listswiperstepper',
    config: {
        
        iconCls: null,
        
        text: null,
        
        undo: {
            docked: 'right',
            ui: 'listswiperstepper-trigger'
        },
        
        step: null,
        
        side: null,
        
        animation: true
    },
    classCls: Ext.baseCSSPrefix + 'listswiperstepper',
    layout: {
        type: 'hbox',
        align: 'center'
    },
    scrollDock: null,
    sideCls: {
        left: Ext.baseCSSPrefix + 'side-left',
        right: Ext.baseCSSPrefix + 'side-right'
    },
    tpl: '<div class="' + Ext.baseCSSPrefix + 'listswiperstepper-text">{text}</div>',
    template: [
        {
            reference: 'bodyElement',
            cls: Ext.baseCSSPrefix + 'body-el',
            uiCls: 'body-el',
            children: [
                {
                    reference: 'iconWrapElement',
                    cls: Ext.baseCSSPrefix + 'icon-wrap-el',
                    uiCls: 'icon-wrap-el',
                    children: [
                        {
                            reference: 'iconElement',
                            cls: Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon'
                        }
                    ]
                },
                {
                    reference: 'innerElement',
                    cls: Ext.baseCSSPrefix + 'inner-el',
                    uiCls: 'inner-el'
                }
            ]
        }
    ],
    initialize: function() {
        this.callParent();
        this.bodyElement.on('tap', 'onTap', this);
    },
    onRender: function() {
        this.steps = this.buildSteps();
    },
    applyAnimation: function(animation) {
        if (animation === true) {
            animation = {
                duration: 500,
                easing: {
                    type: 'ease-out'
                }
            };
        }
        return animation;
    },
    updateTranslationTarget: function(target) {
        this.translatable = Ext.Factory.translatable({
            element: target
        }, 'csstransform');
    },
    revert: function(animate) {
        var me = this,
            action = me.getAction();
        me.invokeAction(action, 'revert');
        me.finalize(animate);
    },
    
    dismiss: function(animate) {
        var me = this,
            action = me.getAction(),
            state = me.getState();
        if (state === 'undo') {
            me.invokeAction(action, 'commit');
        }
        me.finalize(animate);
    },
    sortFn: function(a, b) {
        return b.x - a.x;
    },
    
    buildSteps: function() {
        var me = this,
            item = me.ownerCmp,
            el = item.el,
            left = me.getLeftActions() || {},
            right = me.getRightActions() || {},
            width = el.getWidth(),
            steps = {
                r: [],
                l: []
            },
            totalThreshold = 0,
            fn = function(side, index, action) {
                var threshold = Ext.util.Format.defaultValue(action.threshold, '25%'),
                    number = parseInt(threshold, 10);
                if (isNaN(number)) {
                    return;
                }
                
                if (typeof threshold === 'string' && threshold.indexOf('%') !== -1) {
                    number = width * number / 100;
                }
                totalThreshold += number;
                steps[side].push({
                    action: action,
                    side: side === 'r' ? 'right' : 'left',
                    tx: side === 'r' ? -width : width,
                    x: totalThreshold,
                    key: action.key || index
                });
            };
        Ext.Object.each(left, fn.bind(this, 'l'));
        totalThreshold = 0;
        Ext.Object.each(right, fn.bind(this, 'r'));
        return steps;
    },
    findStep: function(dx, force) {
        var me = this,
            res = {
                step: null,
                active: true
            },
            steps = me.steps[dx > 0 ? 'l' : 'r'],
            ilen = steps.length,
            absDx = Math.abs(dx),
            step, i;
        for (i = ilen - 1; !res.step && i >= 0; --i) {
            step = steps[i];
            if (step.x < absDx) {
                res.step = step;
            }
        }
        if (!res.step && force && ilen > 0) {
            res.step = steps[0];
            res.active = false;
        }
        return res;
    },
    updateStep: function(step, oldStep) {
        var me = this,
            action = (step && step.action),
            oldAction = (oldStep && oldStep.action),
            actionCls = action && action.cls,
            oldActionCls = oldAction && oldAction.cls,
            actionKeyCls = step && ('swipe-action-' + step.key),
            oldActionKeyCls = oldStep && ('swipe-action-' + oldStep.key);
        if (step) {
            me.setSide(step.side);
        }
        me.replaceCls(oldActionCls, actionCls);
        me.replaceCls(oldActionKeyCls, actionKeyCls, Ext.baseCSSPrefix);
        me.syncStep();
    },
    updateSide: function(side, oldSide) {
        var me = this,
            classes = me.sideCls,
            layout = me.getLayout();
        me.replaceCls(classes[oldSide], classes[side]);
        if (layout.setPack) {
            layout.setPack(side === 'right' ? 'end' : 'start');
        }
    },
    onDragStart: function(evt) {
        evt.stopPropagation();
    },
    onDragMove: function(evt) {
        var me = this,
            plugin = me.owner,
            directionLock = plugin.getDirectionLock(),
            state = me.getState(),
            step = me.getStep(),
            translatable = me.translatable,
            dx = evt.deltaX,
            res;
        if (state === 'undo') {
            return;
        }
        if (state === 'consumed') {
            me.setState('reaquired');
            translatable.stopAnimation();
        }
        res = me.findStep(dx, true);
        
        if (directionLock && (res.step && step) && (res.step.side !== step.side)) {
            me.setState('overdrag');
            res.step = null;
        } else {
            me.setState(res.step ? res.active ? 'active' : 'peek' : 'overdrag');
            me.setStep(res.step || null);
        }
        
        
        translatable.translateAxis('x', res.step ? dx : dx * 0.1);
        evt.stopPropagation();
    },
    onDragEnd: function(evt) {
        var me = this,
            state = me.getState(),
            step = me.getStep(),
            dx = evt.deltaX,
            res;
        if (state === 'undo' || state === 'consumed') {
            return;
        }
        evt.stopPropagation();
        res = me.findStep(dx, false);
        if (!res.step || res.step.side !== step.side) {
            me.finalize(true);
            return;
        }
        me.setStep(res.step);
        me.commit(true);
    },
    commit: function(animate) {
        var me = this,
            step = me.getStep(),
            action = step.action,
            plugin = me.owner,
            translatable = me.translatable,
            delay, precommitResult, undo;
        me.setAction(action);
        precommitResult = me.invokeAction(action, 'precommit');
        if (action.undoable) {
            me.setState('undo');
            undo = me.add(me.getUndo());
            undo.setHandler(me.onUndoTap.bind(me));
            me.setSide(undo.getDocked() === 'left' ? 'right' : 'left');
            translatable.translateAxis('x', step.tx, me.getAnimation());
            delay = plugin.getCommitDelay();
            if (delay) {
                if (precommitResult && precommitResult.then) {
                    precommitResult.then(function() {
                        plugin.dismissAllTask.delay(delay);
                    });
                } else {
                    plugin.dismissAllTask.delay(delay);
                }
            }
        } else {
            if (precommitResult && precommitResult.then) {
                precommitResult.then(me.invokeAction.bind(me, action, 'commit')).then(me.finalize.bind(me, animate));
            } else {
                me.invokeAction(action, 'commit');
                me.finalize(animate);
            }
        }
    },
    finalize: function(animate) {
        var me = this,
            animation = me.getAnimation(),
            translatable = me.translatable;
        translatable.stopAnimation();
        me.setState('consumed');
        if (!animate) {
            me.doFinalize();
            return;
        }
        if (translatable.x !== 0) {
            translatable.on({
                animationend: 'doFinalize',
                single: true,
                scope: me
            });
            translatable.translateAxis('x', 0, animate && animation);
        }
    },
    doFinalize: function() {
        var me = this,
            plugin = me.owner,
            item = me.ownerCmp,
            state = me.getState(),
            translatable = me.translatable;
        
        
        if (state === 'consumed') {
            translatable.translateAxis('x', 0, false);
            if (!me.destroyed && item) {
                plugin.destroyItem(item);
            }
        }
    },
    syncStep: function() {
        var me = this,
            item = me.ownerCmp,
            record = item.getRecord(),
            step = me.getStep(),
            ui = null,
            iconCls = '',
            text = '',
            action, data;
        if (step) {
            action = step.action;
            if (action) {
                ui = action.ui;
                iconCls = action.iconCls;
                text = action.text;
                data = action.data;
            }
            data = Ext.apply({
                text: text
            }, data, record ? record.getData(true) : {});
            this.setUi(ui);
            this.setIconCls(iconCls);
            me.setData(data);
        } else {
            this.setUi(null);
            this.setIconCls(null);
            me.setData(null);
        }
    },
    updateIconCls: function(iconCls, oldIconCls) {
        this.iconElement.replaceCls(oldIconCls, iconCls);
    },
    privates: {
        getRenderTarget: function() {
            return this.innerElement;
        },
        onTap: function(evt) {
            var me = this,
                plugin = me.owner,
                dimissOnTap = plugin.getDismissOnTap();
            evt.stopPropagation();
            if (dimissOnTap) {
                me.dismiss();
            }
        },
        onUndoTap: function(button, evt) {
            evt.stopPropagation();
            this.revert();
        }
    }
});


Ext.define('Ext.tip.ToolTip', {
    extend: Ext.Panel,
    xtype: 'tooltip',
    floated: true,
    hidden: true,
    shadow: true,
    border: true,
    bodyBorder: false,
    anchor: false,
    closeAction: 'hide',
    config: {
        
        align: 'l-r?',
        
        alignDelegate: null,
        
        allowOver: null,
        
        anchorToTarget: true,
        
        autoHide: true,
        
        delegate: null,
        
        dismissDelay: 5000,
        
        hideDelay: 300,
        
        mouseOffset: [
            15,
            18
        ],
        
        quickShowInterval: 250,
        
        showDelay: 500,
        
        showOnTap: null,
        
        target: null,
        
        trackMouse: false
    },
    classCls: Ext.baseCSSPrefix + 'tooltip',
    headerCls: Ext.baseCSSPrefix + 'tooltipheader',
    titleCls: Ext.baseCSSPrefix + 'tooltiptitle',
    toolCls: [
        Ext.baseCSSPrefix + 'paneltool',
        Ext.baseCSSPrefix + 'tooltiptool'
    ],
    closeToolText: null,
    constructor: function(config) {
        
        this.currentTarget = new Ext.dom.Fly();
        this.callParent([
            config
        ]);
    },
    getRefOwner: function() {
        var target = this.getTarget();
        return (target && target.isComponent) ? target : this.callParent();
    },
    updateAnchor: function() {
        this.doRealignToTarget();
    },
    applyAlign: function(align) {
        var lastChar = align[align.length - 1];
        
        if (lastChar !== '?' && lastChar !== '!') {
            align += '?';
        }
        return align;
    },
    updateAlign: function() {
        this.doRealignToTarget();
    },
    updateAllowOver: function(allowOver) {
        var me = this;
        me.overListeners = Ext.destroy(me.overListeners);
        
        if (allowOver) {
            me.overListeners = me.el.on({
                mouseenter: 'onTipOver',
                mouseleave: 'onTipOut',
                scope: me,
                destroyable: true
            });
        }
    },
    applyTarget: function(target) {
        if (target) {
            if (!target.isComponent) {
                target = Ext.get(target.el || target);
            }
        }
        return target;
    },
    updateTarget: function(target, oldTarget) {
        var me = this;
        if (oldTarget) {
            oldTarget.un('destroy', me.destroy, me);
        }
        if (target) {
            if (target.isComponent) {
                me.targetElement = target.element;
                
                
                target.on('destroy', me.destroy, me);
            } else {
                me.targetElement = Ext.get(target);
            }
        } else {
            me.targetElement = null;
        }
        me.attachTargetListeners();
    },
    updateTrackMouse: function(trackMouse) {
        
        if (trackMouse) {
            this.setAllowOver(trackMouse);
        }
    },
    updateDisabled: function(disabled, oldDisabled) {
        var me = this,
            val;
        me.callParent([
            disabled,
            oldDisabled
        ]);
        if (disabled) {
            me.clearTimers();
            me.hide();
            val = null;
        }
        
        me.attachTargetListeners(val);
    },
    updateShowOnTap: function(showOnTap) {
        if (!this.isConfiguring) {
            this.attachTargetListeners();
        }
    },
    
    realignToTarget: function() {
        this.doRealignToTarget();
    },
    showBy: function(target, alignment, passedOptions) {
        var me = this,
            alignTarget = target,
            alignDelegate = me.getAlignDelegate();
        
        if (target.isEvent) {
            me.alignToEvent(target);
        } else {
            if (target.isWidget) {
                me.updateCurrentTarget(target.element.dom);
            } else if (target.isElement) {
                me.updateCurrentTarget(target.dom);
            } else if (target.nodeType) {
                me.updateCurrentTarget(target);
            }
            
            if (alignDelegate) {
                target = Ext.fly(target);
                alignTarget = target.down(alignDelegate, true) || target.up(alignDelegate, me.targetElement, true);
            }
            me.callParent([
                alignTarget,
                alignment || me.getAlign(),
                passedOptions
            ]);
        }
    },
    beforeShow: function(options) {
        var me = this,
            result = me.callParent(arguments);
        
        
        if (result !== false) {
            
            if (!options.alignment && (me.pointerEvent || me.getTarget())) {
                options.alignment = {
                    component: me.targetElement,
                    alignment: me.getAlign(),
                    options: {
                        overlap: me.getTrackMouse() && !me.getAnchor()
                    }
                };
            }
            me.clearTimer('dismiss');
        }
    },
    afterShow: function() {
        var me = this;
        me.callParent(arguments);
        me.postprocessShow();
        me.mousedownListener = Ext.on({
            mousedown: 'onDocMouseDown',
            scope: me,
            destroyable: true
        });
    },
    hide: function() {
        var me = this;
        me.clearTimer('hide');
        me.clearTimer('dismiss');
        me.callParent();
        me.lastHidden = new Date();
        me.updateCurrentTarget(null);
        Ext.destroy(me.mousedownListener);
    },
    doDestroy: function() {
        var me = this;
        me.clearTimers();
        me.setTarget(null);
        me.destroyMembers('mousedownListener', 'overListeners');
        me.callParent();
    },
    privates: {
        allowRealign: true,
        onDocMouseDown: function(e) {
            var me = this,
                delegate = me.getDelegate();
            if (e.within(me.el.dom)) {
                
                
                
                if (e.pointerType !== 'mouse' && me.getAllowOver()) {
                    me.clearTimer('dismiss');
                }
            }
            
            
            else if (!me.getClosable()) {
                if (e.within(me.targetElement) && (!delegate || e.getTarget(delegate, me.targetElement))) {
                    me.delayHide();
                } else {
                    me.disable();
                    me.enableTimer = Ext.defer(me.enable, 100, me);
                }
            }
        },
        onTargetOver: function(e) {
            var me = this,
                myTarget = me.targetElement,
                delegate = me.getDelegate(),
                currentTarget = me.currentTarget,
                newTarget;
            if (me.getDisabled()) {
                return;
            }
            
            if (me.getAllowOver() && me.el.contains(e.target)) {
                return;
            }
            if (delegate) {
                
                if (currentTarget.contains(e.target)) {
                    return;
                }
                newTarget = e.getTarget(delegate, myTarget);
                
                if (newTarget && e.getRelatedTarget(delegate) === newTarget) {
                    return;
                }
            }
            
            else if (!myTarget.contains(e.relatedTarget)) {
                newTarget = myTarget.dom;
            } else 
            {
                return;
            }
            
            if (newTarget) {
                me.handleTargetOver(e, newTarget);
            }
            
            else if (currentTarget.dom) {
                me.handleTargetOut();
            }
        },
        handleTargetOver: function(e, newTarget) {
            var me = this,
                myListeners = me.hasListeners;
            me.pointerEvent = e;
            me.updateCurrentTarget(newTarget);
            
            
            
            
            
            
            
            if (me.isVisible()) {
                if (myListeners.beforeshow && me.fireEvent('beforeshow', me) === false) {
                    return me.hide();
                }
                me.clearTimer('hide');
                me.clearTimer('dismiss');
                me.showByTarget(newTarget);
                if (myListeners.show) {
                    me.fireEvent('show', me);
                }
                me.postprocessShow();
            } else {
                me.delayShow(newTarget);
            }
        },
        postprocessShow: function() {
            var me = this,
                dismissDelay = me.getDismissDelay();
            me.clearTimer('show');
            if (dismissDelay && me.getAutoHide()) {
                me.dismissTimer = Ext.defer(me.hide, dismissDelay, me);
            }
            me.toFront();
        },
        onTargetTap: function(e) {
            
            
            
            if (e.pointerType !== 'mouse' && Ext.fly(e.target).isVisible(true)) {
                this.onTargetOver(e);
            }
        },
        onTargetOut: function(e) {
            
            if (this.currentTarget.dom && !this.currentTarget.contains(e.relatedTarget)) {
                
                if (!this.getAllowOver() && e.within(this.el, true)) {
                    this.handleTargetOut();
                }
            }
        },
        handleTargetOut: function() {
            
            var me = this;
            if (me.showTimer) {
                me.clearTimer('show');
            }
            if (me.isVisible() && me.getAutoHide()) {
                me.delayHide();
            }
        },
        onTipOver: function() {
            this.clearTimer('hide');
            this.clearTimer('dismiss');
        },
        onTipOut: function(e) {
            
            
            
            if (!this.currentTarget.dom || !e.getPoint().isContainedBy(this.currentTarget.getRegion())) {
                this.handleTargetOut();
            }
        },
        onMouseMove: function(e) {
            var me = this,
                dismissDelay = me.getDismissDelay();
            
            
            if (!me.el.contains(e.target)) {
                me.pointerEvent = e;
            }
            if (me.isVisible() && me.currentTarget.contains(e.target)) {
                
                if (dismissDelay && me.getAutoHide() !== false) {
                    me.clearTimer('dismiss');
                    me.dismissTimer = Ext.defer(me.hide, dismissDelay, me);
                }
                if (me.getTrackMouse()) {
                    me.alignToEvent(e);
                }
            }
        },
        delayShow: function(target) {
            var me = this;
            me.clearTimer('hide');
            if (me.getHidden() && !me.showTimer) {
                
                if (me.getDelegate() && Ext.Date.getElapsed(me.lastHidden) < me.getQuickShowInterval()) {
                    me.showByTarget(target);
                } else {
                    
                    me.showTimer = Ext.defer(me.showByTarget, (!me.pointerEvent || me.pointerEvent.pointerType === 'mouse') ? me.getShowDelay() : 0, me, [
                        target
                    ]);
                }
            } else if (!me.getHidden() && me.getAutoHide() !== false) {
                me.showByTarget(target);
            }
        },
        showByTarget: function(target) {
            var me = this,
                isTarget = me.getAnchorToTarget() && !me.getTrackMouse();
            me.lastShowWasPointer = !isTarget;
            
            
            
            me.showBy(isTarget ? target : me.pointerEvent, me.getAlign(), {
                overlap: me.getTrackMouse() && !me.getAnchor()
            });
        },
        delayHide: function() {
            var me = this;
            if (!me.isHidden() && !me.hideTimer) {
                me.clearTimer('dismiss');
                me.hideTimer = Ext.defer(me.hide, me.getHideDelay(), me);
            }
        },
        alignToEvent: function(event) {
            var me = this,
                options = {
                    
                    
                    
                    overlap: me.getTrackMouse() && !me.getAnchor()
                },
                mouseOffset = me.getMouseOffset(),
                target = event.getPoint().adjust(-Math.abs(mouseOffset[1]), Math.abs(mouseOffset[0]), Math.abs(mouseOffset[1]), -Math.abs(mouseOffset[0])),
                align = me.getAnchor() ? me.getAlign() : null;
            if (!align && mouseOffset) {
                if (mouseOffset[0] > 0) {
                    if (mouseOffset[1] > 0) {
                        align = 'tl-br?';
                    } else {
                        align = 'bl-tr?';
                    }
                } else {
                    if (mouseOffset[1] > 0) {
                        align = 'tr-bl?';
                    } else {
                        align = 'br-tl?';
                    }
                }
            }
            if (me.isVisible()) {
                me.clearTimer('hide');
                me.alignTo(target, align, options);
            } else {
                me.showBy(target, align, options);
            }
        },
        _timerNames: {},
        clearTimer: function(name) {
            var me = this,
                names = me._timerNames,
                propName = names[name] || (names[name] = name + 'Timer'),
                timer = me[propName];
            if (timer) {
                Ext.undefer(timer);
                me[propName] = null;
                
                if (name === 'show' && me.isHidden()) {
                    me.updateCurrentTarget(null);
                }
            }
        },
        
        clearTimers: function() {
            var me = this;
            me.clearTimer('show');
            me.clearTimer('dismiss');
            me.clearTimer('hide');
            me.clearTimer('enable');
        },
        clipTo: function(clippingEl, sides) {
            
            var clippingRegion;
            
            if (clippingEl.isRegion) {
                clippingRegion = clippingEl;
            } else {
                clippingRegion = (clippingEl.isComponent ? clippingEl.el : Ext.fly(clippingEl)).getConstrainRegion();
            }
            
            
            this.callParent([
                clippingRegion,
                sides
            ]);
            
            this.tipElement.clipTo(clippingRegion, sides);
        },
        doRealignToTarget: function() {
            var me = this,
                currentTarget = me.currentTarget,
                dom = currentTarget && currentTarget.dom;
            me.clearTimers();
            if (me.allowRealign && me.isVisible() && dom) {
                
                me.realign(null, me.getAlign());
            }
        },
        updateCurrentTarget: function(dom) {
            var me = this,
                currentTarget = me.currentTarget,
                was = currentTarget.dom;
            currentTarget.attach(dom);
            if (!me.isConfiguring) {
                me.fireEvent('hovertarget', me, currentTarget, was);
            }
        },
        attachTargetListeners: function(target) {
            var me = this,
                listeners;
            me.targetListeners = Ext.destroy(me.targetListeners);
            
            
            
            if (target === null) {
                return;
            }
            
            
            target = me.targetElement;
            if (target) {
                listeners = {
                    mouseover: 'onTargetOver',
                    mouseout: 'onTargetOut',
                    mousemove: 'onMouseMove',
                    scope: me,
                    destroyable: true
                };
                if (me.getShowOnTap()) {
                    listeners.tap = 'onTargetTap';
                }
                me.targetListeners = target.on(listeners);
            }
        }
    }
});


Ext.define('Ext.dataview.plugin.ItemTip', {
    extend: Ext.tip.ToolTip,
    alias: 'plugin.dataviewtip',
    anchor: true,
    showOnTap: true,
    defaultBindProperty: 'data',
    config: {
        
        
        cmp: null
    },
    listeners: {
        beforeshow: 'onBeforeShow',
        show: 'onShow',
        scope: 'this'
    },
    init: Ext.emptyFn,
    destroy: function() {
        
        
        
        this.parent = null;
        this.callParent();
    },
    applyData: function(data) {
        if (data.isEntity) {
            data = data.getData(true);
        }
        return data;
    },
    updateCmp: function(dataview) {
        var me = this;
        me.dataview = me.parent = dataview;
        dataview.on('initialize', 'onDataViewInitialized', me);
        dataview.getScrollable().on('scroll', 'onDataViewScroll', me);
    },
    onDataViewInitialized: function(dataview) {
        var me = this;
        me.setTarget(dataview.bodyElement);
        me.itemSelector = dataview.itemSelector;
        if (!me.getDelegate()) {
            me.setDelegate(me.itemSelector);
        }
    },
    onBeforeShow: function() {
        var me = this,
            viewModel = me.getViewModel(),
            location = me.getCmp().getNavigationModel().createLocation(me.currentTarget);
        if (me.getBind()) {
            viewModel.set('record', location.record);
            viewModel.set('recordIndex', me.location.recordIndex);
            
            viewModel.notify();
        } else {
            me.setData(location.record.data);
        }
    },
    onShow: function() {
        
        
        this.checkScrollVisibility();
    },
    onDataViewScroll: function() {
        
        
        this.checkScrollVisibility();
    },
    privates: {
        checkScrollVisibility: function() {
            var me = this,
                isInView, testEl;
            if (me.isVisible()) {
                
                me.realignToTarget();
                testEl = me.getAnchor() || me.el;
                isInView = me.dataview.getScrollable().isInView(testEl);
                
                if (!(isInView.x && isInView.y)) {
                    me.hide();
                }
            }
        },
        applyBind: function(binds, currentBindings) {
            var me = this,
                dataview = me.getCmp(),
                viewModel = me.getViewModel(),
                parentViewModel = dataview.lookupViewModel();
            
            if (viewModel) {
                viewModel.setParent(parentViewModel);
            } else {
                me.setViewModel(Ext.Factory.viewModel({
                    parent: parentViewModel,
                    data: {}
                }));
            }
            me.callParent([
                binds,
                currentBindings
            ]);
        }
    }
});


Ext.define('Ext.dataview.plugin.ListPaging', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.listpaging',
    alternateClassName: 'Ext.plugin.ListPaging',
    config: {
        
        autoPaging: false,
        
        bufferZone: 8,
        
        loadMoreText: 'Load More...',
        
        noMoreRecordsText: 'No More Records',
        
        loadMoreCmp: {
            xtype: 'component',
            cls: Ext.baseCSSPrefix + 'listpaging',
            scrollDock: 'end',
            hidden: true,
            inheritUi: true
        },
        
        loading: false
    },
    loadTpl: '<div class="' + Ext.baseCSSPrefix + 'loading-spinner">' + '<span class="' + Ext.baseCSSPrefix + 'loading-top"></span>' + '<span class="' + Ext.baseCSSPrefix + 'loading-right"></span>' + '<span class="' + Ext.baseCSSPrefix + 'loading-bottom"></span>' + '<span class="' + Ext.baseCSSPrefix + 'loading-left"></span>' + '</div>' + '<div class="' + Ext.baseCSSPrefix + 'message">{message}</div>',
    
    init: function(list) {
        var me = this;
        list.on('storechange', 'onStoreChange', me);
        me.bindStore(list.getStore());
        me.addLoadMoreCmp();
    },
    destroy: function() {
        Ext.destroy(this._storeListeners);
        this.callParent();
    },
    updateAutoPaging: function(enabled) {
        var scroller = this.getCmp().getScrollable(),
            listeners = {
                scroll: 'onScroll',
                scope: this
            };
        if (enabled) {
            scroller.on(listeners);
            this.ensureBufferZone();
        } else {
            scroller.un(listeners);
        }
    },
    
    bindStore: function(store) {
        var me = this,
            listeners = {
                beforeload: 'onStoreBeforeLoad',
                load: 'onStoreLoad',
                filter: 'onFilter',
                destroyable: true,
                scope: me
            };
        me._storeListeners = Ext.destroy(me._storeListeners);
        if (store) {
            me._storeListeners = store.on(listeners);
        }
    },
    
    disableDataViewMask: function() {
        var list = this.cmp;
        this._listMask = list.getLoadingText();
        list.setLoadingText(null);
    },
    enableDataViewMask: function() {
        if (this._listMask) {
            var list = this.cmp;
            list.setLoadingText(this._listMask);
            delete this._listMask;
        }
    },
    
    applyLoadMoreCmp: function(config, instance) {
        return Ext.updateWidget(instance, config, this, 'createLoadMoreCmp');
    },
    createLoadMoreCmp: function(config) {
        return Ext.apply({
            html: this.getLoadTpl().apply({
                message: this.getLoadMoreText()
            })
        }, config);
    },
    updateLoadMoreCmp: function(loadMoreCmp, old) {
        Ext.destroy(old);
        if (loadMoreCmp) {
            loadMoreCmp.el.on({
                tap: 'loadNextPage',
                scope: this
            });
        }
    },
    
    onScroll: function() {
        this.ensureBufferZone();
    },
    
    updateLoading: function(isLoading) {
        this.getLoadMoreCmp().toggleCls(this.loadingCls, isLoading);
    },
    
    onStoreChange: function(list, store) {
        this.bindStore(store);
    },
    
    onStoreBeforeLoad: function(store) {
        if (store.getCount() === 0) {
            this.getLoadMoreCmp().hide();
        }
    },
    
    onStoreLoad: function() {
        this.syncState();
    },
    onFilter: function(store) {
        this.getLoadMoreCmp.setVisible(store.getCount() === 0);
    },
    
    addLoadMoreCmp: function() {
        var me = this;
        if (!me.isAdded) {
            me.cmp.add(me.getLoadMoreCmp());
            me.isAdded = true;
            me.syncState();
        }
    },
    
    storeFullyLoaded: function() {
        var store = this.cmp.getStore(),
            total = store ? store.getTotalCount() : null;
        return total !== null ? total <= (store.currentPage * store.getPageSize()) : false;
    },
    
    loadNextPage: function() {
        var me = this,
            list = me.cmp;
        if (me.storeFullyLoaded()) {
            return;
        }
        me.setLoading(true);
        me.disableDataViewMask();
        me.currentScrollToTopOnRefresh = list.getScrollToTopOnRefresh();
        list.setScrollToTopOnRefresh(false);
        list.getStore().nextPage({
            addRecords: true
        });
    },
    privates: {
        loadingCls: Ext.baseCSSPrefix + 'loading',
        ensureBufferZone: function() {
            var me = this,
                list = me.cmp;
            if (list.isPainted()) {
                me.ensureBufferZone = me.doEnsureBufferZone;
                me.doEnsureBufferZone();
                return;
            }
            if (!me.waitingForPainted) {
                me.waitingForPainted = true;
                list.on({
                    painted: {
                        single: true,
                        fn: function() {
                            delete me.waitingForPainted;
                            me.ensureBufferZone();
                        }
                    }
                });
            }
        },
        doEnsureBufferZone: function() {
            var me = this,
                list = me.cmp,
                store = list.getStore(),
                scroller = list.getScrollable(),
                count = store && store.getCount(),
                bufferZone = me.getBufferZone(),
                item, box, y, index;
            if (!store || !count || !scroller || me.getLoading()) {
                return;
            }
            index = Math.min(Math.max(0, count - bufferZone), count - 1);
            item = list.mapToItem(store.getAt(index));
            box = item && item.element.getBox();
            if (!box) {
                return;
            }
            
            
            
            
            y = bufferZone > 0 ? box.top + 1 : box.bottom;
            if (y > scroller.getElement().getBox().bottom) {
                return;
            }
            me.loadNextPage();
        },
        getLoadTpl: function() {
            return Ext.XTemplate.getTpl(this, 'loadTpl');
        },
        syncState: function() {
            var me = this,
                list = me.cmp,
                loadCmp = me.getLoadMoreCmp(),
                full = me.storeFullyLoaded(),
                store = list.store,
                message = full ? me.getNoMoreRecordsText() : me.getLoadMoreText();
            if (store && store.getCount()) {
                loadCmp.show();
            }
            me.setLoading(false);
            
            loadCmp.setHtml(me.getLoadTpl().apply({
                message: message
            }));
            loadCmp.setDisabled(full);
            if (me.currentScrollToTopOnRefresh !== undefined) {
                list.setScrollToTopOnRefresh(me.currentScrollToTopOnRefresh);
                delete me.currentScrollToTopOnRefresh;
            }
            me.enableDataViewMask();
            if (me.getAutoPaging()) {
                me.ensureBufferZone();
            }
        }
    }
});


Ext.define('Ext.dataview.plugin.SortableList', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.sortablelist',
    alternateClassName: 'Ext.plugin.SortableList',
    config: {
        list: null,
        source: {
            xclass: 'Ext.drag.Source',
            handle: '.' + Ext.baseCSSPrefix + 'list-sortablehandle',
            constrain: {
                vertical: true
            },
            proxy: {
                getElement: function(info) {
                    return this.getSource().list.mapToItem(info.initialEvent).el;
                }
            }
        }
    },
    init: function(list) {
        this.setList(list);
    },
    updateList: function(list) {
        var source;
        if (list) {
            source = this.getSource();
            if (source) {
                source.list = list;
                source.setElement(list.getRenderTarget());
            }
        }
    },
    applySource: function(source) {
        if (source) {
            source = Ext.create(source);
        }
        return source;
    },
    updateSource: function(source, oldSource) {
        var list = this.getList();
        Ext.destroy(oldSource);
        if (source) {
            source.on({
                scope: this,
                dragstart: 'onDragStart',
                dragmove: 'onDrag',
                dragend: 'onDragEnd'
            });
            if (list) {
                source.list = list;
                source.setElement(list.getRenderTarget());
            }
        }
    },
    onDragStart: function(source, info) {
        var list = this.getList(),
            item = list.mapToItem(info.initialEvent);
        item.addCls(Ext.baseCSSPrefix + 'item-no-ripple');
        
        item.translate(0, 0);
        info.item = item;
        info.startIndex = item.getRecordIndex();
        info.listTop = list.getRenderTarget().getTop();
        info.itemHeight = item.el.measure('h');
        info.halfHeight = info.itemHeight / 2;
        list.stickItem(item, {
            floated: true
        });
    },
    onDrag: function(source, info) {
        var list = this.getList(),
            top = Math.max(0, info.cursor.current.y - info.listTop),
            idx = list.bisectPosition(top + info.halfHeight),
            o = {};
        o[idx] = info.itemHeight;
        info.index = idx;
        list.setGaps(o);
    },
    onDragEnd: function(source, info) {
        var me = this,
            list = me.getList(),
            item = info.item,
            style = info.item.el.dom.style,
            compareItem = list.mapToItem(info.index),
            top, pos;
        item.getTranslatable().on('animationend', function() {
            if (me.destroyed) {
                return;
            }
            var store = list.getStore(),
                startIndex = info.startIndex,
                index = compareItem ? compareItem.getRecordIndex() : list.getStore().getCount(),
                rec = item.getRecord();
            list.stickItem(item);
            list.setGaps(null);
            if (startIndex !== index) {
                store.insert(index, rec);
                index = store.indexOf(rec);
                
                item = list.mapToItem(rec);
                list.fireEvent('dragsort', list, item, index);
            }
            item.removeCls(Ext.baseCSSPrefix + 'item-no-ripple');
        }, me, {
            single: true
        });
        if (!compareItem) {
            pos = list.mapToItem(info.index - 1).$y1;
        } else {
            pos = compareItem.$y0;
        }
        
        top = item.element.getTop(true);
        style.left = style.top = '';
        item.translate(0, top);
        item.translate(null, pos, {
            duration: 100
        });
    }
});


Ext.define('Ext.dataview.pullrefresh.Item', {
    extend: Ext.Component,
    config: {
        
        lastUpdated: null,
        
        state: null,
        
        pull: null
    },
    privates: {
        clsMap: {
            loaded: Ext.baseCSSPrefix + 'pullrefresh-loaded',
            loading: Ext.baseCSSPrefix + 'pullrefresh-loading',
            pulling: Ext.baseCSSPrefix + 'pullrefresh-pulling',
            holding: Ext.baseCSSPrefix + 'pullrefresh-holding'
        },
        isLoading: function(state) {
            state = state || this.getState();
            return state === 'loading' || state === 'loaded';
        },
        updateState: function(value) {
            var el = this.el,
                map = this.clsMap,
                classes;
            if (el) {
                classes = el.getClassMap(
                false);
                delete classes[map.loaded];
                delete classes[map.loading];
                delete classes[map.pulling];
                delete classes[map.holding];
                classes[map[value]] = 1;
                el.setClassMap(classes, 
                true);
            }
        }
    }
});


Ext.define('Ext.dataview.pullrefresh.Bar', {
    extend: Ext.dataview.pullrefresh.Item,
    xtype: 'pullrefreshbar',
    baseCls: Ext.baseCSSPrefix + 'pullrefreshbar',
    cachedConfig: {
        
        lastUpdatedDateFormat: 'm/d/Y h:iA',
        
        lastUpdatedText: 'Last Updated:\xa0',
        
        loadedText: 'Loaded.',
        
        loadingText: 'Loading...',
        
        mode: 'message',
        
        pullText: 'Pull down to refresh...',
        
        releaseText: 'Release to refresh...'
    },
    hidden: true,
    showInEmptyState: null,
    scrollDock: 'start',
    template: [
        {
            cls: Ext.baseCSSPrefix + 'font-icon ' + Ext.baseCSSPrefix + 'pullrefreshbar-arrow'
        },
        {
            cls: Ext.baseCSSPrefix + 'pullrefreshbar-loading-wrap',
            children: [
                {
                    cls: Ext.baseCSSPrefix + 'pullrefreshbar-loading ' + Ext.baseCSSPrefix + 'loading-spinner',
                    children: [
                        {
                            tag: 'span',
                            cls: Ext.baseCSSPrefix + 'loading-top'
                        },
                        {
                            tag: 'span',
                            cls: Ext.baseCSSPrefix + 'loading-right'
                        },
                        {
                            tag: 'span',
                            cls: Ext.baseCSSPrefix + 'loading-bottom'
                        },
                        {
                            tag: 'span',
                            cls: Ext.baseCSSPrefix + 'loading-left'
                        }
                    ]
                }
            ]
        },
        {
            cls: Ext.baseCSSPrefix + 'pullrefreshbar-info-wrap',
            children: [
                {
                    reference: 'infoMessageEl',
                    cls: Ext.baseCSSPrefix + 'pullrefreshbar-info-message'
                },
                {
                    reference: 'infoUpdatedEl',
                    cls: Ext.baseCSSPrefix + 'pullrefreshbar-info-updated'
                }
            ]
        }
    ],
    privates: {
        modeCls: Ext.baseCSSPrefix + 'pullrefreshbar-mode',
        textMap: {
            loaded: 'getLoadedText',
            loading: 'getLoadingText',
            pulling: 'getPullText',
            holding: 'getReleaseText'
        },
        updateLastUpdated: function(value) {
            var me = this,
                lastUpdated = value ? me.getLastUpdatedText() + Ext.util.Format.date(value, me.getLastUpdatedDateFormat()) : value;
            me.infoUpdatedEl.setText(lastUpdated);
        },
        updateMode: function(value, oldValue) {
            this.el.replaceCls(oldValue, value, this.modeCls);
        },
        updateState: function(state) {
            var me = this,
                fn = me.textMap[state],
                text = fn && me[fn]();
            me.infoMessageEl.setText(text || '');
            this.callParent(arguments);
        }
    }
});


Ext.define('Ext.dataview.pullrefresh.PullRefresh', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.pullrefresh',
    alternateClassName: 'Ext.plugin.PullRefresh',
    mixins: [
        Ext.mixin.ConfigProxy
    ],
    proxyConfig: {
        widget: [
            
            'lastUpdatedDateFormat',
            
            'lastUpdatedText',
            
            'loadedText',
            
            'loadingText',
            
            'pullText',
            
            'releaseText'
        ]
    },
    config: {
        
        autoSnapBack: true,
        
        mergeData: true,
        
        overlay: false,
        
        snappingAnimationDuration: 300,
        
        
        activateOffset: 0.75,
        widget: {
            lazy: true,
            $value: {
                xtype: 'pullrefreshbar'
            }
        },
        
        lastUpdated: new Date(),
        
        list: null,
        overshotMaxDistance: 50,
        
        state: 'pulling'
    },
    init: function(list) {
        this.setList(list);
    },
    destroy: function() {
        this.setList(null);
        this.callParent();
    },
    createWidget: function(config) {
        var ret = this.mergeProxiedConfigs('widget', config);
        
        
        ret.hidden = true;
        return ret;
    },
    privates: {
        overlayCls: Ext.baseCSSPrefix + 'pullrefresh-overlay',
        
        fetchLatest: function() {
            this.getList().getStore().fetch({
                page: 1,
                start: 0,
                callback: this.onLatestFetched,
                scope: this
            });
        },
        reset: function() {
            var me = this,
                widget = me.getWidget();
            widget.setHidden(true);
            widget.setHeight(null);
            widget.setMinHeight(null);
            me.$measuredHeight = null;
        },
        
        snapBack: function(force) {
            var me = this,
                widget = me.getWidget(),
                state = me.getState(),
                hideAnimation = widget.getHideAnimation(),
                duration = me.getSnappingAnimationDuration();
            if (state === 'loaded' || force) {
                if (!hideAnimation) {
                    widget.el.animate({
                        preserveEndState: true,
                        duration: duration,
                        to: {
                            height: 0
                        },
                        callback: function() {
                            me.onSnapBackEnd(true);
                        }
                    });
                } else {
                    me.onSnapBackEnd();
                }
            }
        },
        
        
        onDragEnd: function() {
            var me = this,
                state = me.getState(),
                widget = me.getWidget(),
                overshotMaxDistance = me.getOvershotMaxDistance();
            if (me.running) {
                me.running = false;
                if (state === 'holding') {
                    if (overshotMaxDistance) {
                        widget.el.animate({
                            duration: 75,
                            preserveEndState: true,
                            to: {
                                height: me.$measuredHeight
                            },
                            callback: function() {
                                me.setState('loading');
                                me.fetchLatest();
                            }
                        });
                    } else {
                        me.setState('loading');
                        me.fetchLatest();
                    }
                } else {
                    me.snapBack(true, false);
                }
            }
        },
        onDragMove: function(e) {
            var me = this,
                list = me.getList(),
                widget = me.getWidget(),
                listHeight = list.el.getHeight(),
                pullHeight = me.$measuredHeight,
                activateOffset = me.getActivateOffset() * pullHeight,
                overshotMaxDistance = me.getOvershotMaxDistance(),
                offset, overshot, overshotRange;
            if (me.running) {
                e.stopEvent();
                offset = e.getXY()[1] - me.startY;
                widget.setHidden(offset <= 0);
                if (offset > 0 && offset < pullHeight) {
                    me.setState('pulling');
                    widget.setHeight(offset);
                } else if (overshotMaxDistance && offset >= pullHeight) {
                    overshotRange = listHeight - pullHeight;
                    overshot = ((offset - pullHeight) / overshotRange);
                    widget.setHeight(pullHeight + (overshot * overshotMaxDistance));
                }
                
                widget.setPull(offset / activateOffset);
                if (offset >= activateOffset) {
                    me.setState('holding');
                }
            }
        },
        onDragStart: function(e) {
            var me = this,
                list = me.getList(),
                widget = me.getWidget(),
                dy;
            if (me.running) {
                e.stopEvent();
                return;
            }
            if (!me.$measuredHeight) {
                widget.setHidden(false);
                me.$measuredHeight = widget.el.getHeight();
                widget.setMinHeight(0);
                widget.setHidden(true);
            }
            dy = e.deltaY;
            if (list.getScrollable().getPosition().y === 0 && dy > 0 && dy > e.deltaX) {
                widget.setHidden(false);
                me.running = true;
                widget.setHeight(0);
                e.stopEvent();
            }
        },
        
        onLatestFetched: function(newRecords, operation, success) {
            var me = this,
                list = me.getList(),
                store = list.getStore(),
                length, toInsert, oldRecords, newRecord, oldRecord, i;
            if (success) {
                if (me.getMergeData()) {
                    oldRecords = store.getData();
                    toInsert = [];
                    length = newRecords.length;
                    for (i = 0; i < length; i++) {
                        newRecord = newRecords[i];
                        oldRecord = oldRecords.getByKey(newRecord.getId());
                        if (oldRecord) {
                            oldRecord.set(newRecord.getData());
                        } else {
                            toInsert.push(newRecord);
                        }
                    }
                    store.insert(0, toInsert);
                } else {
                    store.loadRecords(newRecords);
                }
                me.setLastUpdated(new Date());
            }
            me.setState('loaded');
            list.fireEvent('latestfetched', me, toInsert || newRecords);
            if (me.getAutoSnapBack()) {
                me.snapBack(true);
            }
        },
        
        onSnapBackEnd: function(preventAnim) {
            var me = this,
                widget = me.getWidget();
            if (preventAnim) {
                widget.hide(null);
                me.setState('pulling');
                me.reset();
            } else {
                widget.hide();
                widget.on('hide', function() {
                    me.setState('pulling');
                    me.reset();
                }, me, {
                    single: true
                });
            }
        },
        onTouchStart: function(e) {
            this.startY = e.getXY()[1];
        },
        
        
        
        
        updateLastUpdated: function(value) {
            var widget = this.getWidget();
            if (widget) {
                widget.setLastUpdated(value);
            }
        },
        
        
        updateList: function(list, oldList) {
            var me = this,
                widget = me.widget;
            if (oldList) {
                oldList.el.un({
                    scope: me,
                    touchstart: 'onTouchStart',
                    dragstart: 'onDragStart',
                    drag: 'onDragMove',
                    dragend: 'onDragEnd'
                });
            }
            if (list) {
                list.el.on({
                    scope: me,
                    touchstart: 'onTouchStart',
                    dragstart: 'onDragStart',
                    drag: 'onDragMove',
                    dragend: 'onDragEnd'
                });
                if (widget) {
                    list.insert(0, widget);
                }
            }
        },
        
        updateOverlay: function(overlay) {
            var widget = this.getWidget();
            if (widget) {
                widget.el.toggleCls(this.overlayCls, overlay);
                widget.setTop(overlay ? 0 : null);
            }
        },
        
        
        updateState: function(value) {
            var widget = this.getWidget();
            if (widget) {
                widget.setState(value);
            }
        },
        
        applyWidget: function(config, existing) {
            return Ext.updateWidget(existing, config, this, 'createWidget');
        },
        updateWidget: function(widget) {
            var me = this,
                list, overlay;
            
            me.widget = widget;
            if (widget) {
                overlay = me.getOverlay();
                me.updateOverlay(overlay);
                widget.setLastUpdated(me.getLastUpdated());
                widget.setState(me.getState());
                list = me.getList();
                if (list) {
                    list.insert(0, widget);
                }
            }
        }
    }
});



Ext.define('Ext.dataview.pullrefresh.Spinner', {
    extend: Ext.dataview.pullrefresh.Item,
    xtype: 'pullrefreshspinner',
    hideAnimation: {
        type: 'popOut'
    },
    baseCls: Ext.baseCSSPrefix + 'pullrefreshspinner',
    template: [
        {
            cls: Ext.baseCSSPrefix + 'pullrefreshspinner-loading-wrap',
            children: [
                {
                    cls: Ext.baseCSSPrefix + 'pullrefreshspinner-main ' + Ext.baseCSSPrefix + 'shadow',
                    reference: 'bodyEl',
                    children: [
                        {
                            cls: Ext.baseCSSPrefix + 'pullrefreshspinner-loader-wrapper',
                            children: [
                                {
                                    cls: Ext.baseCSSPrefix + 'pullrefreshspinner-arrow-wrapper',
                                    children: [
                                        {
                                            cls: Ext.baseCSSPrefix + 'pullrefreshspinner-arrow-main',
                                            reference: 'arrowEl'
                                        }
                                    ]
                                },
                                {
                                    cls: Ext.baseCSSPrefix + 'pullrefreshspinner-spinner-wrapper',
                                    reference: 'spinnerEl',
                                    children: [
                                        {
                                            cls: Ext.baseCSSPrefix + 'pullrefreshspinner-spinner-main',
                                            children: [
                                                {
                                                    cls: Ext.baseCSSPrefix + 'pullrefreshspinner-spinner-left',
                                                    children: [
                                                        {
                                                            cls: Ext.baseCSSPrefix + 'pullrefreshspinner-half-circle'
                                                        }
                                                    ]
                                                },
                                                {
                                                    cls: Ext.baseCSSPrefix + 'pullrefreshspinner-spinner-right',
                                                    children: [
                                                        {
                                                            cls: Ext.baseCSSPrefix + 'pullrefreshspinner-half-circle'
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ],
    privates: {
        toggleDisplay: function(load) {
            var me = this;
            me.arrowEl.setVisible(!load);
            me.spinnerEl.setVisible(load);
        },
        updatePull: function(pull) {
            var me = this,
                rotation = Math.floor(Math.min(pull, 3) * 100) - 110;
            me.bodyEl.setOpacity(Math.min(1, pull));
            me.arrowEl.dom.style.transform = 'rotate(' + rotation + 'deg)';
        },
        updateState: function(state) {
            var me = this;
            me.toggleDisplay(me.isLoading(state));
        }
    }
});


Ext.define('Ext.field.BoxLabelable', {
    extend: Ext.Mixin,
    mixinConfig: {
        id: 'boxLabelable',
        after: {
            initElement: 'afterInitElement'
        }
    },
    config: {
        
        boxLabel: null,
        
        boxLabelAlign: null
    },
    boxLabeledCls: Ext.baseCSSPrefix + 'box-labeled',
    getBodyTemplate: function() {
        return [
            {
                reference: 'boxWrapElement',
                cls: Ext.baseCSSPrefix + 'box-wrap-el',
                children: [
                    {
                        reference: 'boxElement',
                        cls: Ext.baseCSSPrefix + 'box-el',
                        children: this.getBoxTemplate()
                    },
                    {
                        tag: 'label',
                        reference: 'boxLabelElement',
                        cls: Ext.baseCSSPrefix + 'box-label-el'
                    }
                ]
            }
        ];
    },
    getBoxTemplate: Ext.emptyFn,
    updateBoxLabel: function(boxLabel) {
        this.boxLabelElement.setHtml(boxLabel);
        this.el.toggleCls(this.boxLabeledCls, !!boxLabel);
    },
    updateBoxLabelAlign: function(boxLabelAlign, oldBoxLabelAlign) {
        var me = this,
            el = me.el;
        if (oldBoxLabelAlign) {
            el.removeCls(Ext.baseCSSPrefix + 'box-label-align-' + oldBoxLabelAlign);
        }
        if (boxLabelAlign) {
            el.addCls(Ext.baseCSSPrefix + 'box-label-align-' + boxLabelAlign);
        }
    },
    afterInitElement: function() {
        var inputElement = this.inputElement;
        if (inputElement) {
            this.boxLabelElement.dom.setAttribute('for', inputElement.id);
        }
    }
});


Ext.define('Ext.field.Checkbox', {
    extend: Ext.field.Input,
    alternateClassName: 'Ext.form.Checkbox',
    xtype: [
        'checkbox',
        'checkboxfield'
    ],
    mixins: [
        Ext.field.BoxLabelable
    ],
    qsaLeftRe: /[\[]/g,
    qsaRightRe: /[\]]/g,
    
    shareableName: true,
    isCheckbox: true,
    
    defaultBindProperty: 'checked',
    
    twoWayBindable: {
        checked: 1
    },
    
    publishes: {
        checked: 1
    },
    
    
    
    config: {
        
        value: '',
        
        checked: false
    },
    
    eventHandlers: {
        change: 'onChange'
    },
    inputType: 'checkbox',
    
    classCls: Ext.baseCSSPrefix + 'checkboxfield',
    checkedCls: Ext.baseCSSPrefix + 'checked',
    getBodyTemplate: function() {
        return this.mixins.boxLabelable.getBodyTemplate.call(this);
    },
    
    getBoxTemplate: function() {
        return [
            {
                reference: 'iconElement',
                cls: Ext.baseCSSPrefix + 'font-icon ' + Ext.baseCSSPrefix + 'icon-el',
                children: [
                    this.getInputTemplate()
                ]
            }
        ];
    },
    getInputTemplate: function() {
        var template = this.callParent();
        template.onchange = 'return Ext.doEv(this, event);';
        return template;
    },
    
    getSubmitValue: function() {
        return (this.getChecked()) ? Ext.isEmpty(this._value) ? true : this._value : null;
    },
    
    checkedRe: /^(true|1|on)/i,
    
    getChecked: function() {
        return !!this.inputElement.dom.checked;
    },
    applyChecked: function(checked) {
        if (this.isConfiguring) {
            this.originalState = checked;
        }
        return !!this.checkedRe.test(String(checked));
    },
    updateChecked: function(checked, oldChecked) {
        var me = this,
            eventName;
        if (!me.$onChange) {
            me.inputElement.dom.checked = checked;
        }
        me.toggleCls(me.checkedCls, checked);
        
        if (me.initialized) {
            eventName = checked ? 'check' : 'uncheck';
            me.fireEvent(eventName, me);
            me.fireEvent('change', me, checked, oldChecked);
        }
    },
    
    isChecked: function() {
        return this.getChecked();
    },
    
    check: function() {
        return this.setChecked(true);
    },
    
    uncheck: function() {
        return this.setChecked(false);
    },
    onChange: function(e) {
        var me = this;
        me.$onChange = true;
        me.setChecked(!!e.target.checked);
        delete me.$onChange;
    },
    getSameGroupFields: function() {
        var me = this,
            component = me.lookupNameHolder(),
            name = me.name;
        if (!component) {
            
            Ext.Logger.warn(me.self.$className + ' components must always be descendants of an Ext.field.Panel.');
            
            
            name = name.replace(me.qsaLeftRe, '\\[').replace(me.qsaRightRe, '\\]');
            return Ext.Viewport.query('checkboxfield[name=' + name + ']');
        }
        return component.lookupName(name);
    },
    
    getGroupValues: function() {
        var values = [];
        this.getSameGroupFields().forEach(function(field) {
            if (field.getChecked()) {
                values.push(field.getValue());
            }
        });
        return values;
    },
    
    setGroupValues: function(values) {
        this.getSameGroupFields().forEach(function(field) {
            field.setChecked((values.indexOf(field.getValue()) !== -1));
        });
        return this;
    },
    
    resetGroupValues: function() {
        this.getSameGroupFields().forEach(function(field) {
            field.setChecked(field.originalState);
        });
        return this;
    },
    reset: function() {
        this.setChecked(this.originalState);
        return this;
    }
});


Ext.define('Ext.field.trigger.Expand', {
    extend: Ext.field.trigger.Trigger,
    xtype: 'expandtrigger',
    alias: 'trigger.expand',
    classCls: Ext.baseCSSPrefix + 'expandtrigger',
    handler: 'onExpandTap',
    scope: 'this'
});


Ext.define('Ext.field.Picker', {
    extend: Ext.field.Text,
    xtype: 'pickerfield',
    config: {
        
        picker: {
            lazy: true,
            $value: 'auto'
        },
        
        floatedPicker: {
            lazy: true,
            $value: null
        },
        
        edgePicker: {
            lazy: true,
            $value: null
        },
        clearable: false,
        
        matchFieldWidth: true,
        
        floatedPickerAlign: 'tl-bl?',
        
        pickerSlotAlign: 'center',
        
        hideTrigger: false,
        
        focusTrap: {
            lazy: true,
            $value: {
                tabIndex: -1,
                cls: 'x-hidden-clip'
            }
        }
    },
    triggers: {
        expand: {
            type: 'expand'
        }
    },
    
    alignTarget: 'bodyElement',
    keyMap: {
        scope: 'this',
        DOWN: 'onDownArrow',
        ESC: 'onEsc'
    },
    keyMapTarget: 'inputElement',
    
    autoComplete: false,
    classCls: Ext.baseCSSPrefix + 'pickerfield',
    
    
    
    initialize: function() {
        var me = this;
        me.callParent();
        Ext.on('hide', 'onGlobalHide', me);
        me.inputElement.on('click', 'onInputElementClick', me);
    },
    onFocus: function(e) {
        this.callParent([
            e
        ]);
        if (Ext.isTouchMode()) {
            this.getFocusTrap().focus();
            this.expand();
        }
    },
    onFocusMove: function(info) {
        var me = this,
            focusTrap;
        me.callParent([
            info
        ]);
        if (Ext.isTouchMode()) {
            
            focusTrap = me.getFocusTrap();
            if (info.fromElement === focusTrap.dom && info.toElement === me.getFocusEl().dom) {
                if (me.getEditable()) {
                    
                    me.collapse();
                } else {
                    
                    
                    focusTrap.focus();
                    me.expand();
                }
            }
        }
    },
    onFocusLeave: function(e) {
        this.callParent([
            e
        ]);
        
        this.collapse();
    },
    
    onEsc: function(e) {
        if (Ext.isIE) {
            
            
            
            
            e.preventDefault();
        }
        if (this.expanded) {
            this.collapse();
            e.stopEvent();
        }
    },
    onDownArrow: function(e) {
        var me = this;
        if ((e.time - me.lastDownArrow) > 150) {
            delete me.lastDownArrow;
        }
        if (!me.expanded) {
            
            e.stopEvent();
            
            
            me.onExpandTap();
            
            
            
            
            
            me.setPickerLocation(true);
            me.lastDownArrow = e.time;
        } else if (!e.stopped && (e.time - me.lastDownArrow) < 150) {
            delete me.lastDownArrow;
        }
    },
    
    setPickerLocation: Ext.emptyFn,
    updateHideTrigger: function(hideTrigger) {
        var triggers = this.getTriggers(),
            expand = triggers && triggers.expand;
        if (expand) {
            expand.setHidden(hideTrigger);
        }
    },
    applyPicker: function(picker) {
        var me = this,
            pickerListeners = {
                show: 'onPickerShow',
                hide: 'onPickerHide',
                scope: me
            },
            type = picker,
            config;
        if (!type) {
            type = 'auto';
        } else if (Ext.isObject(picker)) {
            type = null;
            if (!picker.isWidget && !picker.xtype) {
                config = picker;
                type = 'auto';
            }
        }
        if (type) {
            if (type === 'auto') {
                type = Ext.platformTags.phone ? 'edge' : 'floated';
            }
            if (type === 'edge') {
                picker = me.createEdgePicker(config);
            }
            
            else if (type !== 'floated') {
                Ext.raise('Picker type must be "edge" or "floated" not "' + type + '"');
            } else 
            {
                picker = me.createFloatedPicker(config);
                pickerListeners.resize = pickerListeners.hiddenchange = 'realignFloatedPicker';
            }
        }
        if (picker.isWidget) {
            picker.ownerField = me;
        } else {
            picker = Ext.apply({
                ownerField: me
            }, picker);
            
            me.fireEvent('beforepickercreate', me, picker);
            picker = Ext.create(picker);
        }
        
        me.pickerType = type || (picker.isViewportMenu ? 'edge' : 'floated');
        
        me.fireEvent('pickercreate', me, picker);
        picker.on(pickerListeners);
        return picker;
    },
    updatePicker: function(picker) {
        var value = this.getValue();
        if (picker && picker.setValue && value != null) {
            if (this.pickerType === 'floated' || picker.isPicker) {
                picker.setValue(value);
            }
        }
    },
    applyFocusTrap: function(focusTrap) {
        var result = this.el.appendChild(Ext.dom.Element.create(focusTrap));
        
        
        
        result.$isFocusTrap = true;
        return result;
    },
    onResize: function() {
        
        var picker = this.getConfig('picker', false, true);
        if (picker && picker.isVisible()) {
            this.realignFloatedPicker();
        }
    },
    
    realignFloatedPicker: function(picker) {
        var me = this;
        picker = me.getConfig('picker', false, true);
        if (picker && picker.isVisible()) {
            if (me.getMatchFieldWidth()) {
                picker.setWidth(me[me.alignTarget].getWidth());
            }
            picker.realign(me[me.alignTarget], me.getFloatedPickerAlign(), {
                minHeight: 100
            });
            me.setPickerLocation();
        }
    },
    onInputElementClick: function(e) {
        var me = this;
        if (e.pointerType === 'mouse' && (!me.getEditable() && !me.getReadOnly())) {
            me[me.expanded ? 'collapse' : 'expand']();
        }
    },
    onExpandTap: function() {
        if (this.expanded) {
            this.collapse();
        } else {
            this.expand();
        }
        return false;
    },
    expand: function() {
        if (!this.expanded && !this.getDisabled()) {
            this.showPicker();
        }
    },
    collapse: function() {
        var picker;
        if (this.expanded) {
            picker = this.getPicker();
            
            
            
            if (this.pickerType === 'edge') {
                Ext['Viewport'].removeMenu(picker.getSide(), true);
            } else {
                picker.hide();
            }
        }
    },
    
    collapseIf: function(e) {
        var me = this;
        
        if (!me.destroyed && (!e.within(me.bodyElement, false, true) && !me.owns(e.target))) {
            
            
            
            
            
            
            
            
            
            me.collapse();
        }
    },
    showPicker: function() {
        var me = this,
            alignTarget = me[me.alignTarget],
            picker = me.getPicker(),
            value;
        
        if (me.pickerType === 'floated') {
            if (me.getMatchFieldWidth()) {
                picker.setWidth(alignTarget.getWidth());
            }
            picker.showBy(alignTarget, me.getFloatedPickerAlign(), {
                minHeight: 100
            });
            
            
            
            me.touchListeners = Ext.getDoc().on({
                
                
                translate: false,
                touchstart: me.collapseIf,
                scope: me,
                delegated: false,
                destroyable: true
            });
        } else {
            picker.show();
            me.setShowPickerValue(picker);
        }
    },
    updatePickerValue: function(picker, value) {
        var slot = picker.getSlots()[0],
            name = slot.name || slot.getName(),
            pickerValue = {};
        pickerValue[name] = value;
        picker.setValue(pickerValue);
    },
    onPickerShow: function() {
        var me = this;
        me.expanded = true;
        
        if (me.pickerType === 'edge') {
            me.el.dom.scrollIntoView();
        }
        
        
        
        
        me.hideEventListeners = Ext.on({
            mousedown: 'collapseIf',
            scroll: 'onGlobalScroll',
            scope: me,
            destroyable: true
        });
        me.fireEvent('expand', me);
    },
    onPickerHide: function() {
        var me = this;
        me.expanded = false;
        Ext.destroy(me.hideEventListeners, me.touchListeners);
        me.fireEvent('collapse', me);
    },
    doDestroy: function() {
        this.destroyMembers('picker', 'hideEventListeners', 'touchListeners', 'focusTrap');
        this.callParent();
    },
    privates: {
        onGlobalHide: function(cmp) {
            
            if (this === cmp || cmp.isAncestor(this)) {
                this.collapse();
            }
        },
        onGlobalScroll: function(scroller, x, y) {
            var me = this,
                scrollingEl = scroller.getElement();
            if (me.expanded) {
                
                
                if (me.pickerType === 'edge') {
                    return;
                }
                
                
                if (!me.getPicker().owns(scrollingEl) && scrollingEl.dom !== document.body) {
                    me.collapse();
                }
            }
        },
        revertFocusTo: function(target) {
            if (Ext.isTouchMode()) {
                this.getFocusTrap().focus();
            } else {
                target.focus();
            }
        },
        setShowPickerValue: function(picker) {
            var value = this.getValue();
            if (value != null) {
                this.updatePickerValue(picker, value);
            }
        }
    }
});


Ext.define('Ext.picker.Slot', {
    extend: Ext.dataview.DataView,
    xtype: 'pickerslot',
    
    isSlot: true,
    config: {
        
        title: null,
        
        showTitle: true,
        
        cls: Ext.baseCSSPrefix + 'picker-slot',
        
        name: null,
        
        value: null,
        
        flex: 1,
        
        align: 'left',
        
        displayField: 'text',
        
        valueField: 'value',
        
        itemTpl: null,
        
        scrollable: {
            x: false,
            y: true,
            scrollbars: false
        },
        
        verticallyCenterItems: true
    },
    tabIndex: null,
    focusEl: null,
    itemsFocusable: false,
    scrollToTopOnRefresh: false,
    snapSelector: '.' + Ext.baseCSSPrefix + 'dataview-item',
    
    selectedIndex: 0,
    deselectable: false,
    navigationModel: {
        type: 'boundlist',
        keyboard: false
    },
    onFocusEnter: Ext.emptyFn,
    onFocusLeave: Ext.emptyFn,
    
    
    applyTitle: function(title) {
        
        if (title) {
            
            title = Ext.create('Ext.Component', {
                cls: Ext.baseCSSPrefix + 'picker-slot-title',
                docked: 'top',
                html: title
            });
        }
        return title;
    },
    updateTitle: function(newTitle, oldTitle) {
        if (newTitle) {
            this.add(newTitle);
            this.setupBar();
        }
        if (oldTitle) {
            this.remove(oldTitle);
        }
    },
    updateShowTitle: function(showTitle) {
        var title = this.getTitle(),
            mode = showTitle ? 'show' : 'hide';
        if (title) {
            title.on(mode, this.setupBar, this, {
                single: true,
                delay: 50
            });
            title[showTitle ? 'show' : 'hide']();
        }
    },
    updateDisplayField: function(newDisplayField) {
        if (!this.config.itemTpl) {
            this.setItemTpl('<div class="' + Ext.baseCSSPrefix + 'picker-item {cls} <tpl if="extra">' + Ext.baseCSSPrefix + 'picker-invalid</tpl>">{' + newDisplayField + '}</div>');
        }
    },
    
    updateAlign: function(newAlign, oldAlign) {
        var element = this.element;
        element.addCls(Ext.baseCSSPrefix + 'picker-' + newAlign);
        element.removeCls(Ext.baseCSSPrefix + 'picker-' + oldAlign);
    },
    
    applyData: function(data) {
        var parsedData = [],
            ln = data && data.length,
            i, item, obj;
        if (data && Ext.isArray(data) && ln) {
            for (i = 0; i < ln; i++) {
                item = data[i];
                obj = {};
                if (Ext.isArray(item)) {
                    obj[this.valueField] = item[0];
                    obj[this.displayField] = item[1];
                } else if (Ext.isString(item)) {
                    obj[this.valueField] = item;
                    obj[this.displayField] = item;
                } else if (Ext.isObject(item)) {
                    obj = item;
                }
                parsedData.push(obj);
            }
        }
        return data;
    },
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.on({
            scope: me,
            painted: 'onPainted',
            single: true
        });
        me.picker.on({
            scope: me,
            beforehiddenchange: 'onBeforeHiddenChange'
        });
    },
    
    onPainted: function() {
        this.setupBar();
    },
    
    onResize: function() {
        var value = this.getValue();
        if (value) {
            this.doSetValue(value);
        }
    },
    
    onBeforeHiddenChange: function(picker, hidden) {
        if (!hidden) {
            this.doSetValue(this.getValue());
        }
    },
    
    getPicker: function() {
        if (!this.picker) {
            this.picker = this.getParent();
        }
        return this.picker;
    },
    
    setupBar: function() {
        if (!this.isPainted()) {
            
            return;
        }
        var me = this,
            title = me.getTitle(),
            titleHeight = me.getShowTitle() && title ? title.el.measure('h') : 0,
            barHeight = me.getPicker().bar.measure('h'),
            offset;
        if (me.getVerticallyCenterItems()) {
            offset = Math.ceil((me.el.measure('h') - titleHeight - barHeight) / 2);
            me.bodyElement.setStyle({
                'padding-top': offset + 'px'
            });
            
            
            
            
            if (!me.bottomSpacer) {
                me.bottomSpacer = me.add({
                    xtype: 'component',
                    scrollDock: 'end',
                    height: offset,
                    style: 'pointer-events: none'
                });
            } else {
                me.bottomSpacer.setHeight(offset);
            }
        }
        me.doSetValue(me.getValue());
    },
    
    getScrollableClientRegion: function() {
        return this.picker.bar.getClientRegion();
    },
    
    scrollToItem: function(item, animated) {
        
        
        this.getScrollable().scrollIntoView(item.el, false, animated);
    },
    
    onScrollEnd: function(x, y) {
        var me = this,
            viewItems = me.getViewItems(),
            index = Ext.Number.constrain(Math.round(y / me.picker.bar.measure('h')), 0, viewItems.length - 1),
            item = viewItems[index];
        if (item) {
            me.selectedIndex = index;
            me.selectedNode = item;
            me.setValueAnimated(me.getValue(true));
            me.fireEvent('slotpick', me, me.getValue(), me.selectedNode);
        }
    },
    
    getValue: function(useDom) {
        var store = this.getStore(),
            record, value;
        if (!store) {
            return;
        }
        if (!useDom) {
            return this._value;
        }
        
        if (this._value === false) {
            return null;
        }
        record = store.getAt(this.selectedIndex);
        value = record ? record.get(this.getValueField()) : null;
        return value;
    },
    
    setValue: function(value) {
        return this.doSetValue(value);
    },
    
    setValueAnimated: function(value) {
        return this.doSetValue(value, true);
    },
    doSetValue: function(value, animated) {
        var me = this,
            hasSelection = true,
            store, index, item;
        
        store = me.getStore();
        index = store ? store.findExact(me.getValueField(), value) : -1;
        if (index === -1) {
            hasSelection = false;
            index = 0;
        }
        me.selectedIndex = index;
        if (me.refreshCounter) {
            item = Ext.get(me.getViewItems()[index]);
            if (item) {
                me.scrollToItem(item, animated);
                if (hasSelection) {
                    
                    me.select(me.selectedIndex);
                }
            }
        }
        me._value = value;
    },
    privates: {
        forceRefreshOnRender: true
    }
});


Ext.define('Ext.picker.Picker', {
    extend: Ext.Sheet,
    alias: 'widget.picker',
    alternateClassName: 'Ext.Picker',
    isPicker: true,
    
    
    
    config: {
        
        doneButton: true,
        
        cancelButton: true,
        
        useTitles: false,
        
        slots: null,
        
        value: null,
        
        height: 220,
        
        layout: {
            type: 'hbox',
            align: 'stretch'
        },
        
        centered: false,
        
        left: 0,
        
        right: 0,
        
        bottom: 0,
        
        defaultType: 'pickerslot',
        toolbarPosition: 'top',
        
        toolbar: {
            xtype: 'titlebar'
        },
        
        side: 'bottom'
    },
    
    baseCls: Ext.baseCSSPrefix + 'picker',
    
    floated: true,
    
    focusEl: null,
    
    focusable: true,
    
    tabIndex: -1,
    initialize: function() {
        this.callParent();
        this.on({
            scope: this,
            delegate: 'pickerslot',
            slotpick: 'onSlotPick'
        });
    },
    getTemplate: function() {
        var me = this,
            clsPrefix = Ext.baseCSSPrefix,
            template = me.callParent();
        template[0].children[0].children = [
            {
                reference: 'mask',
                cls: clsPrefix + 'picker-mask',
                children: [
                    {
                        reference: 'bar',
                        cls: clsPrefix + 'picker-bar'
                    }
                ]
            }
        ];
        return template;
    },
    
    applyToolbar: function(config, oldToolbar) {
        if (config) {
            if (config === true) {
                config = {};
            }
            Ext.applyIf(config, {
                docked: this.getToolbarPosition()
            });
        }
        return Ext.factory(config, 'Ext.TitleBar', oldToolbar);
    },
    
    updateToolbar: function(newToolbar) {
        if (newToolbar) {
            this.add(newToolbar);
        }
    },
    
    applyDoneButton: function(config, oldButton) {
        if (config) {
            if (config === true) {
                config = {};
            }
            if (typeof config == "string") {
                config = {
                    text: config
                };
            }
            Ext.applyIf(config, {
                align: 'right',
                text: 'Done'
            });
        }
        return Ext.factory(config, 'Ext.Button', oldButton);
    },
    updateDoneButton: function(newDoneButton) {
        var toolbar = this.getToolbar();
        if (newDoneButton) {
            toolbar.add(newDoneButton);
            newDoneButton.on('tap', this.onDoneButtonTap, this);
        }
    },
    
    applyCancelButton: function(config, oldButton) {
        if (config) {
            if (Ext.isBoolean(config)) {
                config = {};
            }
            if (typeof config == "string") {
                config = {
                    text: config
                };
            }
            Ext.applyIf(config, {
                align: 'left',
                text: 'Cancel'
            });
        }
        return Ext.factory(config, 'Ext.Button', oldButton);
    },
    updateCancelButton: function(newCancelButton) {
        var toolbar = this.getToolbar();
        if (newCancelButton) {
            toolbar.add(newCancelButton);
            newCancelButton.on('tap', this.onCancelButtonTap, this);
        }
    },
    
    updateUseTitles: function(useTitles) {
        var innerItems = this.getInnerItems(),
            ln = innerItems.length,
            cls = Ext.baseCSSPrefix + 'use-titles',
            i, innerItem;
        
        if (useTitles) {
            this.addCls(cls);
        } else {
            this.removeCls(cls);
        }
        
        for (i = 0; i < ln; i++) {
            innerItem = innerItems[i];
            if (innerItem.isSlot) {
                innerItem.setShowTitle(useTitles);
            }
        }
    },
    applySlots: function(slots) {
        
        if (slots) {
            var ln = slots.length,
                i;
            for (i = 0; i < ln; i++) {
                slots[i].picker = this;
            }
        }
        return slots;
    },
    
    updateSlots: function(newSlots) {
        var me = this,
            bcss = Ext.baseCSSPrefix,
            innerItems;
        me.removeAll();
        if (newSlots) {
            me.add(newSlots);
        }
        innerItems = me.getInnerItems();
        if (innerItems.length > 0) {
            innerItems[0].addCls(bcss + 'first');
            innerItems[innerItems.length - 1].addCls(bcss + 'last');
        }
        me.updateUseTitles(me.getUseTitles());
        me.setValue(me.getValue());
    },
    
    onDoneButtonTap: function() {
        var me = this,
            oldValue = me._value,
            newValue = me.getValue(true);
        if (newValue != oldValue) {
            me._values = me._value = newValue;
            me.fireEvent('change', me, newValue);
        }
        me.hide();
        Ext.util.InputBlocker.unblockInputs();
    },
    
    onCancelButtonTap: function() {
        this.fireEvent('cancel', this);
        this.hide();
        Ext.util.InputBlocker.unblockInputs();
    },
    
    onSlotPick: function(slot) {
        this.fireEvent('pick', this, this.getValue(true), slot);
    },
    afterShow: function(me) {
        me.callParent([
            me
        ]);
        if (!me.isHidden()) {
            me.setValue(me._value);
        }
        Ext.util.InputBlocker.blockInputs();
    },
    updateDisplayed: function(displayed, oldDisplayed) {
        this.callParent([
            displayed,
            oldDisplayed
        ]);
        Ext.util.InputBlocker.blockInputs();
    },
    
    setValue: function(values, animated) {
        var me = this,
            slots = me.getInnerItems(),
            ln = slots.length,
            key, slot, i, value;
        if (!values) {
            values = {};
            for (i = 0; i < ln; i++) {
                
                values[slots[i].getName()] = null;
            }
        }
        for (key in values) {
            value = values[key];
            for (i = 0; i < slots.length; i++) {
                slot = slots[i];
                if (slot.getName() == key) {
                    if (animated) {
                        slot.setValueAnimated(value);
                    } else {
                        slot.setValue(value);
                    }
                    break;
                }
            }
        }
        me._values = me._value = values;
        return me;
    },
    setValueAnimated: function(values) {
        this.setValue(values, true);
    },
    
    getValue: function(useDom) {
        var values = {},
            items = this.getItems().items,
            ln = items.length,
            item, i;
        if (useDom) {
            for (i = 0; i < ln; i++) {
                item = items[i];
                if (item && item.isSlot) {
                    values[item.getName()] = item.getValue(useDom);
                }
            }
            this._values = values;
        }
        return this._values;
    },
    
    getValues: function() {
        return this.getValue();
    },
    privates: {
        
        _revertFocus: function() {
            var ownerField = this.ownerField;
            if (this.rendered && ownerField && ownerField.containsFocus) {
                ownerField.revertFocusTo(ownerField.ariaEl);
            }
        }
    }
});


Ext.define('Ext.picker.Tablet', {
    extend: Ext.Panel,
    alias: 'widget.tabletpicker',
    isPicker: true,
    focusable: true,
    tabIndex: -1
});


Ext.define('Ext.field.Select', {
    extend: Ext.field.Picker,
    xtype: 'selectfield',
    alternateClassName: 'Ext.form.Select',
    
    isSelectField: true,
    
    
    
    config: {
        
        valueCollection: true,
        
        valueField: 'value',
        
        itemTpl: false,
        
        displayTpl: null,
        
        displayField: 'text',
        
        store: null,
        
        options: null,
        
        hiddenName: null,
        
        autoSelect: false,
        
        autoFocus: true,
        
        autoFocusLast: true,
        
        selection: null,
        
        autoLoadOnValue: false,
        
        forceSelection: true,
        
        valueNotFoundText: null,
        
        selectOnTab: true
    },
    
    editable: false,
    
    floatedPicker: {
        xtype: 'boundlist',
        infinite: false,
        
        
        navigationModel: {
            disabled: true
        },
        scrollToTopOnRefresh: false,
        loadingHeight: 70,
        maxHeight: 300,
        floated: true,
        axisLock: true,
        hideAnimation: null
    },
    
    edgePicker: {
        xtype: 'picker',
        cover: true
    },
    
    classCls: Ext.baseCSSPrefix + 'selectfield',
    
    twoWayBindable: {
        selection: 1
    },
    
    publishes: {
        selection: 1
    },
    applyValueCollection: function(valueCollection) {
        if (!valueCollection.isCollection) {
            valueCollection = new Ext.util.Collection(valueCollection);
        }
        
        
        
        valueCollection.addObserver(this);
        return valueCollection;
    },
    
    createSelectionRecord: function(data) {
        var Model = this.getStore().getModel();
        return new Model(data);
    },
    completeEdit: Ext.emptyFn,
    expand: function() {
        
        if (this.getStore()) {
            this.callParent();
        }
    },
    
    maybeCollapse: function(event) {
        var record = event.to && event.to.record,
            selection = this.getSelection();
        if (record === selection) {
            this.collapse();
        }
    },
    
    onCollectionRemove: function(valueCollection, chunk) {
        var selection = valueCollection.getRange();
        
        if (!chunk.replacement) {
            
            this.setSelection(selection.length ? selection[0] : null);
        }
    },
    
    onCollectionAdd: function(valueCollection, adds) {
        var selection = valueCollection.getRange();
        this.setSelection(selection[0]);
    },
    clearValue: function() {
        var me = this;
        
        
        me.forceInputChange = true;
        me.setValue(null);
        me.forceInputChange = false;
        me.syncEmptyState();
    },
    
    applyValue: function(value, oldValue) {
        
        this.getOptions();
        var me = this,
            store = me.getStore();
        
        me.syncMode = 'value';
        
        
        if (value && value.isEntity) {
            me.setSelection(value);
            return;
        }
        if (me.isConfiguring) {
            me.originalValue = value;
        }
        
        
        
        if (store && value) {
            
            if (me.getAutoLoadOnValue() && !store.isLoaded() && !store.hasPendingLoad()) {
                store.load();
            }
        }
        return me.transformValue(value);
    },
    updateValue: function(value, oldValue) {
        this.syncValue();
        
        
        
        
        Ext.field.Field.prototype.updateValue.call(this, value, oldValue);
    },
    transformValue: function(value) {
        if (value == null || value === '') {
            value = this.getForceSelection() ? null : '';
        }
        return value;
    },
    
    findRecordByValue: function(value) {
        var me = this,
            store = me.getStore(),
            valueField = me.getValueField(),
            result,
            ret = null;
        if (store) {
            result = store.byValue.get(value);
            
            if (result) {
                ret = result[0] || result;
            }
        }
        
        
        
        
        if (!ret) {
            ret = me.getValueCollection().findBy(function(record) {
                return record.get(valueField) === value;
            });
        }
        return ret;
    },
    
    findRecordByDisplay: function(value) {
        var store = this.getStore(),
            result,
            ret = false;
        if (store) {
            result = store.byText.get(value);
            
            if (result) {
                ret = result[0] || result;
            }
        }
        return ret;
    },
    
    updateSelection: function(selection, oldSelection) {
        var me = this,
            isNull = selection == null,
            valueCollection = me.getValueCollection(),
            valueField = me.getValueField(),
            oldValue = me._value,
            newValue = null,
            picker, spliceArgs;
        if (me._ignoreSelection || me.destroyed || me.destroying) {
            return;
        }
        if (isNull || !valueCollection.containsAll(selection)) {
            spliceArgs = [
                0,
                valueCollection.getCount()
            ];
            
            
            if (!isNull) {
                spliceArgs.push(selection);
            }
            
            
            
            
            
            
            
            
            
            valueCollection.splice.apply(valueCollection, spliceArgs);
            
            if (me.destroyed) {
                return;
            }
        }
        if (selection) {
            if (valueField) {
                newValue = selection.get(valueField);
                me.setValue(newValue);
            }
            
            if (me.fireEvent('select', me, selection) === false) {
                me.setValue(oldValue);
                me._selection = oldSelection;
            }
        } else {
            me.clearValue();
        }
        
        if (me.destroyed) {
            return;
        }
        
        
        
        
        me.setFieldDisplay(selection);
        
        picker = me.getConfig('picker', false, true);
        
        
        if (picker && picker.isVisible()) {
            
            
            
            
            
            
            
            
            
            
            if (selection && oldSelection && selection.id === oldSelection.id) {
                picker.refresh();
            } else {
                
                
                if (!(selection && selection.isEntered)) {
                    me.collapse();
                }
            }
        }
    },
    
    getRecordDisplayData: function(record) {
        return record.getData();
    },
    createFloatedPicker: function() {
        var me = this,
            result = Ext.merge({
                ownerCmp: me,
                store: me._pickerStore || me.getStore(),
                selectable: {
                    selected: me.getValueCollection(),
                    selectedRecord: me.getSelection(),
                    mode: 'single',
                    deselectable: false
                },
                itemTpl: me.getItemTpl()
            }, me.getFloatedPicker());
        
        
        result.navigationModel.navigateOnSpace = !me.getEditable();
        return result;
    },
    createEdgePicker: function() {
        var me = this;
        return Ext.merge({
            ownerCmp: me,
            slots: [
                {
                    align: me.getPickerSlotAlign(),
                    name: me.getValueField(),
                    valueField: me.getValueField(),
                    displayField: me.getDisplayField(),
                    value: me.getValue(),
                    store: me._pickerStore || me.getStore()
                }
            ],
            listeners: {
                change: me.onPickerChange,
                scope: me
            },
            setStore: function(store) {
                this.child('pickerslot').setStore(store);
            },
            deselectAll: function() {
                this.child('pickerslot').deselectAll();
            }
        }, me.getEdgePicker());
    },
    setPickerLocation: function(fromKeyboard) {
        var me = this,
            picker = me.getConfig('picker', false, true),
            store, location;
        if (picker && me.expanded) {
            
            if (picker.isPicker) {
                picker = picker.innerItems[0];
            }
            store = picker.getStore();
            if (picker.getViewItems().length) {
                
                location = picker.getSelectable().getLastSelected();
                
                
                
                if (!location || !store.contains(location)) {
                    if (fromKeyboard || me.getAutoFocusLast()) {
                        location = picker.getNavigationModel().lastLocation;
                        if (location) {
                            location = location.refresh();
                        }
                    }
                    if (!location && (fromKeyboard || me.getAutoFocus())) {
                        location = store.getAt(0);
                    }
                }
                picker.getNavigationModel().setLocation(location);
            }
        }
    },
    updatePickerValue: function(picker, value) {
        var name = this.getValueField(),
            pickerValue = {};
        if (!value) {
            value = this.getValue();
        }
        pickerValue[name] = value;
        picker.setValue(pickerValue);
    },
    onPickerShow: function(picker) {
        this.callParent([
            picker
        ]);
        
        
        if (this.pickerType === 'floated') {
            picker.getNavigationModel().enable();
        }
    },
    onPickerHide: function(picker) {
        var navModel;
        this.callParent([
            picker
        ]);
        
        
        
        if (!picker.destroying && this.pickerType === 'floated') {
            navModel = picker.getNavigationModel();
            navModel.setLocation(null);
            navModel.disable();
        }
    },
    
    onPickerChange: function(picker, value) {
        this.setValue(this.findRecordByValue(value[this.getValueField()]));
    },
    applyItemTpl: function(itemTpl) {
        if (itemTpl === false) {
            itemTpl = '<span class="x-list-label">{' + this.getDisplayField() + ':htmlEncode}</span>';
        }
        return itemTpl;
    },
    applyDisplayTpl: function(displayTpl) {
        if (displayTpl && !displayTpl.isTemplate) {
            displayTpl = new Ext.XTemplate(displayTpl);
        }
        return displayTpl;
    },
    applyOptions: function(options) {
        if (options) {
            var len = options.length,
                valueField = this.getValueField(),
                displayField = this.getDisplayField(),
                i, value, option;
            
            options = Ext.Array.slice(options);
            for (i = 0; i < len; i++) {
                value = options[i];
                if (Ext.isPrimitive(value)) {
                    options[i] = option = {};
                    option.id = value;
                    option[valueField] = value;
                    if (displayField && displayField !== valueField) {
                        option[displayField] = value;
                    }
                }
            }
            options = Ext.data.StoreManager.lookup({
                fields: [
                    valueField,
                    displayField
                ],
                data: options
            });
        }
        return options;
    },
    updateOptions: function(options, oldOptions) {
        if (options) {
            this.setStore(options);
        } else {
            if (oldOptions === this.getStore()) {
                this.setStore(null);
            }
        }
    },
    applyStore: function(store) {
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
        }
        return store;
    },
    updateStore: function(store, oldStore) {
        var me = this,
            valueField = me.getValueField(),
            displayField = me.getDisplayField(),
            extraKeySpec;
        if (oldStore) {
            if (oldStore.getAutoDestroy()) {
                oldStore.destroy();
            } else {
                oldStore.byValue = oldStore.byText = Ext.destroy(oldStore.byValue, oldStore.byText);
            }
        }
        if (store) {
            
            
            
            
            
            
            extraKeySpec = {
                byValue: {
                    rootProperty: 'data',
                    unique: false,
                    property: valueField
                }
            };
            if (displayField !== valueField) {
                extraKeySpec.byText = {
                    rootProperty: 'data',
                    unique: false,
                    property: displayField
                };
            }
            store.setExtraKeys(extraKeySpec);
            
            if (displayField === valueField) {
                store.byText = store.byValue;
            }
            store.on({
                scope: me,
                add: 'onStoreDataChanged',
                remove: 'onStoreDataChanged',
                update: 'onStoreRecordUpdated',
                
                load: {
                    fn: 'onStoreLoad',
                    priority: -1
                }
            });
            
            
            
            
            if (store.isLoaded() && !store.hasPendingLoad()) {
                me.syncValue();
            }
            
            
            
            else if (me.getValue() != null && me.getAutoLoadOnValue() && !store.isLoaded() && !store.hasPendingLoad()) {
                store.load();
            }
        }
        
        
        me.updatePickerStore();
    },
    applyValueField: function(valueField) {
        
        
        if (valueField == null) {
            valueField = this.getDisplayField();
        }
        return valueField;
    },
    updateValueField: function(valueField) {
        var store = this.getStore();
        
        if (store && !this.isConfiguring) {
            store.byValue.setCollection(null);
            store.setExtraKeys({
                byValue: {
                    rootProperty: 'data',
                    unique: false,
                    property: valueField
                }
            });
        }
    },
    applyDisplayField: function(displayField) {
        
        
        if (displayField == null) {
            displayField = this.getValueField();
        }
        return displayField;
    },
    updateDisplayField: function(displayField) {
        var store = this.getStore();
        
        if (store && !this.isConfiguring) {
            store.byText.setCollection(null);
            store.setExtraKeys({
                byText: {
                    rootProperty: 'data',
                    unique: false,
                    property: displayField
                }
            });
        }
    },
    
    onStoreLoad: function(store, records, success) {
        var filtering = this.isFiltering;
        this.isFiltering = false;
        if (success) {
            
            
            this.syncMode = filtering ? 'filter' : 'store';
            this.syncValue();
        }
    },
    syncValue: function() {
        var me = this,
            store = me.getStore(),
            valueField = me.getValueField(),
            displayField = me.getDisplayField(),
            forceSelection = me.getForceSelection(),
            valueNotFoundText = me.getValueNotFoundText(),
            is, isCleared, isInput, value, matchedRecord, dataObj;
        
        
        
        
        
        
        
        if (me.reconcilingValue || !store || !store.isLoaded() || store.hasPendingLoad()) {
            return;
        }
        me.reconcilingValue = true;
        me.getSelection();
        
        is = {};
        is[me.syncMode] = true;
        value = (isInput = is.input || is.filter) ? me.getInputValue() : me.getValue();
        isCleared = value == null || value === '';
        
        if (!isCleared) {
            matchedRecord = (isInput ? store.byText : store.byValue).get(value);
            if (matchedRecord) {
                if (!matchedRecord.isEntity) {
                    
                    
                    
                    matchedRecord = matchedRecord[0];
                }
            } else if (!forceSelection) {
                
                
                
                matchedRecord = me.findRecordByValue(value);
            }
        }
        
        
        
        if (!isCleared && !matchedRecord && !forceSelection) {
            dataObj = {};
            dataObj[displayField] = value;
            if (valueField && displayField !== valueField) {
                dataObj[valueField] = value;
            }
            matchedRecord = me.createSelectionRecord(dataObj);
            matchedRecord.isEntered = true;
        } else {
            
            
            if (isInput || is.store) {
                if (!matchedRecord && forceSelection) {
                    me.setValue(null);
                    me.setSelection(null);
                    
                    
                    
                    if (!is.filter) {
                        me.setFieldDisplay();
                    }
                }
            } else 
            {
                if (isCleared) {
                    if (me.mustAutoSelect()) {
                        matchedRecord = store.first();
                        if (me.getAutoSelect() === 'initial') {
                            me.setAutoSelect(false);
                        }
                    } else {
                        me.setSelection(null);
                    }
                }
                
                
                else if (!matchedRecord && valueNotFoundText) {
                    me.setError(valueNotFoundText);
                }
            }
        }
        if (matchedRecord) {
            me.setSelection(matchedRecord);
        }
        me.reconcilingValue = false;
    },
    
    onStoreDataChanged: function() {
        if (this.getForceSelection()) {
            var value = this.getValue();
            
            
            if (value != null) {
                this.setValue(value);
            }
        }
    },
    
    onStoreRecordUpdated: function(store, record) {
        if (this.getValueCollection().contains(record)) {
            this.updateSelection(this.getSelection());
        }
    },
    
    reset: function() {
        var me = this,
            picker = me.getConfig('picker', false, true),
            record = me.originalValue || null,
            store;
        if (me.getAutoSelect()) {
            store = me.getStore();
            record = (record != null) ? record : store && store.getAt(0);
        } else {
            if (picker) {
                picker.deselectAll();
            }
        }
        me.setValue(record);
        return me;
    },
    doDestroy: function() {
        var store = this.getStore();
        if (store && !store.destroyed && store.getAutoDestroy()) {
            store.destroy();
        }
        this.destroyMembers('options');
        this.callParent();
    },
    privates: {
        syncMode: null,
        mustAutoSelect: function() {
            var me = this,
                autoSelect = me.getAutoSelect();
            if (autoSelect && !(me.isConfiguring || autoSelect === 'initial')) {
                autoSelect = !me.getClearable() && me.getRequired();
            }
            return !!autoSelect;
        },
        
        updatePickerStore: function() {
            var me = this,
                picker = me.getConfig('picker', false, true),
                store = me.getStore(),
                localFiltering = me.getQueryMode && me.getQueryMode() === 'local',
                result = store;
            
            
            if (localFiltering) {
                
                if (me._pickerStore && me._pickerStore.isChainedStore) {
                    me._pickerStore.setConfig({
                        source: store
                    });
                } else 
                {
                    me._pickerStore = result = Ext.data.StoreManager.lookup({
                        type: 'chained',
                        source: store
                    }, null, me);
                }
            } else 
            {
                me._pickerStore = result = store;
            }
            
            
            if (picker) {
                picker.setStore(result);
            }
        },
        
        setFieldDisplay: function(selection) {
            var me = this,
                inputValue = '',
                displayTpl;
            if (selection) {
                displayTpl = me.getDisplayTpl();
                if (displayTpl) {
                    inputValue = displayTpl.apply(me.getRecordDisplayData(selection));
                } else {
                    inputValue = selection.get(me.getDisplayField());
                }
            }
            me.setInputValue(inputValue);
            
            me.syncEmptyState();
        }
    }
});


Ext.define('Ext.field.ComboBox', {
    extend: Ext.field.Select,
    xtype: [
        'combobox',
        'comboboxfield'
    ],
    alternateClassName: [
        'Ext.form.field.ComboBox'
    ],
    config: {
        
        primaryFilter: true,
        
        queryParam: 'query',
        
        queryMode: 'remote',
        
        queryCaching: true,
        
        queryDelay: true,
        
        minChars: null,
        
        anyMatch: false,
        
        caseSensitive: false,
        
        typeAhead: false,
        
        typeAheadDelay: 250,
        
        triggerAction: 'all',
        
        allQuery: null,
        
        enableRegEx: null
    },
    
    autoSelect: false,
    
    editable: true,
    
    forceSelection: false,
    
    
    
    
    
    
    lastQuery: {},
    picker: 'floated',
    onInput: function(e) {
        var me = this,
            filterTask = me.doFilterTask,
            value = me.inputElement.dom.value,
            filters = me.getStore().getFilters();
        if (!me.getForceSelection() || (value === '' && !me.getRequired())) {
            me.callParent([
                e
            ]);
        } else {
            
            me._inputValue = value;
            me.syncEmptyState();
        }
        if (value.length) {
            if (!filterTask) {
                filterTask = me.doFilterTask = new Ext.util.DelayedTask(me.doRawFilter, me);
            }
            filterTask.delay(me.getQueryDelay());
        } else {
            me.collapse();
            filters.beginUpdate();
            me.getPrimaryFilter().setDisabled(true);
            filters.endUpdate();
        }
    },
    
    doRawFilter: function() {
        var me = this,
            rawValue = me.inputElement.dom.value,
            lastQuery = me.lastQuery.query,
            isErase = lastQuery && lastQuery.length > rawValue.length;
        me.doFilter({
            query: rawValue,
            isErase: isErase
        });
    },
    
    onExpandTap: function() {
        var me = this,
            triggerAction = me.getTriggerAction();
        
        
        
        if (me.expanded) {
            me.collapse();
        } else if (!me.getReadOnly() && !me.getDisabled()) {
            if (triggerAction === 'all') {
                me.doFilter({
                    query: me.getAllQuery(),
                    force: true
                });
            }
            
            else if (triggerAction === 'last') {
                me.doFilter({
                    query: me.lastQuery.query,
                    force: true
                });
            } else 
            {
                me.doFilter({
                    query: me.inputElement.dom.value
                });
            }
        }
    },
    clearValue: function() {
        var me = this,
            inputMask = me.getInputMask();
        if (inputMask) {} else 
        
        
        {
            me.setValue(null);
            me.setFieldDisplay();
        }
        me.syncEmptyState();
    },
    
    doFilter: function(query) {
        var me = this,
            isLocal = me.getQueryMode() === 'local',
            lastQuery = me.lastQuery,
            store = me.getStore() && me._pickerStore,
            filter = me.getPrimaryFilter(),
            filters = store.getFilters(),
            
            queryPlan = me.beforeFilter(Ext.apply({
                filterGeneration: filter.generation,
                lastQuery: lastQuery || {},
                combo: me,
                cancel: false
            }, query)),
            picker;
        
        if (store && queryPlan !== false && !queryPlan.cancel) {
            
            
            if (me.getEnableRegEx()) {
                try {
                    queryPlan.query = new RegExp(queryPlan.query);
                } catch (e) {
                    queryPlan.query = null;
                }
            }
            
            filter.setValue(queryPlan.query);
            
            
            
            if (!me.getQueryCaching() || filter.generation !== lastQuery.filterGeneration || query.force) {
                
                
                if (Ext.isEmpty(queryPlan.query)) {
                    filter.setDisabled(true);
                } else {
                    filter.setDisabled(false);
                    
                    
                    me.isFiltering = !isLocal;
                }
                me.lastQuery = queryPlan;
                
                
                filters.beginUpdate();
                filters.endUpdate();
            }
            if (me.getTypeAhead()) {
                me.doTypeAhead(queryPlan);
            }
            
            
            
            picker = me.getPicker();
            
            
            if (!isLocal || store.getCount() || (picker.getEmptyText && picker.getEmptyText())) {
                me.expand();
                return true;
            } else 
            
            
            {
                me.collapse();
            }
        }
        return false;
    },
    
    beforeFilter: function(queryPlan) {
        var me = this,
            query = queryPlan.query,
            len;
        
        if (me.fireEvent('beforequery', queryPlan) === false) {
            queryPlan.cancel = true;
        }
        
        else if (!queryPlan.cancel) {
            len = query && query.length;
            
            if (!queryPlan.force && len && len < me._getMinChars()) {
                queryPlan.cancel = true;
            }
        }
        return queryPlan;
    },
    completeEdit: function() {
        var me = this,
            inputValue = me.getInputValue();
        
        if (me.doFilterTask) {
            me.doFilterTask.cancel();
        }
        if (inputValue) {
            me.syncMode = 'input';
            me.syncValue();
        }
        if (me.getTypeAhead()) {
            me.select(inputValue ? inputValue.length : 0);
        }
    },
    
    onStoreDataChanged: function(store) {
        this.callParent([
            store
        ]);
    },
    
    
    
    
    onStoreFilterChange: function() {
        var me = this,
            store = me.getStore(),
            selection = me.getSelection() || null,
            toRemove = [];
        
        
        if (selection && !me.destroying && store && store.isLoaded() && me.getPrimaryFilter().getDisabled()) {
            if (!selection.isEntered && !store.contains(selection)) {
                toRemove.push(selection);
            }
            
            if (toRemove.length) {
                this.getValueCollection().remove(toRemove);
            }
        }
    },
    
    onListSelect: Ext.emptyFn,
    applyQueryDelay: function(queryDelay) {
        if (queryDelay === true) {
            queryDelay = this.getQueryMode() === 'local' ? 10 : 500;
        }
        return queryDelay;
    },
    applyPrimaryFilter: function(filter, oldFilter) {
        var me = this,
            store = me.getStore() && me._pickerStore,
            isInstance = filter && filter.isFilter;
        
        if (store && oldFilter) {
            
            if (filter) {
                if (isInstance) {
                    store.removeFilter(oldFilter, true);
                } else {
                    oldFilter.setConfig(filter);
                    return;
                }
            }
            
            else if (!store.destroyed) {
                store.getFilters().remove(oldFilter);
            }
        }
        
        if (filter) {
            if (filter === true) {
                filter = {
                    id: me.id + '-primary-filter',
                    anyMatch: me.getAnyMatch(),
                    caseSensitive: me.getCaseSensitive(),
                    root: 'data',
                    property: me.getDisplayField(),
                    value: me.inputElement.dom.value,
                    disabled: true
                };
            }
            
            if (!filter.isFilter) {
                filter = new Ext.util.Filter(filter);
            }
            
            filter.serialize = function() {
                return me.serializePrimaryFilter(this);
            };
            
            if (store) {
                store.addFilter(filter, true);
            }
        }
        return filter;
    },
    updateOptions: function(options, oldOptions) {
        if (options) {
            this.setQueryMode('local');
        }
        this.callParent([
            options,
            oldOptions
        ]);
    },
    updatePicker: function(picker, oldPicker) {
        if (picker) {
            picker.getSelectable().ignoredFilter = this.getPrimaryFilter();
        }
        this.callParent([
            picker,
            oldPicker
        ]);
    },
    updateStore: function(store, oldStore) {
        var me = this,
            isRemote = me.getQueryMode() === 'remote',
            primaryFilter, proxy, oldFilters;
        
        if (isRemote) {
            store.setRemoteFilter(true);
            
            proxy = store.getProxy();
            if (proxy.setFilterParam) {
                proxy.setFilterParam(me.getQueryParam());
            }
        }
        
        
        me.callParent([
            store,
            oldStore
        ]);
        
        primaryFilter = me.getPrimaryFilter();
        if (primaryFilter) {
            
            
            if (oldStore && !oldStore.destroyed) {
                oldFilters = oldStore.getFilters();
                
                oldFilters && oldFilters.remove(primaryFilter);
            }
            
            
            
            me._pickerStore.addFilter(primaryFilter, true);
        }
        
        
        
        
        if (me.getQueryMode() === 'local') {
            store.on({
                filterchange: 'onStoreFilterChange',
                scope: me
            });
        }
    },
    
    serializePrimaryFilter: function(filter) {
        return filter.getValue();
    },
    doDestroy: function() {
        var me = this;
        me.setPrimaryFilter(null);
        if (me.typeAheadTask) {
            me.typeAheadTask = me.typeAheadTask.cancel();
        }
        me.callParent();
    },
    doTypeAhead: function(queryPlan) {
        var me = this;
        if (!me.typeAheadTask) {
            me.typeAheadTask = new Ext.util.DelayedTask(me.onTypeAhead, me);
        }
        
        
        if ((!queryPlan.lastQuery.query || !queryPlan.query || queryPlan.query.length > queryPlan.lastQuery.query.length) || !Ext.String.startsWith(queryPlan.lastQuery.query, queryPlan.query)) {
            me.typeAheadTask.delay(me.getTypeAheadDelay());
        }
    },
    onTypeAhead: function() {
        var me = this,
            displayField = me.getDisplayField(),
            inputEl = me.inputElement.dom,
            rawValue = inputEl.value,
            store = me.getStore(),
            record = store.findRecord(displayField, rawValue),
            newValue, len, selStart;
        if (record) {
            newValue = record.get(displayField);
            len = newValue.length;
            selStart = rawValue.length;
            if (selStart !== 0 && selStart !== len) {
                inputEl.value = me._inputValue = newValue;
                me.select(selStart, len);
            }
        }
    },
    privates: {
        _getMinChars: function() {
            var result = this.getMinChars();
            if (result == null) {
                result = this.getQueryMode() === 'remote' ? 4 : 0;
            }
            return result;
        },
        setFieldDisplay: function(selection) {
            var me = this,
                inputValue;
            me.callParent([
                selection
            ]);
            if (me.getTypeAhead()) {
                inputValue = me.getInputValue();
                me.select(inputValue ? inputValue.length : 0);
            }
        }
    }
});


Ext.define('Ext.field.Manager', {
    mixinId: 'fieldmanager',
    
    fillRecord: function(record) {
        var values, name;
        if (record) {
            values = this.getValues();
            for (name in values) {
                if (values.hasOwnProperty(name) && record.getField(name)) {
                    record.set(name, values[name]);
                }
            }
        }
        return this;
    },
    consumeRecord: function(record) {
        var data = record && record.data;
        if (data) {
            this.setValues(data);
        }
    },
    
    setValues: function(values) {
        var fields = this.getFields(),
            name, field, value, ln, i, f;
        values = values || {};
        for (name in values) {
            if (values.hasOwnProperty(name)) {
                field = fields[name];
                value = values[name];
                if (field) {
                    
                    
                    if (Ext.isArray(field)) {
                        ln = field.length;
                        
                        for (i = 0; i < ln; i++) {
                            f = field[i];
                            if (f.isRadio) {
                                
                                
                                f.setGroupValue(value);
                                break;
                            } else if (f.isCheckbox) {
                                if (Ext.isArray(value)) {
                                    f.setChecked((value.indexOf(f._value) != -1));
                                } else {
                                    f.setChecked((value == f._value));
                                }
                            } else {
                                
                                
                                
                                if (Ext.isArray(value)) {
                                    f.setValue(value[i]);
                                }
                            }
                        }
                    } else {
                        if (field.isRadio || field.isCheckbox) {
                            
                            field.setChecked(value);
                        } else {
                            
                            field.setValue(value);
                        }
                    }
                    if (this.getTrackResetOnLoad && this.getTrackResetOnLoad()) {
                        field.resetOriginalValue();
                    }
                }
            }
        }
        return this;
    },
    
    getValues: function(enabled, all) {
        var fields = this.getFields(),
            values = {},
            isArray = Ext.isArray,
            field, value, addValue, bucket, name, ln, i;
        
        
        addValue = function(field, name) {
            if (!all && (!name || name === 'null') || field.isFile) {
                return;
            }
            if (field.isCheckbox) {
                value = field.getSubmitValue();
            } else {
                value = field.getValue();
            }
            if (!(enabled && field.getDisabled())) {
                
                
                if (field.isRadio) {
                    if (field.isChecked()) {
                        values[name] = value;
                    }
                } else {
                    
                    bucket = values[name];
                    if (!Ext.isEmpty(bucket)) {
                        if (!field.isCheckbox || field.isChecked()) {
                            
                            
                            if (!isArray(bucket)) {
                                bucket = values[name] = [
                                    bucket
                                ];
                            }
                            
                            if (isArray(value)) {
                                
                                bucket = values[name] = bucket.concat(value);
                            } else {
                                
                                bucket.push(value);
                            }
                        }
                    } else {
                        values[name] = value;
                    }
                }
            }
        };
        
        for (name in fields) {
            if (fields.hasOwnProperty(name)) {
                field = fields[name];
                if (isArray(field)) {
                    ln = field.length;
                    for (i = 0; i < ln; i++) {
                        addValue(field[i], name);
                    }
                } else {
                    addValue(field, name);
                }
            }
        }
        return values;
    },
    
    reset: function(clearInvalid) {
        this.getFields(false).forEach(function(field) {
            field.reset();
            if (clearInvalid) {
                field.setError(null);
            }
        });
        return this;
    },
    
    updateDisabled: function(newDisabled) {
        this.getFields(false).forEach(function(field) {
            field.setDisabled(newDisabled);
        });
        return this;
    },
    
    setErrors: function(errors) {
        var setError = function(field, fieldname) {
                if (field) {
                    messages = errors[fieldname];
                    if (messages === null || (Ext.isArray(messages) && messages.length === 0)) {
                        field.setError(null);
                    } else {
                        field.setError(messages);
                    }
                }
            },
            fieldname, field, messages, i, length;
        
        if (!Ext.isObject(errors)) {
            Ext.raise('setErrors requires an Object parameter');
        }
        
        for (fieldname in errors) {
            field = this.lookupName(fieldname) || this.lookup(fieldname);
            if (Ext.isArray(field)) {
                for (i = 0 , length = field.length; i < length; i++) {
                    setError(field[i], fieldname);
                }
            } else {
                setError(field, fieldname);
            }
        }
        return this;
    },
    
    clearErrors: function() {
        var fields = this.getFields(false),
            i, length, field;
        for (i = 0 , length = fields.length; i < length; i++) {
            field = fields[i];
            if (field.getName() && field.setError) {
                field.setError(null);
            }
        }
        return this;
    },
    
    getErrors: function() {
        var errors = {},
            fields = this.getFields(false).filter(function(field) {
                return field.getName();
            }),
            i, length, field, error;
        for (i = 0 , length = fields.length; i < length; i++) {
            field = fields[i];
            error = field.getError();
            if (!error || !error.length) {
                error = null;
            }
            errors[field.getName()] = error;
        }
        return errors;
    },
    
    isValid: function() {
        var fields = this.getFields(false),
            i, length;
        for (i = 0 , length = fields.length; i < length; i++) {
            if (!fields[i].isValid()) {
                return false;
            }
        }
        return true;
    },
    
    validate: function(skipLazy) {
        var fields = this.getFields(false),
            valid = true,
            i, length;
        for (i = 0 , length = fields.length; i < length; i++) {
            if (!fields[i].validate(skipLazy)) {
                
                
                valid = false;
            }
        }
        return valid;
    },
    
    getFields: function(byName, deep) {
        var selector = (deep === false ? '> ' : '') + 'field' + (byName ? '[name=' + byName + ']' : ''),
            fields = this.query(selector),
            asArray = byName === false,
            obj, i, length, field, name, bucket;
        if (!fields && asArray) {
            
            return [];
        } else if (fields && !asArray) {
            
            if (!byName) {
                
                
                obj = {};
                for (i = 0 , length = fields.length; i < length; i++) {
                    field = fields[i];
                    name = field.getName();
                    bucket = obj[name];
                    if (bucket) {
                        if (Ext.isArray(bucket)) {
                            bucket.push(field);
                        } else {
                            obj[name] = [
                                bucket,
                                field
                            ];
                        }
                    } else {
                        obj[name] = field;
                    }
                }
                return obj;
            } else if (fields.length < 2) {
                
                
                return fields[0];
            }
        }
        return fields;
    },
    
    getFocusedField: function() {
        var fields = this.getFields(false),
            ln = fields.length,
            field, i;
        for (i = 0; i < ln; i++) {
            field = fields[i];
            if (field.hasFocus) {
                return field;
            }
        }
        return null;
    },
    
    getNextField: function() {
        var fields = this.getFields(false),
            focusedField = this.getFocusedField(),
            index;
        if (focusedField) {
            index = fields.indexOf(focusedField);
            if (index !== fields.length - 1) {
                index++;
                return fields[index];
            }
        }
        return false;
    },
    
    focusNextField: function() {
        var field = this.getNextField();
        if (field) {
            field.focus();
            return field;
        }
        return false;
    },
    
    getPreviousField: function() {
        var fields = this.getFields(false),
            focusedField = this.getFocusedField(),
            index;
        if (focusedField) {
            index = fields.indexOf(focusedField);
            if (index !== 0) {
                index--;
                return fields[index];
            }
        }
        return false;
    },
    
    focusPreviousField: function() {
        var field = this.getPreviousField();
        if (field) {
            field.focus();
            return field;
        }
        return false;
    }
});


Ext.define('Ext.field.Container', {
    extend: Ext.field.Field,
    xtype: [
        'containerfield',
        'fieldcontainer'
    ],
    
    mixins: [
        Ext.field.Manager,
        Ext.mixin.ConfigProxy
    ],
    
    config: {
        
        container: {
            xtype: 'container',
            autoSize: null,
            defaultType: 'textfield',
            defaults: {
                errorTarget: 'parent'
            },
            layout: {
                type: 'hbox'
            }
        }
    },
    proxyConfig: {
        container: {
            configs: [
                
                'defaults',
                
                'defaultType',
                
                'items',
                
                'layout',
                
                'autoSize'
            ],
            methods: [
                
                'add',
                
                'insert',
                
                'remove',
                
                'removeAll',
                
                'getAt',
                
                'child',
                
                'down',
                
                'query'
            ]
        }
    },
    bodyAlign: 'stretch',
    classCls: Ext.baseCSSPrefix + 'containerfield',
    isField: false,
    isContainer: true,
    isContainerField: true,
    errorTpl: '<tpl if="count == 1">' + '<tpl for="errors">{label:htmlEncode}: {error:htmlEncode}</tpl>' + '<tpl elseif="count">' + '<ul class="{listCls}">' + '<tpl for="errors">' + '<li>{label:htmlEncode}: {error:htmlEncode}</li>' + '</tpl>' + '</ul>' + '</tpl>',
    doDestroy: function() {
        this.setContainer(null);
        this.callParent();
    },
    applyContainer: function(container, oldContainer) {
        if (container) {
            if (!container.isInstance) {
                container = this.mergeProxiedConfigs('container', container);
                container.$initParent = this;
                container = Ext.create(container);
                delete container.$initParent;
            }
            container.ownerCmp = this;
        }
        return container;
    },
    updateContainer: function(container, oldContainer) {
        var bodyElement = this.bodyElement;
        if (oldContainer) {
            bodyElement.removeChild(oldContainer.el);
            oldContainer.destroy();
        }
        if (container) {
            bodyElement.appendChild(container.el);
        }
    },
    updateRecord: function(record) {
        this.consumeRecord(record);
    },
    onFieldErrorChange: function(field) {
        var me = this,
            errors = me.getErrors(),
            fields = me.getFields(),
            name, fieldErrors, label, messages;
        for (name in errors) {
            field = fields[name];
            fieldErrors = errors[name];
            if (fieldErrors) {
                label = field.getLabel() || field.getPlaceholder() || field.getName();
                fieldErrors = Ext.Array.from(fieldErrors).map(function(error) {
                    return {
                        label: label,
                        error: error
                    };
                });
                if (messages) {
                    messages = messages.concat(fieldErrors);
                } else {
                    messages = fieldErrors;
                }
            }
        }
        me.setError(messages || null);
    },
    
    getRefItems: function(deep) {
        var refItems = [],
            container = this.getContainer();
        if (container) {
            refItems.push(container);
            if (deep && container.getRefItems) {
                refItems.push.apply(refItems, container.getRefItems(deep));
            }
        }
        return refItems;
    },
    
    getFocusEl: function() {
        var items = this.getFields(false),
            length = items && items.length,
            i, item, focusEl;
        for (i = 0; i < length; i++) {
            item = items[i];
            focusEl = item.getFocusEl();
            if (focusEl) {
                return focusEl;
            }
        }
        return this.callParent();
    },
    reset: function(clearInvalid) {
        if (clearInvalid) {
            this.setError(null);
        }
        return this.mixins.fieldmanager.reset.call(this, clearInvalid);
    },
    
    setErrors: function(errors) {
        var me = this,
            fields = me.getFields(),
            fieldname, field, messages;
        
        if (!Ext.isObject(errors)) {
            Ext.raise('setErrors requires an Object parameter');
        }
        
        me.setError(null);
        for (fieldname in errors) {
            field = fields[fieldname];
            if (field) {
                messages = errors[fieldname];
                if (messages == null || (Ext.isArray(messages) && messages.length === 0)) {
                    field.setError(null);
                } else {
                    field.setError(Ext.Array.from(messages));
                }
            }
        }
        return me;
    },
    isValid: function() {
        return this.mixins.fieldmanager.isValid.call(this);
    },
    validate: function(skiplazy) {
        return this.mixins.fieldmanager.validate.call(this, skiplazy);
    },
    getFields: function(byName, deep) {
        if (deep == null) {
            
            deep = false;
        }
        return this.mixins.fieldmanager.getFields.call(this, byName, deep);
    }
});


Ext.define('Ext.field.trigger.Date', {
    extend: Ext.field.trigger.Trigger,
    xtype: 'datetrigger',
    alias: 'trigger.date',
    classCls: Ext.baseCSSPrefix + 'datetrigger',
    handler: 'onExpandTap',
    scope: 'this'
});


Ext.define('Ext.picker.Date', {
    extend: Ext.picker.Picker,
    xtype: 'datepicker',
    alternateClassName: 'Ext.DatePicker',
    
    config: {
        
        yearFrom: 1980,
        
        yearTo: new Date().getFullYear(),
        
        monthText: 'Month',
        
        dayText: 'Day',
        
        yearText: 'Year',
        
        slotOrder: [
            'month',
            'day',
            'year'
        ],
        
        
        
        doneButton: true
    },
    initialize: function() {
        var me = this;
        me.callParent();
        me.on({
            scope: me,
            delegate: '> slot',
            slotpick: me.onSlotPick
        });
        me.on({
            scope: me,
            show: me.onSlotPick
        });
    },
    setValue: function(value, animated) {
        var me = this;
        if (Ext.isDate(value)) {
            value = {
                day: value.getDate(),
                month: value.getMonth() + 1,
                year: value.getFullYear()
            };
        }
        me.callParent([
            value,
            animated
        ]);
        if (me.rendered) {
            me.onSlotPick();
        }
        return me;
    },
    getValue: function(useDom) {
        var values = {},
            items = this.getItems().items,
            ln = items.length,
            daysInMonth, day, month, year, item, i;
        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.isSlot) {
                values[item.getName()] = item.getValue(useDom);
            }
        }
        
        if (values.year === null && values.month === null && values.day === null) {
            return null;
        }
        year = Ext.isNumber(values.year) ? values.year : 1;
        month = Ext.isNumber(values.month) ? values.month : 1;
        day = Ext.isNumber(values.day) ? values.day : 1;
        if (month && year && month && day) {
            daysInMonth = this.getDaysInMonth(month, year);
        }
        day = (daysInMonth) ? Math.min(day, daysInMonth) : day;
        return new Date(year, month - 1, day);
    },
    
    updateYearFrom: function() {
        if (this.initialized) {
            this.createSlots();
        }
    },
    
    updateYearTo: function() {
        if (this.initialized) {
            this.createSlots();
        }
    },
    
    updateMonthText: function(newMonthText, oldMonthText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;
        
        if (this.initialized) {
            for (i = 0; i < ln; i++) {
                item = innerItems[i];
                if ((typeof item.title == "string" && item.title == oldMonthText) || (item.title.html == oldMonthText)) {
                    item.setTitle(newMonthText);
                }
            }
        }
    },
    
    updateDayText: function(newDayText, oldDayText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;
        
        if (this.initialized) {
            for (i = 0; i < ln; i++) {
                item = innerItems[i];
                if ((typeof item.title == "string" && item.title == oldDayText) || (item.title.html == oldDayText)) {
                    item.setTitle(newDayText);
                }
            }
        }
    },
    
    updateYearText: function(yearText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;
        
        if (this.initialized) {
            for (i = 0; i < ln; i++) {
                item = innerItems[i];
                if (item.title == this.yearText) {
                    item.setTitle(yearText);
                }
            }
        }
    },
    
    constructor: function() {
        this.callParent(arguments);
        this.createSlots();
    },
    
    createSlots: function() {
        var me = this,
            slotOrder = me.getSlotOrder(),
            yearsFrom = me.getYearFrom(),
            yearsTo = me.getYearTo(),
            years = [],
            days = [],
            months = [],
            slots = [],
            reverse = yearsFrom > yearsTo,
            ln, i, daysInMonth;
        while (yearsFrom) {
            years.push({
                text: yearsFrom,
                value: yearsFrom
            });
            if (yearsFrom === yearsTo) {
                break;
            }
            if (reverse) {
                yearsFrom--;
            } else {
                yearsFrom++;
            }
        }
        daysInMonth = me.getDaysInMonth(1, new Date().getFullYear());
        for (i = 0; i < daysInMonth; i++) {
            days.push({
                text: i + 1,
                value: i + 1
            });
        }
        for (i = 0 , ln = Ext.Date.monthNames.length; i < ln; i++) {
            months.push({
                text: Ext.Date.monthNames[i],
                value: i + 1
            });
        }
        slotOrder.forEach(function(item) {
            slots.push(me.createSlot(item, days, months, years));
        });
        me.setSlots(slots);
    },
    
    createSlot: function(name, days, months, years) {
        var me = this,
            result;
        switch (name) {
            case 'year':
                result = {
                    name: 'year',
                    align: 'center',
                    data: years,
                    title: me.getYearText(),
                    flex: 3
                };
                break;
            case 'month':
                result = {
                    name: name,
                    align: 'right',
                    data: months,
                    title: me.getMonthText(),
                    flex: 4
                };
                break;
            case 'day':
                result = {
                    name: 'day',
                    align: 'center',
                    data: days,
                    title: me.getDayText(),
                    flex: 2
                };
        }
        if (me._value) {
            result.value = me._value[name];
        }
        return result;
    },
    onSlotPick: function() {
        var me = this,
            addDays = [],
            value, daySlot, valueField, dayStore, dayData, daysInMonth, slotCount, year, month, i, spliceArgs;
        if (me.isConfiguring) {
            return;
        }
        value = me.getValue(true);
        daySlot = me.getDaySlot();
        me.callParent(arguments);
        
        
        if (!daySlot) {
            return;
        }
        valueField = daySlot.getValueField();
        dayStore = daySlot.getStore();
        dayData = dayStore.getData();
        slotCount = dayStore.getCount();
        year = value.getFullYear();
        month = value.getMonth();
        
        daysInMonth = me.getDaysInMonth(month + 1, year);
        
        if (slotCount === daysInMonth) {
            return;
        }
        
        
        if (daysInMonth > slotCount) {
            for (i = slotCount; i < daysInMonth; i++) {
                addDays.push(dayStore.createModel({
                    text: i + 1,
                    value: i + 1
                }));
            }
            spliceArgs = [
                slotCount,
                0,
                addDays
            ];
        } else {
            spliceArgs = [
                daysInMonth,
                5
            ];
        }
        
        dayData.splice.apply(dayData, spliceArgs);
        
        i = dayStore.find(valueField, value.getDate());
        if (i == -1) {
            return;
        }
        daySlot.selectedIndex = i;
        daySlot.scrollToItem(daySlot.mapToItem(i));
        daySlot.setValue(daySlot.getValue(true));
    },
    getDaySlot: function() {
        var innerItems = this.getInnerItems(),
            ln = innerItems.length,
            i, slot;
        if (this.daySlot) {
            return this.daySlot;
        }
        for (i = 0; i < ln; i++) {
            slot = innerItems[i];
            if (slot.isSlot && slot.getName() == "day") {
                this.daySlot = slot;
                return slot;
            }
        }
        return null;
    },
    
    getDaysInMonth: function(month, year) {
        var daysInMonth = [
                31,
                28,
                31,
                30,
                31,
                30,
                31,
                31,
                30,
                31,
                30,
                31
            ];
        return month == 2 && this.isLeapYear(year) ? 29 : daysInMonth[month - 1];
    },
    
    isLeapYear: function(year) {
        return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
    },
    onDoneButtonTap: function() {
        var me = this,
            oldValue = me._value,
            newValue = me.getValue(true),
            testValue = newValue;
        if (Ext.isDate(newValue)) {
            testValue = newValue.toDateString();
        }
        if (Ext.isDate(oldValue)) {
            oldValue = oldValue.toDateString();
        }
        if (testValue != oldValue) {
            me.ownerField.onPickerChange(me, newValue);
            me.fireEvent('change', me, newValue);
        }
        me.hide();
        Ext.util.InputBlocker.unblockInputs();
    }
});


Ext.define('Ext.layout.Carousel', {
    extend: Ext.layout.Auto,
    alias: 'layout.carousel',
    config: {
        
        visibleChildren: 1,
        
        frontIndex: {
            $value: true,
            lazy: true
        },
        
        animation: true
    },
    vertical: false,
    targetCls: Ext.baseCSSPrefix + 'layout-carousel',
    wrapCls: Ext.baseCSSPrefix + 'layout-carousel-wrap',
    itemCls: Ext.baseCSSPrefix + 'layout-carousel-item',
    singularCls: Ext.baseCSSPrefix + 'layout-carousel-singular',
    destroy: function() {
        var container = this.getContainer();
        Ext.destroy(container.carouselElement, this.activeAnim);
        this.callParent();
    },
    updateContainer: function(container, oldContainer) {
        var me = this;
        me.callParent([
            container,
            oldContainer
        ]);
        container.bodyElement.addCls(me.wrapCls);
        container.carouselElement = container.getRenderTarget().appendChild({
            cls: me.targetCls
        });
        Ext.override(container, {
            privates: {
                getRenderTarget: function() {
                    return this.carouselElement;
                }
            }
        });
    },
    onContainerInitialized: function() {
        var me = this;
        me.callParent();
        
        
        if (!me.frontItem) {
            me.setFrontItem(me.getFrontIndex(), false);
        }
    },
    updateVisibleChildren: function(count) {
        var me = this,
            target = me.getContainer().getRenderTarget(),
            pct, items, item, i, len;
        items = me.getLayoutItems();
        pct = me.calcItemBasis(count) + '%';
        if (items.length > count) {
            target.setStyle('left', '-' + pct);
            target.setStyle('transform', 'translateX(' + pct + ')');
        }
        for (i = 0 , len = items.length; i < len; i++) {
            item = items[i];
            item.el.setStyle('flex-basis', pct);
        }
        target.toggleCls(me.singularCls, count === 1);
    },
    applyFrontIndex: function(itemIdx) {
        var count, index;
        if (typeof itemIdx !== 'number') {
            count = this.getVisibleChildren();
            index = count - 1;
            itemIdx = !index ? index : index % 2 ? Math.floor(index / 2) + 1 : index / 2;
        }
        return itemIdx;
    },
    applyDuration: function(duration) {
        if (typeof duration !== 'number') {
            duration = parseInt(duration, 10) || 500;
        }
        return duration;
    },
    calcItemBasis: function(count) {
        count = count != null ? count : this.getVisibleChildren();
        return count === 1 ? 100 : !(count % 2) ? 100 / count : (100 / count).toFixed(5);
    },
    insertInnerItem: function(item, index) {
        var me = this;
        me.callParent([
            item,
            index
        ]);
        if (index === 0) {
            me.frontItem = item;
        }
        item.el.setStyle('order', index + 1);
        item.el.setStyle('flex-basis', me.calcItemBasis() + '%');
    },
    getLayoutItemCount: function() {
        return this.getLayoutItems().length;
    },
    getLayoutItems: function() {
        return this.getContainer().getInnerItems();
    },
    getItemIndex: function(item) {
        return this.getContainer().innerIndexOf(item);
    },
    shiftIndex: function(index, increment) {
        var count = this.getLayoutItemCount();
        index += increment;
        if (increment < 0) {
            index = index < 0 ? count - 1 : index;
        } else if (increment > 0) {
            index = index >= count ? 0 : index;
        }
        return index;
    },
    getVisibleItems: function() {
        return this.visibleItems;
    },
    getEdgeItem: function(increment) {
        var items = this.getOrderedLayoutItems();
        return increment < 0 ? items[0] : items[items.length - 1];
    },
    getFirstVisibleItem: function() {
        return this.getVisibleItems()[0];
    },
    getLastVisibleItem: function() {
        var items = this.getVisibleItems();
        return items[items.length - 1];
    },
    getFrontItem: function() {
        return this.frontItem;
    },
    getFrontItemIndex: function() {
        return this.getItemIndex(this.getFrontItem());
    },
    getOrderedLayoutItems: function() {
        var items = Ext.Array.clone(this.getLayoutItems());
        return items.sort(this.sortByOrder);
    },
    setFrontItem: function(index, animate) {
        var me = this,
            container = me.getContainer(),
            target = container.getRenderTarget(),
            frontIndex = me.getFrontIndex(),
            visibleChildren = me.getVisibleChildren(),
            items, item, frontItem, oldFrontItem, oldFrontIndex, visibleItems, direction, basis, i, len, ret, deferred;
        items = me.getLayoutItems();
        if (items.length < visibleChildren) {
            return Ext.Deferred.getCachedResolved();
        }
        if (typeof index !== 'number') {
            index = items.indexOf(index);
        }
        basis = me.calcItemBasis();
        target.setStyle('left', '-' + basis + '%');
        oldFrontItem = me.getFrontItem();
        me.frontItem = frontItem = items[index];
        
        
        frontIndex++;
        
        items = items.slice(index).concat(items.slice(0, index));
        oldFrontIndex = items.indexOf(oldFrontItem);
        
        items = items.slice(-frontIndex).concat(items.slice(0, items.length - frontIndex));
        if (animate == null) {
            animate = me.getAnimation();
        }
        if (animate) {
            if (typeof animate === 'boolean') {
                animate = {};
            }
            
            
            direction = oldFrontIndex > -1 && oldFrontIndex <= Math.floor(items.length / 2) ? 1 : -1;
            Ext.destroy(me.activeAnim);
            deferred = new Ext.Deferred();
            ret = deferred.promise;
            me.activeAnim = Ext.Animator.run(Ext.apply({
                element: target,
                to: {
                    transform: {
                        translateX: (basis * direction) + '%'
                    }
                },
                callback: function() {
                    me.orderItems(items);
                    deferred.resolve();
                    me.activeAnim = null;
                }
            }, animate));
        }
        me.visibleItems = visibleItems = [];
        for (i = 0 , len = items.length; i < len; i++) {
            item = items[i];
            
            
            if (i > 0 && i <= visibleChildren) {
                visibleItems.push(item);
            }
            item.$carouselOrder = i + 1;
        }
        if (!animate) {
            me.orderItems(items);
            ret = Ext.Deferred.getCachedResolved();
        }
        visibleItems.sort(me.sortByOrder);
        return ret;
    },
    getMoveItem: function(increment) {
        var index = this.getFrontItemIndex();
        index = this.shiftIndex(index, increment);
        return this.getLayoutItems()[index];
    },
    cancelAnimation: function() {
        Ext.destroy(this.activeAnim);
    },
    move: function(increment, animate) {
        return this.setFrontItem(this.getMoveItem(increment), animate);
    },
    prev: function(animate) {
        return this.move(-1, animate);
    },
    next: function(animate) {
        return this.move(1, animate);
    },
    privates: {
        orderItems: function(items) {
            var len = items.length,
                i, item;
            for (i = 0; i < len; ++i) {
                item = items[i];
                item.el.setStyle('order', item.$carouselOrder + 1);
            }
        },
        sortByOrder: function(a, b) {
            return +a.$carouselOrder - b.$carouselOrder;
        }
    }
});

Ext.define('Ext.panel.DateView', {
    extend: Ext.Widget,
    xtype: 'dateview',
    config: {
        specialDates: null,
        specialDays: null,
        monthOffset: 0
    },
    cachedConfig: {
        captionFormat: null,
        dateCellFormat: null,
        format: null,
        headerLength: null,
        hideCaption: true,
        hideOutside: true,
        startDay: null,
        weekendDays: null
    },
    element: {
        reference: 'element'
    },
    tableTpl: {
        reference: 'tableElement',
        tag: 'table',
        cellspacing: '0',
        cellpadding: '0',
        children: []
    },
    captionTpl: {
        reference: 'captionElement',
        tag: 'caption'
    },
    headTpl: {
        tag: 'thead',
        reference: 'headElement'
    },
    headRowTpl: {
        tag: 'tr'
    },
    headCellTpl: {
        tag: 'th',
        cls: Ext.baseCSSPrefix + 'cell',
        children: [
            {
                tag: 'div',
                cls: Ext.baseCSSPrefix + 'inner ' + Ext.dom.Element.unselectableCls
            }
        ]
    },
    bodyTpl: {
        tag: 'tbody',
        reference: 'bodyElement'
    },
    bodyRowTpl: {
        tag: 'tr'
    },
    bodyCellTpl: {
        tag: 'td',
        cls: Ext.baseCSSPrefix + 'cell',
        tabIndex: -1,
        children: [
            {
                tag: 'div',
                cls: Ext.baseCSSPrefix + 'inner ' + Ext.dom.Element.unselectableCls
            }
        ]
    },
    rows: 6,
    columns: 7,
    cellCls: Ext.baseCSSPrefix + 'cell',
    emptyCls: Ext.baseCSSPrefix + 'empty',
    weekendDayCls: Ext.baseCSSPrefix + 'weekend',
    disabledDayCls: Ext.baseCSSPrefix + 'disabled',
    specialDateCls: Ext.baseCSSPrefix + 'special',
    todayCls: Ext.baseCSSPrefix + 'today',
    outsideCls: Ext.baseCSSPrefix + 'outside',
    prevMonthCls: Ext.baseCSSPrefix + 'prev-month',
    nextMonthCls: Ext.baseCSSPrefix + 'next-month',
    currentMonthCls: Ext.baseCSSPrefix + 'current-month',
    constructor: function(config) {
        this.firstOfMonth = Ext.Date.getFirstDateOfMonth(new Date());
        this.callParent([
            config
        ]);
    },
    initElement: function() {
        var me = this;
        me.callParent();
        me.headCells = me.headElement.query('th');
        me.bodyCells = me.bodyElement.query('td');
        me.cellMap = {};
    },
    getMonth: function() {
        return Ext.Date.add(this.firstOfMonth, Ext.Date.MONTH, this.getMonthOffset());
    },
    getTemplate: function() {
        var me = this,
            table = me.tableTpl,
            headRow = me.headRowTpl,
            headCell = me.headCellTpl,
            bodyRow = me.bodyRowTpl,
            bodyCell = me.bodyCellTpl,
            rows = me.rows,
            columns = me.columns,
            headTpl, bodyTpl, i, len;
        headRow = Ext.apply({
            children: []
        }, headRow);
        bodyRow = Ext.apply({
            children: []
        }, bodyRow);
        for (i = 0 , len = columns; i < len; i++) {
            headRow.children.push(headCell);
            bodyRow.children.push(bodyCell);
        }
        headTpl = Ext.apply({
            children: []
        }, me.headTpl);
        headTpl.children.push(headRow);
        bodyTpl = Ext.apply({
            children: []
        }, me.bodyTpl);
        for (i = 0 , len = rows; i < len; i++) {
            bodyTpl.children.push(bodyRow);
        }
        table.children = [
            me.captionTpl,
            headTpl,
            bodyTpl
        ];
        return [
            table
        ];
    },
    getCellByDate: function(date) {
        return date ? this.cellMap[date.getTime()] : null;
    },
    updateWeekendDays: function() {
        if (!this.isConfiguring) {
            this.refresh();
        }
    },
    updateStartDay: function(dayIndex) {
        var cells = this.headCells,
            weekendDays = this.getWeekendDays(),
            weekendCls = this.weekendDayCls,
            headerLength = this.getHeaderLength(),
            cell, i, len, offsetIdx;
        
        for (i = 0 , len = cells.length; i < len; i++) {
            cell = cells[i];
            offsetIdx = (i + dayIndex) % 7;
            cell.firstChild.innerHTML = Ext.Date.getShortDayName(offsetIdx).substr(0, headerLength);
            Ext.fly(cell).toggleCls(weekendCls, !!weekendDays[offsetIdx]);
        }
    },
    updateSpecialDates: function() {
        if (!this.isConfiguring) {
            this.refresh();
        }
    },
    updateSpecialDays: function() {
        if (!this.isConfiguring) {
            this.refresh();
        }
    },
    applyMonthOffset: function(offset) {
        return !isNaN(offset) ? offset : 0;
    },
    updateMonthOffset: function() {
        this.refresh();
    },
    updateCaptionFormat: function(format) {
        var month = this.getMonth();
        if (month) {
            this.captionElement.setHtml(Ext.Date.format(month, format));
        }
    },
    updateHideCaption: function(hide) {
        this.toggleCls(Ext.baseCSSPrefix + 'hide-caption', hide);
    },
    refresh: function() {
        var me = this,
            ExtDate = Ext.Date,
            cells = me.bodyCells,
            monthStart, startOffset, startDate, startDay, date, cellMap, cell, params, i, len, outPrev, outNext, currentMonth, month;
        
        if (me.refreshing) {
            return;
        }
        me.refreshing = true;
        monthStart = me.getMonth();
        startDay = me.getStartDay();
        startOffset = startDay - monthStart.getDay();
        if (startOffset > 0) {
            startOffset -= 7;
        }
        startDate = ExtDate.add(monthStart, ExtDate.DAY, startOffset);
        cellMap = me.cellMap = {};
        currentMonth = monthStart.getMonth();
        params = {
            today: Ext.Date.clearTime(new Date()),
            weekendDays: me.getWeekendDays(),
            specialDates: me.getSpecialDates(),
            specialDays: me.getSpecialDays(),
            format: me.getFormat(),
            dateCellFormat: me.getDateCellFormat(),
            hideOutside: me.getHideOutside()
        };
        for (i = 0 , len = cells.length; i < len; i++) {
            cell = cells[i];
            date = ExtDate.add(startDate, ExtDate.DAY, i);
            month = date.getMonth();
            outPrev = month < currentMonth;
            outNext = month > currentMonth;
            cellMap[date.getTime()] = cell;
            params.cell = cell;
            params.date = date;
            params.outside = outPrev || outNext;
            params.outsidePrevious = outPrev;
            params.outsideNext = outNext;
            me.refreshCell(params);
        }
        me.captionElement.setHtml(Ext.Date.format(monthStart, me.getCaptionFormat()));
        me.refreshing = false;
    },
    refreshCell: function(params) {
        var me = this,
            cell = params.cell,
            date = params.date,
            dayOfWeek = date.getDay(),
            ms = date.getTime(),
            specialDates = params.specialDates,
            specialDays = params.specialDays,
            cls = [
                me.cellCls
            ],
            formatted = Ext.Date.format(date, params.format),
            empty = params.outside && params.hideOutside,
            html, special, disabled;
        if (!empty) {
            if (params.outsidePrevious) {
                cls.push(me.outsideCls, me.prevMonthCls);
            } else if (params.outsideNext) {
                cls.push(me.outsideCls, me.nextMonthCls);
            } else {
                cls.push(me.currentMonthCls);
                
                if (Ext.Date.isEqual(date, params.today)) {
                    cls.push(me.todayCls);
                }
            }
            if (params.weekendDays[dayOfWeek]) {
                cls.push(me.weekendDayCls);
            }
            if (!special && specialDays) {
                special = specialDays[dayOfWeek];
            }
            if (specialDates) {
                special = specialDates.dates[ms] || (specialDates.re && specialDates.re.test(formatted));
            }
            if (special) {
                cls.push(me.specialDateCls);
            }
        } else {
            cls.push(me.emptyCls);
        }
        disabled = me.getParent().isDateDisabled(date);
        if (!empty && disabled) {
            cls.push(me.disabledDayCls);
        }
        cell.tabIndex = -1;
        if (empty) {
            html = '&#160;';
        } else {
            html = Ext.Date.format(date, params.dateCellFormat);
        }
        cell.firstChild.innerHTML = html;
        if (me.transformCellCls) {
            me.transformCellCls(date, cls);
        }
        cell.className = cls.join(' ');
        
        cell.date = date;
        cell.disabled = disabled;
    },
    ownsDate: function(d) {
        var curr = this.getMonth();
        return d.getFullYear() === curr.getFullYear() && d.getMonth() === curr.getMonth();
    },
    privates: {
        measurePaneSize: function() {
            var el = this.element.first();
            return el.measure('w') + el.getMargin('lr');
        }
    }
});


Ext.define('Ext.panel.Title', {
    extend: Ext.Component,
    xtype: 'paneltitle',
    isPanelTitle: true,
    cachedConfig: {
        
        textAlign: 'left',
        
        iconAlign: 'left',
        
        rotation: 0,
        
        rotateIcon: null
    },
    config: {
        
        text: '',
        
        icon: null,
        
        iconCls: null
    },
    
    weight: -10,
    
    inheritUi: true,
    
    element: {
        reference: 'element',
        cls: Ext.baseCSSPrefix + 'unselectable'
    },
    
    template: [
        {
            reference: 'bodyElement',
            cls: Ext.baseCSSPrefix + 'body-el',
            children: [
                {
                    reference: 'iconElement',
                    cls: Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon'
                },
                {
                    reference: 'textElement',
                    cls: Ext.baseCSSPrefix + 'text-el'
                }
            ]
        }
    ],
    verticalCls: Ext.baseCSSPrefix + 'vertical',
    horizontalCls: Ext.baseCSSPrefix + 'horizontal',
    rotateIconCls: Ext.baseCSSPrefix + 'rotate-icon',
    iconAlignVerticalCls: Ext.baseCSSPrefix + 'icon-align-vertical',
    hasIconCls: Ext.baseCSSPrefix + 'has-icon',
    _textAlignClasses: {
        left: Ext.baseCSSPrefix + 'text-align-left',
        center: Ext.baseCSSPrefix + 'text-align-center',
        right: Ext.baseCSSPrefix + 'text-align-right'
    },
    _iconAlignClasses: {
        top: Ext.baseCSSPrefix + 'icon-align-top',
        right: Ext.baseCSSPrefix + 'icon-align-right',
        bottom: Ext.baseCSSPrefix + 'icon-align-bottom',
        left: Ext.baseCSSPrefix + 'icon-align-left'
    },
    _rotationClasses: {
        90: Ext.baseCSSPrefix + 'rotate-90',
        270: Ext.baseCSSPrefix + 'rotate-270'
    },
    
    classCls: Ext.baseCSSPrefix + 'paneltitle',
    _titleSuffix: '-title',
    afterRender: function() {
        if (Ext.isSafari) {
            this.repaintBodyElement();
        }
        this.callParent();
    },
    updateIcon: function(icon, oldIcon) {
        var me = this,
            iconEl;
        me.syncIconVisibility();
        iconEl = me.iconElement;
        iconEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
    },
    updateIconAlign: function(align, oldAlign) {
        var me = this,
            iconAlignClasses = me._iconAlignClasses,
            el = me.el;
        if (oldAlign) {
            el.removeCls(iconAlignClasses[oldAlign]);
        }
        if (align) {
            el.addCls(iconAlignClasses[align]);
        }
        el.toggleCls(me.iconAlignVerticalCls, align === 'top' || align === 'bottom');
    },
    updateIconCls: function(cls, oldCls) {
        var iconEl = this.iconElement;
        this.syncIconVisibility();
        if (oldCls) {
            iconEl.removeCls(oldCls);
        }
        if (cls) {
            iconEl.addCls(cls);
        }
    },
    updateRotation: function(rotation, oldRotation) {
        var me = this,
            verticalCls = me.verticalCls,
            horizontalCls = me.horizontalCls,
            el = me.el;
        if (oldRotation != 0) {
            el.removeCls(me._rotationClasses[oldRotation]);
        }
        if (rotation == 0) {
            el.replaceCls(verticalCls, horizontalCls);
        } else {
            el.replaceCls(horizontalCls, [
                verticalCls,
                me._rotationClasses[rotation]
            ]);
        }
        if (Ext.isSafari && this.rendered) {
            this.repaintBodyElement();
        }
    },
    updateRotateIcon: function(rotateIcon) {
        this.el.toggleCls(this.rotateIconCls, !!rotateIcon);
    },
    updateText: function(text) {
        var el = this.textElement.dom;
        el.innerHTML = text || '&#160;';
        el.setAttribute('data-title', text);
    },
    updateTextAlign: function(align, oldAlign) {
        var me = this,
            textAlignClasses = me._textAlignClasses;
        if (oldAlign) {
            me.removeCls(textAlignClasses[oldAlign]);
        }
        if (align) {
            me.addCls(textAlignClasses[align]);
        }
    },
    privates: {
        repaintBodyElement: function() {
            var bodyElement = this.bodyElement.dom,
                bodyStyle = bodyElement.style;
            
            
            
            
            
            
            
            
            bodyStyle.width = '-webkit-min-content';
            bodyElement.offsetWidth;
            bodyStyle.width = '';
        },
        syncIconVisibility: function() {
            this.el.toggleCls(this.hasIconCls, !!(this.getIcon() || this.getIconCls()));
        }
    },
    deprecated: {
        '6.5': {
            configs: {
                
                glyph: null
            }
        }
    }
});


Ext.define('Ext.panel.DateTitle', {
    extend: Ext.panel.Title,
    xtype: 'datetitle',
    classCls: Ext.baseCSSPrefix + 'datetitle',
    template: [
        {
            reference: 'bodyElement',
            cls: Ext.baseCSSPrefix + 'body-el',
            children: [
                {
                    reference: 'iconElement',
                    cls: Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon'
                },
                {
                    cls: Ext.baseCSSPrefix + 'text-container-el',
                    children: [
                        {
                            reference: 'yearElement',
                            cls: Ext.baseCSSPrefix + 'year-el'
                        },
                        {
                            reference: 'textElement',
                            cls: Ext.baseCSSPrefix + 'text-el'
                        }
                    ]
                }
            ]
        }
    ],
    config: {
        split: {
            cached: true,
            $value: false
        },
        titleActive: {
            cached: true,
            $value: true
        },
        year: null
    },
    initialize: function() {
        var me = this;
        me.callParent();
        me.yearElement.on('tap', 'onYearTap', me);
        me.textElement.on('tap', 'onTextTap', me);
    },
    updateSplit: function(split) {
        this.yearElement.setDisplayed(split);
        this.toggleCls(Ext.baseCSSPrefix + 'split', split);
    },
    updateTitleActive: function(titleActive) {
        var cls = Ext.baseCSSPrefix + 'inactive';
        this.textElement.toggleCls(cls, !titleActive);
        this.yearElement.toggleCls(cls, titleActive);
    },
    updateYear: function(year) {
        this.yearElement.dom.textContent = year;
    },
    privates: {
        onTextTap: function(e) {
            this.fireEvent('titletap', this, e);
        },
        onYearTap: function(e) {
            this.fireEvent('yeartap', this, e);
        }
    }
});


Ext.define('Ext.panel.YearPicker', {
    extend: Ext.dataview.BoundList,
    xtype: 'yearpicker',
    classCls: Ext.baseCSSPrefix + 'yearpicker',
    config: {
        defaultOffset: 100,
        end: undefined,
        start: undefined
    },
    itemConfig: {
        ui: 'yearpicker',
        tools: null
    },
    itemTpl: '{year}',
    pinFooters: false,
    pinHeaders: false,
    scrollToTopOnRefresh: false,
    initialize: function() {
        this.callParent();
        this.rebuildStore();
    },
    focusYear: function(year) {
        var me = this,
            rec = me.getStore().getById(year),
            item = me.mapToItem(rec),
            scrollable = me.getScrollable(),
            y = scrollable.getEnsureVisibleXY(item.element, {
                align: {
                    y: 'center?'
                }
            }).y;
        scrollable.scrollTo(null, y, false);
        me.getNavigationModel().setLocation(rec);
        me.select(rec);
    },
    onChildTap: function(location) {
        var rec = location.record;
        if (rec) {
            location.event.stopEvent();
            this.fireEvent('yeartap', this, rec.id);
        }
    },
    applyEnd: function(end) {
        if (!end) {
            end = (new Date()).getFullYear() + this.getDefaultOffset();
        }
        return end;
    },
    updateEnd: function() {
        this.rebuildStore();
    },
    applyStart: function(start) {
        if (!start) {
            start = (new Date()).getFullYear() - this.getDefaultOffset();
        }
        return start;
    },
    updateStart: function() {
        this.rebuildStore();
    },
    privates: {
        forceRefreshOnRender: true,
        rebuildStore: function() {
            var me = this,
                start = me.getStart(),
                end = me.getEnd(),
                store = me.getStore(),
                data, i;
            if (me.isConfiguring) {
                return;
            }
            if (!store) {
                store = new Ext.data.Store({
                    autoDestroy: true
                });
                me.setStore(store);
            }
            data = [];
            for (i = start; i <= end; ++i) {
                data.push({
                    id: i,
                    year: i
                });
            }
            store.loadData(data);
        }
    }
});


Ext.define('Ext.panel.Date', {
    extend: Ext.Panel,
    xtype: 'datepanel',
    config: {
        
        animation: true,
        
        autoConfirm: false,
        
        captionFormat: {
            $value: 'F Y',
            cached: true
        },
        
        dateCellFormat: {
            $value: 'j',
            cached: true
        },
        
        disabledDates: null,
        
        disabledDays: null,
        
        focusableDate: null,
        
        format: {
            $value: Ext.Date.defaultFormat,
            cached: true
        },
        
        handler: null,
        
        headerFormat: {
            $value: 'D, M j Y',
            cached: true
        },
        
        headerLength: 1,
        
        hideCaptions: true,
        
        hideOutside: false,
        
        maxDate: null,
        
        minDate: null,
        
        navigationPosition: 'header',
        
        nextText: 'Next Month (Control+Right)',
        
        panes: 1,
        
        prevText: 'Previous Month (Control+Left)',
        
        selectOnNavigate: true,
        
        showAfterMaxDate: false,
        
        showBeforeMinDate: false,
        
        showFooter: null,
        
        showTodayButton: null,
        
        specialDates: null,
        
        specialDays: null,
        
        splitTitle: false,
        
        startDay: {
            $value: Ext.Date.firstDayOfWeek,
            cached: true
        },
        
        titleAnimation: null,
        
        value: undefined,
        
        weekendDays: {
            $value: Ext.Date.weekendDays,
            cached: true
        },
        
        yearPicker: {
            lazy: true,
            $value: {}
        },
        
        yearPickerDefaults: null
    },
    
    
    focusable: true,
    tabIndex: 0,
    border: false,
    mouseWheelBuffer: 500,
    autoSize: null,
    headerCls: Ext.baseCSSPrefix + 'datepanelheader',
    titleCls: Ext.baseCSSPrefix + 'datetitle',
    toolCls: [
        Ext.baseCSSPrefix + 'paneltool',
        Ext.baseCSSPrefix + 'datepaneltool'
    ],
    header: {
        title: {
            xtype: 'datetitle'
        }
    },
    tools: {
        previousMonth: {
            reference: 'navigatePrevMonth',
            iconCls: 'x-fa fa-angle-left',
            cls: Ext.baseCSSPrefix + 'left-year-tool ',
            weight: -100,
            increment: -1,
            focusable: false,
            tabIndex: null,
            forceTabIndex: true,
            listeners: {
                click: 'onMonthToolClick'
            }
        },
        previousYear: {
            reference: 'navigatePrevYear',
            iconCls: 'x-fa fa-angle-double-left',
            cls: Ext.baseCSSPrefix + 'left-month-tool',
            weight: -90,
            increment: -12,
            focusable: false,
            tabIndex: null,
            forceTabIndex: true,
            listeners: {
                click: 'onMonthToolClick'
            }
        },
        nextYear: {
            reference: 'navigateNextYear',
            iconCls: 'x-fa fa-angle-double-right',
            cls: Ext.baseCSSPrefix + 'right-month-tool',
            weight: 90,
            increment: 12,
            focusable: false,
            tabIndex: null,
            forceTabIndex: true,
            listeners: {
                click: 'onMonthToolClick'
            }
        },
        nextMonth: {
            reference: 'navigateNextMonth',
            iconCls: 'x-fa fa-angle-right',
            cls: Ext.baseCSSPrefix + 'right-year-tool',
            weight: 100,
            increment: 1,
            focusable: false,
            tabIndex: null,
            forceTabIndex: true,
            listeners: {
                click: 'onMonthToolClick'
            }
        }
    },
    keyMapTarget: 'bodyElement',
    
    
    keyMap: {
        '*+LEFT': 'onLeftArrowKey',
        '*+RIGHT': 'onRightArrowKey',
        UP: 'onUpArrowKey',
        DOWN: 'onDownArrowKey',
        "*+PAGE_UP": 'onPageUpKey',
        "*+PAGE_DOWN": 'onPageDownKey',
        HOME: 'onHomeKey',
        END: 'onEndKey',
        ENTER: 'onEnterKey',
        SPACE: 'onSpaceKey',
        BACKSPACE: 'onBackspaceKey',
        "*+TAB": 'onTabKey',
        scope: 'this'
    },
    paneXtype: 'dateview',
    classCls: Ext.baseCSSPrefix + 'datepanel',
    layout: {
        type: 'carousel',
        animation: {
            duration: 100
        }
    },
    defaultListenerScope: true,
    referenceHolder: true,
    buttonToolbar: {
        enableFocusableContainer: false,
        cls: Ext.baseCSSPrefix + 'datepanel-footer',
        reference: 'footer'
    },
    buttons: {
        footerTodayButton: {
            text: 'Today',
            tabIndex: -1,
            hidden: true,
            weight: -20,
            handler: 'onTodayButtonClick',
            reference: 'footerTodayButton'
        },
        spacer: {
            xtype: 'component',
            weight: -10,
            flex: 1
        },
        ok: {
            tabIndex: -1,
            handler: 'onOkButtonClick'
        },
        cancel: {
            tabIndex: -1,
            handler: 'onCancelButtonClick'
        }
    },
    initialize: function() {
        var me = this,
            value = me.getValue();
        me.callParent();
        me.setToolText('navigatePrevMonth', me.getPrevText());
        me.setToolText('navigateNextMonth', me.getNextText());
        me.bodyElement.on({
            click: {
                delegate: me.cellSelector,
                fn: 'onDateClick'
            },
            focus: 'onBodyFocus',
            
            
            
            
            wheel: Ext.Function.createThrottled(me.onMouseWheel, me.mouseWheelBuffer),
            scope: me
        });
        
        me.getShowFooter();
        me.preventAnim = true;
        me.setFocusableDate(value);
        me.preventAnim = false;
        me.setTitleByDate(value);
        Ext.fly(me.getCellByDate(value)).addCls(me.selectedCls);
    },
    onRender: function() {
        this.callParent();
        this.measurePaneSize();
    },
    doDestroy: function() {
        var me = this;
        Ext.destroy(me.animTitle, me.animBody);
        me.callParent();
    },
    focusDate: function(date) {
        var me = this;
        me.doFocus = true;
        me.setFocusableDate(date);
        me.doFocus = false;
    },
    updateAnimation: function(animate) {
        this.getLayout().setAnimation(animate);
    },
    updateAutoConfirm: function(autoConfirm) {
        var me = this;
        me.getButtons();
        if (!autoConfirm) {
            me.setShowFooter(true);
        } else {
            me.setShowFooter(me.initialConfig.showFooter);
        }
    },
    updateCaptionFormat: function(format) {
        this.broadcastConfig('captionFormat', format);
    },
    updateDateCellFormat: function(format) {
        this.broadcastConfig('dateCellFormat', format);
    },
    applyDisabledDates: function(dates) {
        if (!dates) {
            return dates;
        }
        var cfg = {
                dates: {}
            },
            re = [],
            item, i, len;
        if (dates instanceof RegExp) {
            cfg.re = dates;
        } else {
            if (!Ext.isArray(dates)) {
                dates = [
                    dates
                ];
            }
            for (i = 0 , len = dates.length; i < len; i++) {
                item = dates[i];
                if (item instanceof Date) {
                    item = Ext.Date.clearTime(item);
                    cfg.dates[item.getTime()] = true;
                } else if (item instanceof RegExp) {
                    re.push(item.source);
                } else {
                    re.push(Ext.String.escapeRegex(item));
                }
            }
            if (re.length) {
                cfg.re = new RegExp('(?:' + re.join('|') + ')');
            }
        }
        return cfg;
    },
    updateDisabledDates: function() {
        this.refreshPanes();
    },
    applyDisabledDays: function(days) {
        return days ? Ext.Array.toMap(days) : days;
    },
    updateDisabledDays: function() {
        this.refreshPanes();
    },
    updateFormat: function(format) {
        this.broadcastConfig('format', format);
    },
    updateHeader: function(header, oldHeader) {
        this.callParent([
            header,
            oldHeader
        ]);
        header.getTitle().on({
            scope: this,
            yeartap: 'onYearTitleTap',
            titletap: 'onTitleTap'
        });
    },
    applyMaxDate: function(date) {
        if (typeof date === 'string') {
            date = Ext.Date.parse(date, this.getFormat());
        }
        return date;
    },
    updateMaxDate: function() {
        this.refreshPanes();
    },
    applyMinDate: function(date) {
        if (typeof date === 'string') {
            date = Ext.Date.parse(date, this.getFormat());
        }
        return date;
    },
    updateMinDate: function() {
        this.refreshPanes();
    },
    updateNavigationPosition: function(pos) {
        var me = this,
            toolList = me.toolList,
            len = toolList.length,
            isHeader = pos === 'header',
            ct = isHeader ? me.toolCt : me.getHeader(),
            tools, i, c;
        if (isHeader && me.isConfiguring) {
            return;
        }
        me.getTools();
        tools = [];
        for (i = 0; i < len; ++i) {
            c = me.lookup(toolList[i]);
            if (c) {
                tools.push(c);
                ct.remove(c, false);
                c.toggleCls(me.toolCls, isHeader);
            }
        }
        me.toolCt = Ext.destroy(me.toolCt);
        if (pos === 'header') {
            me.getHeader().add(tools);
        } else {
            tools.push({
                xtype: 'component',
                flex: 1,
                weight: 0
            });
            me.toolCt = me.add({
                xtype: 'container',
                cls: Ext.baseCSSPrefix + 'navigation-tools',
                defaultType: 'tool',
                weighted: true,
                layout: 'hbox',
                
                bottom: 'auto',
                items: tools
            });
        }
    },
    updateNextText: function(text) {
        this.setToolText('navigateNextMonth', text);
    },
    updatePrevText: function(text) {
        this.setToolText('navigatePrevMonth', text);
    },
    
    applyPanes: function(count) {
        if (count < 1) {
            Ext.raise("Cannot configure less than 1 pane for Calendar picker");
        }
        return count;
    },
    
    updatePanes: function(count) {
        var me = this;
        me.getLayout().setVisibleChildren(count);
        me.initPanes(0);
        me.singlePane = count === 1;
        me.toggleCls(Ext.baseCSSPrefix + 'single', me.singlePane);
    },
    updateShowFooter: function(showFooter) {
        this.lookup('footer').setHidden(!showFooter);
        this.getShowTodayButton();
    },
    updateShowTodayButton: function(showButton) {
        var footerBtn;
        this.getButtons();
        footerBtn = this.lookup('footerTodayButton');
        if (footerBtn) {
            footerBtn.setHidden(!showButton);
        }
    },
    applySpecialDates: function(dates) {
        return this.applyDisabledDates(dates);
    },
    updateSpecialDates: function(cfg) {
        this.broadcastConfig('specialDates', cfg);
    },
    applySpecialDays: function(days) {
        return days ? Ext.Array.toMap(days) : days;
    },
    updateSpecialDays: function(daysMap) {
        this.broadcastConfig('specialDays', daysMap);
    },
    updateSplitTitle: function(splitTitle) {
        this.getHeader().getTitle().setSplit(splitTitle);
    },
    updateStartDay: function(day) {
        this.broadcastConfig('startDay', day);
    },
    applyValue: function(date) {
        if (typeof date === 'string') {
            date = Ext.Date.parse(date, this.getFormat());
        }
        
        
        else if (!date) {
            date = new Date();
        }
        return Ext.isDate(date) ? Ext.Date.clearTime(date, true) : null;
    },
    updateValue: function(value, oldValue) {
        var me = this,
            handler = me.getHandler(),
            selectedCls = me.selectedCls,
            cell;
        if (oldValue) {
            cell = me.getCellByDate(oldValue);
            if (cell) {
                Ext.fly(cell).removeCls(selectedCls);
            }
        }
        if (!me.isConfiguring) {
            if (me.hasFocus) {
                me.focusDate(value);
            } else {
                me.setFocusableDate(value);
            }
            cell = me.getCellByDate(value);
            if (cell) {
                Ext.fly(cell).addCls(selectedCls);
            }
            me.setTitleByDate(value);
            me.fireEvent('change', me, value, oldValue);
            if (handler) {
                Ext.callback(handler, me.scope, [
                    me,
                    value,
                    oldValue
                ]);
            }
        }
    },
    applyWeekendDays: function(days) {
        return Ext.Array.toMap(days);
    },
    updateWeekendDays: function(daysMap) {
        this.broadcastConfig('weekendDays', daysMap);
    },
    applyYearPicker: function(yearPicker, oldYearPicker) {
        return Ext.updateWidget(oldYearPicker, yearPicker, this, 'createYearPicker', 'yearPickerDefaults');
    },
    updateYearPicker: function(yearPicker) {
        if (yearPicker) {
            this.add(yearPicker);
        }
    },
    replacePanes: function(increment, animate) {
        var me = this,
            panes, cb, direction, ret;
        if (me.destroying || me.destroyed) {
            return;
        }
        panes = me.getLayout().getVisibleItems();
        cb = function() {
            var pane, offset, j, jlen;
            for (j = 0 , jlen = panes.length; j < jlen; j++) {
                pane = panes[j];
                offset = pane.getMonthOffset();
                pane.setMonthOffset(offset + increment);
            }
        };
        if (animate == null) {
            animate = me.getAnimation();
        }
        if (animate) {
            direction = increment < 0 ? 'up' : 'down';
            ret = me.animateVertical(me.carouselElement, direction, 0, cb, 'animBody');
        } else {
            cb();
            ret = Ext.Deferred.getCachedResolved();
        }
        return ret;
    },
    initPanes: function(offset) {
        var me = this,
            count = me.getPanes() + 2,
            panes = [],
            oldPanes, index, center, i;
        index = count - 1;
        center = !index ? index : index % 2 ? Math.floor(index / 2) + 1 : Math.floor(index / 2);
        for (i = 0; i < count; i++) {
            panes.push(me.getPaneTemplate((i + offset) - center));
        }
        oldPanes = me.getInnerItems();
        for (i = 0; i < oldPanes.length; i++) {
            me.remove(oldPanes[i], true);
        }
        me.add(panes);
        me.getLayout().setFrontItem(center, false);
    },
    getPaneByDate: function(date) {
        var me = this,
            panes = me.getInnerItems(),
            month, pane, i, len;
        month = Ext.Date.getFirstDateOfMonth(date);
        for (i = 0 , len = panes.length; i < len; i++) {
            pane = panes[i];
            if (Ext.Date.isEqual(pane.getMonth(), month)) {
                return pane;
            }
        }
        return null;
    },
    getCellByDate: function(date) {
        var pane = this.getPaneByDate(date);
        return pane ? pane.getCellByDate(date) : null;
    },
    updateCellTabIndex: function(date, tabIndex) {
        var cell = date && this.getCellByDate(date);
        if (cell) {
            Ext.fly(cell).setTabIndex(tabIndex);
        }
        return cell;
    },
    canSwitchTo: function(date, offset) {
        var me = this,
            boundary, prevent;
        if (offset < 0) {
            boundary = me.getMinDate();
            prevent = !me.getShowBeforeMinDate();
            if (boundary && prevent) {
                if (date.getTime() < Ext.Date.getFirstDateOfMonth(boundary).getTime()) {
                    return false;
                }
            }
        } else if (offset > 0) {
            boundary = me.getMaxDate();
            prevent = !me.getShowAfterMaxDate();
            if (boundary && prevent) {
                if (date.getTime() > Ext.Date.getLastDateOfMonth(boundary).getTime()) {
                    return false;
                }
            }
        }
        return true;
    },
    navigateTo: function(date, animate) {
        var me = this,
            layout = me.getLayout(),
            month, increment, boundary, prevent;
        
        
        
        
        
        
        if (date.getTime() < (month = layout.getFirstVisibleItem().getMonth()).getTime()) {
            boundary = month;
        } else if (date.getTime() > (month = layout.getLastVisibleItem().getMonth()).getTime()) {
            boundary = month;
        } else {
            boundary = date;
        }
        increment = (date.getFullYear() * 12 + date.getMonth()) - (boundary.getFullYear() * 12 + boundary.getMonth());
        if (increment < 0) {
            boundary = me.getMinDate();
            prevent = !me.getShowBeforeMinDate();
            if (boundary && prevent) {
                if (date.getTime() < Ext.Date.getFirstDateOfMonth(boundary).getTime()) {
                    increment = 0;
                }
            }
        } else if (increment > 0) {
            boundary = me.getMaxDate();
            prevent = !me.getShowAfterMaxDate();
            if (boundary && prevent) {
                if (date.getTime() > Ext.Date.getLastDateOfMonth(boundary).getTime()) {
                    increment = 0;
                }
            }
        }
        return me.navigateByIncrement(increment, animate, 0);
    },
    switchPanes: function(increment, animate) {
        var me = this,
            layout = me.getLayout(),
            edgePane, pane;
        edgePane = increment < 0 ? layout.getFirstVisibleItem() : layout.getLastVisibleItem();
        pane = layout.getEdgeItem(increment);
        pane.setMonthOffset(edgePane.getMonthOffset() + increment);
        return layout.move(increment, animate);
    },
    onMonthToolClick: function(tool) {
        var me = this,
            panes = me.getInnerItems(),
            D = Ext.Date,
            increment = tool.increment,
            date = D.add(me.getFocusableDate(), D.MONTH, increment),
            hasFocus = me.hasFocus,
            index, pane, month;
        index = me.getCenterIndex();
        pane = panes[index];
        month = D.add(pane.getMonth(), D.MONTH, increment);
        if (!me.canSwitchTo(month, increment)) {
            return;
        }
        me.navIncrement = me.singlePane ? 0 : increment;
        if (hasFocus || me.getSelectOnNavigate()) {
            me.setValue(date);
        } else {
            me.doFocus = hasFocus;
            me.setFocusableDate(date);
            me.doFocus = false;
        }
        me.navIncrement = 0;
    },
    onDateClick: function(e) {
        var me = this,
            cell = e.getTarget(me.cellSelector, me.bodyElement),
            date = cell && cell.date,
            focus = true,
            disabled = cell && cell.disabled;
        
        if (!date || me.getDisabled()) {
            return;
        }
        if (!disabled) {
            me.setValue(date);
            if (me.getAutoConfirm()) {
                
                
                
                if (e.pointerType === 'touch') {
                    e.preventDefault();
                }
                focus = false;
                me.fireEvent('select', me, date);
            }
        }
        if (focus) {
            
            
            
            me.focusDate(date);
        }
    },
    onMouseWheel: function(e) {
        var dy = e.browserEvent.deltaY;
        if (dy && !this.pickerVisible) {
            this.onMonthToolClick({
                increment: Math.sign(dy)
            });
        }
    },
    onOkButtonClick: function() {
        this.setValue(this.getFocusableDate());
    },
    onCancelButtonClick: function() {
        this.fireEventArgs('tabout', [
            this
        ]);
    },
    onTodayButtonClick: function() {
        var me = this,
            offset;
        offset = me.getLayout().getFrontItem().getMonthOffset();
        if (offset !== 0) {
            
            if (Math.abs(offset) === 1) {
                me.switchPanes(-offset);
            } else {
                me.replacePanes(-offset);
            }
        }
        me.setValue(Ext.Date.clearTime(new Date()));
    },
    getFocusEl: function() {
        if (!this.initialized) {
            return null;
        }
        return this.getCellByDate(this.getFocusableDate());
    },
    onLeftArrowKey: function(e) {
        this.walkCells(e.target.date, e.ctrlKey ? Ext.Date.MONTH : Ext.Date.DAY, -1);
        
        
        
        e.preventDefault();
    },
    onRightArrowKey: function(e) {
        this.walkCells(e.target.date, e.ctrlKey ? Ext.Date.MONTH : Ext.Date.DAY, 1);
        e.preventDefault();
    },
    onUpArrowKey: function(e) {
        this.walkCells(e.target.date, Ext.Date.DAY, -7);
        e.preventDefault();
    },
    onDownArrowKey: function(e) {
        this.walkCells(e.target.date, Ext.Date.DAY, 7);
        e.preventDefault();
    },
    onPageUpKey: function(e) {
        var unit = e.ctrlKey || e.shiftKey ? Ext.Date.YEAR : Ext.Date.MONTH;
        this.walkCells(e.target.date, unit, -1);
        e.preventDefault();
    },
    onPageDownKey: function(e) {
        var unit = e.ctrlKey || e.shiftKey ? Ext.Date.YEAR : Ext.Date.MONTH;
        this.walkCells(e.target.date, unit, 1);
        e.preventDefault();
    },
    onHomeKey: function(e) {
        this.walkCells(Ext.Date.getFirstDateOfMonth(e.target.date));
        e.preventDefault();
    },
    onEndKey: function(e) {
        this.walkCells(Ext.Date.getLastDateOfMonth(e.target.date));
        e.preventDefault();
    },
    onBackspaceKey: function(e) {
        this.walkCells(new Date());
        e.preventDefault();
    },
    onEnterKey: function(e) {
        var target = e.target,
            date = target && target.date;
        if (date && !target.disabled) {
            this.setValue(date);
            this.fireEvent('select', this, target.date);
        }
    },
    onSpaceKey: function(e) {
        this.onEnterKey(e);
        
        e.preventDefault();
    },
    onTabKey: function(e) {
        
        
        
        
        
        this.handleTabKey(e);
        
        return true;
    },
    handleTabKey: function(e) {
        var me = this,
            target = e.target,
            picker = me.pickerField;
        
        
        if (!me.getDisabled() && me.getAutoConfirm() && target && target.date && !target.disabled) {
            me.setValue(target.date);
            
            
            if (picker && picker.isEditorComponent) {
                e.preventDefault();
            }
        } else 
        
        {
            me.fireEventArgs('tabout', [
                me
            ]);
        }
    },
    walkCells: function(date, unit, increment) {
        var me = this,
            newDate;
        if (!me.getDisabled()) {
            date = me.getFocusableDate();
            newDate = unit ? Ext.Date.add(date, unit, increment) : date;
            if (me.isDateDisabled(newDate)) {
                me.focusDate(newDate);
            } else {
                me.setValue(newDate);
            }
        }
    },
    onBodyFocus: function(e) {
        var me = this,
            date = me.getFocusableDate(),
            cell = me.getCellByDate(date);
        
        if (!cell) {
            me.navigateTo(date, false);
        }
        cell = me.updateCellTabIndex(date, me.getTabIndex());
        cell.focus();
    },
    getTabIndex: function() {
        
        
        return this.getConfig('tabIndex', true);
    },
    getFocusClsEl: function() {
        return this.bodyElement;
    },
    onFocusEnter: function(e) {
        if (this.bodyElement.contains(e.target)) {
            this.onFocus(e);
        }
        this.callParent([
            e
        ]);
    },
    onFocusLeave: function(e) {
        this.onBlur(e);
        this.callParent([
            e
        ]);
    },
    privates: {
        cellSelector: '.' + Ext.baseCSSPrefix + 'cell',
        clonedCls: Ext.baseCSSPrefix + 'cloned',
        lastNavigate: 0,
        hideFocusCls: Ext.baseCSSPrefix + 'hide-focus',
        selectedCls: Ext.baseCSSPrefix + 'selected',
        toolList: [
            'navigatePrevMonth',
            'navigatePrevYear',
            'navigateNextYear',
            'navigateNextMonth'
        ],
        paneWidthMap: {},
        pickerVisible: false,
        applyFocusableDate: function(date) {
            var me = this,
                D = Ext.Date,
                boundary;
            
            
            if (date) {
                
                
                date = D.clearTime(date || new Date());
                if ((boundary = me.getMinDate()) && !me.getShowBeforeMinDate() && date.getTime() < boundary.getTime()) {
                    date = boundary;
                } else if ((boundary = me.getMaxDate()) && !me.getShowAfterMaxDate() && date.getTime() > boundary.getTime()) {
                    date = boundary;
                }
            }
            return date;
        },
        updateFocusableDate: function(date, oldDate) {
            var me = this,
                focus = me.doFocus,
                layout = me.getLayout(),
                cls = me.hideFocusCls,
                increment = me.navIncrement,
                visibleItems, toPane, anim, navigate, oldCell, p;
            if (me.destroying || me.destroyed) {
                return;
            }
            if (oldDate) {
                oldCell = me.getCellByDate(oldDate);
                me.updateCellTabIndex(oldDate, -1);
            }
            if (date) {
                toPane = me.getPaneByDate(date);
                if (!me.preventAnim) {
                    anim = me.getAnimation();
                }
                visibleItems = layout.getVisibleItems();
                
                
                me.lastNavigate = navigate = Date.now();
                if (!increment && (!anim || visibleItems.indexOf(toPane) > -1)) {
                    me.navigateTo(date, false);
                    if (focus) {
                        me.getCellByDate(date).focus();
                    }
                } else {
                    
                    if (oldCell) {
                        Ext.fly(oldCell).addCls(cls);
                    }
                    p = increment ? me.navigateByIncrement(increment) : me.navigateTo(date);
                    p.then(function() {
                        oldCell = me.getCellByDate(oldDate);
                        if (oldCell) {
                            Ext.fly(oldCell).removeCls(cls);
                        }
                        
                        if (focus && me.lastNavigate === navigate) {
                            me.getCellByDate(date).focus();
                        }
                    });
                }
                me.updateCellTabIndex(date, me.getTabIndex());
            }
        },
        animateVertical: function(el, direction, offset, beforeFn, prop) {
            var me = this,
                clone = el.dom.cloneNode(true),
                ret = new Ext.Deferred();
            clone.id = '';
            Ext.fly(clone).addCls(me.clonedCls);
            el.parent().appendChild(clone);
            if (beforeFn) {
                beforeFn();
            }
            Ext.destroy(me[prop]);
            me[prop] = Ext.Animator.run([
                {
                    offset: offset,
                    type: 'slide',
                    direction: direction,
                    element: el
                },
                {
                    offset: offset,
                    type: 'slideOut',
                    direction: direction,
                    element: clone,
                    callback: function() {
                        Ext.fly(clone).destroy();
                        me[prop] = null;
                        ret.resolve();
                    }
                }
            ]);
            return ret.promise;
        },
        broadcastConfig: function(config, value) {
            if (this.isConfiguring) {
                return;
            }
            var panes = this.getInnerItems(),
                setter, pane, i, len;
            setter = Ext.Config.get(config).names.set;
            for (i = 0 , len = panes.length; i < len; i++) {
                pane = panes[i];
                if (pane[setter]) {
                    pane[setter](value);
                }
            }
        },
        createYearPicker: function(config) {
            return Ext.apply({
                xtype: 'yearpicker',
                hidden: true,
                top: 0,
                right: 0,
                bottom: 0,
                left: 0,
                listeners: {
                    yeartap: 'onYearPickerTap'
                }
            }, config);
        },
        getCenterIndex: function() {
            var count = this.getPanes(),
                index = count - 1;
            return !index ? index : index % 2 ? Math.floor(index / 2) + 1 : Math.floor(index / 2);
        },
        getPaneTemplate: function(offset) {
            var me = this;
            return {
                xtype: me.paneXtype,
                monthOffset: offset,
                hideOutside: me.getHideOutside(),
                hideCaption: me.getHideCaptions(),
                startDay: me.getStartDay(),
                weekendDays: me.getWeekendDays(),
                specialDates: me.getSpecialDates(),
                specialDays: me.getSpecialDays(),
                format: me.getFormat(),
                captionFormat: me.getCaptionFormat(),
                dateCellFormat: me.getDateCellFormat(),
                headerLength: me.getHeaderLength(),
                transformCellCls: me.transformCellCls
            };
        },
        getPositionedItemTarget: function() {
            return this.bodyElement;
        },
        isDateDisabled: function(date) {
            var me = this,
                ms = date.getTime(),
                minDate = me.getMinDate(),
                maxDate = me.getMaxDate(),
                disabled = false,
                disabledDays, disabledDates, formatted, re;
            disabled = (minDate && ms < minDate.getTime()) || (maxDate && ms > maxDate.getTime());
            if (!disabled) {
                disabledDays = me.getDisabledDays();
                if (disabledDays) {
                    disabled = disabledDays[date.getDay()];
                }
            }
            if (!disabled) {
                disabledDates = me.getDisabledDates();
                if (disabledDates) {
                    disabled = disabledDates.dates[ms];
                    re = disabledDates.re;
                    if (!disabled && re) {
                        formatted = Ext.Date.format(date, me.getFormat());
                        disabled = re.test(formatted);
                    }
                }
            }
            return !!disabled;
        },
        measurePaneSize: function() {
            var me = this,
                count = me.getPanes(),
                ui = me.getUi() || 'default',
                map = me.paneWidthMap,
                borderWidth;
            
            
            if (!map.hasOwnProperty(ui)) {
                map[ui] = this.getLayout().getFrontItem().measurePaneSize();
            }
            borderWidth = me.el.getBorderWidth('lr');
            me.setWidth(borderWidth + count * map[ui]);
        },
        navigateByIncrement: function(increment, animate) {
            var ret;
            if (Math.abs(increment) === 1) {
                ret = this.switchPanes(increment, animate);
            } else {
                if (increment !== 0) {
                    ret = this.replacePanes(increment, animate);
                } else if (!animate) {
                    this.getLayout().cancelAnimation();
                    ret = Ext.Deferred.getCachedResolved();
                }
            }
            return ret;
        },
        onTitleTap: function() {
            var visible;
            if (this.getSplitTitle()) {
                visible = false;
            } else {
                visible = !this.pickerVisible;
            }
            this.toggleYearPicker(visible);
        },
        onYearPickerTap: function(picker, year) {
            this.toggleYearPicker(false);
            var d = Ext.Date.clone(this.getFocusableDate());
            d.setFullYear(year);
            this.setValue(d);
        },
        onYearTitleTap: function() {
            this.toggleYearPicker(!this.pickerVisible);
        },
        refreshPanes: function() {
            if (this.isConfiguring) {
                return;
            }
            var panes = this.getPanes(),
                len = panes.length,
                i;
            for (i = 0; i < len; ++i) {
                panes[i].refresh();
            }
        },
        setTitleByDate: function(date) {
            var me = this,
                prev = me.lastTitleDate,
                anim;
            if (prev && prev.getTime() === date.getTime()) {
                anim = false;
            }
            me.setTitleText(Ext.Date.format(date, me.getHeaderFormat()), date, prev, anim);
            me.lastTitleDate = date;
        },
        setTitleText: function(text, date, oldDate, animate) {
            var me = this,
                title, direction, titleAnim;
            if (me.destroying || me.destroyed) {
                return;
            }
            if (animate === undefined) {
                titleAnim = me.getTitleAnimation();
                if (titleAnim !== null) {
                    animate = titleAnim;
                } else {
                    animate = me.getAnimation();
                }
            }
            animate = me.rendered ? animate : false;
            title = me.getHeader().getTitle();
            if (animate) {
                
                if (me.getSplitTitle()) {
                    Ext.raise('Animation is not supported with title split');
                }
                
                direction = (oldDate || date).getTime() < date.getTime() ? 'bottom' : 'top';
                me.animateVertical(title.textElement, direction, '150%', function() {
                    title.setText(text);
                }, 'animTitle');
            } else {
                if (me.getSplitTitle()) {
                    title.setYear(date.getFullYear());
                    title.setText(text);
                } else {
                    title.setText(text);
                }
            }
        },
        setToolText: function(type, text) {
            var tool = this.lookup(type);
            if (tool) {
                tool.setTooltip(text);
            }
        },
        toggleYearPicker: function(visible) {
            var me = this,
                picker = me.getYearPicker();
            if (picker) {
                if (me.getSplitTitle()) {
                    me.getHeader().getTitle().setTitleActive(!visible);
                }
                picker.setHidden(!visible);
                if (visible) {
                    picker.focusYear(me.getFocusableDate().getFullYear());
                }
                me.pickerVisible = visible;
            }
        }
    }
});


Ext.define('Ext.field.Date', {
    extend: Ext.field.Picker,
    alternateClassName: [
        'Ext.form.DatePicker',
        'Ext.field.DatePicker'
    ],
    xtype: [
        'datefield',
        'datepickerfield'
    ],
    
    config: {
        
        
        destroyPickerOnHide: false,
        
        dateFormat: '',
        
        minDate: null,
        
        maxDate: null,
        triggers: {
            expand: {
                type: 'date'
            }
        }
    },
    classCls: Ext.baseCSSPrefix + 'datepickerfield',
    matchFieldWidth: false,
    
    minDateMessage: "The date in this field must be equal to or after {0}",
    
    maxDateMessage: "The date in this field must be equal to or before {0}",
    floatedPicker: {
        xtype: 'datepanel',
        autoConfirm: true,
        floated: true,
        listeners: {
            tabout: 'onTabOut',
            select: 'onPickerChange',
            scope: 'owner'
        },
        keyMap: {
            ESC: 'onTabOut',
            scope: 'owner'
        }
    },
    edgePicker: {
        xtype: 'datepicker',
        cover: true
    },
    parseValidator: 'date',
    applyValue: function(value, oldValue) {
        if (!(value || value === 0)) {
            value = null;
        }
        value = this.callParent([
            value,
            oldValue
        ]);
        if (value) {
            if (this.isConfiguring) {
                this.originalValue = value;
            }
            
            
            
            if (Ext.isDate(value) && Ext.isDate(oldValue) && value.getTime() === oldValue.getTime()) {
                return;
            }
        }
        return value;
    },
    updateValue: function(value, oldValue) {
        var picker = this._picker;
        if (picker && picker.isPicker && Ext.isDate(value)) {
            this.updatePickerValue(picker, value);
        }
        this.callParent([
            value,
            oldValue
        ]);
    },
    updatePickerValue: function(picker, value) {
        picker.setValue(value);
    },
    applyInputValue: function(value, oldValue) {
        if (Ext.isDate(value)) {
            value = Ext.Date.format(value, this.getDateFormat());
        }
        return this.callParent([
            value,
            oldValue
        ]);
    },
    applyDateFormat: function(dateFormat) {
        return dateFormat || Ext.util.Format.defaultDateFormat;
    },
    
    updateDateFormat: function() {
        var me = this,
            value;
        if (!me.isConfiguring && !me.hasFocus) {
            value = me.getValue();
            if (Ext.isDate(value)) {
                me.setInputValue(value);
            }
        }
    },
    applyMinDate: function(minDate) {
        if (typeof minDate === 'string') {
            minDate = Ext.Date.parse(minDate, this.getDateFormat());
        }
        
        if (!Ext.isDate(minDate)) {
            Ext.raise("Date object or string in dateFormat required");
        }
        
        return Ext.Date.clearTime(minDate, true);
    },
    applyMaxDate: function(maxDate) {
        if (typeof maxDate === 'string') {
            maxDate = Ext.Date.parse(maxDate, this.getDateFormat());
        }
        
        if (!Ext.isDate(maxDate)) {
            Ext.raise("Date object or string in dateFormat required");
        }
        
        return Ext.Date.clearTime(maxDate, true);
    },
    
    getFormattedValue: function(format) {
        var value = this.getValue();
        return Ext.isDate(value) ? Ext.Date.format(value, format || this.getDateFormat()) : '';
    },
    applyPicker: function(picker, oldPicker) {
        var me = this,
            type;
        picker = me.callParent([
            picker,
            oldPicker
        ]);
        me.pickerType = type = picker.xtype === 'datepicker' ? 'edge' : 'floated';
        picker.ownerCmp = me;
        return picker;
    },
    createFloatedPicker: function() {
        return this.getFloatedPicker();
    },
    createEdgePicker: function() {
        var me = this,
            minDate = this.getMinDate(),
            maxDate = this.getMaxDate();
        return Ext.merge({
            yearFrom: minDate ? minDate.getFullyear() : (new Date().getFullYear() - 20),
            yearTo: maxDate ? maxDate.getFullyear() : (new Date().getFullYear() + 20)
        }, me.getEdgePicker());
    },
    setPickerLocation: function(fromKeyboard) {
        var me = this,
            pickerType = me.pickerType,
            picker = me.getPicker(),
            value = me.getValue(),
            limit;
        me.$ignorePickerChange = true;
        if (value != null) {
            picker.setValue(value);
        } else if (pickerType === 'edge') {
            picker.setValue(new Date());
        }
        delete me.$ignorePickerChange;
        if (pickerType === 'floated') {
            picker.el.dom.tabIndex = -1;
            limit = me.getMinDate();
            if (limit) {
                picker.setMinDate(limit);
            }
            limit = me.getMaxDate();
            if (limit) {
                picker.setMaxDate(limit);
            }
            value = value || new Date();
            
            picker.navigateTo(value, false);
            if (fromKeyboard) {
                picker.focusDate(value);
            }
        }
    },
    doValidate: function(value, errors, skipLazy) {
        var me = this,
            format = me.getDateFormat(),
            limit, t;
        me.callParent([
            value,
            errors,
            skipLazy
        ]);
        limit = me.getMinDate();
        t = +value;
        
        if (limit && t < +limit) {
            limit = Ext.Date.format(limit, format);
            errors.push(Ext.String.format(me.minDateMessage, limit));
        }
        limit = me.getMaxDate();
        if (limit && t > +limit) {
            limit = Ext.Date.format(limit, format);
            errors.push(Ext.String.format(me.maxDateMessage, limit));
        }
    },
    
    onPickerChange: function(picker, value) {
        var me = this;
        if (me.$ignorePickerChange) {
            return;
        }
        me.forceInputChange = true;
        me.setValue(value);
        me.forceInputChange = false;
        me.fireEvent('select', me, value);
        
        
        
        
        
        
        me.onTabOut(picker);
    },
    onTabOut: function() {
        
        this.collapse();
    },
    parseValue: function(value, errors) {
        var date;
        if (value) {
            date = Ext.Date.parse(value, this.getDateFormat());
            if (date !== null) {
                return date;
            }
        }
        return this.callParent([
            value,
            errors
        ]);
    },
    transformValue: function(value) {
        if (Ext.isObject(value)) {
            value = new Date(value.year, value.month, value.day);
            if (isNaN(value.getTime())) {
                value = null;
            }
        }
        return value;
    },
    doDestroy: function() {
        var picker = this._picker;
        if (picker && picker.isPicker) {
            picker.destroy();
        }
        this.callParent();
    },
    privates: {
        setShowPickerValue: function(picker) {
            this.updatePickerValue(picker, this.getValue() || new Date());
        }
    },
    deprecated: {
        '6.5': {
            configs: {
                format: 'dateFormat'
            }
        }
    }
});


Ext.define('Ext.field.DatePickerNative', {
    extend: Ext.field.Date,
    alternateClassName: 'Ext.form.DatePickerNative',
    xtype: 'datepickernativefield',
    onFocus: function(e) {
        var me = this;
        if (!(navigator.plugins && navigator.plugins.dateTimePicker)) {
            me.callParent();
            return;
        }
        var success = function(res) {
                me.setValue(res);
            };
        var fail = function(e) {
                console.log("DateTimePicker: error occurred or cancelled: " + e);
            };
        try {
            var dateTimePickerFunc = me.getName() == 'date' ? navigator.plugins.dateTimePicker.selectDate : navigator.plugins.dateTimePicker.selectTime;
            dateTimePickerFunc(success, fail, {
                value: me.getValue()
            });
        } catch (ex) {
            fail(ex);
        }
    }
});


Ext.define('Ext.field.Display', {
    extend: Ext.field.Field,
    xtype: 'displayfield',
    alternateClassName: 'Ext.form.Display',
    submitValue: false,
    validateOnChange: false,
    
    focusable: false,
    
    readOnly: true,
    config: {
        
        encodeHtml: true,
        
        renderer: null
    },
    classCls: Ext.baseCSSPrefix + 'displayfield',
    
    initialize: function() {
        this.callParent();
        this.syncDom();
    },
    
    getBodyTemplate: function() {
        return [
            {
                reference: 'inputElement',
                cls: Ext.baseCSSPrefix + 'input-el'
            }
        ];
    },
    updateValue: function(newValue, oldValue) {
        this.callParent([
            newValue,
            oldValue
        ]);
        this.syncDom();
    },
    updateHtmlEncode: function() {
        this.syncDom();
    },
    updateRenderer: function() {
        this.syncDom();
    },
    validate: Ext.returnTrue,
    isValid: Ext.returnTrue,
    privates: {
        getDisplayValue: function() {
            var me = this,
                value = Ext.valueFrom(me.getValue(), ''),
                renderer = me.getRenderer();
            if (renderer) {
                value = Ext.callback(renderer, me.scope, [
                    value,
                    me
                ], 0, me);
            }
            return value;
        },
        syncDom: function() {
            var me = this,
                dom = me.inputElement.dom,
                value;
            if (!me.isConfiguring) {
                value = me.getDisplayValue();
                if (me.getEncodeHtml()) {
                    dom.textContent = value;
                } else {
                    dom.innerHTML = value;
                }
            }
        }
    }
});


Ext.define('Ext.field.Email', {
    extend: Ext.field.Text,
    alternateClassName: 'Ext.form.Email',
    xtype: 'emailfield',
    config: {
        
        autoCapitalize: false
    },
    
    inputType: 'email',
    
    classCls: Ext.baseCSSPrefix + 'emailfield'
});


Ext.define('Ext.field.File', {
    extend: Ext.field.Text,
    xtype: 'filefield',
    mixins: [
        Ext.mixin.ConfigProxy
    ],
    
    isFile: true,
    proxyConfigs: {
        fileButton: [
            
            'multiple',
            
            'accept',
            
            'capture'
        ]
    },
    readOnly: true,
    editable: false,
    focusable: false,
    inputTabIndex: -1,
    triggers: {
        file: {
            type: 'file'
        }
    },
    classCls: Ext.baseCSSPrefix + 'filefield',
    captureLookup: {
        video: "camcorder",
        image: "camera",
        audio: "microphone"
    },
    onChange: function(me, value, startValue) {
        me.fireEvent('change', this, value, startValue);
    },
    applyName: function(name) {
        var multiple;
        if (name) {
            if (multiple && name.substr(-2, 2) !== "[]") {
                name += "[]";
            } else if ((!multiple) && name.substr(-2, 2) === "[]") {
                name = name.substr(0, name.length - 2);
            }
        }
        return name;
    },
    updateName: function(name) {
        var fileTrigger = this.getTriggers().file,
            inputElement = fileTrigger && fileTrigger.getComponent().buttonElement.dom;
        if (name && inputElement) {
            inputElement.name = name;
        }
    },
    updateMultiple: function() {
        var name = this.getName();
        if (!Ext.isEmpty(name)) {
            this.setName(name);
        }
    },
    updateTriggers: function(triggers, oldTriggers) {
        this.callParent([
            triggers,
            oldTriggers
        ]);
        this.getFileButton().on('change', 'onFileButtonChange', this);
    },
    updateValue: function(value, oldValue) {
        this.callParent([
            value,
            oldValue
        ]);
        this.getFileButton().setValue(value);
    },
    getFileButton: function() {
        return this.getTriggers().file.getComponent();
    },
    
    reset: function() {
        var me = this,
            original = me.originalValue;
        if (original == null) {
            me.setInputValue('');
            me._value = '';
        }
        me.getFileButton().buttonElement.dom.value = '';
    },
    
    getFiles: function() {
        return this.getFileButton().getFiles();
    },
    privates: {
        onFileButtonChange: function(fileButton, value) {
            var buttonElement = fileButton.buttonElement.dom,
                files = buttonElement.files,
                len, i, file;
            if (files) {
                value = [];
                for (i = 0 , len = files.length; i < len; i++) {
                    file = files[i];
                    value.push(file.name);
                }
                value = value.join(', ');
            }
            this.setValue(value);
        }
    }
});


Ext.define('Ext.field.FileButton', {
    extend: Ext.Button,
    xtype: 'filebutton',
    
    config: {
        
        value: null,
        
        multiple: false,
        
        accept: null,
        
        capture: null
    },
    buttonType: 'file',
    ui: 'action',
    text: 'Browse...',
    preventDefaultAction: false,
    keyHandlersAdded: true,
    eventHandlers: {
        change: 'onChange'
    },
    getButtonTemplate: function() {
        var template = this.callParent();
        template.tag = 'input';
        template.onchange = 'return Ext.doEv(this, event);';
        return template;
    },
    applyAccept: function(value) {
        switch (value) {
            case "video":
            case "audio":
            case "image":
                value = value + "/*";
                break;
        }
        this.setInputAttribute('accept', value);
    },
    applyCapture: function(value) {
        this.setInputAttribute('capture', value);
        return value;
    },
    applyMultiple: function(value) {
        this.setInputAttribute('multiple', value ? '' : null);
        return value;
    },
    onChange: function(e) {
        this.setValue(this.buttonElement.dom.value);
    },
    updateValue: function(value, oldValue) {
        this.fireEvent('change', this, value, oldValue);
    },
    
    getFiles: function() {
        return this.buttonElement.dom.files;
    },
    privates: {
        setInputAttribute: function(attribute, newValue) {
            var buttonElement = this.buttonElement.dom;
            if (!Ext.isEmpty(newValue, true)) {
                buttonElement.setAttribute(attribute, newValue);
            } else {
                buttonElement.removeAttribute(attribute);
            }
        }
    }
});


Ext.define('Ext.field.Hidden', {
    extend: Ext.field.Input,
    alternateClassName: 'Ext.form.Hidden',
    xtype: 'hiddenfield',
    
    inputType: 'hidden',
    hidden: true,
    classCls: Ext.baseCSSPrefix + 'hiddenfield'
});


Ext.define('Ext.field.Number', {
    extend: Ext.field.Text,
    xtype: 'numberfield',
    alternateClassName: 'Ext.form.Number',
    config: {
        
        minValue: null,
        
        maxValue: null,
        
        decimals: 2,
        
        trim: true
    },
    
    inputType: Ext.os.is.Desktop ? 'text' : 'number',
    
    minValueText: 'The minimum value for this field is {0}',
    
    maxValueText: 'The maximum value for this field is {0}',
    
    decimalsText: 'The maximum decimal places is {0}',
    badFormatMessage: 'Value is not a valid number',
    classCls: Ext.baseCSSPrefix + 'numberfield',
    parseValidator: 'number',
    initialize: function() {
        
        this.getDecimals();
        this.callParent();
        
        this.inputElement.dom.setAttribute('inputmode', 'numeric');
        
        
        
        
        if (this.getInputMask()) {
            Ext.raise('NumberFields cannot use input masks');
        }
    },
    
    updateDecimals: function(decimals) {
        var me = this,
            format = '0',
            zeroChar = me.getTrim() ? '#' : '0',
            value;
        if (decimals) {
            format += '.' + Ext.String.repeat(zeroChar, decimals);
        }
        me.numberFormat = format;
        if (!me.isConfiguring) {
            value = me.getValue();
            if (Ext.isDate(value)) {
                me.setInputValue(value);
            }
        }
    },
    applyInputValue: function(value) {
        
        this.getDecimals();
        if (typeof value === 'number') {
            value = Ext.util.Format.number(value, this.numberFormat);
        }
        return value;
    },
    doValidate: function(value, errors, skipLazy) {
        var me = this,
            String = Ext.String,
            minValue = me.getMinValue(),
            maxValue = me.getMaxValue();
        me.callParent([
            value,
            errors,
            skipLazy
        ]);
        if (minValue != null && value < minValue) {
            errors.push(String.format(me.minValueText, minValue));
        } else if (maxValue != null && value > maxValue) {
            errors.push(String.format(me.maxValueText, maxValue));
        }
    },
    onKeyDown: function(e) {
        var me = this,
            raw;
        
        if (me.getInputType() !== 'number') {
            if (!e.ctrlKey && !e.altKey) {
                raw = me.calculateNewValue(e.key());
                
                if (!me.specialKeys[e.getCharCode()] && !me.isAllowableValue(raw)) {
                    e.preventDefault();
                    return false;
                }
            }
        }
        me.callParent([
            e
        ]);
    },
    transformValue: function(value) {
        if (!(value || value === 0)) {
            value = null;
        }
        return value;
    },
    privates: {
        calculateNewValue: function(text) {
            var me = this,
                textSelection = me.getTextSelection(),
                raw = me.getInputValue();
            
            if (textSelection[1]) {
                raw = raw.substr(0, textSelection[0]) + text + raw.substr(textSelection[1]);
            } else 
            {
                raw = Ext.String.insert(raw, text, me.getCaretPos());
            }
            return raw;
        },
        handlePaste: function(e) {
            var me = this,
                text;
            if (me.getInputType() !== 'number') {
                text = e.getClipboardData('text/plain');
                if (text) {
                    text = me.calculateNewValue(text);
                    if (me.isAllowableValue(text)) {
                        me.setValue(text);
                    }
                    e.preventDefault();
                    return false;
                }
            }
            me.callParent([
                e
            ]);
            me.validate();
        },
        isAllowableValue: function(value) {
            var minValue = this.getMinValue(),
                allowNegative = minValue == null || minValue < 0;
            if (!allowNegative && Ext.String.startsWith(value, '-')) {
                return false;
            }
            return this.isPartialValue(value) || this.parseValue(value) !== null;
        },
        isPartialValue: function(value) {
            var me = this,
                minValue = me.getMinValue(),
                allowNegative = minValue == null || minValue < 0;
            
            
            
            
            
            
            if (allowNegative && value === '-') {
                return true;
            }
            if (me.getDecimals() && (value === '.' || (allowNegative && value === '-.'))) {
                return true;
            }
            return false;
        }
    }
}, function(C) {
    var E = Ext.event.Event;
    C.prototype.specialKeys = Ext.Array.toMap([
        E.BACKSPACE,
        E.TAB,
        E.RETURN,
        E.CTRL,
        E.DELETE,
        E.LEFT,
        E.RIGHT,
        E.UP,
        E.DOWN,
        E.HOME,
        E.END,
        E.META
    ]);
});


Ext.define('Ext.form.Borders', {
    mixinId: 'formborders',
    config: {
        
        fieldSeparators: null,
        
        inputBorders: null
    },
    fieldSeparatorsCls: Ext.baseCSSPrefix + 'form-field-separators',
    noInputBordersCls: Ext.baseCSSPrefix + 'form-no-input-borders',
    updateFieldSeparators: function(fieldSeparators, oldFieldSeparators) {
        var bodyElement = this.bodyElement,
            cls = this.fieldSeparatorsCls;
        if (fieldSeparators) {
            bodyElement.addCls(cls);
        } else if (oldFieldSeparators) {
            bodyElement.removeCls(cls);
        }
    },
    updateInputBorders: function(inputBorders, oldInputBorders) {
        var bodyElement = this.bodyElement,
            cls = this.noInputBordersCls;
        if (inputBorders === false) {
            bodyElement.addCls(cls);
        } else if (oldInputBorders === false) {
            bodyElement.removeCls(cls);
        }
    }
});


Ext.define('Ext.field.Panel', {
    extend: Ext.Panel,
    xtype: 'fieldpanel',
    mixins: [
        Ext.field.Manager,
        Ext.form.Borders
    ],
    
    
    scrollable: true,
    
    nameable: true,
    
    shareableName: true,
    
    nameHolder: true,
    
    config: {
        
        api: null,
        
        baseParams: null,
        
        paramOrder: null,
        
        paramsAsHash: null,
        
        timeout: 30,
        
        url: null
    },
    
    load: function(options) {
        options = options || {};
        var me = this,
            api = me.getApi(),
            url = options.url || me.getUrl(),
            waitMsg = options.waitMsg,
            successFn = function(response, data) {
                me.setValues(data.data);
                if (Ext.isFunction(options.success)) {
                    options.success.call(options.scope || me, me, response, data);
                }
                me.fireEvent('load', me, response);
            },
            failureFn = function(response, data) {
                if (Ext.isFunction(options.failure)) {
                    options.failure.call(options.scope, me, response, data);
                }
                me.fireEvent('exception', me, response);
            },
            load, args;
        if (options.waitMsg) {
            if (typeof waitMsg === 'string') {
                waitMsg = {
                    xtype: 'loadmask',
                    message: waitMsg
                };
            }
            me.setMasked(waitMsg);
        }
        if (api) {
            api = Ext.direct.Manager.resolveApi(api, me);
            me.setApi(api);
            load = api.load;
            if (!load) {
                Ext.raise("Cannot find Ext Direct API method for load action");
            }
            args = load.$directCfg.method.getArgs({
                params: me.getParams(options.params),
                paramOrder: me.getParamOrder(),
                paramsAsHash: me.getParamsAsHash(),
                scope: me,
                callback: function(data, response, success) {
                    me.setMasked(false);
                    if (success) {
                        successFn(response, data);
                    } else {
                        failureFn(response, data);
                    }
                }
            });
            load.apply(window, args);
        } else if (url) {
            return Ext.Ajax.request({
                url: url,
                timeout: (options.timeout || me.getTimeout()) * 1000,
                method: options.method || 'GET',
                autoAbort: options.autoAbort,
                headers: Ext.apply({
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
                }, options.headers || {}),
                callback: function(callbackOptions, success, response) {
                    var responseText = response.responseText,
                        statusResult = Ext.data.request.Ajax.parseStatus(response.status, response);
                    me.setMasked(false);
                    if (success) {
                        if (statusResult && responseText.length === 0) {
                            success = true;
                        } else {
                            response = Ext.decode(responseText);
                            success = !!response.success;
                        }
                        if (success) {
                            successFn(response, responseText);
                        } else {
                            failureFn(response, responseText);
                        }
                    } else {
                        failureFn(response, responseText);
                    }
                }
            });
        }
    },
    
    getParams: function(params) {
        return Ext.apply({}, params, this.getBaseParams());
    },
    updateDisabled: function(newDisabled, oldDisabled) {
        this.mixins.fieldmanager.updateDisabled.call(this, newDisabled, oldDisabled);
        this.callParent([
            newDisabled,
            oldDisabled
        ]);
    },
    updateRecord: function(record) {
        this.consumeRecord(record);
    }
});


Ext.define('Ext.field.trigger.Reveal', {
    extend: Ext.field.trigger.Trigger,
    xtype: 'revealtrigger',
    alias: 'trigger.reveal',
    classCls: Ext.baseCSSPrefix + 'revealtrigger',
    weight: -1000,
    hidden: true,
    handler: 'onRevealTap',
    scope: 'this'
});


Ext.define('Ext.field.Password', {
    extend: Ext.field.Text,
    xtype: 'passwordfield',
    alternateClassName: 'Ext.form.Password',
    config: {
        
        autoCapitalize: false,
        
        revealable: false,
        
        revealed: {
            $value: false,
            lazy: true
        }
    },
    
    inputType: 'password',
    
    classCls: Ext.baseCSSPrefix + 'passwordfield',
    revealedCls: Ext.baseCSSPrefix + 'revealed',
    isPassword: true,
    applyTriggers: function(triggers, oldTriggers) {
        if (triggers && this.getRevealable() && !triggers.reveal) {
            triggers = Ext.apply({
                reveal: {
                    type: 'reveal'
                }
            }, triggers);
        }
        return this.callParent([
            triggers,
            oldTriggers
        ]);
    },
    updateRevealed: function(newValue, oldValue) {
        var me = this;
        if (newValue) {
            me.element.addCls(me.revealedCls);
            me.setInputType("text");
        } else {
            me.element.removeCls(me.revealedCls);
            me.setInputType("password");
        }
    },
    updateValue: function(value, oldValue) {
        this.syncRevealTrigger();
        this.callParent([
            value,
            oldValue
        ]);
    },
    doKeyUp: function(me, e) {
        this.callParent([
            me,
            e
        ]);
        this.syncRevealTrigger();
    },
    onRevealTap: function(e) {
        this.fireAction('revealicontap', [
            this,
            e
        ], 'doRevealTap');
    },
    
    doRevealTap: function(me, e) {
        this.setRevealed(!this.getRevealed());
    },
    privates: {
        isValidTextValue: function(value) {
            
            return (value !== undefined && value !== null && value !== '');
        },
        syncRevealTrigger: function() {
            var me = this,
                triggers = me.getTriggers(),
                revealTrigger = triggers && triggers.reveal,
                visible, value;
            if (revealTrigger) {
                if (me.getRevealable()) {
                    value = me.getValue();
                    if (value != null && value !== '' && !me.getDisabled() && !me.getReadOnly()) {
                        visible = true;
                    }
                }
                if (visible) {
                    revealTrigger.show();
                } else {
                    revealTrigger.hide();
                }
            }
        }
    }
});


Ext.define('Ext.field.Radio', {
    extend: Ext.field.Checkbox,
    xtype: [
        'radio',
        'radiofield'
    ],
    alternateClassName: 'Ext.form.Radio',
    isRadio: true,
    inputType: 'radio',
    classCls: Ext.baseCSSPrefix + 'radiofield',
    getValue: function() {
        return this._value === undefined ? null : this._value;
    },
    setValue: function(value) {
        this._value = value;
        return this;
    },
    getSubmitValue: function() {
        var value = this._value;
        if (value === undefined || value === null) {
            value = true;
        }
        return (this.getChecked()) ? value : null;
    },
    updateChecked: function(checked, oldChecked) {
        var me = this;
        me.callParent([
            checked,
            oldChecked
        ]);
        if (me.initialized && checked) {
            me.refreshGroupValues(me);
        }
    },
    
    getGroupValue: function() {
        var fields = this.getSameGroupFields(),
            ln = fields.length,
            i = 0,
            field;
        for (; i < ln; i++) {
            field = fields[i];
            if (field.getChecked()) {
                return field.getValue();
            }
        }
        return null;
    },
    
    setGroupValue: function(value) {
        var fields = this.getSameGroupFields(),
            ln = fields.length,
            i = 0,
            field;
        for (; i < ln; i++) {
            field = fields[i];
            if (field.getValue() === value) {
                field.setChecked(true);
                return field;
            }
        }
    },
    
    refreshGroupValues: function(trigger) {
        var fields = this.getSameGroupFields(),
            ln = fields.length,
            i = 0,
            field;
        for (; i < ln; i++) {
            field = fields[i];
            if (field !== trigger) {
                field.setChecked(false);
            }
        }
    }
});

Ext.define('Ext.field.trigger.Search', {
    extend: Ext.field.trigger.Trigger,
    xtype: 'searchtrigger',
    alias: 'trigger.search',
    classCls: Ext.baseCSSPrefix + 'searchtrigger'
});


Ext.define('Ext.field.Search', {
    extend: Ext.field.Text,
    xtype: 'searchfield',
    alternateClassName: 'Ext.form.Search',
    inputType: 'search',
    triggers: {
        search: {
            type: 'search',
            side: 'left'
        }
    },
    classCls: Ext.baseCSSPrefix + 'searchfield'
});


Ext.define('Ext.slider.Thumb', {
    extend: Ext.Component,
    xtype: 'thumb',
    baseCls: Ext.baseCSSPrefix + 'thumb',
    isSliderThumb: true,
    config: {
        
        fillCls: null,
        
        fillTrack: null,
        
        dragMax: null,
        dragMin: null,
        slider: null
    },
    template: [
        {
            reference: 'iconElement',
            classList: [
                Ext.baseCSSPrefix + 'icon-el',
                Ext.baseCSSPrefix + 'font-icon'
            ]
        }
    ],
    
    draggable: {
        local: true,
        constrain: {
            horizontal: true
        },
        listeners: {
            beforedragstart: 'onBeforeDragStart',
            dragstart: 'onDragStart',
            dragmove: 'onDragMove',
            dragend: 'onDragEnd',
            scope: 'this'
        }
    },
    touchAction: {
        panX: false
    },
    translatable: {
        
        
        type: 'cssposition',
        listeners: {
            animationstart: 'onAnimationStart',
            animationend: 'onAnimationEnd',
            translate: 'onTranslate',
            scope: 'this'
        }
    },
    elementWidth: 0,
    pressingCls: Ext.baseCSSPrefix + 'pressing',
    fillCls: Ext.baseCSSPrefix + 'fill-el',
    sizerCls: Ext.baseCSSPrefix + 'thumb-sizer',
    constructor: function(config) {
        
        
        
        
        this.sizerElement = Ext.Element.create({
            cls: this.sizerCls
        });
        this.callParent([
            config
        ]);
    },
    initialize: function() {
        var me = this,
            fillElement;
        me.callParent();
        me.el.addClsOnClick(me.pressingCls, me.shouldAddPressingCls, me);
        fillElement = me.fillElement = Ext.Element.create({
            cls: me.fillCls
        });
        fillElement.setVisibilityMode(1);
    },
    
    updateFillTrack: function(fillTrack) {
        var fillElement = this.fillElement;
        if (fillTrack === false) {
            fillElement.hide();
        } else {
            fillElement.show();
            fillElement.setStyle('background-color', (typeof fillTrack === 'string') ? fillTrack : '');
        }
    },
    updateFillCls: function(fillCls, oldFillCls) {
        this.fillElement.replaceCls(oldFillCls, fillCls);
    },
    shouldAddPressingCls: function() {
        return !this.isDisabled();
    },
    initDragConstraints: function() {
        
        if (this.isDisabled()) {
            return false;
        }
        this.getSlider().refreshAllThumbConstraints();
    },
    onAnimationStart: function(translatable, x, y) {
        this.getSlider().onThumbAnimationStart(this, x, y);
    },
    onAnimationEnd: function(translatable, x, y) {
        if (!this.destroyed) {
            this.getSlider().onThumbAnimationEnd(this, x, y);
        }
    },
    onBeforeDragStart: function(source, info, event) {
        if (this.isDisabled()) {
            return false;
        }
        var xy = info.proxy.current;
        this.getSlider().onThumbBeforeDragStart(this, event, xy.x, xy.y);
    },
    onDragStart: function(source, info, event) {
        var xy = info.proxy.current;
        this.getSlider().onThumbDragStart(this, event, xy.x, xy.y);
    },
    onDragMove: function(source, info, event) {
        if (this.isDisabled()) {
            return false;
        }
        var xy = info.proxy.current;
        this.getSlider().onThumbDragMove(this, event, xy.x, xy.y);
    },
    onDragEnd: function(source, info, event) {
        if (this.isDisabled()) {
            return false;
        }
        var xy = info.proxy.current;
        this.getSlider().onThumbDragEnd(this, event, xy.x, xy.y);
    },
    onTranslate: function(translatable, x, y) {
        if (this.initialized) {
            this.getSlider().syncFill(this, x);
        }
    },
    onResize: function(width) {
        var me = this,
            slider = me.ownerCmp;
        me.elementWidth = width;
        if (slider && slider.thumbs && slider.thumbs[0] === me) {
            slider.onThumbResize(me, width);
        }
    },
    getElementWidth: function() {
        return this.elementWidth;
    },
    updateUi: function(ui, oldUi) {
        var me = this,
            sizerCls = me.sizerCls,
            sizerElement = me.sizerElement;
        if (oldUi) {
            sizerElement.removeCls(oldUi, sizerCls);
        }
        if (ui) {
            sizerElement.addCls(ui, sizerCls);
        }
        me.callParent([
            ui,
            oldUi
        ]);
    },
    updateDragMax: function(max) {
        var constraint = this.getDraggable().getConstrain(),
            range = constraint.getX();
        constraint.setX([
            range && range[0],
            max
        ]);
    },
    updateDragMin: function(min) {
        var constraint = this.getDraggable().getConstrain(),
            range = constraint.getX();
        constraint.setX([
            min,
            range && range[1]
        ]);
    },
    destroy: function() {
        Ext.destroyMembers(this, 'fillElement', 'sizerElement');
        this.callParent();
    }
});


Ext.define('Ext.slider.Slider', {
    extend: Ext.Component,
    xtype: 'slider',
    
    
    
    
    config: {
        
        
        thumbDefaults: {
            xtype: 'thumb',
            inheritUi: true,
            translatable: {
                easingX: {
                    duration: 300,
                    type: 'ease-out'
                }
            }
        },
        
        increment: 1,
        
        value: 0,
        
        valueIsArray: false,
        
        minValue: 0,
        
        maxValue: 100,
        
        allowThumbsOverlapping: false,
        
        animation: true,
        
        readOnly: false
    },
    defaultBindProperty: 'value',
    twoWayBindable: {
        value: 1
    },
    
    classCls: Ext.baseCSSPrefix + 'slider',
    elementWidth: 0,
    offsetValueRatio: 0,
    activeThumb: null,
    isThumbAnimating: 0,
    template: [
        {
            reference: 'trackElement',
            cls: Ext.baseCSSPrefix + 'track-el'
        },
        {
            reference: 'thumbWrapElement',
            cls: Ext.baseCSSPrefix + 'thumb-wrap-el'
        }
    ],
    fillSelector: '.' + Ext.baseCSSPrefix + 'fill-el',
    constructor: function(config) {
        if (config && config.hasOwnProperty('values')) {
            config = Ext.apply({
                value: config.values
            }, config);
            delete config.values;
        }
        this.thumbs = [];
        this.callParent([
            config
        ]);
    },
    
    initialize: function() {
        this.callParent();
        this.element.on('tap', 'onTap', this);
    },
    onRender: function() {
        this.callParent();
        this.whenVisible('refreshSizes');
    },
    applyThumbDefaults: function(defaults) {
        return Ext.apply({
            slider: this,
            ownerCmp: this
        }, defaults);
    },
    
    factoryThumb: function() {
        var thumb = Ext.create(this.getThumbDefaults());
        thumb.doInheritUi();
        return thumb;
    },
    
    getThumbs: function() {
        return this.thumbs;
    },
    
    getThumb: function(index) {
        if (typeof index !== 'number') {
            index = 0;
        }
        return this.thumbs[index];
    },
    refreshOffsetValueRatio: function() {
        var me = this,
            valueRange = me.getMaxValue() - me.getMinValue(),
            trackWidth = me.elementWidth - me.thumbWidth;
        me.offsetValueRatio = valueRange === 0 ? 0 : trackWidth / valueRange;
    },
    onThumbResize: function(thumb, thumbWidth) {
        this.thumbWidth = thumbWidth;
        this.refresh();
    },
    onResize: function(width) {
        this.elementWidth = width;
        this.refresh();
    },
    refresh: function() {
        this.refreshing = true;
        this.refreshValue();
        this.refreshing = false;
    },
    setActiveThumb: function(thumb) {
        var oldActiveThumb = this.activeThumb;
        if (oldActiveThumb && oldActiveThumb !== thumb) {
            oldActiveThumb.setZIndex(null);
        }
        this.activeThumb = thumb;
        thumb.setZIndex(2);
        return this;
    },
    onThumbBeforeDragStart: function(thumb, e) {
        if (this.offsetValueRatio === 0 || e.absDeltaX <= e.absDeltaY || this.getReadOnly()) {
            return false;
        }
    },
    onThumbDragStart: function(thumb, e) {
        var me = this;
        if (me.getAllowThumbsOverlapping()) {
            me.setActiveThumb(thumb);
        }
        me.dragStartValue = me.getArrayValues()[me.getThumbIndex(thumb)];
        me.fireEvent('dragstart', me, thumb, me.dragStartValue, e);
    },
    onThumbDragMove: function(thumb, e, offsetX) {
        var me = this,
            index = me.getThumbIndex(thumb),
            offsetValueRatio = me.offsetValueRatio,
            constrainedValue = me.constrainValue(me.getMinValue() + offsetX / offsetValueRatio);
        e.stopPropagation();
        me.setIndexValue(index, constrainedValue);
        me.fireEvent('drag', me, thumb, me.getArrayValues(), e);
        return false;
    },
    setIndexValue: function(index, value, animation) {
        var me = this,
            thumb = me.thumbs[index],
            values = me.getArrayValues(),
            minValue = me.getMinValue(),
            offsetValueRatio = me.offsetValueRatio,
            increment = me.getIncrement(),
            pos = (value - minValue) * offsetValueRatio;
        
        thumb.setXY(pos, null, animation);
        values[index] = minValue + Math.round((pos / offsetValueRatio) / increment) * increment;
        me.setValue(values);
        me.refreshAdjacentThumbConstraints(thumb);
    },
    onChange: function(thumb, newValue, oldValue) {
        var me = this;
        if (me.hasListeners.change) {
            me.fireEvent('change', me, thumb, newValue, oldValue);
        }
    },
    onThumbDragEnd: function(thumb, e) {
        var me = this,
            index = me.getThumbIndex(thumb),
            newValue = me.getArrayValues()[index],
            oldValue = me.dragStartValue;
        me.snapThumbPosition(thumb, newValue);
        me.fireEvent('dragend', me, thumb, me.getArrayValues(), e);
        if (oldValue !== newValue) {
            me.onChange(thumb, newValue, oldValue);
        }
    },
    getThumbIndex: function(thumb) {
        return this.thumbs.indexOf(thumb);
    },
    refreshAdjacentThumbConstraints: function(thumb) {
        var me = this,
            offsetX = thumb.getLeft(),
            thumbs = me.thumbs,
            index = me.getThumbIndex(thumb),
            previousThumb = thumbs[index - 1],
            nextThumb = thumbs[index + 1],
            thumbWidth = me.getAllowThumbsOverlapping() ? 0 : me.thumbWidth;
        if (previousThumb) {
            previousThumb.setDragMax(offsetX - thumbWidth);
        }
        if (nextThumb) {
            nextThumb.setDragMin(offsetX + thumbWidth);
        }
    },
    
    onTap: function(e) {
        var me = this,
            element = me.element,
            minDistance = Infinity,
            i, absDistance, testValue, closestIndex, oldValue, thumb, ln, values, value, offset, elementX, targetElement, touchPointX;
        if (me.offsetValueRatio === 0 || me.isDisabled() || me.getReadOnly()) {
            return;
        }
        targetElement = Ext.get(e.target);
        if (!targetElement || (Ext.browser.engineName == 'WebKit' && targetElement.hasCls('x-thumb'))) {
            return;
        }
        touchPointX = e.touch.point.x;
        elementX = element.getX();
        offset = touchPointX - elementX - (me.thumbWidth / 2);
        value = me.constrainValue(me.getMinValue() + offset / me.offsetValueRatio);
        values = me.getArrayValues();
        ln = values.length;
        if (ln === 1) {
            closestIndex = 0;
        } else {
            for (i = 0; i < ln; i++) {
                testValue = values[i];
                absDistance = Math.abs(testValue - value);
                if (absDistance < minDistance) {
                    minDistance = absDistance;
                    closestIndex = i;
                }
            }
        }
        oldValue = values[closestIndex];
        thumb = me.thumbs[closestIndex];
        me.setIndexValue(closestIndex, value, me.getAnimation());
        if (oldValue !== value) {
            me.onChange(thumb, value, oldValue);
        }
    },
    
    updateThumbs: function(newThumbs) {
        this.add(newThumbs);
    },
    applyValue: function(value, oldValue) {
        var me = this,
            values = Ext.Array.from(value || 0),
            valueIsArray = me.getValueIsArray(),
            filteredValues = [],
            previousFilteredValue = me.getMinValue(),
            filteredValue, i, ln;
        for (i = 0 , ln = values.length; i < ln; i++) {
            filteredValue = me.constrainValue(values[i]);
            if (filteredValue < previousFilteredValue) {
                
                Ext.log.warn("Invalid values of '" + Ext.encode(values) + "', values at smaller indexes must " + "be smaller than or equal to values at greater indexes");
                
                filteredValue = previousFilteredValue;
            }
            filteredValues.push(filteredValue);
            previousFilteredValue = filteredValue;
        }
        if (!me.refreshing && oldValue && Ext.Array.equals(values, oldValue)) {
            filteredValues = undefined;
        } else {
            me.values = filteredValues;
            if (!valueIsArray && filteredValues.length === 1) {
                filteredValues = filteredValues[0];
            }
        }
        return filteredValues;
    },
    
    updateValue: function() {
        var me = this,
            thumbs = me.thumbs,
            values = me.getArrayValues(),
            len = values.length,
            i;
        me.setThumbsCount(len);
        if (!this.isThumbAnimating) {
            for (i = 0; i < len; i++) {
                me.snapThumbPosition(thumbs[i], values[i]);
            }
        }
    },
    
    refreshValue: function() {
        this.refreshOffsetValueRatio();
        this.updateValue();
    },
    
    constrainValue: function(value) {
        var me = this,
            minValue = me.getMinValue(),
            maxValue = me.getMaxValue(),
            increment = me.getIncrement(),
            remainder;
        value = parseFloat(value);
        if (isNaN(value)) {
            value = minValue;
        }
        remainder = (value - minValue) % increment;
        value -= remainder;
        if (Math.abs(remainder) >= (increment / 2)) {
            value += (remainder > 0) ? increment : -increment;
        }
        value = Math.max(minValue, value);
        value = Math.min(maxValue, value);
        return value;
    },
    setThumbsCount: function(count) {
        var me = this,
            thumbs = me.thumbs,
            thumbsCount = thumbs.length,
            i, thumb;
        while (count < thumbs.length) {
            thumb = thumbs.pop();
            thumb.destroy();
        }
        while (count > thumbs.length) {
            thumb = me.factoryThumb();
            thumbs.push(thumb);
            me.trackElement.appendChild(thumb.fillElement);
            me.thumbWrapElement.appendChild(thumb.element);
            me.element.appendChild(thumb.sizerElement);
        }
        if (thumbsCount !== count) {
            for (i = 0; i < count; i++) {
                
                
                
                
                
                thumb = thumbs[i];
                if (count > 2) {
                    thumb.setFillTrack(false);
                } else if (count === 2) {
                    thumb.setFillTrack(i === 1);
                } else {
                    thumb.setFillTrack(true);
                }
            }
        }
        return this;
    },
    
    setValues: function(value) {
        this.setValue(value);
    },
    
    getValues: function() {
        return this.getValue();
    },
    
    getArrayValues: function() {
        return this.values;
    },
    
    applyIncrement: function(increment) {
        if (increment === 0) {
            increment = 1;
        }
        return Math.abs(increment);
    },
    
    updateAllowThumbsOverlapping: function(newValue, oldValue) {
        if (typeof oldValue != 'undefined') {
            this.refreshValue();
        }
    },
    
    updateMinValue: function(newValue, oldValue) {
        if (typeof oldValue != 'undefined') {
            this.refreshValue();
        }
    },
    
    updateMaxValue: function(newValue, oldValue) {
        if (typeof oldValue != 'undefined') {
            this.refreshValue();
        }
    },
    
    updateIncrement: function(newValue, oldValue) {
        if (typeof oldValue != 'undefined') {
            this.refreshValue();
        }
    },
    updateDisabled: function(disabled) {
        this.callParent(arguments);
        var thumbs = this.thumbs,
            ln = thumbs.length,
            i;
        for (i = 0; i < ln; i++) {
            thumbs[i].setDisabled(disabled);
        }
    },
    doDestroy: function() {
        this.thumbs = Ext.destroy(this.thumbs);
        this.callParent();
    },
    getRefItems: function(deep) {
        return this.thumbs;
    },
    privates: {
        
        refreshAllThumbConstraints: function() {
            var me = this,
                thumbs = me.thumbs,
                len = thumbs.length,
                thumbWidth = me.getAllowThumbsOverlapping() ? 0 : me.thumbWidth,
                i;
            for (i = 0; i < len; i++) {
                me.refreshAdjacentThumbConstraints(thumbs[i]);
            }
            thumbs[0].setDragMin(0);
            thumbs[len - 1].setDragMax(me.elementWidth - thumbWidth);
        },
        refreshSizes: function() {
            var me = this,
                thumb = me.thumbs[0];
            me.elementWidth = me.element.measure('w');
            if (thumb) {
                me.thumbWidth = thumb.element.measure('w');
            }
            me.refresh();
        },
        snapThumbPosition: function(thumb, value) {
            var ratio = this.offsetValueRatio,
                offset;
            if (isFinite(ratio)) {
                offset = Ext.Number.correctFloat((value - this.getMinValue()) * ratio);
                thumb.setXY(offset, null);
            }
        },
        syncFill: function(thumb, offset) {
            var me = this,
                thumbs = me.thumbs,
                values = me.getArrayValues(),
                ln = values.length,
                prevOffset = 0,
                fillElements = me.trackElement.query(me.fillSelector, false),
                thumbIndex = thumbs.indexOf(thumb),
                thumbOffset, fillElement, i;
            offset = offset + Math.ceil(thumb.element.getWidth() / 2);
            for (i = 0; i < ln; i++) {
                thumb = thumbs[i];
                fillElement = fillElements[i];
                thumbOffset = (i === thumbIndex) ? offset : thumb.getLeft() + (thumb.element.getWidth() / 2);
                fillElement.setWidth(thumbOffset - prevOffset);
                fillElement.setLocalX(prevOffset);
                prevOffset = thumbOffset;
            }
        },
        onThumbAnimationStart: function() {
            this.isThumbAnimating++;
        },
        onThumbAnimationEnd: function() {
            this.isThumbAnimating--;
        }
    }
});


Ext.define('Ext.field.Slider', {
    extend: Ext.field.Field,
    xtype: 'sliderfield',
    alternateClassName: 'Ext.form.Slider',
    mixins: [
        Ext.mixin.ConfigProxy,
        Ext.field.BoxLabelable
    ],
    
    
    
    
    
    config: {
        
        slider: {
            xtype: 'slider',
            inheritUi: true
        },
        
        liveUpdate: false,
        
        tabIndex: -1,
        
        readOnly: false,
        
        value: 0
    },
    
    classCls: Ext.baseCSSPrefix + 'sliderfield',
    proxyConfig: {
        slider: [
            
            'increment',
            
            'minValue',
            
            'maxValue'
        ]
    },
    
    bodyAlign: 'stretch',
    
    defaultBindProperty: 'value',
    
    twoWayBindable: {
        values: 1,
        value: 1
    },
    
    constructor: function(config) {
        config = config || {};
        if (config.hasOwnProperty('values')) {
            config.value = config.values;
        }
        this.callParent([
            config
        ]);
        this.updateMultipleState();
    },
    
    initialize: function() {
        this.callParent();
        this.getSlider().on({
            scope: this,
            change: 'onSliderChange',
            dragstart: 'onSliderDragStart',
            drag: 'onSliderDrag',
            dragend: 'onSliderDragEnd'
        });
    },
    getBodyTemplate: function() {
        return this.mixins.boxLabelable.getBodyTemplate.call(this);
    },
    applySlider: function(slider) {
        if (slider && !slider.isInstance) {
            slider = this.mergeProxiedConfigs('slider', slider);
            slider.$initParent = this;
            slider = Ext.create(slider);
            delete slider.$initParent;
        }
        this.boxElement.appendChild(slider.el);
        slider.ownerCmp = this;
        return slider;
    },
    updateSlider: function(slider) {
        slider.doInheritUi();
    },
    getValue: function() {
        return this._value;
    },
    applyValue: function(value, oldValue) {
        value = this.callParent([
            value,
            oldValue
        ]) || 0;
        
        
        if (this.dragging && this.isSyncing('value')) {
            value = undefined;
        } else if (Ext.isArray(value)) {
            value = value.slice(0);
            if (oldValue && Ext.Array.equals(value, oldValue)) {
                value = undefined;
            }
        } else {
            value = [
                value
            ];
        }
        return value;
    },
    updateValue: function(value, oldValue) {
        if (!this.dragging) {
            value = this.setSliderValue(value);
        }
        this.callParent([
            value,
            oldValue
        ]);
    },
    setSliderValue: function(value) {
        
        return this.getSlider().setValue(value).getValue();
    },
    onSliderChange: function(slider, thumb, newValue, oldValue) {
        this.setValue(slider.getValue());
        this.fireEvent('dragchange', this, slider, thumb, newValue, oldValue);
    },
    onSliderDragStart: function(slider, thumb, startValue, e) {
        this.dragging = true;
        this.fireEvent('dragstart', this, slider, thumb, startValue, e);
    },
    onSliderDrag: function(slider, thumb, value, e) {
        var me = this;
        if (me.getLiveUpdate()) {
            me.setValue(slider.getValue());
        }
        me.fireEvent('drag', me, slider, thumb, value, e);
    },
    onSliderDragEnd: function(slider, thumb, startValue, e) {
        this.dragging = false;
        this.fireEvent('dragend', this, slider, thumb, startValue, e);
    },
    
    setValues: function(value) {
        this.setValue(value);
        this.updateMultipleState();
    },
    
    getValues: function() {
        return this.getValue();
    },
    reset: function() {
        var config = this.config,
            initialValue = (this.config.hasOwnProperty('values')) ? config.values : config.value;
        this.setValue(initialValue);
    },
    updateReadOnly: function(newValue) {
        this.getSlider().setReadOnly(newValue);
    },
    updateMultipleState: function() {
        var value = this.getValue();
        if (value && value.length > 1) {
            this.addCls(Ext.baseCSSPrefix + 'slider-multiple');
        }
    },
    updateDisabled: function(disabled, oldDisabled) {
        this.callParent([
            disabled,
            oldDisabled
        ]);
        this.getSlider().setDisabled(disabled);
    },
    doDestroy: function() {
        this.getSlider().destroy();
        this.callParent();
    },
    getRefItems: function(deep) {
        var refItems = [],
            slider = this.getSlider();
        if (slider) {
            refItems.push(slider);
            if (deep && slider.getRefItems) {
                refItems.push.apply(refItems, slider.getRefItems(deep));
            }
        }
        return refItems;
    }
});


Ext.define('Ext.field.SingleSlider', {
    extend: Ext.field.Slider,
    xtype: 'singlesliderfield',
    
    twoWayBindable: {
        value: 1
    },
    
    
    
    
    
    
    
    defaultBindProperty: 'value',
    
    publishes: {
        value: 1
    },
    applyValue: function(value, oldValue) {
        value = this.callParent([
            value,
            oldValue
        ]);
        if (value && Ext.isArray(value)) {
            value = value[0];
        }
        return value;
    },
    getValue: function() {
        var value = this.callParent();
        if (value && Ext.isArray(value)) {
            value = value[0];
        }
        return value;
    },
    onSliderChange: function(slider, thumb, newValue, oldValue) {
        this.setValue(newValue);
        this.fireEvent('dragchange', this, slider, newValue, oldValue);
    },
    onSliderDragStart: function(slider, thumb, startValue, e) {
        this.fireEvent('dragstart', this, slider, startValue, e);
    },
    onSliderDrag: function(slider, thumb, value, e) {
        var me = this;
        if (me.getLiveUpdate()) {
            me.setValue(value);
        }
        me.fireEvent('drag', me, slider, value, e);
    },
    onSliderDragEnd: function(slider, thumb, startValue, e) {
        this.fireEvent('dragend', this, slider, startValue, e);
    }
});


Ext.define('Ext.field.trigger.SpinDown', {
    extend: Ext.field.trigger.Trigger,
    xtype: 'spindowntrigger',
    alias: 'trigger.spindown',
    classCls: Ext.baseCSSPrefix + 'spindowntrigger',
    handler: 'onSpinDown',
    scope: 'this',
    focusOnTap: false
});


Ext.define('Ext.field.trigger.SpinUp', {
    extend: Ext.field.trigger.Trigger,
    xtype: 'spinuptrigger',
    alias: 'trigger.spinup',
    classCls: Ext.baseCSSPrefix + 'spinuptrigger',
    handler: 'onSpinUp',
    scope: 'this',
    focusOnTap: false
});


Ext.define('Ext.field.Spinner', {
    extend: Ext.field.Number,
    xtype: 'spinnerfield',
    alternateClassName: 'Ext.form.Spinner',
    
    
    
    
    
    config: {
        
        stepValue: 1,
        
        accelerateOnTapHold: true,
        
        cycle: false,
        
        clearable: false,
        
        groupButtons: true
    },
    triggers: {
        spindown: {
            type: 'spindown',
            group: 'spin',
            repeat: true
        },
        spinup: {
            type: 'spinup',
            group: 'spin',
            repeat: true
        }
    },
    
    value: 0,
    
    decimals: 0,
    
    classCls: Ext.baseCSSPrefix + 'spinnerfield',
    groupedButtonsCls: Ext.baseCSSPrefix + 'grouped-buttons',
    initElement: function() {
        this.callParent();
        this.inputElement.dom.readOnly = true;
    },
    updateGroupButtons: function(groupButtons) {
        var downTrigger = this.getTriggers().spindown;
        downTrigger.setGroup(groupButtons ? 'spin' : null);
        downTrigger.setSide(groupButtons ? null : 'left');
    },
    applyTriggers: function(triggers, oldTriggers) {
        var accelerate = this.getAccelerateOnTapHold(),
            upTrigger, downTrigger, upRepeat, downRepeat;
        if (triggers && accelerate) {
            upTrigger = triggers.spinup;
            downTrigger = triggers.spindown;
            upRepeat = upTrigger.repeat;
            if (upRepeat) {
                upTrigger.repeat = Ext.apply({
                    accelerate: accelerate
                }, upRepeat);
            }
            downRepeat = downTrigger.repeat;
            if (downRepeat) {
                downTrigger.repeat = Ext.apply({
                    accelerate: accelerate
                }, downRepeat);
            }
        }
        return this.callParent([
            triggers,
            oldTriggers
        ]);
    },
    onKeyDown: function(e) {
        var limit;
        if (this.getInputType() !== 'number') {
            switch (e.getKey()) {
                case e.UP:
                    e.stopEvent();
                    this.spin(false);
                    break;
                case e.DOWN:
                    e.stopEvent();
                    this.spin(true);
                    break;
                
                case e.HOME:
                    limit = this.getMinValue();
                    if (limit != null) {
                        e.stopEvent();
                        this.setValue(limit);
                    };
                    break;
                case e.END:
                    limit = this.getMaxValue();
                    if (limit != null) {
                        e.stopEvent();
                        this.setValue(limit);
                    };
                    break;
            }
        }
        this.callParent([
            e
        ]);
    },
    
    onSpinDown: function() {
        if (!this.getDisabled() && !this.getReadOnly()) {
            this.spin(true);
        }
    },
    
    onSpinUp: function() {
        if (!this.getDisabled() && !this.getReadOnly()) {
            this.spin(false);
        }
    },
    
    spin: function(down) {
        var me = this,
            originalValue = me.getValue(),
            stepValue = me.getStepValue(),
            direction = down ? 'down' : 'up',
            minValue = me.getMinValue(),
            maxValue = me.getMaxValue(),
            value;
        if (down) {
            value = originalValue - stepValue;
        } else {
            value = originalValue + stepValue;
        }
        
        if (me.getCycle()) {
            if (originalValue == minValue && value < minValue) {
                value = maxValue;
            }
            if (originalValue == maxValue && value > maxValue) {
                value = minValue;
            }
        } else if (minValue != null && value < minValue) {
            value = minValue;
        } else if (maxValue != null && value > maxValue) {
            value = maxValue;
        }
        me.spinning = true;
        me.setValue(value);
        me.spinning = false;
        value = me.getValue();
        me.fireEvent('spin', me, value, direction);
        me.fireEvent('spin' + direction, me, value);
    },
    privates: {
        spinning: false,
        canSetInputValue: function() {
            return this.spinning || this.callParent();
        }
    }
});


Ext.define('Ext.slider.Toggle', {
    extend: Ext.slider.Slider,
    xtype: 'toggleslider',
    config: {
        onThumbUi: 'toggle-on',
        offThumbUi: 'toggle-off'
    },
    
    value: 0,
    classCls: Ext.baseCSSPrefix + 'toggleslider',
    
    minValueCls: Ext.baseCSSPrefix + 'off',
    maxValueCls: Ext.baseCSSPrefix + 'on',
    applyMinValue: function() {
        return 0;
    },
    applyMaxValue: function() {
        return 1;
    },
    applyIncrement: function() {
        return 1;
    },
    updateMinValueCls: function(newCls, oldCls) {
        var element = this.element;
        if (oldCls && element.hasCls(oldCls)) {
            element.replaceCls(oldCls, newCls);
        }
    },
    updateMaxValueCls: function(newCls, oldCls) {
        var element = this.element;
        if (oldCls && element.hasCls(oldCls)) {
            element.replaceCls(oldCls, newCls);
        }
    },
    setIndexValue: function(index, value, animation) {
        var oldValue = this.getValue()[index];
        this.callParent(arguments);
        var thumb = this.thumbs[index],
            newValue = this.getValue();
        if (oldValue !== newValue) {
            this.fireEvent('change', this, thumb, newValue, oldValue);
        }
    },
    onChange: function(thumb, newValue, oldValue) {
        var me = this,
            isOn = newValue > 0,
            onCls = me.maxValueCls,
            offCls = me.minValueCls,
            element = me.element;
        element.addCls(isOn ? onCls : offCls);
        element.removeCls(isOn ? offCls : onCls);
        me.thumbs[0].setUi(isOn ? me.getOnThumbUi() : me.getOffThumbUi());
        me.callParent([
            thumb,
            newValue,
            oldValue
        ]);
    },
    toggle: function() {
        var value = this.getValue();
        this.setValue((value == 1) ? 0 : 1);
        return this;
    },
    onTap: function() {
        if (this.isDisabled() || this.getReadOnly()) {
            return;
        }
        var oldValue = this.getValue(),
            newValue = (oldValue == 1) ? 0 : 1,
            thumb = this.thumbs[0];
        this.setIndexValue(0, newValue, this.getAnimation());
        this.refreshAdjacentThumbConstraints(thumb);
    },
    privates: {
        syncFill: function() {
            var me = this,
                fillElement = me.trackElement.down(me.fillSelector),
                values = me.getArrayValues();
            if (values && (values[0] === 1)) {
                fillElement.show();
            } else {
                fillElement.hide();
            }
        }
    }
});


Ext.define('Ext.field.Toggle', {
    extend: Ext.field.SingleSlider,
    xtype: 'togglefield',
    alternateClassName: 'Ext.form.Toggle',
    
    twoWayBindable: {
        value: 1
    },
    
    publishes: {
        value: 1
    },
    config: {
        
        slider: {
            xtype: 'toggleslider'
        },
        
        activeLabel: null,
        
        inactiveLabel: null,
        
        value: false
    },
    
    bodyAlign: 'start',
    
    classCls: Ext.baseCSSPrefix + 'togglefield',
    
    
    
    
    
    updateActiveLabel: function(newActiveLabel, oldActiveLabel) {
        this.getSlider().element.dom.setAttribute('data-activelabel', newActiveLabel);
    },
    
    updateInactiveLabel: function(newInactiveLabel, oldInactiveLabel) {
        this.getSlider().element.dom.setAttribute('data-inactivelabel', newInactiveLabel);
    },
    applyValue: function(value, oldValue) {
        value = this.callParent([
            value,
            oldValue
        ]);
        if (typeof value !== 'boolean') {
            value = value !== 0;
        }
        return value;
    },
    updateValue: function(value, oldValue) {
        var me = this,
            active = me.getActiveLabel(),
            inactive = me.getInactiveLabel();
        if (active || inactive) {
            me.setLabel(value ? active : inactive);
        }
        me.callParent([
            value,
            oldValue
        ]);
    },
    setSliderValue: function(value) {
        this.getSlider().setValue(value ? 1 : 0);
        return !!value;
    },
    
    toggle: function() {
        
        this.setValue(!this.getValue());
        return this;
    }
});


Ext.define('Ext.field.Url', {
    extend: Ext.field.Text,
    xtype: 'urlfield',
    alternateClassName: 'Ext.form.Url',
    config: {
        
        autoCapitalize: false
    },
    
    inputType: 'url',
    
    classCls: Ext.baseCSSPrefix + 'urlfield'
});


Ext.define('Ext.field.trigger.Component', {
    extend: Ext.field.trigger.Base,
    alias: 'trigger.component',
    classCls: Ext.baseCSSPrefix + 'componenttrigger',
    config: {
        
        component: null
    },
    doDestroy: function() {
        this.setComponent(null);
        this.callParent();
    },
    applyComponent: function(config, existing) {
        return Ext.updateWidget(existing, config, this, 'createComponent');
    },
    createComponent: function(config) {
        return Ext.apply({
            $initParent: this,
            ownerCmp: this
        }, config);
    },
    updateComponent: function(component) {
        if (component) {
            delete component.$initParent;
            component.ownerCmp = this;
            this.el.appendChild(component.el);
        }
    },
    updateDisabled: function(disabled, oldDisabled) {
        this.callParent([
            disabled,
            oldDisabled
        ]);
        this.getComponent().setDisabled(disabled);
    },
    getRefItems: function(deep) {
        var refItems = [],
            component = this.getComponent();
        if (component) {
            refItems.push(component);
            if (deep && component.getRefItems) {
                refItems.push.apply(refItems, component.getRefItems(deep));
            }
        }
        return refItems;
    }
});

Ext.define('Ext.field.trigger.File', {
    extend: Ext.field.trigger.Component,
    alias: 'trigger.file',
    classCls: Ext.baseCSSPrefix + 'filetrigger',
    component: {
        xtype: 'filebutton'
    },
    
    disableOnReadOnly: false
});


Ext.define('Ext.field.trigger.Menu', {
    extend: Ext.field.trigger.Trigger,
    xtype: 'menutrigger',
    alias: 'trigger.menu',
    cachedConfig: {
        
        menuAlign: 'tl-bl?',
        
        destroyMenu: true
    },
    config: {
        
        
        menu: {
            lazy: true,
            $value: null
        }
    },
    doDestroy: function() {
        this.setMenu(null);
        this.callParent();
    },
    applyMenu: function(menu, oldMenu) {
        if (menu) {
            if (Ext.isArray(menu)) {
                menu = {
                    items: menu
                };
            }
        }
        if (oldMenu && !this.getDestroyMenu()) {
            
            oldMenu = null;
        }
        return Ext.factory(menu, Ext.menu.Menu, oldMenu);
    },
    updateMenu: function(menu, oldMenu) {
        if (oldMenu && oldMenu.ownerCmp === this) {
            delete oldMenu.ownerCmp;
        }
        if (menu) {
            menu.ownerCmp = this;
        }
    },
    onClick: function(e) {
        var menu = this.getMenu();
        if (menu) {
            this.showMenu(e, menu);
        } else {
            this.callParent([
                e
            ]);
        }
    },
    showMenu: function(e, menu) {
        var isPointerEvent = !e || e.pointerType;
        menu = menu || this.getMenu();
        if (menu) {
            if (menu.isVisible()) {
                
                if (isPointerEvent) {
                    menu.hide();
                } else {
                    menu.focus();
                }
            } else {
                menu.autoFocus = !isPointerEvent;
                if (menu.isMenu) {
                    menu.showBy(this.element, this.getMenuAlign());
                } else if (menu.isViewportMenu) {
                    menu.setDisplayed(!menu.getDisplayed());
                } else {
                    menu.show();
                }
            }
        }
    }
});


Ext.define('Ext.form.FieldSet', {
    extend: Ext.Container,
    xtype: 'fieldset',
    mixins: [
        Ext.form.Borders
    ],
    config: {
        
        title: null,
        
        instructions: null
    },
    autoSize: null,
    baseCls: Ext.baseCSSPrefix + 'form-fieldset',
    
    applyTitle: function(title) {
        if (typeof title == 'string') {
            title = {
                title: title
            };
        }
        Ext.applyIf(title, {
            docked: 'top',
            cls: this.baseCls + '-title'
        });
        return Ext.factory(title, Ext.Title, this._title);
    },
    
    updateTitle: function(newTitle, oldTitle) {
        if (newTitle) {
            this.add(newTitle);
        }
        if (oldTitle) {
            this.remove(oldTitle);
        }
    },
    
    getTitle: function() {
        var title = this._title;
        if (title && title instanceof Ext.Title) {
            return title.getTitle();
        }
        return title;
    },
    
    applyInstructions: function(instructions) {
        if (typeof instructions == 'string') {
            instructions = {
                title: instructions
            };
        }
        Ext.applyIf(instructions, {
            docked: 'bottom',
            cls: this.baseCls + '-instructions'
        });
        return Ext.factory(instructions, Ext.Title, this._instructions);
    },
    
    updateInstructions: function(newInstructions, oldInstructions) {
        if (newInstructions) {
            this.add(newInstructions);
        }
        if (oldInstructions) {
            this.remove(oldInstructions);
        }
    },
    
    getInstructions: function() {
        var instructions = this._instructions;
        if (instructions && instructions instanceof Ext.Title) {
            return instructions.getTitle();
        }
        return instructions;
    },
    
    updateDisabled: function(newDisabled) {
        this.query('field').forEach(function(field) {
            field.setDisabled(newDisabled);
        });
        return this;
    }
});


Ext.define('Ext.form.Panel', {
    extend: Ext.field.Panel,
    xtype: 'formpanel',
    alternateClassName: 'Ext.form.FormPanel',
    classCls: Ext.baseCSSPrefix + 'formpanel',
    element: {
        reference: 'element',
        tag: 'form',
        novalidate: 'novalidate'
    },
    
    
    config: {
        
        enableSubmissionForm: true,
        
        enctype: null,
        
        method: 'post',
        
        multipartDetection: true,
        
        standardSubmit: false,
        
        submitOnAction: false,
        
        trackResetOnLoad: false
    },
    getTemplate: function() {
        var template = this.callParent();
        
        template.push({
            tag: 'input',
            type: 'submit',
            cls: Ext.baseCSSPrefix + 'hidden-submit'
        });
        return template;
    },
    
    initialize: function() {
        this.callParent();
        this.element.on('submit', 'onSubmit', this);
    },
    applyEnctype: function(newValue) {
        var form = this.element.dom || null;
        if (form) {
            if (newValue) {
                form.setAttribute("enctype", newValue);
            } else {
                form.setAttribute("enctype");
            }
        }
    },
    
    onSubmit: function(event) {
        var me = this;
        if (event && !me.getStandardSubmit()) {
            event.stopEvent();
        } else {
            this.submit(null, event);
        }
    },
    updateSubmitOnAction: function(value) {
        this[value ? 'on' : 'un']({
            action: 'onFieldAction',
            scope: this
        });
    },
    
    onFieldAction: function(field) {
        if (this.getSubmitOnAction()) {
            field.blur();
            this.submit();
        }
    },
    
    submit: function(options, e) {
        options = options || {};
        var me = this,
            formValues = me.getValues(me.getStandardSubmit() || !options.submitDisabled),
            form = me.element.dom || {};
        if (this.getEnableSubmissionForm()) {
            form = this.createSubmissionForm(form, formValues);
        }
        options = Ext.apply({
            url: me.getUrl() || form.action,
            submit: false,
            form: form,
            method: me.getMethod() || form.method || 'post',
            autoAbort: false,
            params: null,
            waitMsg: null,
            headers: null,
            success: null,
            failure: null
        }, options || {});
        return me.fireAction('beforesubmit', [
            me,
            formValues,
            options,
            e
        ], 'doBeforeSubmit', null, null, 'after');
    },
    privates: {
        
        applyExtraParams: function(options) {
            var form = options.form,
                params = Ext.merge(this.getBaseParams() || {}, options.params),
                name, input;
            for (name in params) {
                input = document.createElement('input');
                input.setAttribute('type', 'text');
                input.setAttribute('name', name);
                input.setAttribute('value', params[name]);
                form.appendChild(input);
            }
        },
        
        beforeAjaxSubmit: function(form, options, successFn, failureFn) {
            var me = this,
                url = options.url || me.getUrl(),
                request = Ext.merge({}, {
                    url: url,
                    timeout: me.getTimeout() * 1000,
                    form: form,
                    scope: me
                }, options);
            delete request.success;
            delete request.failure;
            request.params = Ext.merge(me.getBaseParams() || {}, options.params);
            request.header = Ext.apply({
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
            }, options.headers || {});
            request.callback = function(callbackOptions, success, response) {
                var responseText = response.responseText,
                    responseXML = response.responseXML,
                    statusResult = Ext.data.request.Ajax.parseStatus(response.status, response);
                if (form.$fileswap) {
                    var original, placeholder;
                    Ext.each(form.$fileswap, function(item) {
                        original = item.original;
                        placeholder = item.placeholder;
                        placeholder.parentNode.insertBefore(original, placeholder.nextSibling);
                        placeholder.parentNode.removeChild(placeholder);
                    });
                    form.$fileswap = null;
                    delete form.$fileswap;
                }
                me.setMasked(false);
                if (response.success === false) {
                    success = false;
                }
                if (success) {
                    if (statusResult && responseText && responseText.length === 0) {
                        success = true;
                    } else {
                        if (!Ext.isEmpty(response.responseBytes)) {
                            success = statusResult.success;
                        } else {
                            if (Ext.isString(responseText) && response.request.options.responseType === "text") {
                                response.success = true;
                            } else if (Ext.isString(responseText)) {
                                try {
                                    response = Ext.decode(responseText);
                                } catch (e) {
                                    response.success = false;
                                    response.error = e;
                                    response.message = e.message;
                                }
                            } else if (Ext.isSimpleObject(responseText)) {
                                response = responseText;
                                Ext.applyIf(response, {
                                    success: true
                                });
                            }
                            if (!Ext.isEmpty(responseXML)) {
                                response.success = true;
                            }
                            success = !!response.success;
                        }
                    }
                    if (success) {
                        successFn(response, responseText);
                    } else {
                        failureFn(response, responseText);
                    }
                } else {
                    failureFn(response, responseText);
                }
            };
            if (Ext.feature.has.XHR2 && request.xhr2) {
                delete request.form;
                var formData = request.data = new FormData(form);
                if (request.params) {
                    Ext.iterate(request.params, function(name, value) {
                        if (Ext.isArray(value)) {
                            Ext.each(value, function(v) {
                                formData.append(name, v);
                            });
                        } else {
                            formData.append(name, value);
                        }
                    });
                    delete request.params;
                }
            }
            return Ext.Ajax.request(request);
        },
        
        beforeDirectSubmit: function(api, form, options, successFn, failureFn) {
            var me = this,
                submit;
            me.applyExtraParams(options);
            api = Ext.direct.Manager.resolveApi(api, me);
            me.setApi(api);
            submit = api.submit;
            if (!submit) {
                Ext.raise("Cannot find Ext Direct API method for submit action");
            }
            return submit(form, function(data, response, success) {
                me.setMasked(false);
                if (success) {
                    if (data.success) {
                        successFn(response, data);
                    } else {
                        failureFn(response, data);
                    }
                } else {
                    failureFn(response, data);
                }
            }, me);
        },
        
        beforeStandardSubmit: function(form, options) {
            if (options.url && Ext.isEmpty(form.action)) {
                form.action = options.url;
            }
            
            
            var fields = this.query('spinnerfield'),
                ln = fields.length,
                body = document.body,
                i, field;
            for (i = 0; i < ln; i++) {
                field = fields[i];
                if (!field.getDisabled()) {
                    field.setDisabled(false);
                }
            }
            body.appendChild(form);
            form.method = (options.method || form.method).toLowerCase();
            form.submit();
            
            
            
            body.removeChild(form);
        },
        
        createSubmissionForm: function(form, values) {
            var fields = this.getFields(),
                name, input, field, fileTrigger, inputDom;
            if (form.nodeType === 1) {
                form = form.cloneNode(false);
                for (name in values) {
                    input = document.createElement("input");
                    input.setAttribute("type", "text");
                    input.setAttribute("name", name);
                    input.setAttribute("value", values[name]);
                    form.appendChild(input);
                }
            }
            for (name in fields) {
                if (fields.hasOwnProperty(name)) {
                    field = fields[name];
                    if (field.isFile) {
                        
                        fileTrigger = field.getTriggers().file;
                        inputDom = fileTrigger && fileTrigger.getComponent().buttonElement.dom;
                        if (inputDom) {
                            if (!form.$fileswap)  {
                                form.$fileswap = [];
                            }
                            
                            input = inputDom.cloneNode(true);
                            inputDom.parentNode.insertBefore(input, inputDom.nextSibling);
                            form.appendChild(inputDom);
                            form.$fileswap.push({
                                original: inputDom,
                                placeholder: input
                            });
                        }
                    } else if (field.isPassword) {
                        if (field.getInputType() !== "password") {
                            field.setRevealed(false);
                        }
                    }
                }
            }
            return form;
        },
        
        doBeforeSubmit: function(me, formValues, options) {
            var form = options.form || {},
                multipartDetected = false,
                ret;
            if (this.getMultipartDetection() === true) {
                this.getFields(false).forEach(function(field) {
                    if (field.isFile === true) {
                        multipartDetected = true;
                        return false;
                    }
                });
                if (multipartDetected) {
                    form.setAttribute("enctype", "multipart/form-data");
                }
            }
            if (options.enctype) {
                form.setAttribute("enctype", options.enctype);
            }
            if (me.getStandardSubmit()) {
                ret = me.beforeStandardSubmit(form, options);
            } else {
                var api = me.getApi(),
                    scope = options.scope || me,
                    failureFn = function(response, responseText) {
                        if (Ext.isFunction(options.failure)) {
                            options.failure.call(scope, me, response, responseText);
                        }
                        me.fireEvent('exception', me, response);
                    },
                    successFn = function(response, responseText) {
                        if (Ext.isFunction(options.success)) {
                            options.success.call(options.scope || me, me, response, responseText);
                        }
                        me.fireEvent('submit', me, response);
                    },
                    waitMsg = options.waitMsg;
                if (options.waitMsg) {
                    if (typeof waitMsg === 'string') {
                        waitMsg = {
                            xtype: 'loadmask',
                            message: waitMsg
                        };
                    }
                    me.setMasked(waitMsg);
                }
                if (api) {
                    ret = me.beforeDirectSubmit(api, form, options, successFn, failureFn);
                } else {
                    ret = me.beforeAjaxSubmit(form, options, successFn, failureFn);
                }
            }
            return ret;
        }
    }
});


Ext.define('Ext.grid.CellEditor', {
    extend: Ext.Editor,
    xtype: 'celleditor',
    isCellEditor: true,
    floated: false,
    classCls: Ext.baseCSSPrefix + 'celleditor',
    config: {
        
        autoPin: true
    },
    swallowKeys: false,
    layout: 'fit',
    shadow: false,
    allowBlur: true,
    alignment: 'tl-tl',
    zIndex: 10,
    
    useBoundValue: false,
    inheritUi: true,
    field: {
        inheritUi: true
    },
    
    startEdit: function(location, value, doFocus) {
        var me = this,
            cell, el, row, grid, result;
        if (location) {
            cell = location.cell;
            el = cell.el;
            value = value != null ? value : location.record.get(cell.dataIndex);
            
            
            
            
            me.ownerCmp = cell;
            
            me.render(el);
            me.callParent([
                el,
                value,
                doFocus
            ]);
            
            
            if (me.editing) {
                me.$activeRow = row = location.row;
                me.$activeGrid = grid = row.getGrid();
                me.editingPlugin.editing = true;
                me.editingPlugin.location = me.$activeLocation = result = new Ext.grid.Location(grid, me.getField().getFocusEl());
                me.editingPlugin.activeEditor = me;
                grid.stickItem(row, {
                    autoPin: me.getAutoPin()
                });
            }
        }
        return result;
    },
    onFocusLeave: function(e) {
        
        if (!this.editingPlugin.getGrid().destroying) {
            if (this.isCancelling) {
                this.cancelEdit();
            } else {
                this.completeEdit(false);
            }
        }
        this.isCancelling = false;
    },
    onFocusEnter: function(e) {
        
        if (this.$activeLocation) {
            e.relatedTarget = e.fromElement = this.$activeLocation.getFocusEl('dom');
        }
        this.callParent([
            e
        ]);
    },
    
    getLocation: function() {
        return this.$activeLocation;
    },
    onSpecialKey: function(field, event) {
        var me = this;
        
        
        
        if (event.getKey() === event.ESC) {
            me.isCancelling = true;
        } else {
            me.callParent([
                field,
                event
            ]);
        }
    },
    onEditComplete: function(remainVisible, cancelling) {
        var me = this,
            location = me.$activeLocation,
            value = me.getValue(),
            record, dataIndex, row, grid;
        me.callParent([
            remainVisible,
            cancelling
        ]);
        if (location) {
            grid = location.row.getGrid();
            
            
            if (!cancelling && value !== me.startValue) {
                record = location.record;
                dataIndex = location.cell.dataIndex;
                if (record) {
                    record.set(dataIndex, value);
                    
                    grid.ensureVisible(location.record);
                    location.refresh();
                }
            }
            if (!remainVisible) {
                row = location.row;
                grid.stickItem(row, null);
                me.$stickyVisibility = me.$activeLocation = me.$activeRow = me.$activeGrid = null;
                me.editingPlugin.editing = false;
                me.editingPlugin.location = me.editingPlugin.activeEditor = null;
            }
        }
    },
    
    realign: Ext.emptyFn,
    toggleBoundEl: function(visible) {
        var location = this.$activeLocation,
            cell, bodyEl;
        if (location && this.hideEl) {
            cell = location.cell;
            
            if (cell) {
                bodyEl = cell.bodyElement;
                bodyEl.setVisibility(visible);
            }
        }
    }
});


Ext.define('Ext.grid.Location', {
    extend: Ext.dataview.Location,
    
    isGridLocation: true,
    
    actionable: false,
    
    cell: null,
    
    column: null,
    
    columnIndex: -1,
    
    summary: false,
    
    row: null,
    
    rowBody: null,
    
    isTreeLocation: false,
    inheritableStatics: {
        
        defineProtoProperty: function(propName, getterName) {
            Object.defineProperty(this.prototype, propName, {
                get: function() {
                    
                    var v = this[getterName]();
                    
                    Object.defineProperty(this, propName, {
                        value: v,
                        configurable: true
                    });
                    return v;
                }
            });
        }
    },
    
    attach: function(source) {
        var me = this,
            view = me.view,
            store = view.store,
            item, cell, column, columns, sourceRec, sourceCol, first;
        if (source.constructor === Object) {
            sourceRec = source.record;
            if (typeof sourceRec === 'number') {
                sourceRec = store.getAt(Math.max(Math.min(sourceRec, store.getCount() - 1), 0));
            }
            sourceCol = source.column;
            if (typeof sourceCol === 'number') {
                columns = view.getVisibleColumns();
                sourceCol = columns[Math.max(Math.min(sourceCol, columns.length - 1), 0)];
            }
            if (!(sourceRec && sourceCol)) {
                if (sourceRec) {
                    sourceCol = view.getFirstVisibleColumn();
                } else {
                    sourceRec = store.getAt(0);
                }
            }
            cell = view.mapToCell(sourceRec, sourceCol);
            if (cell) {
                source = cell.element;
            } else {
                me._setColumn(sourceCol);
                source = sourceRec;
            }
        }
        me.callParent([
            source
        ]);
        item = me.item;
        if (item && item.isGridRow) {
            me.row = item;
            me.summary = item.isSummaryRow;
            if (!cell) {
                cell = view.mapToCell(source);
                if (!cell) {
                    columns = view.getVisibleColumns();
                    first = columns[0];
                    if (first) {
                        cell = item.getCellByColumn(first);
                    }
                }
            }
            me.cell = cell;
            if (cell) {
                me.column = column = cell.getColumn();
                columns = columns || view.getVisibleColumns();
                me.columnIndex = columns.indexOf(column);
                me.isTreeLocation = !!cell.isTreeCell;
            } else {
                me.rowBody = view.mapToRowBody(source);
            }
        }
    },
    
    clone: function(options) {
        var me = this,
            ret = me.callParent(),
            record, column, cell;
        if (options) {
            if (options.record !== undefined) {
                record = options.record;
            }
            if (options.column !== undefined) {
                column = options.column;
            }
            delete ret.sourceElement;
        }
        if (record) {
            delete me.source;
            me.superclass.attach.call(ret, record);
            ret.row = ret.item;
        } else {
            ret.row = ret.child = me.row;
            ret.summary = me.summary;
            ret.rowBody = me.rowBody;
        }
        if (column) {
            ret._setColumn(column);
        } else {
            ret.cell = cell = me.cell;
            ret.column = me.column;
            ret.columnIndex = me.columnIndex;
            me.isTreeLocation = !!(cell && cell.isTreeCell);
        }
        return ret;
    },
    cloneForColumn: function(column) {
        return this.clone({
            column: column
        });
    },
    
    equals: function(other) {
        var me = this;
        if (other && other.view === me.view && other.isGridLocation) {
            
            if (me.actionable !== other.actionable) {
                return false;
            }
            
            if (me.sourceElement && me.actionable) {
                return other.sourceElement === me.sourceElement;
            }
            
            
            
            
            
            
            if (me.recordIndex !== other.recordIndex) {
                return false;
            }
            
            
            
            
            return me.column === other.column;
        }
        return false;
    },
    
    equalCell: function(other) {
        var me = this;
        return other && other.view === me.view && other.isGridLocation && me.recordIndex === other.recordIndex && me.column === other.column;
    },
    getFocusEl: function(as) {
        var cell = this.get(),
            ret;
        if (this.actionable) {
            ret = this.sourceElement;
        } else {
            ret = cell && !cell.destroyed && cell.el.dom;
        }
        
        return Ext.getBody().contains(ret) ? (as === 'dom' || as === true) ? ret : Ext.get(ret) : null;
    },
    
    getCell: function(as) {
        var result = this.cell,
            ret = null;
        if (result) {
            ret = (as === 'dom' || as === true) ? result.el.dom : (as === 'cmp' ? result : result.el);
        }
        return ret;
    },
    
    get: function() {
        return this.cell;
    },
    isFirstColumn: function() {
        var column = this.column,
            ret = false;
        if (column) {
            ret = this.view.isFirstVisibleColumn(column);
        }
        return ret;
    },
    isLastColumn: function() {
        var column = this.column,
            ret = false;
        if (column) {
            ret = this.view.isLastVisibleColumn(column);
        }
        return ret;
    },
    
    refresh: function() {
        var me = this,
            column = me.column,
            oldColumnIndex = me.columnIndex,
            newColumnIndex = me.view.getHeaderContainer().indexOfLeaf(column),
            location;
        if (newColumnIndex === -1) {
            newColumnIndex = (oldColumnIndex === -1) ? 0 : oldColumnIndex;
        }
        
        location = me.callParent();
        
        return location._setColumn(newColumnIndex);
    },
    
    next: function(options) {
        var me = this,
            candidate;
        if (me.actionable) {
            return me.navigate();
        } else {
            
            for (candidate = me.nextCell(options); candidate && !candidate.get().el.isFocusable(); candidate = candidate.nextCell(options)){}
            return candidate || me;
        }
    },
    
    previous: function(options) {
        var me = this,
            candidate;
        if (me.actionable) {
            return me.navigate(true);
        } else {
            
            for (candidate = me.previousCell(options); candidate && !candidate.get().el.isFocusable(); candidate = candidate.previousCell(options)){}
            return candidate || me;
        }
    },
    
    down: function(options) {
        var me = this,
            column = options && options.column || me.column,
            candidate = me.nextItem(options),
            cell;
        if (candidate) {
            candidate._setColumn(column);
            cell = candidate.get();
            while (candidate && (!cell || !cell.el.isFocusable())) {
                candidate = candidate.nextItem(options);
                if (candidate) {
                    candidate._setColumn(column);
                    cell = candidate.get();
                }
            }
            if (candidate && !candidate.equals(me)) {
                return candidate;
            }
        }
        return me;
    },
    
    up: function(options) {
        var me = this,
            column = options && options.column || me.column,
            candidate = me.previousItem(options),
            cell;
        if (candidate) {
            candidate._setColumn(column);
            cell = candidate.get();
            while (candidate && (!cell || !cell.el.isFocusable())) {
                candidate = candidate.previousItem(options);
                if (candidate) {
                    candidate._setColumn(column);
                    cell = candidate.get();
                }
            }
        }
        if (candidate && !candidate.equals(me)) {
            return candidate;
        }
        return me;
    },
    privates: {
        determineActionable: function() {
            var target = this.sourceElement,
                cell = this.cell,
                actionable = false;
            
            
            
            
            if (target && (!cell || cell.destroyed || cell.element.dom !== target)) {
                actionable = Ext.fly(target).isFocusable(true);
            }
            return actionable;
        },
        
        navigate: function(reverse) {
            var me = this,
                activeEl = me.sourceElement,
                view = me.view,
                scrollable = view.getScrollable(),
                actionables = view.getNavigationModel().actionables,
                len = actionables && actionables.length,
                candidate = me.clone(),
                previousCandidate = me.clone(),
                testEl,
                visitOptions = {
                    callback: function(el) {
                        testEl = Ext.fly(el);
                        if (!testEl.$isFocusTrap && testEl.isFocusable()) {
                            component = Ext.Component.from(el);
                            
                            
                            if (!component || !component.getDisabled()) {
                                focusables.push(el);
                            }
                        }
                    },
                    reverse: reverse,
                    skipSelf: true
                },
                i, result, component,
                focusables = [];
            
            
            while (candidate && !result && candidate.get()) {
                
                focusables.length = 0;
                candidate.get().el.visit(visitOptions);
                
                activeEl = focusables[activeEl ? (Ext.Array.indexOf(focusables, activeEl) + 1) : 0];
                if (activeEl) {
                    result = candidate;
                    result.source = result.sourceElement = activeEl;
                    delete result.actionable;
                    
                    
                    
                    if (candidate.child) {
                        scrollable.ensureVisible(candidate.child.el);
                    }
                    scrollable.ensureVisible(activeEl);
                    activeEl.focus();
                } else 
                
                {
                    candidate = candidate[reverse ? 'previousCell' : 'nextCell']();
                    
                    
                    
                    if (candidate.equals(previousCandidate)) {
                        return me;
                    }
                    
                    if (candidate && len) {
                        for (i = 0; !result && i < len; i++) {
                            result = actionables[i].activateCell(candidate);
                        }
                    }
                }
                previousCandidate = candidate;
            }
            return result || me;
        },
        activate: function() {
            var me = this,
                view = me.view,
                scrollable = view.getScrollable(),
                actionables = view.getNavigationModel().actionables,
                len = actionables && actionables.length,
                candidate = me.clone(),
                activeEl, i, result;
            
            candidate.get().el.visit({
                callback: function(el) {
                    if (Ext.fly(el).isFocusable()) {
                        activeEl = el;
                        return false;
                    }
                },
                skipSelf: true
            });
            if (activeEl) {
                result = candidate;
                result.source = result.sourceElement = activeEl;
                delete result.actionable;
                
                
                
                if (candidate.child) {
                    scrollable.ensureVisible(candidate.child.el);
                }
                scrollable.ensureVisible(activeEl);
                activeEl.focus();
            } else 
            
            {
                for (i = 0; !result && i < len; i++) {
                    result = actionables[i].activateCell(candidate);
                }
            }
            return result;
        },
        getFocusables: function() {
            var focusables = [],
                element = this.sourceElement;
            if (element) {
                Ext.fly(element).visit({
                    callback: function(el) {
                        if (Ext.fly(el).isFocusable()) {
                            focusables.push(el);
                        }
                    },
                    skipSelf: true
                });
            }
            return focusables;
        },
        
        nextCell: function(options) {
            var me = this,
                view = me.view,
                startPoint = me.clone(),
                result = me.clone(),
                columns = view.getVisibleColumns(),
                len = columns.length,
                wrap;
            if (options) {
                if (typeof options === 'boolean') {
                    wrap = options;
                } else {
                    wrap = options.wrap;
                }
            }
            do {
                if (result.column === columns[len - 1] || !me.child.isGridRow) {
                    result = me.down(Ext.apply({
                        column: columns[0]
                    }, options));
                } else {
                    result._setColumn(result.columnIndex + 1);
                }
                if (result && result.equals(startPoint)) {
                    break;
                }
            } while (
            
            
            
            result && !result.sourceElement);
            return result;
        },
        
        previousCell: function(options) {
            var me = this,
                view = me.view,
                startPoint = me.clone(),
                result = me.clone(),
                columns = view.getVisibleColumns(),
                wrap;
            if (options) {
                if (typeof options === 'boolean') {
                    wrap = options;
                } else {
                    wrap = options.wrap;
                }
            }
            do {
                if (result.column === columns[0] || !me.child.isGridRow) {
                    result = me.up(Ext.apply({
                        column: columns.length - 1
                    }, options));
                } else {
                    result._setColumn(result.columnIndex - 1);
                }
                if (result && result.equals(startPoint)) {
                    break;
                }
            } while (
            
            
            
            result && !result.sourceElement);
            return result;
        },
        
        _setColumn: function(column) {
            var me = this,
                columns = me.view.getVisibleColumns(),
                index;
            if (typeof column === 'number') {
                index = column;
                column = columns[index];
            } else {
                index = columns.indexOf(column);
            }
            delete me.event;
            delete me.actionable;
            me.column = column;
            me.columnIndex = index;
            me.cell = me.row && me.row.getCellByColumn(column);
            if (me.cell) {
                me.isTreeLocation = !!me.cell.isTreeCell;
                me.sourceElement = me.cell.el.dom;
            }
            return me;
        }
    }
}, function(Cls) {
    Cls.defineProtoProperty('actionable', 'determineActionable');
});


Ext.define('Ext.grid.NavigationModel', {
    extend: Ext.dataview.NavigationModel,
    alias: 'navmodel.grid',
    locationClass: 'Ext.grid.Location',
    statics: {
        
        ignoreInputFieldKeys: {
            PAGE_UP: true,
            PAGE_DOWN: true,
            END: true,
            HOME: true,
            LEFT: true,
            UP: true,
            RIGHT: true,
            DOWN: true
        }
    },
    
    setLocation: function(location, options) {
        var me = this,
            view = me.getView(),
            event = options && options.event;
        me.columnIndex = -1;
        
        if (location != null && !location.isGridLocation) {
            if (Ext.isArray(location)) {
                location = {
                    column: location[0],
                    record: location[1]
                };
            } else if (typeof location === 'number') {
                location = view.store.getAt(location);
            }
            location = me.createLocation(location);
            if (event) {
                location.event = event;
            }
        }
        return me.callParent([
            location,
            options
        ]);
    },
    clearLocation: function() {
        var me = this,
            item;
        if (me.location) {
            me.previousLocation = me.location;
            item = me.location.sourceElement;
            if (item) {
                Ext.fly(item).removeCls(me.focusedCls);
            }
            me.location = null;
        }
    },
    registerActionable: function(actionable) {
        var me = this,
            view = me.getView(),
            actionables = me.actionables || (me.actionables = []),
            triggerEvent, listeners;
        if (!Ext.Array.contains(actionables, actionable)) {
            actionables.push(actionable);
            triggerEvent = actionable.getTriggerEvent();
            if (triggerEvent) {
                
                listeners = {
                    scope: me,
                    args: [
                        actionable
                    ]
                };
                listeners[triggerEvent] = 'triggerActionable';
                
                
                
                
                actionable.triggerEventListener = view.bodyElement.on(listeners);
            }
        }
    },
    unregisterActionable: function(actionable) {
        var actionables = this.actionables;
        if (actionables) {
            Ext.Array.remove(actionables, actionable);
        }
    },
    privates: {
        
        
        onFocusMove: function(e) {
            var me = this,
                view = me.getView(),
                location = me.getLocation();
            
            
            
            
            if (e.toElement === view.el.dom && location) {
                me.clearLocation();
                return me.setLocation(location);
            }
            location = me.createLocation(e);
            
            
            if (!location.equals(me.location)) {
                me.handleLocationChange(location, {
                    event: e,
                    navigate: false
                });
            }
        },
        
        processViewEvent: function(e) {
            var me = this,
                view = me.getView(),
                cell = view.mapToCell(e);
            if (Ext.fly(e.target).isInputField() && me.self.ignoreInputFieldKeys[e.getKeyName()]) {
                return false;
            }
            
            if (cell && cell.row.grid === view) {
                return e;
            }
        },
        
        activateCell: function(location) {
            
            location.clone().activate();
        },
        triggerActionable: function(actionable, e) {
            var actionLocation;
            
            actionLocation = actionable.activateCell(this.createLocation(e));
            
            
            
            
            if (actionLocation) {
                this.setLocation(actionLocation);
            }
        },
        onChildTouchStart: function(view, location) {
            var e = location.event;
            
            if (location.header || location.footer) {
                e.preventDefault();
            } else {
                
                
                
                
                
                
                if (this.location && !this.location.equalCell(location)) {
                    this.setLocation(location, {
                        event: location.event,
                        navigate: this.getView().getTriggerEvent() === 'childtouchstart'
                    });
                }
            }
        },
        onKeyUp: function(e) {
            
            e.preventDefault();
            if (!this.location.actionable) {
                if (this.location) {
                    this.moveUp(e);
                } else {
                    this.setLocation(0);
                }
            }
        },
        onKeyDown: function(e) {
            
            e.preventDefault();
            if (!this.location.actionable) {
                if (this.location) {
                    this.moveDown(e);
                } else {
                    this.setLocation(0);
                }
            }
        },
        onKeyLeft: function(e) {
            var location = this.location,
                isSimpleTree = location.isLastColumn() && location.isFirstColumn();
            if (!location.actionable) {
                
                e.preventDefault();
                
                if (location.isTreeLocation && !location.record.isLeaf() && location.record.isExpanded()) {
                    
                    
                    if (isSimpleTree === !e.ctrlKey) {
                        return location.cell.collapse();
                    }
                }
                
                if (!(e.shiftKey && location.isFirstColumn())) {
                    this.movePrevious({
                        event: e
                    });
                }
            }
            
            else if (Ext.fly(e.target).isInputField()) {
                return true;
            }
        },
        onKeyRight: function(e) {
            var location = this.location,
                isSimpleTree = location.isLastColumn() && location.isFirstColumn();
            if (!location.actionable) {
                
                e.preventDefault();
                
                if (location.isTreeLocation && !location.record.isLeaf() && !location.record.isExpanded()) {
                    
                    
                    if (isSimpleTree === !e.ctrlKey) {
                        return location.cell.expand();
                    }
                }
                
                if (!(e.shiftKey && location.isLastColumn())) {
                    this.moveNext({
                        event: e
                    });
                }
            }
            
            else if (Ext.fly(e.target).isInputField()) {
                return true;
            }
        },
        onKeyF2: function(e) {
            
            
            
            if (this.location.actionable) {
                this.onKeyEsc();
            } else {
                this.activateCell(this.location);
            }
        },
        onKeyEsc: function(e) {
            
            
            if (this.location.actionable) {
                this.location.get().el.focus();
            }
        },
        onKeyTab: function(e) {
            var me = this,
                view = me.getView(),
                location = me.location,
                navigate;
            if (location.actionable) {
                navigate = function() {
                    me.location = e.shiftKey ? location.previous() : location.next();
                };
                
                view.ensureVisible(location.record).then(function() {
                    
                    
                    
                    if (view.mapToItem(location.record)) {
                        navigate();
                    } else {
                        Ext.defer(navigate, 100);
                    }
                });
            } else 
            {
                return true;
            }
        },
        onKeyPageDown: function(e) {
            
            e.preventDefault();
            if (!this.location.actionable) {
                var me = this,
                    view = me.getView(),
                    y = (view.infinite ? view.getItemTop(me.location.child) : me.location.child.el.dom.offsetTop) + view.getVisibleHeight(),
                    candidate = view.getRecordIndexFromPoint(0, y);
                view.ensureVisible(candidate).then(function() {
                    candidate = new Ext.grid.Location(view, {
                        record: candidate,
                        column: me.location.column
                    });
                    
                    
                    if (!(candidate.sourceElement && Ext.fly(candidate.sourceElement).isFocusable())) {
                        candidate = candidate.up();
                    }
                    
                    me.setLocation(candidate, {
                        event: e
                    });
                });
            }
        },
        onKeyPageUp: function(e) {
            
            e.preventDefault();
            if (!this.location.actionable) {
                var me = this,
                    view = me.getView(),
                    y = (view.infinite ? view.getItemTop(me.location.child) : me.location.child.el.dom.offsetTop) - view.getVisibleHeight(),
                    candidate = view.getRecordIndexFromPoint(0, y);
                view.ensureVisible(candidate).then(function() {
                    candidate = new Ext.grid.Location(view, {
                        record: candidate,
                        column: me.location.column
                    });
                    
                    
                    if (!(candidate.sourceElement && Ext.fly(candidate.sourceElement).isFocusable())) {
                        candidate = candidate.down();
                    }
                    
                    me.setLocation(candidate, {
                        event: e
                    });
                });
            }
        },
        onKeyHome: function(e) {
            
            e.preventDefault();
            if (!this.location.actionable) {
                
                if (e.ctrlKey) {
                    this.setLocation({
                        record: this.getView().getStore().first(),
                        column: this.location.column
                    }, {
                        event: e
                    });
                } else 
                {
                    this.setLocation({
                        record: this.location.record,
                        column: this.getView().getFirstVisibleColumn()
                    }, {
                        event: e
                    });
                }
            }
        },
        onKeyEnd: function(e) {
            
            e.preventDefault();
            if (!this.location.actionable) {
                
                if (e.ctrlKey) {
                    this.setLocation({
                        record: this.getView().getStore().last(),
                        column: this.location.column
                    }, {
                        event: e
                    });
                } else 
                {
                    this.setLocation({
                        record: this.location.record,
                        column: this.getView().getLastVisibleColumn()
                    }, {
                        event: e
                    });
                }
            }
        },
        onKeySpace: function(e) {
            var target = Ext.fly(e.target),
                events, focusables, result;
            
            
            this.onNavigate(e);
            if (!this.location.actionable) {
                focusables = this.location.getFocusables();
                if (focusables.length) {
                    events = Ext.get(focusables[0]).events;
                }
            } else 
            
            {
                if (target.isInputField()) {
                    result = true;
                } else {
                    events = target.events;
                }
            }
            
            if (events) {
                if (events.tap) {
                    events.tap.fire(e);
                }
                if (events.click) {
                    events.click.fire(e);
                }
            }
            return result;
        },
        
        onKeyEnter: function(e) {
            var l = this.location;
            
            
            e.stopEvent();
            
            if (!l.actionable) {
                
                if (l.isTreeLocation && l.record.data.checked != null) {
                    l.record.set('checked', !l.record.data.checked);
                } else {
                    this.activateCell(l);
                }
            } else 
            {
                this.onKeySpace(e);
            }
        },
        onSelectAllKeyPress: function(e) {
            
            if (Ext.fly(e.target).isInputField()) {
                return true;
            } else 
            {
                return this.callParent([
                    e
                ]);
            }
        },
        moveUp: function(e) {
            var location = this.getLocation();
            if (location) {
                location = location.up();
                if (location) {
                    this.setLocation(location, {
                        event: e
                    });
                }
            }
        },
        moveDown: function(e) {
            var location = this.getLocation();
            if (location) {
                location = location.down();
                if (location) {
                    this.setLocation(location, {
                        event: e
                    });
                }
            }
        }
    }
});


Ext.define('Ext.grid.cell.Base', {
    extend: Ext.Widget,
    xtype: 'gridcellbase',
    isGridCell: true,
    mixins: [
        Ext.mixin.Toolable
    ],
    cachedConfig: {
        
        align: null,
        
        cls: null,
        
        bodyCls: null,
        
        bodyStyle: null,
        
        cellCls: null,
        
        selectable: null
    },
    config: {
        
        column: null,
        
        hidden: false,
        
        record: null,
        
        value: null
    },
    classCls: Ext.baseCSSPrefix + 'gridcell',
    dirtyCls: Ext.baseCSSPrefix + 'dirty',
    alignCls: {
        left: Ext.baseCSSPrefix + 'align-left',
        center: Ext.baseCSSPrefix + 'align-center',
        right: Ext.baseCSSPrefix + 'align-right'
    },
    inheritUi: true,
    cellSelector: '.' + Ext.baseCSSPrefix + 'gridcell',
    defaultBindProperty: 'value',
    toolDefaults: {
        zone: 'head',
        ui: 'gridcell'
    },
    getTemplate: function() {
        var template = {
                reference: 'bodyElement',
                cls: Ext.baseCSSPrefix + 'body-el',
                uiCls: 'body-el'
            };
        
        
        if (!(template.children = this.innerTemplate)) {
            
            template.html = '\xa0';
        }
        return [
            template
        ];
    },
    doDestroy: function() {
        this.setColumn(null);
        this.setRecord(null);
        this.mixins.toolable.doDestroy.call(this);
        this.callParent();
    },
    getComputedWidth: function() {
        return this.getHidden() ? 0 : this.getWidth();
    },
    updateAlign: function(align, oldAlign) {
        var me = this,
            alignCls = me.alignCls;
        if (oldAlign) {
            me.removeCls(alignCls[oldAlign]);
        }
        if (align) {
            
            if (!alignCls[align]) {
                Ext.raise("Invalid value for align: '" + align + "'");
            }
            
            me.addCls(alignCls[align]);
        }
        me.syncToolableAlign();
    },
    updateBodyCls: function(cellCls, oldCellCls) {
        if (cellCls || oldCellCls) {
            this.bodyElement.replaceCls(oldCellCls, cellCls);
        }
    },
    updateBodyStyle: function(style) {
        this.bodyElement.applyStyles(style);
    },
    updateCellCls: function(cls, oldCls) {
        this.element.replaceCls(oldCls, cls);
    },
    updateCls: function(cls, oldCls) {
        this.element.replaceCls(oldCls, cls);
    },
    updateColumn: function(column) {
        var dataIndex = null,
            row = this.row;
        if (column) {
            dataIndex = ((row && row.isSummaryRow) && column.getSummaryDataIndex()) || column.getDataIndex();
        }
        this.dataIndex = dataIndex;
    },
    updateRecord: function() {
        if (!this.destroyed && !this.destroying) {
            this.refresh();
        }
    },
    updateSelectable: function(value) {
        this.toggleCls(Ext.baseCSSPrefix + 'item-no-select', value === false);
    },
    refresh: function(ctx) {
        var me = this,
            was = me.refreshContext,
            context, modified, value;
        if (!me.isBound('value')) {
            ctx = ctx || was;
            modified = ctx && ctx.modified;
            if (!modified || me.bound(modified)) {
                me.refreshContext = context = me.beginRefresh(ctx);
                value = me.refreshValue(context);
                if (value !== me.getValue()) {
                    me.setValue(value);
                } else if (me.writeValue) {
                    me.writeValue();
                }
                me.refreshContext = was;
            }
        }
    },
    refreshValue: function(context) {
        var me = this,
            record = context.record,
            dataIndex = context.dataIndex,
            value, dirty, modified;
        if (context.summary) {
            value = me.summarize(context);
        } else if (record && dataIndex) {
            value = record.get(dataIndex);
            modified = record.modified;
            dirty = !!(modified && modified.hasOwnProperty(dataIndex));
            if (dirty !== me.$dirty) {
                me.toggleCls(me.dirtyCls, dirty);
                me.$dirty = dirty;
            }
        }
        return value;
    },
    privates: {
        
        refreshCounter: 0,
        
        $dirty: false,
        
        refreshContext: null,
        storeMethodRe: /^(?:average|max|min|sum)$/,
        augmentToolHandler: function(tool, args) {
            
            var info = args[1] = {
                    event: args.pop(),
                    record: this.getRecord(),
                    column: this.getColumn(),
                    cell: args[0],
                    tool: args[1]
                };
            args[0] = info.grid = info.column.getGrid();
        },
        beginRefresh: function(context) {
            var me = this,
                column = me.getColumn(),
                row = me.row;
            
            context = context || (row ? row.beginRefresh() : {
                record: me.getRecord()
            });
            
            ++me.refreshCounter;
            
            context.from = context.from || 'cell';
            
            context.cell = me;
            context.column = column;
            context.dataIndex = me.dataIndex;
            context.scope = column.getScope();
            return context;
        },
        
        bound: function(fields) {
            return !!fields[this.dataIndex];
        },
        summarize: function(context) {
            var me = this,
                column = context.column,
                summaryType = column.getSummaryType(),
                dataIndex = context.dataIndex,
                group = context.group,
                store = context.store,
                records = context.records,
                value;
            if (summaryType) {
                
                if (!column.$warnSummaryType) {
                    column.$warnSummaryType = true;
                    Ext.log.warn('[column] summaryType is deprecated; use summaryRenderer (' + column.getId() + ')');
                }
                
                if (Ext.isFunction(summaryType)) {
                    value = summaryType.call(store, store.data.items.slice(), dataIndex);
                } else if (summaryType === 'count') {
                    value = store.getCount();
                } else if (me.storeMethodRe.test(summaryType)) {
                    value = store[summaryType](dataIndex);
                } else {
                    value = Ext.callback(summaryType, null, [
                        store.data.items.slice(),
                        dataIndex,
                        store
                    ], 0, me);
                }
            } else if (!(summaryType = column.getSummary())) {
                if (dataIndex) {
                    value = context.record.get(dataIndex);
                }
            }
            
            
            else if (!dataIndex) {
                Ext.raise('Cannot use summary config w/o summaryDataIndex or dataIndex (' + context.grid.getId() + ')');
            } else 
            {
                
                if (group) {
                    if (group.isVirtualGroup) {
                        Ext.raise('Cannot calculate a group summary on a virtual store (' + context.grid.getId() + ')');
                    }
                } else if (store.getRemoteSort()) {
                    Ext.raise('Cannot calculate a summary on a remoteSort store (' + context.grid.getId() + ')');
                }
                
                value = summaryType.calculate(records, dataIndex, 'data', 0, records.length);
            }
            return value;
        }
    },
    
    deprecated: {
        '6.5': {
            configs: {
                innerStyle: 'bodyStyle',
                innerCls: 'bodyCls'
            }
        }
    }
});


Ext.define('Ext.grid.cell.Text', {
    extend: Ext.grid.cell.Base,
    xtype: 'textcell',
    config: {
        
        encodeHtml: true,
        
        rawValue: null,
        
        zeroValue: null
    },
    getTemplate: function() {
        var template = this.callParent();
        template[0]["data-qoverflow"] = true;
        return template;
    },
    formatValue: function(v) {
        var me = this,
            context = me.refreshContext,
            column = context.column,
            zeroValue = me.getZeroValue(),
            format = column.getFormatter(),
            renderer, scope;
        if (context.summary) {
            renderer = column.getSummaryRenderer();
            if (renderer) {
                format = null;
                
                scope = context.scope;
                if (typeof renderer === 'string') {
                    v = Ext.callback(renderer, scope, [
                        v,
                        context
                    ], 0, column);
                } else {
                    v = renderer.call(scope || me, v, context);
                }
            }
            format = column.getSummaryFormatter() || format;
        } else if (v === 0 && zeroValue !== null) {
            v = zeroValue;
            format = null;
        }
        if (format) {
            v = format(v);
        }
        if (v != null) {
            v = String(v);
        } else {
            v = '';
        }
        return v;
    },
    printValue: function(v) {
        var me = this,
            was = me.refreshContext,
            s;
        
        
        me.refreshContext = me.beginRefresh(was);
        s = me.formatValue(v);
        if (me.getEncodeHtml()) {
            s = Ext.htmlEncode(s);
        }
        me.refreshContext = was;
        return s;
    },
    updateRawValue: function(rawValue) {
        var dom = this.bodyElement.dom,
            value = rawValue == null ? '' : rawValue;
        if (this.getEncodeHtml()) {
            dom.textContent = value;
        } else {
            dom.innerHTML = value;
        }
    },
    updateValue: function() {
        var me = this,
            was = me.refreshContext,
            row = me.row;
        
        
        
        
        if (row && row.parent) {
            
            
            if (!was) {
                me.refreshContext = me.beginRefresh();
            }
            me.writeValue();
            me.refreshContext = was;
        }
    },
    updateZeroValue: function() {
        if (!this.isConfiguring) {
            this.refresh();
        }
    },
    writeValue: function() {
        var me = this,
            value = me.getValue();
        if (!(value = me.formatValue(value))) {
            
            value = me.getColumn().getEmptyText();
        }
        me.setRawValue(value);
    }
});


Ext.define('Ext.grid.cell.Cell', {
    extend: Ext.grid.cell.Text,
    xtype: 'gridcell',
    config: {
        
        tpl: null,
        
        renderer: null,
        
        formatter: null,
        
        scope: null
    },
    friendly: null,
    updateColumn: function(column, oldColumn) {
        var me = this,
            friendly = true,
            tpl, renderer, formatter;
        me.callParent([
            column,
            oldColumn
        ]);
        
        if (column) {
            tpl = column.getTpl();
            renderer = column.getRenderer();
            formatter = column.getFormatter();
            if (renderer !== null) {
                me.setRenderer(renderer);
                friendly = (typeof renderer === 'function') && renderer.length === 1;
            }
            if (tpl !== null) {
                me.setTpl(tpl);
                friendly = false;
            }
            if (formatter !== null) {
                me.setFormatter(formatter);
            }
            me.friendly = friendly;
        }
    },
    applyTpl: function(tpl) {
        return Ext.XTemplate.get(tpl);
    },
    applyFormatter: function(format) {
        var me = this,
            fmt = format,
            parser;
        if (typeof fmt === 'string') {
            parser = Ext.app.bind.Parser.fly(fmt);
            fmt = parser.compileFormat();
            parser.release();
            return function(v) {
                return fmt(v, me.getScope() || me.resolveListenerScope());
            };
        }
        
        else if (typeof fmt !== 'function') {
            Ext.raise('Invalid formatter');
        }
        
        return fmt;
    },
    updateTpl: function() {
        if (!this.isConfiguring) {
            this.refresh();
        }
    },
    updateRenderer: function() {
        if (!this.isConfiguring) {
            this.refresh();
        }
    },
    updateFormatter: function() {
        if (!this.isConfiguring) {
            this.refresh();
        }
    },
    formatValue: function(v) {
        var me = this,
            context = me.refreshContext,
            dataIndex = context.dataIndex,
            column = context.column,
            record = context.record,
            zeroValue = me.getZeroValue(),
            raw = v,
            
            summary = context.summary,
            args, data, format, renderer, scope, tpl;
        if (!context.summary && v === 0 && zeroValue !== null) {
            raw = zeroValue;
        } else if (!(tpl = me.getTpl(context))) {
            format = me.getFormatter();
            if (summary) {
                renderer = column.getSummaryRenderer();
                if (renderer) {
                    format = null;
                    
                    scope = context.scope;
                    if (typeof renderer === 'string') {
                        raw = Ext.callback(renderer, scope, [
                            v,
                            context
                        ], 0, column);
                        me.friendly = false;
                    } else {
                        raw = renderer.call(scope || me, v, context);
                        if (renderer.length > 1) {
                            me.friendly = false;
                        }
                    }
                }
                format = column.getSummaryFormatter() || format;
            } else {
                renderer = me.getRenderer();
                if (renderer) {
                    args = [
                        v,
                        record,
                        dataIndex,
                        me,
                        column
                    ];
                    scope = me.getScope() || context.scope;
                    if (typeof renderer === 'function') {
                        raw = renderer.apply(scope || column, args);
                    } else {
                        raw = Ext.callback(renderer, scope, args, 0, me);
                    }
                }
            }
            if (format) {
                raw = format(raw);
            }
        } else {
            
            
            
            
            if (!(data = context.data)) {
                context.data = data = context.summary ? context.record.getData() : context.grid.gatherData(context.record);
            }
            raw = tpl.apply(data);
        }
        if (raw != null) {
            raw = String(raw);
        } else {
            raw = '';
        }
        return raw;
    },
    privates: {
        bound: function(fields) {
            var me = this,
                bound = !!fields[me.dataIndex],
                column, depends, i;
            if (!bound) {
                column = me.getColumn();
                depends = column && column.getDepends();
                if (depends) {
                    for (i = depends.length; !bound && i-- > 0; ) {
                        bound = !!fields[depends[i]];
                    }
                } else if (!me.friendly) {
                    bound = true;
                }
            }
            return bound;
        }
    }
});


Ext.define('Ext.grid.RowBody', {
    extend: Ext.Component,
    xtype: 'rowbody',
    config: {
        widget: null
    },
    classCls: Ext.baseCSSPrefix + 'rowbody',
    inheritUi: true,
    template: [
        {
            reference: 'spacerElement',
            cls: Ext.baseCSSPrefix + 'spacer-el'
        },
        {
            reference: 'contentElement',
            cls: Ext.baseCSSPrefix + 'content-el'
        }
    ],
    initialize: function() {
        var me = this,
            grid, rowExpander;
        me.callParent();
        grid = me.row.getGrid();
        if (grid && grid.hasRowExpander) {
            rowExpander = grid.findPlugin('rowexpander');
            if (rowExpander) {
                me.spacerElement.setWidth(rowExpander.getColumn().getWidth());
            }
        }
    },
    applyWidget: function(widget) {
        var row = this.row;
        if (widget) {
            widget = Ext.apply({
                ownerCmp: row
            }, widget);
            widget = Ext.widget(widget);
        }
        return widget;
    },
    updateWidget: function(widget, oldWidget) {
        if (oldWidget) {
            oldWidget.destroy();
        }
        if (widget) {
            this.contentElement.appendChild(widget.element);
        }
    },
    updateRecord: function(record, oldRecord) {
        var tpl = this.getTpl();
        if (tpl) {
            this.callParent([
                record,
                oldRecord
            ]);
        }
    },
    getInnerHtmlElement: function() {
        return this.contentElement;
    },
    doDestroy: function() {
        this.setWidget(null);
        this.callParent();
    }
});


Ext.define('Ext.grid.Row', {
    extend: Ext.Component,
    xtype: 'gridrow',
    mixins: [
        Ext.mixin.Queryable,
        Ext.dataview.GenericItem,
        Ext.dataview.Pinnable
    ],
    isGridRow: true,
    isRecordRefreshable: true,
    cachedConfig: {
        collapsed: true
    },
    config: {
        
        body: null,
        
        expandedField: null,
        
        defaultCellUI: null,
        
        stickyVisibility: null
    },
    classCls: [
        Ext.baseCSSPrefix + 'listitem',
        Ext.baseCSSPrefix + 'gridrow'
    ],
    inheritUi: true,
    expandedCls: Ext.baseCSSPrefix + 'expanded',
    element: {
        reference: 'element',
        children: [
            {
                reference: 'cellsElement',
                className: Ext.baseCSSPrefix + 'cells-el'
            }
        ]
    },
    constructor: function(config) {
        this.cells = [];
        this.columnMap = {};
        this.callParent([
            config
        ]);
    },
    doDestroy: function() {
        var me = this;
        me.setRecord(null);
        me.setBody(null);
        me.cells = Ext.destroy(me.cells);
        me.callParent();
    },
    
    collapse: function() {
        this.setCollapsed(true);
    },
    
    expand: function() {
        this.setCollapsed(false);
    },
    toggleCollapsed: function() {
        this.setCollapsed(!this.getCollapsed());
    },
    updateCollapsed: function(collapsed) {
        var me = this,
            body = me.getBody(),
            grid = me.getParent(),
            record = me.getRecord(),
            expandField = me.getExpandedField(),
            expandedCls = me.expandedCls,
            expanderCell = me.expanderCell,
            recordsExpanded;
        
        if (record) {
            
            
            if (expandField) {
                record.set(expandField, !collapsed);
            } else {
                recordsExpanded = grid.$recordsExpanded || (grid.$recordsExpanded = {});
                if (collapsed) {
                    delete recordsExpanded[record.internalId];
                } else {
                    recordsExpanded[record.internalId] = true;
                }
            }
        }
        if (expanderCell) {
            expanderCell.setCollapsed(collapsed);
        }
        if (body) {
            if (collapsed) {
                body.hide();
                me.removeCls(expandedCls);
            } else {
                body.show();
                me.addCls(expandedCls);
            }
        }
    },
    
    
    
    
    
    
    
    
    
    
    applyBody: function(config, existing) {
        return Ext.updateWidget(existing, config, this, 'createBody');
    },
    createBody: function(body) {
        return Ext.merge({
            xtype: 'rowbody',
            ownerCmp: this,
            row: this,
            hidden: true
        }, body);
    },
    updateBody: function(body) {
        var me = this,
            grid = me.getParent();
        if (body) {
            me.bodyElement.appendChild(body.element);
            if (me.rendered && !body.rendered) {
                body.setRendered(true);
            }
        }
        if (grid) {
            grid.setVariableHeights(true);
            if (!grid.hasRowExpander) {
                me.expand();
            }
        }
    },
    onAdded: function(grid) {
        var me = this,
            cells = me.cells,
            cell, col, columns, i, k, n;
        me.callParent(arguments);
        if (grid) {
            columns = grid.getColumns();
            for (i = 0 , n = columns.length; i < n; i++) {
                cell = cells[i];
                col = columns[i];
                
                
                
                
                if (cell) {
                    if (cell.getColumn() === col) {
                        
                        continue;
                    }
                    
                    for (k = cells.length; k-- > i; ) {
                        cell = cells[k];
                        me.removeColumn(cell.getColumn());
                    }
                }
                me.addColumn(columns[i]);
            }
        }
    },
    addColumn: function(column) {
        this.insertColumn(this.cells.length, column);
    },
    
    getCells: function(selector) {
        return selector ? Ext.ComponentQuery.query(selector, this.cells) : this.cells;
    },
    getRefItems: function(deep) {
        var result = [],
            body = this.getConfig('body', false, true),
            
            cells = this.cells,
            len = cells && cells.length,
            i, cell;
        for (i = 0; i < len; i++) {
            cell = cells[i];
            result.push(cell);
            if (deep && cell.getRefItems) {
                result.push.apply(result, cell.getRefItems());
            }
        }
        if (body) {
            result.push(body);
            if (deep && body.getRefItems) {
                result.push.apply(result, body.getRefItems());
            }
        }
        return result;
    },
    insertColumn: function(index, column) {
        var me = this,
            cells = me.cells,
            cell;
        if (column.isHeaderGroup) {
            return;
        }
        cell = me.createCell(column);
        if (index >= cells.length) {
            me.cellsElement.appendChild(cell.element);
            cells.push(cell);
        } else {
            cell.element.insertBefore(cells[index].element);
            cells.splice(index, 0, cell);
        }
        me.columnMap[column.getId()] = cell;
        if (cell.isExpanderCell) {
            me.expanderCell = cell;
        }
        if (me.rendered) {
            cell.setRendered(true);
        }
    },
    insertColumnBefore: function(column, ref) {
        var me = this,
            map = me.columnMap,
            id = column.getId(),
            cell = map[id],
            cells = me.cells,
            refCell, refIndex, index;
        if (ref) {
            refCell = me.getCellByColumn(ref);
            refIndex = cells.indexOf(refCell);
        } else {
            refIndex = cells.length;
        }
        if (cell) {
            
            index = cells.indexOf(cell);
            Ext.Array.move(cells, index, refIndex);
            if (refCell) {
                cell.element.insertBefore(refCell.element);
            } else {
                me.cellsElement.appendChild(cell.element);
            }
        } else {
            me.insertColumn(refIndex, column);
        }
    },
    removeColumn: function(column) {
        var me = this,
            columnMap = me.columnMap,
            columnId = column.getId(),
            cell = columnMap[columnId];
        if (cell) {
            Ext.Array.remove(me.cells, cell);
            delete columnMap[columnId];
            cell.destroy();
        }
    },
    updateRecord: function(record) {
        if (!this.destroyed && !this.destroying) {
            this.refresh();
        }
    },
    setColumnWidth: function(column) {
        var cell = this.getCellByColumn(column);
        if (cell) {
            cell.setWidth(column.getComputedWidth());
        }
    },
    showColumn: function(column) {
        this.setCellHidden(column, false);
    },
    hideColumn: function(column) {
        this.setCellHidden(column, true);
    },
    getCellByColumn: function(column) {
        return this.columnMap[column.getId()];
    },
    getColumnByCell: function(cell) {
        return cell.getColumn();
    },
    updateStickyVisibility: function(value) {
        this.fireEvent('stickyvisiblitychange', value);
    },
    refresh: function(context) {
        var me = this,
            cells = me.cells,
            body = me.getBody(),
            len = cells.length,
            expandField = me.getExpandedField(),
            grid = me.getParent(),
            sm = grid.getSelectable(),
            selection = sm.getSelection(),
            isCellSelection = selection.isCells || selection.isColumns,
            i, visibleIndex, cell, record, recordsExpanded;
        
        
        me.refreshContext = context = me.beginRefresh(context);
        record = context.record;
        me.syncDirty(record);
        for (i = 0 , visibleIndex = 0; i < len; ++i) {
            cell = cells[i];
            if (!context.summary || !cell.getColumn().getIgnore()) {
                if (cell.getRecord() === record) {
                    cell.refresh(context);
                } else {
                    cell.refreshContext = context;
                    cell.setRecord(record);
                    cell.refreshContext = null;
                }
                if (isCellSelection) {
                    cell.toggleCls(grid.selectedCls, sm.isCellSelected(me._recordIndex, visibleIndex));
                }
            }
            
            if (!cell.isHidden()) {
                visibleIndex++;
            }
        }
        context.cell = context.column = context.dataIndex = context.scope = null;
        if (body) {
            body.refreshContext = context;
            if (body.getRecord() === record) {
                body.updateRecord(record);
            } else {
                body.setRecord(record);
            }
            body.refreshContext = null;
            
            
            
            
            if (expandField) {
                me.setCollapsed(!record.get(expandField));
            } else {
                recordsExpanded = grid.$recordsExpanded || (grid.$recordsExpanded = {});
                if (grid.hasRowExpander) {
                    me.setCollapsed(!recordsExpanded[record.internalId]);
                }
            }
        }
        me.refreshContext = null;
    },
    privates: {
        refreshContext: null,
        beginRefresh: function(context) {
            var me = this,
                grid = me.getParent();
            context = context || {};
            
            context.from = context.from || 'row';
            
            context.grid = grid;
            context.record = me.getRecord();
            context.row = me;
            context.store = grid.store;
            return context;
        },
        createCell: function(column) {
            var cell = column.createCell(this);
            cell = Ext.create(cell);
            delete cell.$initParent;
            if (cell.inheritUi) {
                cell.doInheritUi();
            }
            
            
            
            
            
            cell.el.setTabIndex(-1);
            return cell;
        },
        setCellHidden: function(column, hidden) {
            var cell = this.getCellByColumn(column);
            if (cell) {
                cell.setHidden(hidden);
            }
        },
        getGrid: function() {
            return this.getParent();
        }
    }
});



Ext.define('Ext.grid.HeaderContainer', {
    extend: Ext.Container,
    xtype: 'headercontainer',
    
    isHeaderContainer: true,
    config: {
        docked: 'top',
        
        defaultColumnUI: null,
        
        columns: null,
        defaultType: 'column',
        layout: {
            type: 'hbox',
            align: 'stretch'
        },
        
        sortable: true,
        scrollable: {
            x: false,
            y: false
        },
        grid: null,
        
        verticalOverflow: null,
        
        reserveScrollbar: null
    },
    inheritUi: true,
    weighted: true,
    autoSize: null,
    constructor: function(config) {
        this.isRootHeader = !this.isGridColumn;
        
        if (this.isRootHeader) {
            config.grid._headerContainer = this;
        }
        this.columns = [];
        this.callParent([
            config
        ]);
        
        if (this.isRootHeader) {
            config.grid._headerContainer = null;
        }
    },
    initialize: function() {
        var me = this;
        me.callParent();
        
        if (me.isRootHeader) {
            me.setInstanceCls(Ext.baseCSSPrefix + 'headercontainer');
            me.on({
                tap: 'onHeaderTap',
                triggertap: 'onHeaderTriggerTap',
                columnresize: 'onColumnResize',
                show: 'onColumnShow',
                hide: 'onColumnHide',
                sort: 'onColumnSort',
                scope: me,
                delegate: '[isLeafHeader]'
            });
            me.on({
                tap: 'onGroupTap',
                triggertap: 'onGroupTriggerTap',
                show: 'onGroupShow',
                hide: 'onGroupHide',
                add: 'onColumnAdd',
                move: 'onColumnMove',
                remove: 'onColumnRemove',
                scope: me,
                delegate: '[isHeaderGroup]'
            });
            me.on({
                add: 'onColumnAdd',
                move: 'onColumnMove',
                remove: 'onColumnRemove',
                scope: me
            });
        }
    },
    
    getRootHeaderCt: function() {
        var grid = this.getGrid();
        return grid && grid.getHeaderContainer();
    },
    getColumnForField: function(fieldName) {
        var columns = this.columns,
            n = columns.length,
            c, i;
        for (i = 0; i < n; ++i) {
            c = columns[i].getColumnForField(fieldName);
            if (c) {
                return c;
            }
        }
        return null;
    },
    
    getColumns: function(selector) {
        var result = this.columns;
        if (selector) {
            if (typeof selector === 'string') {
                result = Ext.ComponentQuery.query(selector, result);
            } else if (Ext.isFunction(selector)) {
                return result.filter(selector);
            }
        }
        return result;
    },
    
    getVisibleColumns: function() {
        var me = this,
            result = me.visibleColumns;
        if (!result) {
            result = me.visibleColumns = me.columns.filter(me.visibleLeafFilter);
        }
        return result;
    },
    
    getClosestVisibleHeader: function(index) {
        var result = typeof index === 'number' ? this.getVisibleColumns()[index] : index;
        if (result && result.hidden) {
            result = result.next(':visible') || result.prev(':visible');
        }
        return result;
    },
    indexOfLeaf: function(column) {
        return this.getVisibleColumns().indexOf(column);
    },
    factoryItem: function(item) {
        var grid = this.getGrid();
        if (item.isComponent) {
            if (item.isGridColumn) {
                item.setGrid(grid);
            }
        } else {
            item = Ext.apply({
                grid: grid
            }, item);
        }
        return this.callParent([
            item
        ]);
    },
    updateColumns: function(columns) {
        var me = this;
        
        if (me.isRootHeader) {
            me.columns = [];
            me.visibleColumns = null;
            me.add(columns);
        }
    },
    beginColumnUpdate: function() {
        var me = this;
        if (!me.isRootHeader) {
            return;
        }
        me.hasBulkUpdate = me.hasBulkUpdate || 0;
        me.hasBulkUpdate++;
        if (me.hasBulkUpdate === 1) {
            me.bulkAdd = [];
            
            
            me.updateMenuDisabledState = Ext.emptyFn;
        }
    },
    endColumnUpdate: function() {
        var me = this,
            length, i, columns, item;
        if (!me.isRootHeader || !me.hasBulkUpdate) {
            return;
        }
        me.hasBulkUpdate--;
        if (me.hasBulkUpdate === 0) {
            columns = me.bulkAdd;
            length = columns && columns.length;
            if (length) {
                me.visibleColumns = null;
                me.columns = me.query('[isLeafHeader]');
                for (i = 0; i < length; i++) {
                    item = columns[i];
                    item.columnIndex = me.columns.indexOf(item.column);
                }
                
                Ext.Array.sort(columns, me.sortByColumnIndex);
                for (i = 0; i < length; i++) {
                    item = columns[i];
                    me.fireEvent('columnadd', me, item.column, item.columnIndex);
                }
            }
            
            me.getGrid().refreshInnerWidth();
            me.bulkAdd = null;
            
            delete me.updateMenuDisabledState;
            me.updateMenuDisabledState();
        }
    },
    sortByColumnIndex: function(a, b) {
        return a.columnIndex - b.columnIndex;
    },
    add: function(items) {
        var ret,
            rootHeaders = this.getRootHeaderCt();
        if (rootHeaders) {
            rootHeaders.beginColumnUpdate();
        }
        ret = this.callParent([
            items
        ]);
        if (rootHeaders) {
            rootHeaders.endColumnUpdate();
        }
        return ret;
    },
    insert: function(index, item) {
        var ret,
            rootHeaders = this.getRootHeaderCt();
        if (rootHeaders) {
            rootHeaders.beginColumnUpdate();
        }
        ret = this.callParent([
            index,
            item
        ]);
        if (rootHeaders) {
            rootHeaders.endColumnUpdate();
        }
        return ret;
    },
    remove: function(which, destroy) {
        var ret,
            rootHeaders = this.getRootHeaderCt();
        if (rootHeaders) {
            rootHeaders.beginColumnUpdate();
        }
        ret = this.callParent([
            which,
            destroy
        ]);
        if (rootHeaders) {
            rootHeaders.endColumnUpdate();
        }
        return ret;
    },
    onColumnAdd: function(container, column) {
        var me = this,
            grid = me.getGrid(),
            groupColumns, ln, i, ui;
        if (column.isHeaderGroup) {
            groupColumns = column.getItems().items;
            for (i = 0 , ln = groupColumns.length; i < ln; i++) {
                me.onColumnAdd(column, groupColumns[i]);
            }
        } else {
            ui = column.getUi();
            if (ui == null) {
                column.setUi(me.getDefaultColumnUI());
            }
            column.setGrid(grid);
            me.bulkAdd.push({
                column: column
            });
        }
        me.updateMenuDisabledState();
    },
    onColumnMove: function(parent, column, toIdx, fromIdx) {
        var me = this,
            columns = me.columns,
            group = null,
            cols;
        
        
        me.visibleColumns = null;
        if (column.isHeaderGroup) {
            cols = column.getItems().items;
            group = column;
        } else {
            cols = [
                column
            ];
        }
        fromIdx = columns.indexOf(cols[0]);
        me.columns = me.getLeaves();
        me.fireEvent('columnmove', me, cols, group, fromIdx);
    },
    onColumnRemove: function(parent, column) {
        var me = this;
        
        me.visibleColumns = null;
        if (column.isHeaderGroup) {
            if (!column.destroying) {
                var columns = column.getItems().items,
                    ln = columns.length,
                    i;
                for (i = 0; i < ln; i++) {
                    me.onColumnRemove(column, columns[i]);
                }
            }
        } else {
            Ext.Array.remove(me.columns, column);
            me.fireEvent('columnremove', me, column);
        }
        me.updateMenuDisabledState();
    },
    onHeaderTap: function(column, e) {
        var selModel = this.getGrid().getSelectable(),
            ret = this.fireEvent('columntap', this, column, e);
        if (ret !== false) {
            if (selModel.onHeaderTap) {
                selModel.onHeaderTap(this, column, e);
            }
        }
    },
    onGroupTriggerTap: function(column) {
        column.showMenu();
    },
    onHeaderTriggerTap: function(column) {
        column.showMenu();
    },
    onColumnShow: function(column) {
        var me = this;
        
        me.visibleColumns = null;
        me.fireEvent('columnshow', me, column);
        me.updateMenuDisabledState();
    },
    onColumnHide: function(column) {
        var me = this;
        
        me.visibleColumns = null;
        me.fireEvent('columnhide', me, column);
        me.updateMenuDisabledState();
    },
    onGroupShow: function(group) {
        var columns = group.getInnerItems(),
            ln = columns.length,
            i, column;
        
        this.visibleColumns = null;
        for (i = 0; i < ln; i++) {
            column = columns[i];
            if (!column.isHidden()) {
                this.fireEvent('columnshow', this, column);
            }
        }
        this.updateMenuDisabledState();
    },
    onGroupHide: function(group) {
        var columns = group.getInnerItems(),
            ln = columns.length,
            i, column;
        
        this.visibleColumns = null;
        for (i = 0; i < ln; i++) {
            column = columns[i];
            this.fireEvent('columnhide', this, column);
        }
        this.updateMenuDisabledState();
    },
    onGroupTap: function(column, e) {
        return this.fireEvent('headergrouptap', this, column, e);
    },
    onColumnResize: function(column, width, oldWidth) {
        this.fireEvent('columnresize', this, column, width, oldWidth);
    },
    onColumnSort: function(column, direction, newDirection) {
        if (direction !== null) {
            this.fireEvent('columnsort', this, column, direction, newDirection);
        }
    },
    scrollTo: function(x) {
        this.getScrollable().scrollTo(x);
    },
    updateGrid: function(grid) {
        if (this.isRootHeader) {
            this.parent = grid;
        }
    },
    doDestroy: function() {
        var me = this,
            task = me.spacerTask;
        if (task) {
            task.cancel();
            me.spacerTask = null;
        }
        me.setGrid(null);
        me.callParent();
    },
    afterRender: function() {
        this.callParent();
        if (this.isRootHeader) {
            this.onColumnComputedWidthChange();
        }
    },
    privates: {
        columnsResizing: null,
        
        updateVerticalOverflow: function() {
            this.syncReserveSpace();
        },
        
        updateReserveScrollbar: function() {
            this.syncReserveSpace();
        },
        
        updateMenuDisabledState: function() {
            if (this.rendered) {
                var me = this.isRootHeader ? this : this.getRootHeaderCt(),
                    columns = [],
                    menuOfferingColumns = [],
                    len, i, column, columnIsHideable, checkItem;
                
                
                
                
                
                me.visitPreOrder('gridcolumn:not([hidden])', function(col) {
                    columns.push(col);
                    
                    
                    if (!col.isHidden(true) && !col.getMenuDisabled() && col.getConfig('menu', true)) {
                        menuOfferingColumns.push(col);
                    }
                });
                len = columns.length;
                for (i = 0; i < len; ++i) {
                    column = columns[i];
                    checkItem = column.getHideShowMenuItem(false);
                    
                    if (checkItem) {
                        columnIsHideable = menuOfferingColumns.length > 1 || menuOfferingColumns[0] !== column;
                        checkItem['set' + (checkItem.getMenu() ? 'CheckChange' : '') + 'Disabled'](!columnIsHideable);
                    }
                }
            }
        },
        getLeaves: function() {
            return this.query('[isLeafHeader]');
        },
        onColumnComputedWidthChange: function(column, computedWidth) {
            
            
            
            
            
            
            var me = this,
                totalColumnWidth = 0,
                changedColumns = me.columnsResizing,
                columns, len, i, c, width;
            if (me.destroying) {
                return;
            }
            if (changedColumns) {
                changedColumns.push(column);
                return;
            }
            
            
            me.columnsResizing = changedColumns = [];
            columns = me.getColumns();
            len = columns.length;
            
            
            
            for (i = 0; i < len; i++) {
                c = columns[i];
                
                if (c === column) {
                    changedColumns.push(c);
                    width = computedWidth;
                } else 
                
                
                
                {
                    width = c.isHidden(true) ? 0 : c.measureWidth();
                }
                
                
                totalColumnWidth += width;
            }
            totalColumnWidth = Math.floor(totalColumnWidth);
            me.getGrid().onColumnComputedWidthChange(changedColumns, totalColumnWidth);
            me.columnsResizing = null;
        },
        setRendered: function(rendered) {
            
            
            this.visibleColumns = null;
            this.callParent([
                rendered
            ]);
        },
        
        setSortState: function() {
            var grid = this.getGrid(),
                store = grid.getStore(),
                columns = grid.getColumns(),
                len = columns && columns.length,
                sorters = store.getSorters(),
                grouper = store.getGrouper(),
                i, header, sorter;
            for (i = 0; i < len; i++) {
                header = columns[i];
                
                
                
                sorter = header.sorter;
                if (sorter) {
                    
                    
                    
                    
                    if (!(sorters.contains(sorter) || grouper === sorter)) {
                        sorter = null;
                    }
                }
                
                header.setSortState(sorter);
            }
        },
        syncReserveSpace: function() {
            var reserve = this.getVerticalOverflow() || this.getReserveScrollbar();
            
            
            this.el.setStyle('padding-right', reserve ? Ext.getScrollbarSize().width + 'px' : 0);
        },
        visibleLeafFilter: function(c) {
            return c.isLeafHeader && !c.isHidden();
        }
    }
});


Ext.define('Ext.menu.CheckItem', {
    extend: Ext.menu.Item,
    xtype: 'menucheckitem',
    
    isMenuCheckItem: true,
    
    hideOnClick: false,
    config: {
        
        checked: false,
        
        checkHandler: null,
        
        
        checkChangeDisabled: false,
        
        value: null,
        showCheckbox: null
    },
    
    classCls: Ext.baseCSSPrefix + 'menucheckitem',
    checkedCls: Ext.baseCSSPrefix + 'checked',
    checkboxIconElCls: Ext.baseCSSPrefix + 'checkbox-icon-el',
    ariaRole: 'menuitemcheckbox',
    defaultBindProperty: 'checked',
    
    submenuText: '{0} submenu',
    
    href: null,
    
    target: null,
    
    
    element: {
        reference: 'element',
        cls: Ext.baseCSSPrefix + 'unselectable ' + 
        Ext.baseCSSPrefix + 'has-left-icon',
        onmousedown: 'return Ext.doEv(this, event);'
    },
    eventHandlers: {
        change: 'onCheckboxChange',
        mousedown: 'onCheckboxMousedown'
    },
    focusEl: 'checkboxElement',
    ariaEl: 'checkboxElement',
    getTemplate: function() {
        var template = this.callParent(),
            body = template[0];
        body.tag = 'div';
        body.href = null;
        body.children.push({
            
            tag: 'input',
            type: 'checkbox',
            reference: 'checkboxElement',
            cls: Ext.baseCSSPrefix + 'checkbox-el',
            onchange: 'return Ext.doEv(this, event);'
        });
        return template;
    },
    initialize: function() {
        this.callParent();
        this.syncCheckboxCls();
    },
    enableFocusable: function() {
        this.mixins.focusable.enableFocusable();
        
        this.checkboxElement.dom.readOnly = '';
    },
    disableFocusable: function() {
        this.mixins.focusable.disableFocusable();
        
        this.checkboxElement.dom.readOnly = 'readonly';
    },
    
    setChecked: function(checked, suppressEvents) {
        var me = this,
            isConfiguring = me.isConfiguring;
        
        if (suppressEvents) {
            me.isConfiguring = true;
        }
        me.callParent([
            checked
        ]);
        if (suppressEvents) {
            me.isConfiguring = isConfiguring;
        }
    },
    updateChecked: function(checked) {
        this.checkboxElement.dom.checked = checked;
        
        
        this.onCheckChange();
    },
    updateCheckChangeDisabled: function(checkChangeDisabled) {
        this.checkboxElement.dom.readOnly = checkChangeDisabled;
    },
    updateValue: function(value) {
        this.checkboxElement.dom.value = value;
    },
    updateText: function(text) {
        var me = this,
            ariaDom = me.ariaEl.dom;
        me.callParent([
            text
        ]);
        
        if (me.getValue() === null) {
            me.setValue(text);
        }
        if (ariaDom && me.getMenu()) {
            ariaDom.setAttribute('aria-label', Ext.String.formatEncode(me.submenuText, text));
        }
    },
    applyShowCheckbox: function(showCheckbox) {
        return !!showCheckbox;
    },
    updateShowCheckbox: function(showCheckbox) {
        this.checkboxElement.setDisplayed(showCheckbox);
    },
    updateIcon: function(icon, oldIcon) {
        this.callParent([
            icon,
            oldIcon
        ]);
        if (!this.isConfiguring) {
            this.syncCheckboxCls();
        }
    },
    updateIconCls: function(iconCls, oldIconCls) {
        this.callParent([
            iconCls,
            oldIconCls
        ]);
        if (!this.isConfiguring) {
            this.syncCheckboxCls();
        }
    },
    updateIconAlign: function(iconAlign, oldIconAlign) {
        this.callParent([
            iconAlign,
            oldIconAlign
        ]);
        if (!this.isConfiguring) {
            this.syncCheckboxCls();
        }
    },
    privates: {
        onSpace: function(e) {
            
            if (this.getDisabled()) {
                e.preventDefault();
            }
        },
        onClick: function(e) {
            var me = this,
                arrowElement = me.arrowElement,
                result, parentResult, region;
            
            if (me.getDisabled()) {
                e.preventDefault();
            }
            
            
            if (e.pointerType !== 'mouse') {
                region = me.bodyElement.getRegion();
                if (me.getMenu()) {
                    region.setWidth(region.getWidth() - arrowElement.getWidth() - arrowElement.getMargin('lr'));
                }
                
                
                
                if (region.contains(e.getPoint())) {
                    
                    result = false;
                } else {
                    
                    
                    e.preventDefault();
                }
            }
            parentResult = me.callParent([
                e
            ]);
            
            return (result === false) ? result : parentResult;
        },
        onCheckboxMousedown: function(e) {
            
            
            
            
            
            if ((Ext.isApple && !Ext.isChrome) || !this.checkboxElement.contains(e.target)) {
                e.preventDefault();
            }
        },
        onCheckboxChange: function() {
            var me = this,
                checkboxElement = me.checkboxElement.dom,
                meChecked = me.getChecked(),
                isChecked = checkboxElement.checked;
            if (me.getCheckChangeDisabled()) {
                checkboxElement.checked = meChecked;
                return false;
            }
            if (isChecked === meChecked || me.getDisabled()) {
                return;
            }
            
            
            
            if (me.fireEvent('beforecheckchange', me, isChecked) === false) {
                checkboxElement.checked = !isChecked;
            } else {
                
                me.setChecked(isChecked);
            }
        },
        onCheckChange: function() {
            var me = this,
                checked = me.checkboxElement.dom.checked,
                el = me.el,
                ariaDom = me.ariaEl.dom;
            el.toggleCls(me.checkedCls, !!checked);
            if (ariaDom) {
                ariaDom.setAttribute('aria-checked', me.getMenu() ? 'mixed' : checked);
            }
            me.publishState('checked', checked);
            
            if (!me.isConfiguring) {
                Ext.callback(me.getCheckHandler(), me.scope, [
                    me,
                    checked
                ], 0, me);
                me.fireEvent('checkchange', me, checked);
            }
        },
        syncHasIconCls: function() {
            var me = this;
            me.toggleCls(me.hasRightIconCls, me.hasIcon());
        },
        syncCheckboxCls: function() {
            var me = this,
                leftIconElement = me.leftIconElement,
                rightIconElement = me.rightIconElement,
                checkboxIconElCls = me.checkboxIconElCls,
                checkboxIconElement, oldCheckboxIconElement;
            if (me.hasIcon() && (me.getIconAlign() === 'left')) {
                checkboxIconElement = rightIconElement;
                oldCheckboxIconElement = leftIconElement;
            } else {
                checkboxIconElement = leftIconElement;
                oldCheckboxIconElement = rightIconElement;
            }
            checkboxIconElement.addCls(checkboxIconElCls);
            oldCheckboxIconElement.removeCls(checkboxIconElCls);
        }
    }
});


Ext.define('Ext.grid.column.Column', {
    extend: Ext.grid.HeaderContainer,
    alternateClassName: 'Ext.grid.column.Template',
    xtype: [
        'gridcolumn',
        'column',
        'templatecolumn'
    ],
    
    isGridColumn: true,
    mixins: [
        Ext.mixin.StyleCacher,
        Ext.mixin.Toolable
    ],
    
    
    config: {
        
        align: undefined,
        
        
        cell: {
            xtype: 'gridcell'
        },
        
        dataIndex: null,
        
        defaultWidth: 100,
        
        depends: null,
        emptyText: {
            cached: true,
            $value: '\xa0'
        },
        
        text: '\xa0',
        
        sortable: true,
        
        groupable: true,
        
        resizable: true,
        
        hideable: true,
        
        renderer: null,
        
        formatter: null,
        
        scope: null,
        
        editable: null,
        
        editor: null,
        
        defaultEditor: {
            lazy: true,
            $value: {}
        },
        
        ignore: false,
        
        ignoreExport: false,
        
        exportStyle: null,
        
        exportRenderer: false,
        
        summary: null,
        
        summaryCell: null,
        
        summaryDataIndex: null,
        
        summaryFormatter: null,
        
        summaryRenderer: null,
        
        summaryType: null,
        
        exportSummaryRenderer: false,
        minWidth: 40,
        
        tpl: null,
        
        computedWidth: null,
        
        grouper: {
            lazy: true,
            $value: null
        },
        
        groupHeaderTpl: null,
        
        sorter: {
            lazy: true,
            $value: true
        },
        
        scratchCell: {
            lazy: true,
            $value: true
        },
        
        menu: {
            lazy: true,
            $value: {}
        },
        
        menuDisabled: null,
        
        hideShowMenuItem: {
            lazy: true,
            $value: {
                xtype: 'menucheckitem'
            }
        }
    },
    toolDefaults: {
        ui: 'gridcolumn',
        zone: 'tail'
    },
    toolAnchorName: 'titleWrapElement',
    dockTools: false,
    scrollable: false,
    docked: null,
    sortState: null,
    
    ariaSortStates: {
        ASC: 'ascending',
        DESC: 'descending'
    },
    inheritUi: true,
    classCls: Ext.baseCSSPrefix + 'gridcolumn',
    sortedCls: Ext.baseCSSPrefix + 'sorted',
    secondarySortCls: Ext.baseCSSPrefix + 'secondary-sort',
    auxSortCls: Ext.baseCSSPrefix + 'aux-sort',
    resizableCls: Ext.baseCSSPrefix + 'resizable',
    groupCls: Ext.baseCSSPrefix + 'group',
    leafCls: Ext.baseCSSPrefix + 'leaf',
    menuOpenCls: Ext.baseCSSPrefix + 'menu-open',
    alignCls: {
        left: Ext.baseCSSPrefix + 'align-left',
        center: Ext.baseCSSPrefix + 'align-center',
        right: Ext.baseCSSPrefix + 'align-right'
    },
    
    constructor: function(config) {
        var me = this,
            isHeaderGroup, menu;
        
        
        if (config.columns || me.columns) {
            isHeaderGroup = me.isHeaderGroup = true;
        } else {
            me.isLeafHeader = true;
        }
        me.callParent([
            config
        ]);
        me.addCls(isHeaderGroup ? me.groupCls : me.leafCls);
        menu = me.getConfig('menu', 
        true);
        if (!menu && me.getMenuDisabled() === null) {
            me.setMenuDisabled(true);
        }
    },
    getTemplate: function() {
        var me = this,
            beforeTitleTemplate = me.beforeTitleTemplate,
            afterTitleTemplate = me.afterTitleTemplate,
            titleTpl = [];
        
        if (beforeTitleTemplate) {
            titleTpl.push.apply(titleTpl, beforeTitleTemplate);
        }
        titleTpl.push({
            reference: 'titleElement',
            className: Ext.baseCSSPrefix + 'title-el',
            children: [
                {
                    reference: 'textElement',
                    className: Ext.baseCSSPrefix + 'text-el',
                    "data-qoverflow": true
                },
                {
                    reference: 'sortIconElement',
                    cls: Ext.baseCSSPrefix + 'sort-icon-el ' + Ext.baseCSSPrefix + 'font-icon'
                }
            ]
        });
        
        if (afterTitleTemplate) {
            titleTpl.push.apply(titleTpl, afterTitleTemplate);
        }
        return [
            {
                reference: 'headerElement',
                cls: Ext.baseCSSPrefix + 'header-el',
                children: [
                    {
                        reference: 'titleWrapElement',
                        cls: Ext.baseCSSPrefix + 'title-wrap-el',
                        uiCls: 'title-wrap-el',
                        children: titleTpl
                    },
                    {
                        reference: 'resizerElement',
                        cls: Ext.baseCSSPrefix + 'resizer-el ' + Ext.baseCSSPrefix + 'item-no-tap'
                    },
                    {
                        reference: 'triggerElement',
                        cls: Ext.baseCSSPrefix + 'trigger-el ' + Ext.baseCSSPrefix + 'font-icon ' + Ext.baseCSSPrefix + 'item-no-tap'
                    }
                ]
            },
            {
                reference: 'bodyElement',
                cls: Ext.baseCSSPrefix + 'body-el',
                uiCls: 'body-el'
            }
        ];
    },
    initialize: function() {
        var me = this;
        if (me.isLeafHeader && !me.getWidth() && me.getFlex() == null) {
            me.setWidth(me.getDefaultWidth());
        }
        me.callParent();
        me.element.on({
            tap: 'onColumnTap',
            longpress: 'onColumnLongPress',
            scope: this
        });
        me.triggerElement.on({
            tap: 'onTriggerTap',
            scope: this
        });
        me.resizerElement.on({
            tap: 'onResizerTap',
            scope: this
        });
        if (me.isHeaderGroup) {
            me.on({
                add: 'doVisibilityCheck',
                remove: 'doVisibilityCheck',
                show: 'onColumnShow',
                hide: 'onColumnHide',
                move: 'onColumnMove',
                delegate: '> column',
                scope: me
            });
            me.on({
                show: 'onShow',
                scope: me
            });
        }
    },
    doDestroy: function() {
        var me = this;
        me.destroyMembers('editor', 'resizeListener', 'menu', 'hideShowMenuItem', 'childColumnsMenu');
        me.setScratchCell(null);
        me.mixins.toolable.doDestroy.call(me);
        me.callParent();
    },
    onAdded: function(parent, instanced) {
        this.visibleIndex = null;
        this.callParent([
            parent,
            instanced
        ]);
    },
    
    getVisibleIndex: function() {
        
        
        var visibleIndex = this.visibleIndex,
            rootHeaders;
        if (visibleIndex == null) {
            if (this.isHeaderGroup) {
                visibleIndex = false;
            } else {
                rootHeaders = this.getRootHeaderCt();
                if (rootHeaders) {
                    visibleIndex = rootHeaders.indexOfLeaf(this);
                }
            }
            this.visibleIndex = visibleIndex;
        }
        return visibleIndex;
    },
    _columnScopeRe: /^column\./,
    _gridScopeRe: /^grid\./,
    applyMenu: function(menu) {
        var me = this,
            grid = me.getGrid(),
            columnScopeRe = me._columnScopeRe,
            gridScopeRe = me._gridScopeRe,
            extraItems, gridColumnMenu, i, item, items, s;
        Ext.destroy(me.sortChangeListener);
        
        
        if (menu && !menu.isMenu) {
            if (Ext.isArray(menu)) {
                extraItems = menu;
                menu = null;
            } else if (!menu.items) {
                menu = {
                    items: menu
                };
            }
            if (!(gridColumnMenu = grid.getColumnMenu())) {
                
                menu = menu ? Ext.clone(menu) : {};
            } else {
                gridColumnMenu = Ext.clone(gridColumnMenu);
                menu = menu ? Ext.merge(gridColumnMenu, menu) : gridColumnMenu;
            }
            menu.ownerCmp = me;
            menu = Ext.create(menu);
            
            me.sortChangeListener = menu.on({
                groupchange: 'onColumnMenuGroupChange',
                scope: me
            });
            
            
            
            
            
            
            
            for (items = menu.getItems().items , i = items && items.length; i-- > 0; ) {
                item = items[i];
                if (columnScopeRe.test(s = item.getHandler() || '')) {
                    item.setHandler(s.substr(7));
                    
                    item.scope = me;
                } else if (gridScopeRe.test(s)) {
                    item.setHandler(s.substr(5));
                    
                    item.scope = grid;
                } else if (item.isMenuCheckItem) {
                    if (columnScopeRe.test(s = item.getCheckHandler() || '')) {
                        item.setCheckHandler(s.substr(7));
                        item.scope = me;
                    } else if (gridScopeRe.test(s)) {
                        item.setCheckHandler(s.substr(5));
                        item.scope = grid;
                    }
                }
            }
            if (extraItems) {
                menu.add(extraItems);
            }
            grid.fireEvent('columnmenucreated', grid, me, menu);
        }
        return menu;
    },
    updateMenu: function(menu, oldMenu) {
        if (oldMenu) {
            oldMenu.destroy();
        }
    },
    beforeShowMenu: function(menu) {
        var me = this,
            store = me.getGrid().getStore(),
            isGrouped = store && !!store.getGrouper(),
            groupByThis = menu.getComponent('groupByThis'),
            showInGroups = menu.getComponent('showInGroups'),
            sortAsc = menu.getComponent('sortAsc'),
            sortDesc = menu.getComponent('sortDesc');
        sortAsc.setDisabled(!store);
        sortDesc.setDisabled(!store);
        
        if (!store) {
            groupByThis.setHidden(true);
            showInGroups.setHidden(true);
            return;
        }
        
        
        
        
        
        
        menu.suspendEvent('groupchange');
        if (sortAsc) {
            me.syncMenuItemState(sortAsc);
        }
        if (sortDesc) {
            me.syncMenuItemState(sortDesc);
        }
        if (groupByThis) {
            groupByThis.setHidden(!(me.canGroup() && !store.isTreeStore));
        }
        menu.resumeEvent('groupchange');
        if (showInGroups) {
            
            showInGroups.setHidden(store.isTreeStore);
            
            showInGroups.setChecked(isGrouped);
            showInGroups.setDisabled(!isGrouped);
        }
    },
    showMenu: function() {
        var me = this,
            menu = !me.getMenuDisabled() && me.getMenu(),
            menuOpenCls = me.menuOpenCls,
            columnsMenu, grid;
        
        if (menu) {
            grid = me.getGrid();
            columnsMenu = grid.getColumnsMenuItem();
            menu.add(columnsMenu);
            if (me.beforeShowMenu(menu) !== false && grid.beforeShowColumnMenu(me, menu) !== false) {
                menu.showBy(me.triggerElement);
                
                me.addCls(menuOpenCls);
                menu.on({
                    single: true,
                    hide: function() {
                        if (!(me.destroyed || me.destroying)) {
                            me.removeCls(menuOpenCls);
                            menu.remove(columnsMenu, 
                            false);
                        }
                    }
                });
            }
        }
    },
    getCells: function() {
        var cells = [],
            rows = this.getGrid().items.items,
            len = rows.length,
            i, row;
        for (i = 0; i < len; ++i) {
            row = rows[i];
            if (row.isGridRow) {
                cells.push(row.getCellByColumn(this));
            }
        }
        return cells;
    },
    getColumnForField: function(fieldName) {
        if (fieldName === this.getDataIndex()) {
            return this;
        }
        return this.callParent([
            fieldName
        ]);
    },
    
    isHideable: function() {
        var menuOfferingColumns = [];
        
        
        
        this.getRootHeaderCt().visitPreOrder('gridcolumn:not([hidden])', function(col) {
            if (!col.getMenuDisabled() && col.getConfig('menu', true)) {
                menuOfferingColumns.push(col);
            }
        });
        return menuOfferingColumns.length > 1 || menuOfferingColumns[0] !== this;
    },
    applyTpl: function(tpl) {
        return Ext.XTemplate.get(tpl);
    },
    applyAlign: function(align, oldAlign) {
        if (align == null) {
            align = this.isHeaderGroup ? 'center' : 'left';
        }
        return align;
    },
    updateAlign: function(align, oldAlign) {
        var me = this,
            alignCls = me.alignCls;
        if (oldAlign) {
            me.removeCls(alignCls[oldAlign]);
        }
        if (align) {
            
            if (!alignCls[align]) {
                Ext.raise("Invalid value for align: '" + align + "'");
            }
            
            me.addCls(alignCls[align]);
        }
        me.syncToolableAlign();
    },
    updateMenuDisabled: function(menuDisabled) {
        if (this.triggerElement) {
            this.triggerElement.setVisible(!menuDisabled);
        }
    },
    onColumnTap: function(e) {
        var me = this,
            grid = me.getGrid(),
            selModel = grid.getSelectable(),
            store = grid.getStore(),
            sorters = store && store.getSorters(true),
            sorter = store && me.pickSorter(),
            sorterIndex = sorter ? sorters.indexOf(sorter) : -1,
            isSorted = sorter && (sorterIndex !== -1 || sorter === store.getGrouper());
        
        
        
        if (Ext.Component.from(e) !== me || e.getTarget('.' + Ext.baseCSSPrefix + 'item-no-tap', me)) {
            return;
        }
        
        
        if (store && me.isSortable() && (!selModel || !selModel.getColumns())) {
            
            if (sorter.isGrouper) {
                sorter.toggle();
                store.group(sorter);
            }
            
            
            else if (sorterIndex === 0) {
                me.toggleSortState();
            } else 
            
            {
                
                if (isSorted) {
                    store.sort(sorter, 'prepend');
                } else 
                {
                    me.sort('ASC');
                }
            }
        }
        return me.fireEvent('tap', me, e);
    },
    onTriggerTap: function(e) {
        this.fireEvent('triggertap', this, e);
    },
    onResizerTap: function(e) {
        
        
        if (e.getPoint().isContainedBy(this.triggerElement.getRegion())) {
            this.fireEvent('triggertap', this, e);
        }
    },
    onColumnLongPress: function(e) {
        this.fireEvent('longpress', this, e);
    },
    onGroupByThis: function() {
        var me = this,
            grid = me.getGrid(),
            grouper = me.getGrouper(),
            store = grid.getStore(),
            dataIndex;
        if (!grouper) {
            dataIndex = me.getDataIndex();
            if (dataIndex != null) {
                me.setGrouper({
                    property: dataIndex
                });
                grouper = me.getGrouper();
            }
        }
        if (grouper) {
            store.setGrouper(grouper);
        }
    },
    
    onColumnMenuGroupChange: function(menu, groupName, value) {
        if (groupName === 'sortDir') {
            this.setSortDirection(value);
        }
    },
    getSortDirection: function() {
        var sorter = this.pickSorter();
        return sorter && sorter.getDirection();
    },
    setSortDirection: function(direction) {
        var me = this,
            grid = me.getGrid(),
            store = grid.getStore(),
            sorter = me.pickSorter(),
            sorters = store.getSorters(true),
            isSorted = sorter && (sorters.contains(sorter) || sorter.isGrouper);
        
        if (direction) {
            if (isSorted) {
                if (sorter.getDirection() !== direction) {
                    sorter.setDirection(direction);
                    if (sorter.isGrouper) {
                        store.group(sorter);
                    } else {
                        sorters.beginUpdate();
                        sorters.endUpdate();
                    }
                }
            } else 
            
            {
                return me.sort(direction);
            }
        }
        
        
        
        else if (sorter) {
            sorters.remove(sorter);
        }
        
        
        
        
        if (!store.getRemoteSort()) {
            me.getRootHeaderCt().setSortState();
        }
    },
    syncMenuItemState: function(menuItem) {
        if (menuItem) {
            var me = this,
                sortable = me.isSortable(),
                store = me.getGrid().getStore(),
                sorter = me.pickSorter(),
                isSorted = sorter && (store.getSorters().contains(sorter) || sorter.isGrouper);
            menuItem.setDisabled(!sortable);
            menuItem.setChecked(sortable && isSorted && sorter.getDirection() === menuItem.getValue());
        }
    },
    onToggleShowInGroups: function(menuItem) {
        var grid = this.getGrid(),
            store = grid.getStore();
        store.setGrouper(null);
    },
    updateResizable: function(resizable) {
        var me = this,
            widthed = me.getWidth() != null,
            flexed = me.getFlex() != null;
        
        
        me.toggleCls(me.resizableCls, !!(me.getResizable() && (widthed || flexed || me.isLeafHeader)));
    },
    updateText: function(text) {
        this.setHtml(text || '\xa0');
    },
    onResize: function() {
        if (!this.isHidden(true)) {
            
            
            this.updateResizable(this.getResizable());
            
            
            
            this.measureWidth();
        }
    },
    getComputedWidth: function() {
        return this.isVisible(true) ? this._computedWidth : 0;
    },
    updateColumns: function(columns) {
        this.getItems();
        this.add(columns);
    },
    measureWidth: function() {
        
        
        
        var width = this.el.measure('w');
        this.setComputedWidth(width);
        return width;
    },
    updateComputedWidth: function(value, oldValue) {
        var me = this,
            rootHeaderCt = !me.isConfiguring && me.getRootHeaderCt();
        
        
        
        
        
        
        if (rootHeaderCt) {
            
            rootHeaderCt.onColumnComputedWidthChange(me, value);
            
            me.fireEvent('columnresize', me, value, oldValue);
        }
    },
    updateDataIndex: function(dataIndex) {
        var sorter;
        if (!this.isConfiguring) {
            sorter = this.pickSorter();
            if (sorter) {
                this.setSorter(null);
            }
        }
    },
    applyGroupHeaderTpl: function(tpl) {
        return Ext.XTemplate.get(tpl);
    },
    updateGroupHeaderTpl: function(tpl) {
        var grouper = this.grouper;
        if (grouper) {
            grouper.headerTpl = tpl;
        }
    },
    isSortable: function() {
        var me = this;
        
        
        
        
        
        
        return me.isLeafHeader && me.getSortable() && (me.pickSorter() || me.getDataIndex()) && me.getRootHeaderCt().getSortable() && me.getGrid().sortableColumns !== false;
    },
    applyEditor: function(value) {
        if (value && !value.isInstance) {
            if (typeof (value) === 'string') {
                value = {
                    xtype: value
                };
            }
            if (!value.xtype) {
                value = Ext.apply({
                    xtype: value.field ? 'celleditor' : 'textfield'
                }, value);
            }
            return Ext.create(value);
        }
        return value;
    },
    applyDefaultEditor: function(editor) {
        var dataIndex = this.getDataIndex(),
            model, field;
        if (dataIndex && !editor.isInstance) {
            
            editor = Ext.clone(editor);
            
            if (!editor.isInstance && !editor.xtype) {
                model = this.getGrid().getStore().getModel();
                field = model.getField(dataIndex);
                if (field) {
                    switch (field.type) {
                        case 'date':
                            editor.xtype = 'datefield';
                            break;
                        case 'int':
                        case 'integer':
                            editor.xtype = 'numberfield';
                            editor.decimals = 0;
                            break;
                        case 'float':
                        case 'number':
                            editor.xtype = 'numberfield';
                            break;
                        case 'boolean':
                        case 'bool':
                            editor.xtype = 'checkboxfield';
                            break;
                        default:
                            editor.xtype = 'textfield';
                    }
                } else {
                    editor.xtype = 'textfield';
                }
            }
        }
        return editor;
    },
    updateEditor: function(editor, oldEditor) {
        
        
        
        if (oldEditor && (!editor || (editor.isCellEditor && editor.getField() !== oldEditor))) {
            oldEditor.destroy();
        }
    },
    applyFormatter: function(format) {
        var me = this,
            fmt = format,
            parser;
        if (fmt) {
            parser = Ext.app.bind.Parser.fly(fmt);
            fmt = parser.compileFormat();
            parser.release();
            return function(v) {
                return fmt(v, me.getScope() || me.resolveListenerScope());
            };
        }
        return fmt;
    },
    applySummaryFormatter: function(format) {
        var me = this,
            fmt = format,
            parser;
        if (fmt) {
            parser = Ext.app.bind.Parser.fly(fmt);
            fmt = parser.compileFormat();
            parser.release();
            return function(v) {
                return fmt(v, me.getScope() || me.resolveListenerScope());
            };
        }
        return fmt;
    },
    applyGrouper: function(grouper) {
        var me = this,
            cfg = grouper;
        if (cfg && !cfg.isInstance) {
            if (typeof cfg === 'string') {
                cfg = {
                    groupFn: cfg
                };
            } else {
                cfg = Ext.apply({}, cfg);
            }
            if (typeof cfg.groupFn === 'string') {
                cfg = me.scopeReplacer(cfg, grouper, 'groupFn', 'setGroupFn');
            }
            if (typeof cfg.sorterFn === 'string') {
                cfg = me.scopeReplacer(cfg, grouper, 'sorterFn', 'setSorterFn');
            }
            grouper = new Ext.util.Grouper(cfg);
        }
        
        
        if (grouper) {
            grouper.owner = me.getGrid();
            grouper.headerTpl = me.getGroupHeaderTpl();
        }
        
        
        return grouper;
    },
    updateGrouper: function(grouper, oldGrouper) {
        var store = this.getGrid().getStore();
        if (store && oldGrouper) {
            if (oldGrouper === store.getGrouper()) {
                store.setGrouper(grouper);
            }
        }
        this.grouper = grouper;
    },
    applySorter: function(sorter) {
        var me = this,
            cfg = sorter,
            sortProperty;
        if (cfg && !cfg.isInstance) {
            
            if (cfg === true) {
                sortProperty = me.getSortParam();
                if (!sortProperty) {
                    return null;
                }
                cfg = {
                    property: sortProperty,
                    direction: 'ASC'
                };
            } else {
                if (typeof cfg === 'string') {
                    cfg = {
                        sorterFn: cfg
                    };
                }
                if (typeof cfg.sorterFn === 'string') {
                    cfg = me.scopeReplacer(cfg, sorter, 'sorterFn', 'setSorterFn');
                }
            }
            sorter = new Ext.util.Sorter(cfg);
        }
        if (sorter) {
            sorter.owner = me.getGrid();
        }
        return sorter;
    },
    updateSorter: function(sorter, oldSorter) {
        var store = this.getGrid().getStore(),
            sorters = store ? store.getSorters() : null,
            at;
        
        
        if (sorters) {
            if (oldSorter && (at = sorters.indexOf(oldSorter)) > -1) {
                if (sorter) {
                    sorters.splice(at, 1, sorter);
                } else {
                    sorters.remove(oldSorter);
                }
            }
        }
        
        
        this.sorter = sorter;
    },
    pickSorter: function() {
        var me = this,
            store = me.getGrid().getStore(),
            result;
        
        
        if (store.isGrouped() && store.getGroupField() === me.getDataIndex()) {
            result = me.getGrouper() || store.getGrouper();
            
            me.sortState = result.getDirection();
        } else {
            result = me.getSorter();
        }
        return result;
    },
    applyHideShowMenuItem: function(config, existing) {
        return Ext.updateWidget(existing, config, this, 'createHideShowMenuItem');
    },
    createHideShowMenuItem: function(defaults) {
        return Ext.apply({
            text: this.getText(),
            checked: !this.getHidden(),
            column: this
        }, defaults);
    },
    getHideShowMenuItem: function(deep) {
        var me = this,
            result = me.callParent(),
            items = me.items.items,
            len = items.length,
            childItems = [],
            childColumnsMenu = me.childColumnsMenu,
            i;
        
        
        if (me.isHeaderGroup && deep !== false) {
            if (!childColumnsMenu) {
                result.setMenu({});
                me.childColumnsMenu = childColumnsMenu = result.getMenu();
            }
            if (!childColumnsMenu.items.length || me.rebuildChildColumnsMenu) {
                for (i = 0; i < len; i++) {
                    if (items[i].getHideable()) {
                        childItems.push(items[i].getHideShowMenuItem());
                    }
                }
                childColumnsMenu.removeAll(false);
                childColumnsMenu.add(childItems);
            }
        }
        
        result['set' + (result.getMenu() ? 'CheckChange' : '') + 'Disabled'](!me.isHideable());
        return result;
    },
    getInnerHtmlElement: function() {
        return this.textElement;
    },
    
    getSortParam: function() {
        return this.getDataIndex();
    },
    applyCell: function(cell, oldCell) {
        
        if (oldCell) {
            cell = Ext.apply(oldCell, cell);
        }
        return cell;
    },
    createCell: function(row) {
        var me = this,
            cfg = {
                row: row,
                ownerCmp: row || me,
                column: me,
                width: me.rendered ? (me.getComputedWidth() || me.measureWidth()) : me.getWidth(),
                minWidth: me.getMinWidth()
            },
            align = me.getAlign(),
            cellCfg;
        if (row && row.isSummaryRow) {
            cellCfg = me.getSummaryCell();
            if (!cellCfg) {
                cellCfg = me.getCell();
                if (cellCfg.xtype === 'widgetcell') {
                    
                    
                    cellCfg = Ext.apply({}, cellCfg);
                    cellCfg.xtype = 'gridcell';
                    delete cellCfg.widget;
                }
            }
        } else {
            cellCfg = me.getCell();
        }
        if (align) {
            
            
            
            cfg.align = align;
        }
        if (row) {
            cfg.hidden = me.isHidden(row.getGrid().getHeaderContainer());
            cfg.record = row.getRecord();
            if (!(cfg.ui = row.getDefaultCellUI())) {
                delete cfg.ui;
            }
        }
        if (typeof cellCfg === 'string') {
            cfg.xtype = cellCfg;
        } else {
            Ext.apply(cfg, cellCfg);
        }
        return cfg;
    },
    applyScratchCell: function(cell, oldCell) {
        var me = this;
        if (cell) {
            cell = Ext.create(me.createCell());
            if (!cell.printValue) {
                
                
                Ext.destroy(cell);
                cell = me.createCell();
                cell.xtype = 'gridcell';
                cell = Ext.create(cell);
            }
            
            
            cell.addCls(me.floatingCls);
        }
        if (oldCell) {
            oldCell.destroy();
        }
        return cell;
    },
    printValue: function(value) {
        var me = this,
            rows = me.getGrid().dataItems,
            cell;
        if (rows.length) {
            cell = rows[0].getCellByColumn(me);
        }
        cell = (cell && cell.printValue) ? cell : me.getScratchCell();
        return cell.printValue(value);
    },
    privates: {
        
        directionSequence: {
            "null": "ASC",
            "ASC": "DESC",
            "DESC": null
        },
        applySummary: function(summary) {
            if (summary) {
                summary = Ext.Factory.dataSummary(summary);
            }
            return summary;
        },
        beginRefresh: function(context) {
            
            var me = this,
                grid = me.getGrid();
            context = context || {};
            context.column = me;
            context.grid = grid;
            
            
            context.store = grid.store;
            return context;
        },
        canGroup: function() {
            return this.getGroupable() && (this.getDataIndex() || this.getGrouper());
        },
        
        sort: function(direction, mode) {
            var me = this,
                sorter = me.pickSorter(),
                grid = me.getGrid(),
                store = grid.getStore(),
                sorters = store.getSorters();
            if (!me.isSortable()) {
                return;
            }
            
            
            
            
            if (sorter.isGrouper) {
                if (sorter.getDirection() !== direction) {
                    sorter.toggle();
                    store.group(sorter);
                }
            }
            
            else if (direction) {
                
                if (sorter) {
                    
                    
                    if (sorters.indexOf(sorter) !== 0) {
                        sorter.setDirection(direction);
                    }
                } else 
                {
                    me.setSorter({
                        property: me.getSortParam(),
                        direction: 'ASC'
                    });
                    sorter = me.getSorter();
                }
                
                
                
                
                store.sort(sorter, mode || grid.getMultiColumnSort() ? 'multi' : 'replace');
            } else 
            {
                if (sorter) {
                    sorters.remove(sorter);
                    
                    
                    
                    
                    
                    if (!store.getRemoteSort()) {
                        me.getRootHeaderCt().setSortState();
                    }
                }
            }
        },
        
        toggleSortState: function() {
            this.sort(this.directionSequence[this.sortState]);
        },
        
        setSortState: function(sorter) {
            
            
            var me = this,
                store = me.getGrid().getStore(),
                grouper = store.isGrouped() && store.getGrouper(),
                oldDirection = me.sortState,
                direction = null,
                sortedCls = me.sortedCls,
                secondarySortCls = me.secondarySortCls,
                auxSortCls = me.auxSortCls,
                ascCls = sortedCls + '-asc',
                descCls = sortedCls + '-desc',
                ariaDom = me.ariaEl.dom,
                sortPrioClass = '',
                changed, index,
                remove = [
                    secondarySortCls,
                    auxSortCls
                ],
                add;
            if (sorter) {
                if (typeof sorter === 'string') {
                    direction = sorter;
                } else {
                    
                    if (!sorter.isSorter) {
                        Ext.raise('Must pass a sorter instance into HeaderContainer#saveState');
                    }
                    
                    
                    if (sorter === grouper) {
                        index = 0;
                    } else {
                        index = store.getSorters().indexOf(sorter);
                    }
                    
                    if (index === -1) {
                        Ext.raise("Sorter passed into HeaderContainer#saveState is not used by the grid's store");
                    }
                    
                    direction = sorter.getDirection();
                    sortPrioClass = index === 1 ? secondarySortCls : index > 1 ? auxSortCls : '';
                }
            }
            
            changed = direction !== oldDirection;
            me.sortState = direction;
            switch (direction) {
                case 'DESC':
                    add = [
                        sortedCls,
                        descCls,
                        sortPrioClass
                    ];
                    remove.push(ascCls);
                    break;
                case 'ASC':
                    add = [
                        sortedCls,
                        ascCls,
                        sortPrioClass
                    ];
                    remove.push(descCls);
                    break;
                default:
                    remove.push(sortedCls, ascCls, descCls);
                    break;
            }
            me.replaceCls(remove, add);
            if (ariaDom) {
                if (direction) {
                    ariaDom.setAttribute('aria-sort', me.ariaSortStates[direction]);
                } else {
                    ariaDom.removeAttribute('aria-sort');
                }
            }
            
            if (changed) {
                me.fireEvent('sort', me, direction, oldDirection);
            }
        },
        getVisibleCount: function() {
            var columns = this.getInnerItems(),
                len = columns.length,
                count = 0,
                i;
            for (i = 0; i < len; ++i) {
                if (columns[i].isHeaderGroup) {
                    count += columns[i].getVisibleCount();
                } else {
                    count += columns[i].isHidden() ? 0 : 1;
                }
            }
            return count;
        },
        onShow: function() {
            var toShow;
            
            if (!this.getVisibleCount()) {
                toShow = this.getComponent(0);
                if (toShow) {
                    toShow.show();
                }
            }
        },
        doVisibilityCheck: function() {
            var me = this,
                columns = me.getInnerItems(),
                ln = columns.length,
                i, column;
            for (i = 0; i < ln; i++) {
                column = columns[i];
                if (!column.isHidden()) {
                    if (me.isHidden()) {
                        if (me.initialized) {
                            me.show();
                        } else {
                            me.setHidden(false);
                        }
                    }
                    return;
                }
            }
            me.hide();
            
            me.rebuildChildColumnsMenu = true;
            
            me.updateMenuDisabledState();
        },
        onColumnShow: function() {
            var me = this,
                hideShowItem;
            if (me.getVisibleCount() > 0) {
                me.show();
                hideShowItem = me.getHideShowMenuItem(false);
                hideShowItem.setChecked(true);
                hideShowItem.setCheckChangeDisabled(false);
            }
            
            me.rebuildChildColumnsMenu = true;
            
            me.updateMenuDisabledState();
        },
        onColumnHide: function(column) {
            var me = this,
                hideShowItem;
            if (me.getVisibleCount() === 0) {
                me.hide();
                hideShowItem = me.getHideShowMenuItem(false);
                hideShowItem.setChecked(false);
                hideShowItem.setCheckChangeDisabled(true);
            }
            
            me.rebuildChildColumnsMenu = true;
            
            me.updateMenuDisabledState();
        },
        onColumnMove: function(column) {
            
            this.rebuildChildColumnsMenu = true;
        },
        scopeReplacer: function(config, original, prop, setter) {
            var me = this,
                name = config[prop];
            if (typeof name === 'string') {
                prop = prop || 'sorterFn';
                setter = setter || 'setSorterFn';
                if (original === config) {
                    config = Ext.apply({}, config);
                }
                
                
                
                config[prop] = function() {
                    
                    var scope = me.resolveListenerScope(),
                        fn = scope && scope[name],
                        ret = 0;
                    if (fn) {
                        this[setter](fn.bind(scope));
                        ret = fn.apply(scope, arguments);
                    }
                    
                    else if (!scope) {
                        Ext.raise('Cannot resolve scope for column ' + me.id);
                    } else {
                        Ext.raise('No such method "' + name + '" on ' + scope.$className);
                    }
                    
                    return ret;
                };
            }
            return config;
        }
    }
});



Ext.define('Ext.grid.cell.Date', {
    extend: Ext.grid.cell.Text,
    xtype: 'datecell',
    isDateCell: true,
    config: {
        
        format: ''
    },
    updateColumn: function(column, oldColumn) {
        var format;
        this.callParent([
            column,
            oldColumn
        ]);
        if (column && column.isDateColumn) {
            format = column.getFormat();
            if (format !== null) {
                this.setFormat(format);
            }
        }
    },
    applyFormat: function(format) {
        return format || Ext.Date.defaultFormat;
    },
    updateFormat: function(format) {
        if (!this.isConfiguring) {
            this.writeValue();
        }
    },
    formatValue: function(value) {
        return value ? Ext.Date.format(value, this.getFormat()) : '';
    }
});


Ext.define('Ext.grid.column.Date', {
    extend: Ext.grid.column.Column,
    xtype: 'datecolumn',
    isDateColumn: true,
    config: {
        
        format: null,
        defaultEditor: {
            xtype: 'datepickerfield'
        },
        cell: {
            xtype: 'datecell'
        }
    }
});


Ext.define('Ext.grid.menu.Columns', {
    extend: Ext.menu.Item,
    xtype: 'gridcolumnsmenu',
    iconCls: Ext.baseCSSPrefix + 'headermenu-columns-icon',
    
    text: 'Columns',
    menu: {},
    updateMenu: function(menu, oldMenu) {
        this.callParent([
            menu,
            oldMenu
        ]);
        if (menu) {
            this.menuListeners = menu.on({
                beforeshow: 'onBeforeShowColumnsMenu',
                checkchange: {
                    fn: 'onCheckItem',
                    delegate: 'menucheckitem'
                },
                scope: this,
                destroyable: true
            });
        } else {
            Ext.destroy(this.menuListeners);
        }
    },
    onBeforeShowColumnsMenu: function(menu) {
        var me = this,
            grid = me.grid,
            columns = grid.getHeaderContainer().items.items,
            items = [],
            len = columns.length,
            i, column;
        for (i = 0; i < len; ++i) {
            column = columns[i];
            
            
            
            
            
            
            if (column.getHideable()) {
                items.push(column.getHideShowMenuItem());
            }
        }
        
        
        menu.removeAll(false);
        menu.add(items);
    },
    onCheckItem: function(menuItem, checked) {
        menuItem.column.setHidden(!checked);
    }
});


Ext.define('Ext.grid.menu.GroupByThis', {
    extend: Ext.menu.Item,
    xtype: 'gridgroupbythismenuitem',
    iconCls: Ext.baseCSSPrefix + 'headermenu-group-by-this',
    
    text: 'Group by this field'
});


Ext.define('Ext.grid.menu.ShowInGroups', {
    extend: Ext.menu.CheckItem,
    xtype: 'gridshowingroupsmenuitem',
    hideOnClick: true,
    
    text: 'Show in groups'
});


Ext.define('Ext.menu.RadioItem', {
    extend: Ext.menu.CheckItem,
    alias: 'widget.menuradioitem',
    classCls: Ext.baseCSSPrefix + 'menuradioitem',
    nameable: true,
    shareableName: true,
    ariaRole: 'menuitemradio',
    
    config: {
        
        group: null,
        
        allowUncheck: null
    },
    
    initialize: function() {
        if (!this.getGroup()) {
            Ext.raise('Menu RadioItems must be configured with a group');
        }
        this.callParent();
    },
    
    privates: {
        onSpace: function(e) {
            
            if (this.checkboxElement.dom.checked) {
                e.preventDefault();
            }
        },
        updateGroup: function(group) {
            
            this.name = group;
        },
        onCheckboxChange: function() {
            var checkboxElement = this.checkboxElement.dom,
                isChecked = checkboxElement.checked;
            
            if (isChecked === this.getChecked() || this.getDisabled()) {
                return;
            }
            
            
            
            
            
            
            
            if (!isChecked && !this.getAllowUncheck()) {
                checkboxElement.checked = true;
            } else 
            {
                this.callParent();
            }
        },
        onCheckChange: function() {
            var me = this,
                checkboxElement = me.checkboxElement.dom,
                parentMenu = me.getParent(),
                name, groups, siblings, len, i;
            
            me.getGroup();
            name = me.name;
            
            
            if (parentMenu && name) {
                groups = {};
                if (checkboxElement.checked) {
                    groups[name] = me.getValue();
                    parentMenu.setGroups(groups);
                } else 
                
                {
                    siblings = parentMenu.lookupName(name);
                    len = siblings && siblings.length;
                    for (i = 0; i < len && !siblings[i].checkboxElement.dom.checked; i++) {}
                    
                    
                    
                    
                    
                    
                    
                    if (i === len) {
                        groups[name] = null;
                        parentMenu.setGroups(groups);
                    }
                }
            }
            me.callParent();
        }
    }
});


Ext.define('Ext.grid.menu.SortAsc', {
    extend: Ext.menu.RadioItem,
    xtype: 'gridsortascmenuitem',
    iconCls: Ext.baseCSSPrefix + 'headermenu-sort-asc',
    
    text: 'Sort Ascending',
    value: 'ASC',
    allowUncheck: true,
    group: 'grid-sorters'
});


Ext.define('Ext.grid.menu.SortDesc', {
    extend: Ext.menu.RadioItem,
    xtype: 'gridsortdescmenuitem',
    iconCls: Ext.baseCSSPrefix + 'headermenu-sort-desc',
    
    text: 'Sort Descending',
    value: 'DESC',
    allowUncheck: true,
    group: 'grid-sorters'
});


Ext.define('Ext.grid.selection.Cells', {
    extend: Ext.dataview.selection.Selection,
    alias: 'selection.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;
    },
    
    isSelected: function(recordIndex, columnIndex) {
        var range;
        if (this.startCell) {
            if (recordIndex.isGridLocation) {
                columnIndex = recordIndex.columnIndex;
                recordIndex = recordIndex.recordIndex;
            }
            
            if (!(Ext.isNumber(recordIndex) && Ext.isNumber(columnIndex))) {
                Ext.raise('Cells#isSelected must be passed either a GridLocation of a row and column index');
            }
            
            
            range = this.getRowRange();
            if (recordIndex >= range[0] && recordIndex <= range[1]) {
                
                range = this.getColumnRange();
                return (columnIndex >= range[0] && columnIndex <= range[1]);
            }
        }
        return false;
    },
    eachRow: function(fn, scope) {
        var me = this,
            rowRange = me.getRowRange(),
            store = me.view.store,
            rowIdx;
        for (rowIdx = rowRange[0]; rowIdx <= rowRange[1]; rowIdx++) {
            if (fn.call(scope || me, store.getAt(rowIdx)) === false) {
                return;
            }
        }
    },
    eachColumn: function(fn, scope) {
        var colRange = this.getColumnRange(),
            columns = this.view.getVisibleColumns(),
            i;
        for (i = colRange[0]; i <= colRange[1]; i++) {
            if (fn.call(scope || this, columns[i], i) === false) {
                return;
            }
        }
    },
    eachCell: function(fn, scope) {
        var me = this,
            view = me.view,
            store = view.store,
            rowRange = me.getRowRange(),
            colRange = me.getColumnRange(),
            baseLocation, location, rowIdx, colIdx;
        for (rowIdx = rowRange[0]; rowIdx <= rowRange[1]; rowIdx++) {
            baseLocation = new Ext.grid.Location(view, store.getAt(rowIdx));
            for (colIdx = colRange[0]; colIdx <= colRange[1]; colIdx++) {
                location = baseLocation.cloneForColumn(colIdx);
                if (fn.call(scope || me, location, colIdx, rowIdx) === false) {
                    return;
                }
            }
        }
    },
    
    getFirstRowIndex: function() {
        return this.startCell ? Math.min(this.startCell.recordIndex, this.endCell.recordIndex) : 0;
    },
    
    getLastRowIndex: function() {
        return this.startCell ? Math.max(this.startCell.recordIndex, this.endCell.recordIndex) : -1;
    },
    
    getFirstColumnIndex: function() {
        return this.startCell ? Math.min(this.startCell.columnIndex, this.endCell.columnIndex) : 0;
    },
    
    getLastColumnIndex: function() {
        return this.startCell ? Math.max(this.startCell.columnIndex, this.endCell.columnIndex) : -1;
    },
    
    privates: {
        
        clear: function(suppressEvent) {
            var me = this,
                view = me.view,
                changed;
            if (view.getVisibleColumns().length) {
                me.eachCell(function(location) {
                    view.onCellDeselect(location);
                    changed = true;
                });
            }
            me.startCell = me.endCell = null;
            if (changed && !suppressEvent) {
                this.getSelectionModel().fireSelectionChange();
            }
        },
        
        setRangeStart: function(startCell) {
            
            this.startCell = (this.endCell = startCell.clone()).clone();
            this.view.onCellSelect(startCell);
        },
        
        setRangeEnd: function(endCell) {
            var me = this,
                view = me.view,
                store = view.store,
                renderInfo = view.renderInfo,
                maxColIdx = view.getVisibleColumns().length - 1,
                range, lastRange, rowStart, rowEnd, colStart, colEnd, rowIdx, colIdx, location, baseLocation;
            me.endCell = endCell.clone();
            range = me.getRange();
            lastRange = me.lastRange || range;
            rowStart = Math.max(Math.min(range[0][1], lastRange[0][1]), renderInfo.indexTop);
            rowEnd = Math.min(Math.max(range[1][1], lastRange[1][1]), renderInfo.indexBottom - 1);
            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++) {
                baseLocation = new Ext.grid.Location(view, store.getAt(rowIdx));
                for (colIdx = colStart; colIdx <= colEnd; colIdx++) {
                    location = baseLocation.cloneForColumn(colIdx);
                    
                    if (rowIdx < range[0][1] || rowIdx > range[1][1] || colIdx < range[0][0] || colIdx > range[1][0]) {
                        view.onCellDeselect(location);
                    } else {
                        view.onCellSelect(location);
                    }
                }
            }
            me.lastRange = range;
        },
        extendRange: function(extensionVector) {
            var me = this,
                view = me.view,
                newEndCell;
            if (extensionVector[extensionVector.type] < 0) {
                newEndCell = new Ext.grid.Location(view, {
                    record: me.getLastRowIndex(),
                    column: me.getLastColumnIndex()
                });
                me.startCell = extensionVector.start.clone();
                me.setRangeEnd(newEndCell);
                me.view.getNavigationModel().setLocation(extensionVector.start);
            } else {
                me.startCell = new Ext.grid.Location(view, {
                    record: me.getFirstRowIndex(),
                    column: me.getFirstColumnIndex()
                });
                me.setRangeEnd(extensionVector.end);
                me.view.getNavigationModel().setLocation(extensionVector.end);
            }
        },
        
        getRange: function() {
            return [
                [
                    this.getFirstColumnIndex(),
                    this.getFirstRowIndex()
                ],
                [
                    this.getLastColumnIndex(),
                    this.getLastRowIndex()
                ]
            ];
        },
        
        getRangeSize: function() {
            return this.getCount();
        },
        
        getRecords: function() {
            var rowRange = this.getRowRange();
            return this.getSelectionModel().getStore().getRange(rowRange[0], rowRange[1]);
        },
        
        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,
                columns = view.getVisibleColumns();
            me.clear();
            me.setRangeStart(new Ext.grid.Location(view, {
                record: 0,
                column: 0
            }));
            me.setRangeEnd(new Ext.grid.Location(view, {
                record: view.store.last(),
                column: columns[columns.length - 1]
            }));
        },
        
        isAllSelected: function() {
            var start = this.startCell,
                end = this.endCell;
            
            if (start) {
                if (!start.columnIndex && !start.recordIndex) {
                    return end.columnIndex === end.view.getVisibleColumns().length - 1 && end.recordIndex === end.view.store.getCount() - 1;
                }
            }
            return false;
        },
        
        getColumnRange: function() {
            return [
                this.getFirstColumnIndex(),
                this.getLastColumnIndex()
            ];
        },
        
        getLastSelected: function() {
            return this.view.getStore().getAt(this.endCell.recordIndex);
        },
        
        getRowRange: function() {
            return [
                this.getFirstRowIndex(),
                this.getLastRowIndex()
            ];
        },
        onSelectionFinish: function() {
            var me = this,
                view = me.view;
            if (me.getCount()) {
                me.getSelectionModel().onSelectionFinish(me, new Ext.grid.Location(view, {
                    record: me.getFirstRowIndex(),
                    column: me.getFirstColumnIndex()
                }), new Ext.grid.Location(view, {
                    record: me.getLastRowIndex(),
                    column: me.getLastColumnIndex()
                }));
            } else {
                me.getSelectionModel().onSelectionFinish(me);
            }
        }
    }
});


Ext.define('Ext.grid.selection.Columns', {
    extend: Ext.dataview.selection.Selection,
    alias: 'selection.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.getStore().each(fn, scope || this);
        }
    },
    eachColumn: function(fn, scope) {
        var me = this,
            columns = me.selectedColumns,
            len, i;
        if (columns) {
            len = columns.length;
            for (i = 0; i < len; i++) {
                if (fn.call(scope || me, columns[i], i) === false) {
                    return false;
                }
            }
        }
    },
    eachCell: function(fn, scope) {
        var me = this,
            view = me.view,
            columns = me.selectedColumns,
            context = new Ext.grid.Location(view),
            len, i;
        if (columns) {
            len = columns.length;
            
            view.getStore().each(function(record) {
                context = context.clone({
                    record: record
                });
                for (i = 0; i < len; i++) {
                    context = context.clone({
                        column: columns[i]
                    });
                    if (fn.call(scope || me, context, context.columnIndex, context.recordIndex) === false) {
                        return false;
                    }
                }
            });
        }
    },
    
    
    
    isSelected: function(column) {
        var selectedColumns = this.selectedColumns;
        if (column && column.isGridColumn && 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, suppressEvent) {
            
            if (!column.isGridColumn) {
                Ext.raise('Column selection must be passed a grid Column header object');
            }
            
            var me = this,
                selModel = me.getSelectionModel();
            Ext.Array.include((me.selectedColumns || (me.selectedColumns = [])), column);
            me.refreshColumns(column);
            selModel.updateHeaderState();
            if (!suppressEvent) {
                selModel.fireSelectionChange();
            }
        },
        
        clear: function(suppressEvent) {
            var me = this,
                selModel = me.getSelectionModel(),
                prevSelection = me.selectedColumns;
            if (prevSelection && prevSelection.length) {
                me.selectedColumns = [];
                me.refreshColumns.apply(me, prevSelection);
                selModel.updateHeaderState();
                if (!suppressEvent) {
                    selModel.fireSelectionChange();
                }
            }
        },
        setRangeStart: function(startColumn) {
            var me = this,
                prevSelection = me.getColumns();
            me.startColumn = startColumn;
            me.selectedColumns = [
                startColumn
            ];
            prevSelection.push(startColumn);
            me.refreshColumns.apply(me, prevSelection);
        },
        setRangeEnd: function(endColumn) {
            var me = this,
                prevSelection = me.getColumns(),
                headerCt = this.view.ownerGrid.getHeaderContainer(),
                columns = headerCt.getVisibleColumns(),
                start = headerCt.indexOfLeaf(me.startColumn),
                end = headerCt.indexOfLeaf(endColumn),
                i;
            
            if (end < start) {
                i = start;
                start = end;
                end = i;
            }
            me.selectedColumns = [];
            for (i = start; i <= end; i++) {
                me.selectedColumns.push(columns[i]);
                prevSelection.push(columns[i]);
            }
            me.refreshColumns.apply(me, prevSelection);
        },
        
        isAllSelected: function() {
            var selectedColumns = this.selectedColumns;
            
            return selectedColumns && selectedColumns.length === this.view.getHeaderContainer().getVisibleColumns().length;
        },
        
        refreshColumns: function(column) {
            var me = this,
                view = me.view,
                store = view.store,
                renderInfo = view.renderInfo,
                columns = arguments,
                len = columns.length,
                selected = [],
                location, rowIdx, colIdx;
            if (view.rendered) {
                for (colIdx = 0; colIdx < len; colIdx++) {
                    selected[colIdx] = me.isSelected(columns[colIdx]);
                }
                for (rowIdx = renderInfo.indexTop; rowIdx < renderInfo.indexBottom; rowIdx++) {
                    location = new Ext.grid.Location(view, store.getAt(rowIdx));
                    for (colIdx = 0; colIdx < len; colIdx++) {
                        
                        location = location.cloneForColumn(columns[colIdx]);
                        if (selected[colIdx]) {
                            view.onCellSelect(location);
                        } else {
                            view.onCellDeselect(location);
                        }
                    }
                }
            }
        },
        
        remove: function(column, suppressEvent) {
            var selModel = this.getSelectionModel();
            
            if (!column.isGridColumn) {
                Ext.raise('Column selection must be passed a grid Column header object');
            }
            
            if (this.selectedColumns) {
                Ext.Array.remove(this.selectedColumns, column);
                
                
                if (column.getGrid() && column.isVisible()) {
                    this.refreshColumns(column);
                    selModel.updateHeaderState();
                    if (!suppressEvent) {
                        selModel.fireSelectionChange();
                    }
                }
            }
        },
        
        selectAll: function() {
            var me = this;
            me.clear();
            me.selectedColumns = me.getSelectionModel().lastContiguousColumnRange = me.view.getHeaderContainer().getVisibleColumns();
            me.refreshColumns.apply(me, me.selectedColumns);
        },
        extendRange: function(extensionVector) {
            var me = this,
                columns = me.view.getHeaderContainer().getVisibleColumns(),
                i;
            for (i = extensionVector.start.columnIndex; i <= extensionVector.end.columnIndex; i++) {
                me.add(columns[i]);
            }
        },
        onSelectionFinish: function() {
            var me = this,
                range = me.getContiguousSelection();
            if (range) {
                me.getSelectionModel().onSelectionFinish(me, new Ext.grid.Location(me.view, {
                    record: 0,
                    column: range[0]
                }), new Ext.grid.Location(me.view, {
                    record: me.view.getStore().getCount() - 1,
                    column: range[1]
                }));
            } else {
                me.getSelectionModel().onSelectionFinish(me);
            }
        },
        
        getContiguousSelection: function() {
            var selection = Ext.Array.sort(this.getColumns(), function(c1, c2) {
                    
                    return c1.getGrid().ownerGrid.getHeaderContainer().indexOfLeaf(c1) - c2.getGrid().ownerGrid.getHeaderContainer().indexOfLeaf(c2);
                }),
                len = selection.length,
                i;
            if (len) {
                for (i = 1; i < len; i++) {
                    if (selection[i].getVisibleIndex() !== selection[i - 1].getVisibleIndex() + 1) {
                        return false;
                    }
                }
                return [
                    selection[0],
                    selection[len - 1]
                ];
            }
        }
    }
});


Ext.define('Ext.grid.selection.Replicator', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.selectionreplicator',
    
    init: function(grid) {
        this.gridListeners = grid.on({
            beforeselectionextend: this.onBeforeSelectionExtend,
            scope: this,
            destroyable: true
        });
    },
    onBeforeSelectionExtend: function(ownerGrid, sel, extension) {
        var columns = this.columns = [];
        sel.eachColumn(function(column) {
            columns.push(column);
        });
        return this.replicateSelection(ownerGrid, sel, extension);
    },
    
    replicateSelection: function(ownerGrid, sel, extension) {
        
        if (extension.columns || sel.isColumns) {
            return;
        }
        var me = this,
            columns = me.columns,
            colCount, j, column, values, startIdx, endIdx, i, increment, store, record, prevValues, prevValue,
            selFirstRowIdx = sel.getFirstRowIndex(),
            selLastRowIdx = sel.getLastRowIndex(),
            selectedRowCount = selLastRowIdx - selFirstRowIdx + 1,
            lastTwoRecords = [],
            x, y;
        colCount = columns.length;
        store = columns[0].getGrid().getStore();
        
        if (selectedRowCount === 1) {
            values = me.getColumnValues(sel.view.getStore().getAt(selFirstRowIdx));
        } else 
        {
            values = new Array(colCount);
            if (extension.rows < 0) {
                lastTwoRecords = [
                    store.getAt(selFirstRowIdx + 1),
                    store.getAt(selFirstRowIdx)
                ];
            } else {
                lastTwoRecords = [
                    store.getAt(selLastRowIdx - 1),
                    store.getAt(selLastRowIdx)
                ];
            }
            lastTwoRecords[0] = me.getColumnValues(lastTwoRecords[0]);
            lastTwoRecords[1] = me.getColumnValues(lastTwoRecords[1]);
            
            
            for (j = 0; j < colCount; j++) {
                x = lastTwoRecords[1][j];
                y = lastTwoRecords[0][j];
                if (!isNaN(x) && !isNaN(y)) {
                    values[j] = Number(x) - Number(y);
                }
            }
        }
        
        if (extension.rows < 0) {
            startIdx = extension.end.recordIndex;
            endIdx = extension.start.recordIndex - 1;
            increment = -1;
        } else {
            startIdx = extension.start.recordIndex;
            endIdx = extension.end.recordIndex + 1;
            increment = 1;
        }
        
        if (selectedRowCount === 1) {
            for (i = startIdx; i !== endIdx; i += increment) {
                record = store.getAt(i);
                for (j = 0; j < colCount; j++) {
                    column = columns[j];
                    if (column.getDataIndex()) {
                        record.set(column.getDataIndex(), values[j]);
                    }
                }
            }
        } else 
        {
            for (i = startIdx; i !== endIdx; i += increment) {
                record = store.getAt(i);
                prevValues = me.getColumnValues(store.getAt(i - increment));
                for (j = 0; j < colCount; j++) {
                    column = columns[j];
                    if (column.getDataIndex()) {
                        prevValue = prevValues[j];
                        if (!isNaN(prevValue)) {
                            record.set(column.getDataIndex(), Ext.coerce(Number(prevValue) + values[j], prevValue));
                        }
                    }
                }
            }
        }
    },
    
    getColumnValues: function(record) {
        var columns = this.columns,
            len = columns.length,
            i, column,
            result = new Array(columns.length);
        for (i = 0; i < len; i++) {
            column = columns[i];
            
            if (column.getDataIndex()) {
                result[i] = record.get(column.getDataIndex());
            }
        }
        return result;
    },
    destroy: function() {
        this.gridListeners = Ext.destroy(this.gridListeners);
        this.callParent();
    }
});

Ext.define('Ext.grid.selection.SelectionExtender', {
    maskBox: {},
    constructor: function(config) {
        var me = this,
            view = config.view,
            handleListeners = {
                dragstart: 'onDragStart',
                dragend: 'onDragEnd',
                scope: me
            };
        Ext.apply(me, config);
        me.el = view.outerCt;
        me.handle = view.bodyElement.createChild({
            cls: Ext.baseCSSPrefix + 'selmodel-extender-drag-handle'
        }).hide();
        
        if (Ext.supports.Touch) {
            handleListeners.longpress = 'onHandleLongpress';
        }
        me.handle.on(handleListeners);
        me.mask = view.outerCt.createChild({
            cls: Ext.baseCSSPrefix + 'selmodel-extender-mask'
        }).hide();
        me.scrollListener = view.getScrollable().on({
            scroll: me.onViewScroll,
            scope: me,
            destroyable: true
        });
        me.viewListener = view.on({
            columnresize: 'alignHandle',
            columnhide: 'alignHandle',
            columnshow: 'alignHandle',
            columnmove: 'alignHandle',
            scope: me,
            destroyable: true
        });
        if (config && config.axes) {
            me.setAxes(config.axes);
        }
    },
    setAxes: function(axes) {
        var me = this;
        me.axes = axes;
        me.extendX = !!(axes & 1);
        me.extendY = !!(axes & 2);
    },
    setHandle: function(firstPos, endPos) {
        var me = this;
        me.firstPos = firstPos;
        me.endPos = endPos;
        if (firstPos && endPos && endPos.getCell(true)) {
            me.curPos = endPos;
            
            me.alignHandle();
        } else {
            me.disable();
        }
    },
    alignHandle: function() {
        var me = this,
            lastCell = me.endPos;
        
        
        if (me.firstPos && lastCell && me.view.isRecordRendered(lastCell.recordIndex)) {
            
            
            lastCell = lastCell.clone({
                record: lastCell.record,
                column: lastCell.column
            }).getCell();
            if (lastCell && lastCell.isVisible()) {
                me.enable();
            } else {
                me.disable();
            }
            me.handle.alignTo(lastCell, 'c-br');
        } else {
            me.disable();
        }
    },
    enable: function() {
        this.handle.show();
    },
    disable: function() {
        this.handle.hide();
        this.mask.hide();
    },
    onHandleLongpress: function(e) {
        e.startDrag();
    },
    onDragStart: function(e) {
        
        if (e.pointerType !== 'touch' || e.longpress) {
            e.claimGesture();
            this.handle.on('drag', this.onDrag, this);
        }
    },
    onDrag: function(e) {
        
        
        
        
        if (e.changedTouches) {
            var touch = e.changedTouches[0],
                realTarget;
            
            if (touch && !Ext.fly(touch.target).getRegion().contains(touch.point)) {
                realTarget = Ext.event.Event.resolveTextNode(Ext.Element.fromPagePoint(touch.pageX, touch.pageY, true));
                
                if (realTarget) {
                    e.target = realTarget;
                }
            }
        }
        var me = this,
            target = e.target,
            view = me.view,
            scrollClientRegion = view.getScrollable().getElement().getClientRegion(),
            overCell = new Ext.grid.Location(view, target),
            scrollTask = me.scrollTask || (me.scrollTask = Ext.util.TaskManager.newTask({
                run: me.doAutoScroll,
                scope: me,
                interval: 10
            })),
            thresh = 25 * (window.devicePixelRatio || 1),
            scrollDelta = 3 * (window.devicePixelRatio || 1),
            scrollBy = me.scrollBy || (me.scrollBy = []);
        e.claimGesture();
        me.lastXY = [
            e.pageX,
            e.pageY
        ];
        
        if (!me.el.contains(target)) {
            scrollBy[0] = scrollBy[1] = 0;
            return scrollTask.stop();
        }
        
        if (me.lastXY[1] > scrollClientRegion.bottom - thresh) {
            if (me.extendY) {
                scrollBy[1] = scrollDelta;
                scrollTask.start();
            }
        }
        
        else if (me.lastXY[1] < scrollClientRegion.top + thresh) {
            if (me.extendY) {
                scrollBy[1] = -scrollDelta;
                scrollTask.start();
            }
        }
        
        else if (me.lastXY[0] > scrollClientRegion.right - thresh) {
            if (me.extendX) {
                scrollBy[0] = scrollDelta;
                scrollTask.start();
            }
        }
        
        else if (me.lastXY[0] < scrollClientRegion.left + thresh) {
            if (me.extendX) {
                scrollBy[0] = -scrollDelta;
                scrollTask.start();
            }
        } else 
        {
            scrollBy[0] = scrollBy[1] = 0;
            scrollTask.stop();
        }
        if (overCell && overCell.getCell() && !overCell.equals(me.lastOverCell)) {
            me.lastOverCell = overCell;
            me.syncMaskOnCell(overCell);
        }
    },
    doAutoScroll: function() {
        var me = this,
            view = me.view,
            scroller = view.getScrollable(),
            scrollOverCell;
        
        scroller.scrollBy.apply(scroller, me.scrollBy);
        
        scrollOverCell = document.elementFromPoint.apply(document, me.lastXY);
        if (scrollOverCell) {
            scrollOverCell = new Ext.grid.Location(me.view, scrollOverCell);
            if (scrollOverCell && scrollOverCell.getCell() && !scrollOverCell.equals(me.lastOverCell)) {
                me.lastOverCell = scrollOverCell;
                me.syncMaskOnCell(scrollOverCell);
            }
        }
    },
    onDragEnd: function(e) {
        var me = this;
        
        me.handle.un('drag', me.onDrag, me);
        if (me.scrollTask) {
            me.scrollTask.stop();
        }
        if (me.extensionDescriptor) {
            me.disable();
            me.view.getSelectable().extendSelection(me.extensionDescriptor);
        }
    },
    onViewScroll: function() {
        var me = this;
        
        if ((me.active && me.lastOverCell) || me.firstPos) {
            me.endPos = me.endPos.clone({
                record: me.endPos.recordIndex
            });
            
            me.alignHandle();
        }
    },
    stopEvent: function(e) {
        e.stopEvent();
    },
    syncMaskOnCell: function(overCell) {
        var me = this,
            view = me.view,
            renderInfo = view.renderInfo,
            maskBox = me.maskBox,
            startRecordIndex = me.firstPos.recordIndex,
            endRecordIndex = me.endPos.recordIndex,
            extensionStart = me.firstPos.clone({
                record: startRecordIndex
            }),
            extensionEnd = me.endPos.clone({
                record: endRecordIndex
            }),
            selRegion, firstCell, endCell, curPos;
        
        firstCell = me.firstPos.clone({
            record: Ext.Number.constrain(Math.min(startRecordIndex, endRecordIndex), renderInfo.indexTop, renderInfo.indexBottom - 1),
            column: me.firstPos.column
        });
        endCell = me.endPos.clone({
            record: Ext.Number.constrain(Math.max(firstCell.recordIndex, endRecordIndex), renderInfo.indexTop, renderInfo.indexBottom - 1)
        });
        me.selectionRegion = selRegion = firstCell.getCell().getRegion().union(endCell.getCell().getRegion());
        me.curPos = curPos = overCell;
        overCell = overCell.getCell('el');
        
        
        me.mask.dom.style.borderTopWidth = me.mask.dom.style.borderRightWidth = me.mask.dom.style.borderBottomWidth = me.mask.dom.style.borderLeftWidth = '';
        
        if (curPos.recordIndex < me.firstPos.recordIndex && me.extendY) {
            me.extensionDescriptor = {
                type: 'rows',
                start: extensionStart.clone({
                    record: curPos.recordIndex
                }),
                end: extensionEnd.clone({
                    record: me.firstPos.recordIndex - 1
                }),
                rows: curPos.recordIndex - me.firstPos.recordIndex,
                mousePosition: me.lastXY
            };
            me.mask.dom.style.borderBottomWidth = '0';
            maskBox.x = selRegion.x;
            maskBox.y = overCell.getY();
            maskBox.width = selRegion.right - selRegion.left;
            maskBox.height = selRegion.top - overCell.getY();
        }
        
        else if (curPos.recordIndex > me.endPos.recordIndex && me.extendY) {
            me.extensionDescriptor = {
                type: 'rows',
                start: extensionStart.clone({
                    record: me.endPos.recordIndex + 1
                }),
                end: extensionEnd.clone({
                    record: curPos.recordIndex
                }),
                rows: curPos.recordIndex - me.endPos.recordIndex,
                mousePosition: me.lastXY
            };
            me.mask.dom.style.borderTopWidth = '0';
            maskBox.x = selRegion.x;
            maskBox.y = selRegion.bottom;
            maskBox.width = selRegion.right - selRegion.left;
            maskBox.height = overCell.getRegion().bottom - selRegion.bottom;
        } else 
        {
            
            if (curPos.columnIndex < me.firstPos.columnIndex && me.extendX) {
                me.extensionDescriptor = {
                    type: 'columns',
                    start: extensionStart.clone({
                        column: curPos.columnIndex
                    }),
                    end: extensionEnd.clone({
                        column: me.firstPos.columnIndex - 1
                    }),
                    columns: curPos.columnIndex - me.firstPos.columnIndex,
                    mousePosition: me.lastXY
                };
                me.mask.dom.style.borderRightWidth = '0';
                maskBox.x = overCell.getX();
                maskBox.y = selRegion.top;
                maskBox.width = selRegion.left - overCell.getX();
                maskBox.height = selRegion.bottom - selRegion.top;
            }
            
            else if (curPos.columnIndex > me.endPos.columnIndex && me.extendX) {
                me.extensionDescriptor = {
                    type: 'columns',
                    start: extensionStart.clone({
                        column: me.endPos.columnIndex + 1
                    }),
                    end: extensionEnd.clone({
                        column: curPos.columnIndex
                    }),
                    columns: curPos.columnIndex - me.endPos.columnIndex,
                    mousePosition: me.lastXY
                };
                me.mask.dom.style.borderLeftWidth = '0';
                maskBox.x = selRegion.right;
                maskBox.y = selRegion.top;
                maskBox.width = overCell.getRegion().right - selRegion.right;
                maskBox.height = selRegion.bottom - selRegion.top;
            } else {
                me.extensionDescriptor = null;
            }
        }
        if (view.hasListeners.selectionextenderdrag) {
            view.fireEvent('selectionextenderdrag', view, view.getSelectable().getSelection(), me.extensionDescriptor);
        }
        if (me.extensionDescriptor) {
            me.mask.show();
            me.mask.setBox(maskBox);
        } else {
            me.mask.hide();
        }
    },
    destroy: function() {
        this.destroyMembers('viewListener', 'scrollListener', 'mask', 'handle');
    }
});


Ext.define('Ext.grid.cell.Number', {
    extend: Ext.grid.cell.Text,
    xtype: 'numbercell',
    isNumberCell: true,
    config: {
        
        format: '0,000.00'
    },
    classCls: Ext.baseCSSPrefix + 'numbercell',
    zeroValue: null,
    updateColumn: function(column, oldColumn) {
        this.callParent([
            column,
            oldColumn
        ]);
        if (column && column.isNumberColumn) {
            var format = column.getFormat();
            if (format !== null) {
                this.setFormat(format);
            }
        }
    },
    updateFormat: function(format) {
        if (!this.isConfiguring) {
            this.writeValue();
        }
    },
    formatValue: function(value) {
        var hasValue = value || value === 0,
            zeroValue;
        if (value === 0 && (zeroValue = this.getZeroValue()) !== null) {
            value = zeroValue || '';
        } else {
            value = hasValue ? Ext.util.Format.number(value, this.getFormat()) : '';
        }
        return value;
    }
});


Ext.define('Ext.grid.cell.Check', {
    extend: Ext.grid.cell.Base,
    xtype: 'checkcell',
    config: {
        
        disabled: null
    },
    innerTemplate: [
        {
            reference: 'checkboxElement',
            tabIndex: -1,
            cls: Ext.baseCSSPrefix + 'checkbox-el ' + Ext.baseCSSPrefix + 'font-icon'
        }
    ],
    classCls: Ext.baseCSSPrefix + 'checkcell',
    disabledCls: Ext.baseCSSPrefix + 'disabled',
    checkedCls: Ext.baseCSSPrefix + 'checked',
    constructor: function(config) {
        this.callParent([
            config
        ]);
        this.checkboxElement.on('tap', 'onTap', this);
    },
    applyValue: function(value) {
        return !!value;
    },
    updateValue: function(value, oldValue) {
        var me = this,
            column = me.getColumn();
        me.el.toggleCls(me.checkedCls, !!value);
        
        if (value) {
            column.updateHeaderState();
        } else {
            column.setHeaderStatus(value);
        }
    },
    updateColumn: function(column, oldColumn) {
        this.callParent([
            column,
            oldColumn
        ]);
        if (column) {
            this.setDisabled(column.getDisabled());
        }
    },
    applyDisabled: function(disabled) {
        return Boolean(disabled);
    },
    updateDisabled: function(disabled) {
        this.element.toggleCls(this.disabledCls, disabled);
    },
    
    disable: function() {
        this.setDisabled(true);
    },
    
    enable: function() {
        this.setDisabled(false);
    },
    onTap: function(e) {
        var me = this,
            record = me.getRecord(),
            column = me.getColumn(),
            recordIndex = column.up('grid').getStore().indexOf(record),
            checked;
        if (record) {
            checked = !column.isRecordChecked(record);
            if (me.getDisabled()) {
                return;
            }
            if (column.fireEvent('beforecheckchange', me, recordIndex, checked, record, e) !== false) {
                if (me.getColumn().getStopSelection()) {
                    e.stopSelection = true;
                }
                if (record) {
                    column.setRecordChecked(record, checked, e);
                }
                if (column.hasListeners.checkchange) {
                    column.fireEvent('checkchange', me, recordIndex, checked, record, e);
                }
            }
        }
    }
});


Ext.define('Ext.grid.column.Check', {
    extend: Ext.grid.column.Column,
    isCheckColumn: true,
    xtype: 'checkcolumn',
    
    
    cachedConfig: {
        
        headerCheckboxAlign: 'bottom'
    },
    config: {
        
        stopSelection: true,
        
        headerCheckbox: null
    },
    
    align: 'center',
    
    classCls: Ext.baseCSSPrefix + 'checkcolumn',
    noHeaderCheckboxCls: Ext.baseCSSPrefix + 'no-header-checkbox',
    checkedCls: Ext.baseCSSPrefix + 'checked',
    hasTextCls: Ext.baseCSSPrefix + 'has-text',
    checkboxAlignCls: {
        top: Ext.baseCSSPrefix + 'checkbox-align-top',
        right: Ext.baseCSSPrefix + 'checkbox-align-right',
        bottom: Ext.baseCSSPrefix + 'checkbox-align-bottom',
        left: Ext.baseCSSPrefix + 'checkbox-align-left'
    },
    
    text: '',
    
    ignoreExport: true,
    
    cell: {
        xtype: 'checkcell'
    },
    afterTitleTemplate: [
        {
            reference: 'checkboxElement',
            classList: [
                Ext.baseCSSPrefix + 'checkbox-el',
                Ext.baseCSSPrefix + 'font-icon'
            ]
        }
    ],
    onColumnTap: function(e) {
        var me = this;
        if ((e.target === me.checkboxElement.dom) && !me.getDisabled()) {
            me.toggleAll(e);
        }
        me.callParent([
            e
        ]);
    },
    toggleAll: function(e) {
        var me = this,
            checked = !me.allChecked;
        if (me.fireEvent('beforeheadercheckchange', me, checked, e) !== false) {
            me.doToggleAll(checked);
            me.setHeaderStatus(checked);
            me.fireEvent('headercheckchange', me, checked, e);
        }
    },
    doToggleAll: function(checked) {
        var me = this,
            store = me.getGrid().getStore();
        store.each(function(record) {
            me.setRecordChecked(record, checked);
        });
    },
    setRecordChecked: function(record, checked, e) {
        checked = !!checked;
        this.doSetRecordChecked(record, checked);
        
        if (checked) {
            this.updateHeaderState();
        } else {
            this.setHeaderStatus(checked);
        }
    },
    doSetRecordChecked: function(record, checked) {
        var dataIndex = this.getDataIndex();
        
        if (record.get(dataIndex) != checked) {
            record.set(dataIndex, checked);
        }
    },
    areAllChecked: function() {
        var me = this,
            store = me.getGrid().getStore(),
            records, len, i;
        if (store && !store.isVirtualStore && store.getCount() > 0) {
            records = store.getData().items;
            len = records.length;
            for (i = 0; i < len; ++i) {
                if (!me.isRecordChecked(records[i])) {
                    return false;
                }
            }
            return true;
        }
    },
    isRecordChecked: function(record) {
        return record.get(this.getDataIndex());
    },
    updateHeaderState: function() {
        
        if (!this.destroyed && (this.getHeaderCheckbox() !== false)) {
            this.setHeaderStatus(this.areAllChecked());
        }
    },
    setHeaderStatus: function(checked) {
        var me = this;
        
        if (me.allChecked !== checked) {
            me.allChecked = checked;
            me.el.toggleCls(me.checkedCls, checked);
        }
    },
    updateDisabled: function(disabled, oldDisabled) {
        var me = this,
            grid = me.getGrid(),
            rows, len, i;
        me.callParent([
            disabled,
            oldDisabled
        ]);
        if (grid) {
            rows = grid.getViewItems();
            len = rows.length;
            for (i = 0; i < len; i++) {
                rows[i].getCellByColumn(me).setDisabled(disabled);
            }
        }
    },
    updateHeaderCheckboxAlign: function(align, oldAlign) {
        var me = this,
            checkboxAlignCls = me.checkboxAlignCls;
        if (oldAlign) {
            me.removeCls(checkboxAlignCls[oldAlign]);
        }
        if (align) {
            
            if (!checkboxAlignCls[align]) {
                Ext.raise("Invalid value for checkboxAlign: '" + align + "'");
            }
            
            me.addCls(checkboxAlignCls[align]);
        }
    },
    updateHeaderCheckbox: function(headerCheckbox) {
        var me = this,
            grid = me.getGrid();
        me.el.toggleCls(me.noHeaderCheckboxCls, !headerCheckbox);
        me.setSortable(!headerCheckbox);
        
        if (grid) {
            
            me.updateHeaderState();
        }
    },
    updateText: function(text) {
        
        this.setHtml(text);
        this.toggleCls(this.hasTextCls, !!text);
    }
});


Ext.define('Ext.grid.column.Selection', {
    extend: Ext.grid.column.Check,
    xtype: 'selectioncolumn',
    classCls: Ext.baseCSSPrefix + 'selectioncolumn',
    cell: {
        cls: Ext.baseCSSPrefix + 'selection-cell'
    },
    
    weight: -900,
    menu: null,
    sortable: false,
    draggable: false,
    resizable: false,
    hideable: false,
    ignore: true,
    
    stopSelection: false,
    updateHeaderState: function() {
        if (!this.isConfiguring) {
            this.getGrid().getSelectable().updateHeaderState();
        }
    },
    toggleAll: function(e) {
        this.getGrid().getSelectable().toggleAll(this, e);
    },
    setRecordChecked: function(record, checked, e) {
        var selectionModel = this.getGrid().getSelectable();
        if (checked) {
            selectionModel.select(record, selectionModel.getMode() !== 'single');
        } else {
            selectionModel.deselect(record);
        }
    },
    isRecordChecked: function(record) {
        return this.getGrid().getSelectable().isRowSelected(record);
    }
});


Ext.define('Ext.grid.column.Number', {
    extend: Ext.grid.column.Column,
    xtype: 'numbercolumn',
    isNumberColumn: true,
    config: {
        
        format: null
    },
    cell: {
        xtype: 'numbercell'
    },
    defaultEditor: {
        xtype: 'numberfield'
    }
});


Ext.define('Ext.grid.cell.RowNumberer', {
    extend: Ext.grid.cell.Number,
    xtype: 'rownumberercell',
    classCls: Ext.baseCSSPrefix + 'rownumberercell',
    
    format: '0,000',
    refreshValue: function(context) {
        var row = context.row,
            ret;
        if (context.summary) {
            ret = '\xa0';
        } else {
            ret = row ? row.$datasetIndex + 1 : null;
        }
        return ret;
    }
});


Ext.define('Ext.grid.column.RowNumberer', {
    extend: Ext.grid.column.Number,
    xtype: 'rownumberer',
    isRowNumberer: true,
    cell: {
        xtype: 'rownumberercell'
    },
    
    menu: null,
    align: 'right',
    hideable: false,
    ignore: true,
    ignoreExport: true,
    sortable: false,
    text: '',
    width: 'auto',
    minWidth: null,
    onAdded: function(parent, instanced) {
        var me = this,
            grid;
        me.callParent([
            parent,
            instanced
        ]);
        me.checkWidth();
        grid = me.getGrid();
        me.gridListeners = grid.on({
            storechange: 'attachStoreListeners',
            scope: me,
            destroyable: true
        });
        me.attachStoreListeners(grid.getStore());
    },
    onRemoved: function(destroying) {
        Ext.destroy(this.gridListeners, this.storeListeners);
        this.callParent([
            destroying
        ]);
    },
    privates: {
        attachStoreListeners: function(store) {
            Ext.destroy(this.storeListeners);
            if (store) {
                this.storeListeners = store.on({
                    datachanged: 'checkWidth',
                    totalcountchange: 'checkWidth',
                    scope: this,
                    destroyable: true
                });
            }
        },
        getCharWidth: function() {
            var me = this,
                charWidth = me._charWidth,
                text, cell, cellUi, gridUi, textWidth;
            if (!charWidth) {
                text = '0000000000';
                cell = me.getScratchCell();
                
                
                
                cellUi = me.getCell().ui;
                if (cellUi) {
                    cell.addUi(cellUi);
                }
                gridUi = me.getGrid().getUi();
                if (gridUi) {
                    cell.addUi(gridUi);
                }
                
                document.body.appendChild(cell.el.dom);
                textWidth = Ext.util.TextMetrics.measure(cell.bodyElement, text).width;
                document.body.removeChild(cell.el.dom);
                me._charWidth = charWidth = textWidth / text.length;
            }
            return charWidth;
        },
        checkWidth: function() {
            var me = this;
            
            
            if (document.fonts) {
                document.fonts.ready.then(function() {
                    if (!me.destroyed) {
                        me.doCheckWidth();
                    }
                });
            } else {
                me.doCheckWidth();
            }
        },
        doCheckWidth: function() {
            var me = this,
                store = me.getGrid().getStore(),
                charLen = 1,
                charWidth = me.getCharWidth();
            if (store && store.getTotalCount()) {
                
                charLen = me.getScratchCell().printValue(store.getTotalCount()).length;
            }
            me.textElement.setStyle('min-width', Math.ceil(charLen * charWidth) + 'px');
        }
    }
});


Ext.define('Ext.grid.selection.Model', {
    extend: Ext.dataview.selection.Model,
    alias: 'selmodel.grid',
    isGridSelectionModel: true,
    config: {
        
        columns: {
            $value: false,
            lazy: true
        },
        
        cells: {
            $value: false,
            lazy: true
        },
        
        rows: {
            $value: true,
            lazy: true
        },
        
        drag: false,
        
        extensible: {
            $value: false,
            lazy: true
        },
        
        checkbox: false,
        
        headerCheckbox: true,
        
        checkboxDefaults: {
            xtype: 'selectioncolumn',
            text: null,
            width: 30
        },
        showNumbererColumn: false
    },
    
    
    checkboxSelect: false,
    
    checkboxColumnIndex: 0,
    mode: 'multi',
    columnSelectCls: Ext.baseCSSPrefix + 'selmodel-column-select',
    rowNumbererHeaderCls: Ext.baseCSSPrefix + 'selmodel-row-numberer-hd',
    
    
    
    updateView: function(view, oldView) {
        var me = this,
            rowNumberer = me.numbererColumn = view.getRowNumbers(),
            checkbox = me.getCheckbox();
        me.callParent([
            view,
            oldView
        ]);
        if (oldView) {
            me.navigationModel = null;
            Ext.destroy(me.viewListeners);
        }
        if (view) {
            
            
            
            if (rowNumberer) {
                
                rowNumberer.setCell({
                    cls: me.rowNumbererCellCls
                });
                rowNumberer.setCls(me.rowNumbererHeaderCls);
            }
            if (checkbox) {
                view.registerColumn(checkbox);
            }
            me.viewListeners = view.on(me.getViewListeners());
        }
    },
    
    onViewCreated: function(view) {
        
        if (this.getColumns()) {
            view.addCls(this.columnSelectCls);
        }
        this.updateHeaderState();
    },
    updateDrag: function(drag) {
        var view = this.getView(),
            viewListeners = {
                dragstart: 'onViewDragStart',
                delegate: view.eventDelegate,
                scope: this
            };
        
        if (Ext.supports.Touch) {
            viewListeners.longpress = 'onViewLongpress';
        }
        view.innerCt[drag ? 'on' : 'un'](viewListeners);
    },
    
    getSelection: function(what, reset) {
        
        
        if (what === 'rows' || what === 'records') {
            what = this.getStore().isVirtualStore ? 'rows' : 'records';
        }
        var result = this.callParent(),
            config;
        
        
        if (what) {
            what = what.toLowerCase();
            if (!result || result.type !== what) {
                config = {
                    type: what
                };
                if (what === 'records') {
                    config.selected = this.getSelected();
                }
                this.setSelection(config);
                result = this.callParent();
            } else if (reset) {
                result.clear(true);
            }
        }
        return result;
    },
    
    createCheckboxColumn: function(checkboxDefaults) {
        var me = this;
        return Ext.apply({
            headerCheckbox: me.getHeaderCheckbox() !== false
        }, checkboxDefaults);
    },
    
    onHeaderTap: function(headerCt, header, e) {
        var me = this,
            sel = me.getSelection(),
            range, columns, i;
        
        if (header === this.numbererColumn) {
            me.toggleAll(header, e);
        }
        
        else if (me.getColumns() && header !== me.getCheckbox()) {
            
            if (e.shiftKey && sel && sel.lastColumnSelected) {
                
                if (!e.ctrlKey) {
                    sel.clear();
                }
                headerCt = me.getView().getHeaderContainer();
                columns = headerCt.getColumns();
                range = Ext.Array.sort([
                    headerCt.indexOfLeaf(sel.lastColumnSelected),
                    headerCt.indexOf(header)
                ], Ext.Array.numericSortFn);
                for (i = range[0]; i <= range[1]; i++) {
                    me.selectColumn(columns[i], true);
                }
            } else {
                if (me.isColumnSelected(header)) {
                    me.deselectColumn(header);
                    me.getSelection().lastColumnSelected = null;
                } else {
                    me.selectColumn(header, e.ctrlKey);
                    me.getSelection().lastColumnSelected = header;
                }
            }
        }
    },
    
    toggleAll: function(header, e) {
        var me = this,
            sel = me.getSelection();
        e.stopEvent();
        
        if (!sel || !sel.isAllSelected()) {
            me.selectAll();
        } else {
            me.deselectAll();
        }
        me.updateHeaderState();
        me.lastColumnSelected = null;
    },
    selectByLocation: function(location) {
        
        if (!location.isGridLocation) {
            Ext.raise('selectByLocation MUST be passed an Ext.grid.Location');
        }
        
        var me = this,
            record = location.record,
            column = location.column;
        if (me.getCells()) {
            me.selectCells(location, location);
        } else if (me.getRows() && record) {
            this.select(record);
        } else if (me.getColumns() && column) {
            me.selectColumn(column);
        }
    },
    
    updateHeaderState: function() {
        
        var me = this,
            store = me.getStore(),
            sel = me.getSelection(),
            isChecked = false,
            checkHd = me.getCheckbox(),
            storeCount;
        if (store && sel && sel.isRows) {
            storeCount = store.getCount();
            if (store.isBufferedStore) {
                isChecked = sel.allSelected;
            } else {
                isChecked = storeCount > 0 && (storeCount === sel.getCount());
            }
        }
        if (checkHd) {
            checkHd.setHeaderStatus(isChecked);
        }
    },
    
    onColumnUpdate: function(headerCt, columns) {
        var me = this,
            checkColumn = me.getCheckbox();
        if (checkColumn) {
            
            
            if (headerCt) {
                headerCt.remove(checkColumn, false);
            }
            columns.push(checkColumn);
        }
    },
    select: function(records, keepExisting, suppressEvent) {
        
        var me = this,
            sel = me.getSelection('records'),
            store = me.getStore(),
            len, i, record;
        if (!Ext.isArray(records)) {
            records = [
                records
            ];
        }
        len = records.length;
        for (i = 0; i < len; i++) {
            record = records[i];
            if (typeof record === 'number') {
                records[i] = record = store.getAt(record);
            }
        }
        
        sel.add(records, keepExisting, suppressEvent);
    },
    deselect: function(records, suppressEvent) {
        
        var me = this,
            sel = me.getSelection('records'),
            store = me.getView().getStore(),
            len, i, record;
        if (sel && sel.isRecords) {
            if (!Ext.isArray(records)) {
                records = [
                    records
                ];
            }
            len = records.length;
            for (i = 0; i < len; i++) {
                record = records[i];
                if (typeof record === 'number') {
                    records[i] = record = store.getAt(record);
                }
            }
        }
        
        sel.remove(records, suppressEvent);
    },
    onCollectionRemove: function(selectedCollection, chunk) {
        this.updateHeaderState();
        this.callParent([
            selectedCollection,
            chunk
        ]);
    },
    onCollectionAdd: function(selectedCollection, adds) {
        this.updateHeaderState();
        this.callParent([
            selectedCollection,
            adds
        ]);
    },
    
    selectCells: function(rangeStart, rangeEnd, suppressEvent) {
        var me = this,
            view = me.getView(),
            sel;
        rangeStart = rangeStart.isGridLocation ? rangeStart.clone() : new Ext.grid.Location(view, {
            record: rangeStart[0],
            column: rangeStart[1]
        });
        rangeEnd = rangeEnd.isGridLocation ? rangeEnd.clone() : new Ext.grid.Location(view, {
            record: rangeEnd[0],
            column: rangeEnd[1]
        });
        me.resetSelection(true);
        sel = me.getSelection('cells');
        sel.setRangeStart(rangeStart);
        sel.setRangeEnd(rangeEnd);
        if (!suppressEvent) {
            me.fireSelectionChange();
        }
    },
    
    selectAll: function(suppressEvent) {
        var me = this,
            sel = me.getSelection(),
            doSelect;
        if (me.getRows()) {
            sel = me.getSelection('records');
            doSelect = true;
        } else if (me.getCells()) {
            sel = me.getSelection('cells');
            doSelect = true;
        } else if (me.getColumns()) {
            sel = me.getSelection('columns');
            doSelect = true;
        }
        if (doSelect) {
            sel.selectAll(suppressEvent);
        }
    },
    
    
    deselectAll: function(suppressEvent) {
        var sel = this.getSelection();
        if (sel && sel.getCount()) {
            sel.clear(suppressEvent);
        }
    },
    
    selectRows: function(rows, keepSelection, suppressEvent) {
        var sel = this.getSelection('records');
        if (!keepSelection) {
            this.resetSelection(true);
        }
        sel.add(rows, keepSelection, suppressEvent);
    },
    isSelected: function(record) {
        
        return this.isRowSelected(record);
    },
    
    selectColumn: function(column, keepSelection, suppressEvent) {
        var selData = this.getSelection('columns');
        if (!selData.isSelected(column)) {
            if (!keepSelection) {
                selData.clear(suppressEvent);
            }
            selData.add(column);
        }
    },
    
    deselectColumn: function(column, suppressEvent) {
        var selData = this.getSelection();
        if (selData && selData.isColumns && selData.isSelected(column)) {
            selData.remove(column, suppressEvent);
        }
    },
    destroy: function() {
        var me = this,
            view = me.getView(),
            checkbox = me.checkbox;
        if (view && !view.destroying && checkbox) {
            view.unregisterColumn(checkbox, true);
        }
        Ext.destroy(me.viewListeners, me.extensible);
        me.callParent();
    },
    
    privates: {
        
        axesConfigs: {
            x: 1,
            y: 2,
            xy: 3,
            both: 3,
            "true": 3
        },
        
        
        getViewListeners: function() {
            return {
                columnschanged: 'onColumnsChanged',
                columnmove: 'onColumnMove',
                keyup: {
                    element: 'innerCt',
                    fn: 'onViewKeyUp',
                    scope: this
                },
                scope: this,
                destroyable: true
            };
        },
        
        onViewKeyUp: function(e) {
            var sel = this.getSelection();
            
            if (e.keyCode === e.SHIFT && sel && sel.isRows && sel.getRangeSize()) {
                
                sel.addRange();
            }
        },
        
        refreshSelection: function() {
            if (this.getSelection().isRecords) {
                this.callParent();
            } else {
                this.resetSelection();
            }
        },
        
        onColumnsChanged: function() {
            var me = this,
                selData = me.getSelection(),
                view, selectionChanged;
            
            
            if (selData) {
                view = selData.view;
                if (selData.isCells) {
                    if (view.visibleColumns().length) {
                        selData.eachCell(function(location) {
                            view.onCellDeselect(location);
                        });
                    } else {
                        me.clearSelections();
                    }
                }
                
                else if (selData.isColumns) {
                    selectionChanged = false;
                    selData.eachColumn(function(column) {
                        if (!column.isVisible() || !view.isAncestor(column)) {
                            me.remove(column);
                            selectionChanged = true;
                        }
                    });
                }
            }
            
            
            
            Ext.on('idle', selectionChanged ? me.fireSelectionChange : me.updateSelectionExtender, me, {
                single: true
            });
        },
        
        
        onColumnMove: function() {
            this.updateSelectionExtender();
        },
        
        resetSelection: function(suppressEvent) {
            var sel = this.getSelection();
            if (sel) {
                sel.clear(suppressEvent);
            }
        },
        onViewLongpress: function(e) {
            if (e.pointerType === 'touch') {
                e.startDrag();
            }
        },
        
        onViewDragStart: function(e) {
            
            if (e.pointerType === 'touch' && !e.longpress) {
                return;
            }
            var me = this,
                view = me.getView(),
                location = new Ext.grid.Location(view, e),
                header = location.column,
                viewLocation = view.getNavigationModel().getLocation(),
                isCheckClick = header === me.getCheckbox(),
                sel;
            if (!location.cell) {
                return;
            }
            
            
            if (e.claimed || e.button > 0 || e.shiftKey || e.altKey || (viewLocation && viewLocation.actionable) || !view.shouldSelectItem(e)) {
                return;
            }
            if (header) {
                e.claimGesture();
                me.mousedownPosition = location.clone();
                if (isCheckClick) {
                    me.checkCellClicked = location.cell.element.dom;
                }
                
                if (header === me.numbererColumn || isCheckClick || !me.getCells()) {
                    
                    if (me.getRows()) {
                        
                        
                        if (!isCheckClick && me.checkboxOnly) {
                            return;
                        }
                        sel = me.getSelection('records');
                        if (!e.ctrlKey && !isCheckClick) {
                            sel.clear();
                        }
                    } else if (me.getColumns()) {
                        sel = me.getSelection('columns');
                        if (!e.ctrlKey && !isCheckClick) {
                            sel.clear();
                        }
                    } else {
                        return false;
                    }
                } else {
                    sel = me.getSelection('cells');
                    sel.clear();
                }
                me.lastDragLocation = null;
                
                
                if (e.longpress) {
                    location.row.removeCls(view.pressedCls);
                    me.onViewSelectionDrag(e);
                }
                
                if (sel) {
                    
                    view.innerCt.on('dragend', me.onViewDragEnd, me, {
                        single: true
                    });
                    me.mousemoveListener = view.innerCt.on({
                        drag: 'onViewSelectionDrag',
                        scope: me,
                        delegate: view.eventDelegate,
                        destroyable: true
                    });
                }
            }
        },
        
        onViewSelectionDrag: function(e) {
            me = this;
            view = me.getView();
            
            
            
            
            if (e.changedTouches) {
                touch = e.changedTouches[0];
                
                if (touch && !Ext.fly(touch.target).getRegion().contains(touch.point)) {
                    realTarget = Ext.event.Event.resolveTextNode(Ext.Element.fromPagePoint(touch.pageX, touch.pageY, true));
                    
                    if (realTarget) {
                        e.target = realTarget;
                    }
                }
            }
            
            
            if (!Ext.fly(e.target).up(view.eventDelegate)) {
                return;
            }
            var me, view,
                newLocation = me.dragLocation = new Ext.grid.Location(view, e),
                overColumn = newLocation.column,
                overRecord = newLocation.record,
                overRowIdx = newLocation.recordIndex,
                lastDragLocation = me.lastDragLocation,
                selData, lastOverRecord, lastOverColumn, recChange, colChange, touch, realTarget;
            e.claimGesture();
            if (lastDragLocation) {
                lastOverRecord = lastDragLocation.record;
                lastOverColumn = lastDragLocation.column;
            }
            
            if (me.checkCellClicked) {
                selData = me.getSelection('rows');
                selData.setRangeStart(me.getStore().indexOf(overRecord));
                me.checkCellClicked = null;
                return;
            } else {
                selData = me.getSelection();
            }
            
            if (me.getExtensible()) {
                me.getExtensible().disable();
            }
            if (overColumn) {
                recChange = overRecord !== lastOverRecord;
                colChange = overColumn !== lastOverColumn;
                
                if (selData.isRows || selData.isRecords) {
                    
                    if (recChange) {
                        if (lastOverRecord) {
                            selData.setRangeEnd(overRowIdx);
                        } else {
                            selData.setRangeStart(overRowIdx);
                        }
                    }
                }
                
                else if (selData.isCells) {
                    
                    if (recChange || colChange) {
                        if (lastOverRecord) {
                            selData.setRangeEnd(newLocation);
                        } else {
                            selData.setRangeStart(newLocation);
                        }
                    }
                }
                
                else if (selData.isColumns) {
                    
                    if (colChange) {
                        if (lastOverColumn) {
                            selData.setRangeEnd(newLocation.column);
                        } else {
                            selData.setRangeStart(newLocation.column);
                        }
                    }
                }
                
                
                if (recChange || colChange) {
                    view.getNavigationModel().setLocation(newLocation);
                }
                me.lastDragLocation = newLocation;
            }
        },
        
        onViewDragEnd: function(e) {
            var me = this,
                view = me.getView(),
                dragLocation = me.dragLocation,
                changedCell = !dragLocation || !dragLocation.equals(me.mousedownPosition),
                location = e.location;
            me.checkCellClicked = null;
            if (view && !view.destroyed) {
                
                
                if (!location) {
                    e.location = new Ext.grid.Location(view, e);
                }
                
                
                if (me.getExtensible() && changedCell) {
                    me.getExtensible().disable();
                }
                me.mousemoveListener.destroy();
                
                if (me.getSelection().isRows) {
                    me.getSelection().addRange();
                }
                
                
                
                else if (changedCell) {
                    me.fireSelectionChange();
                }
            }
        },
        
        onNavigate: function(navigateEvent) {
            var me = this,
                selectingRows = me.getRows(),
                selectingCells = me.getCells(),
                selectingColumns = me.getColumns(),
                checkbox = me.getCheckbox(),
                checkboxOnly = me.checkboxOnly,
                mode = me.getMode(),
                location = navigateEvent.to,
                toColumn = location.column,
                record = location.record,
                sel = me.getSelection(),
                ctrlKey = navigateEvent.ctrlKey,
                shiftKey = navigateEvent.shiftKey,
                changedRow, selectionChanged;
            
            
            if (navigateEvent.stopSelection || toColumn === me.checkboxColumn) {
                return;
            }
            
            if (!navigateEvent.pointerType && navigateEvent.getKey() !== navigateEvent.SPACE) {
                
                if (ctrlKey) {
                    return;
                }
                
                changedRow = !navigateEvent.from || (location.recordIndex !== navigateEvent.from.recordIndex);
                if (!changedRow && !(selectingCells || selectingColumns)) {
                    return;
                }
            }
            
            if (sel && (sel.isCells || (sel.isColumns && selectingRows && !(ctrlKey || shiftKey))) && sel.getCount() > 1 && !shiftKey && navigateEvent.type === 'click') {
                return;
            }
            
            if (!(selectingCells || selectingColumns || selectingRows) || !record || navigateEvent.type === 'mousedown') {
                return;
            }
            
            if (ctrlKey && navigateEvent.keyCode === navigateEvent.A && mode === 'multi') {
                
                if (!sel || sel.getCount() < 2) {
                    me.selectAll();
                } else {
                    me.deselectAll();
                }
                me.updateHeaderState();
                return;
            }
            if (shiftKey && mode === 'multi') {
                
                
                if (toColumn === me.numbererColumn || toColumn === me.checkColumn || !(selectingCells || selectingColumns) || (sel && (sel.isRows || sel.isRecords))) {
                    if (selectingRows) {
                        
                        
                        if (toColumn !== checkbox && checkboxOnly) {
                            return;
                        }
                        
                        sel = me.getSelection('records');
                        
                        if (!sel.getRangeSize()) {
                            sel.setRangeStart(me.selectionStart || location.recordIndex);
                        }
                        sel.setRangeEnd(location.recordIndex);
                        sel.addRange(true);
                        selectionChanged = true;
                    }
                } else 
                {
                    if (selectingCells) {
                        
                        sel = me.getSelection('cells');
                        
                        if (!sel.getRangeSize()) {
                            sel.setRangeStart(navigateEvent.from || new Ext.grid.Location(me.getView(), {
                                record: 0,
                                column: 0
                            }));
                        }
                        sel.setRangeEnd(location);
                        selectionChanged = true;
                    } else if (selectingColumns) {
                        
                        sel = me.getSelection('columns');
                        if (!sel.getCount()) {
                            sel.setRangeStart(toColumn);
                        }
                        sel.setRangeEnd(toColumn);
                        selectionChanged = true;
                    }
                }
            } else {
                me.selectionStart = null;
                if (sel && mode !== 'multi') {
                    sel.clear(true);
                }
                
                
                if (selectingRows && (toColumn === me.numbererColumn || toColumn === checkbox || !selectingCells)) {
                    
                    
                    
                    if (toColumn !== checkbox && checkboxOnly || (navigateEvent.keyCode && navigateEvent.from && record === navigateEvent.from.record)) {
                        return;
                    }
                    
                    sel = me.getSelection('records');
                    if (sel.isSelected(record)) {
                        if (ctrlKey || toColumn === checkbox || me.getDeselectable()) {
                            sel.remove(record);
                            selectionChanged = true;
                        }
                    } else {
                        sel.add(record, ctrlKey || toColumn === checkbox);
                        selectionChanged = true;
                    }
                    me.selectionStart = location.recordIndex;
                } else 
                {
                    
                    if (selectingCells) {
                        
                        sel = me.getSelection('cells', true);
                        sel.setRangeStart(location);
                        selectionChanged = true;
                    } else if (selectingColumns) {
                        
                        sel = me.getSelection('columns');
                        if (ctrlKey) {
                            if (sel.isSelected(toColumn)) {
                                sel.remove(toColumn);
                            } else {
                                sel.add(toColumn);
                            }
                        } else {
                            sel.setRangeStart(toColumn);
                        }
                        selectionChanged = true;
                    }
                }
            }
            
            if (selectionChanged) {
                
                
                if (!sel.isRecords) {
                    me.fireSelectionChange(null, true);
                }
            }
        },
        
        isColumnSelected: function(column) {
            var me = this,
                sel = me.getSelection(),
                ret = false;
            if (sel && sel.isColumns) {
                ret = sel.isSelected(column);
            }
            return ret;
        },
        
        isCellSelected: function(row, column) {
            var sel = this.getSelection();
            if (sel) {
                if (sel.isColumns) {
                    if (typeof column === 'number') {
                        column = this.getView().getVisibleColumns()[column];
                    }
                    return sel.isSelected(column);
                }
                if (sel.isCells) {
                    return sel.isSelected(row, column);
                }
                
                
                return sel.isSelected(row);
            }
            return false;
        },
        
        updateSelection: function(selection, oldSelection) {
            var view = this.getView();
            
            Ext.destroy(oldSelection);
            
            if (selection && selection.getCount()) {
                view = selection.view;
                
                if (selection.isRows) {
                    selection.eachRow(view.onRowSelect, view);
                }
                
                else if (selection.isColumns) {
                    selection.eachCell(view.onCellSelect, view);
                }
                
                else if (selection.isCells) {
                    selection.eachCell(view.onCellSelect, view);
                }
            }
        },
        
        updateRows: function(rows) {
            var sel;
            if (!rows) {
                
                this.setCheckbox(false);
                sel = this.getSelection();
                if (sel && sel.isRows) {
                    sel.clear();
                }
            }
        },
        
        updateColumns: function(columns) {
            var me = this,
                view = me.getView(),
                sel = me.getSelection();
            if (!columns && sel && sel.isColumns) {
                sel.clear();
                me.fireSelectionChange();
            }
            view.toggleCls(me.columnSelectCls, !!columns);
        },
        
        updateCells: function(cells) {
            var me = this,
                sel = me.getSelection();
            if (!cells && sel && sel.isCells) {
                sel.clear();
                me.fireSelectionChange();
            }
        },
        updateMode: function(mode) {
            
            if (mode === 'multi') {
                this.setDrag(this.getInitialConfig().drag);
            }
            
            else if (!this.isConfiguring) {
                this.setDrag(false);
            }
        },
        
        fireSelectionChange: function(records, selecting) {
            var me = this,
                view = me.getView(),
                selection = me.getSelection();
            
            me.updateSelectionExtender();
            
            me.fireEvent('selectionchange', view, me.getSelection());
            
            
            view.fireEvent('selectionchange', view, selection.isRecords ? records : (selection.isCells ? selection.getRecords() : null), selecting, me.getSelection());
        },
        updateSelectionExtender: function() {
            var sel = this.getSelection();
            if (sel) {
                sel.onSelectionFinish();
            }
        },
        
        onSelectionFinish: function(sel, firstCell, lastCell) {
            var extensible = this.getExtensible();
            if (extensible) {
                extensible.setHandle(firstCell, lastCell);
            }
        },
        applyExtensible: function(extensible, oldExtensible) {
            var me = this,
                axes;
            
            
            if (!extensible) {
                return undefined;
            }
            if (extensible === true || typeof extensible === 'string') {
                axes = me.axesConfigs[extensible];
                
                if (oldExtensible) {
                    oldExtensible.setAxes(axes);
                    return oldExtensible;
                }
                extensible = {
                    axes: axes
                };
            } else {
                extensible = Ext.Object.chain(extensible);
            }
            
            extensible.view = me.getView();
            
            
            if (oldExtensible) {
                oldExtensible.destroy();
            }
            return new Ext.grid.selection.SelectionExtender(extensible);
        },
        applyCheckbox: function(checkbox) {
            var me = this;
            if (checkbox) {
                me.checkboxOnly = checkbox === 'only';
                me.checkboxColumn = checkbox = Ext.create(me.createCheckboxColumn(me.getCheckboxDefaults()));
            }
            return checkbox;
        },
        updateCheckbox: function(checkbox, oldCheckbox) {
            var me = this,
                view;
            if (!me.isConfiguring) {
                view = me.getView();
                if (oldCheckbox) {
                    view.unregisterColumn(oldCheckbox, true);
                }
                if (checkbox) {
                    view.registerColumn(checkbox);
                    
                    me.setRows(true);
                }
            }
        },
        applyView: function(view) {
            
            return view.ownerGrid;
        },
        
        extendSelection: function(extension) {
            var me = this,
                view = me.getView(),
                sel = me.getSelection();
            
            if (view.fireEvent('beforeselectionextend', view, sel, extension) !== false) {
                sel.extendRange(extension);
                
                
                if (!sel.isRows) {
                    me.fireSelectionChange();
                }
            }
        },
        
        onIdChanged: function(store, rec, oldId, newId) {
            var sel = this.getSelection();
            if (sel && sel.isRecords) {
                sel.getSelected().updateKey(rec, oldId);
            }
        },
        
        onSelectionStoreAdd: function() {
            this.callParent(arguments);
            this.updateHeaderState();
        },
        
        onSelectionStoreClear: function() {
            this.callParent(arguments);
            this.updateHeaderState();
        },
        
        onSelectionStoreLoad: function() {
            this.callParent(arguments);
            this.updateHeaderState();
        }
    }
}, function(GridModel) {
    var RowNumberer = Ext.ClassManager.get('Ext.grid.column.RowNumberer'),
        cellCls;
    if (RowNumberer) {
        cellCls = Ext.grid.column.RowNumberer.prototype.cellCls;
        
        GridModel.prototype.rowNumbererCellCls = (cellCls ? (cellCls + ' ') : '') + Ext.baseCSSPrefix + 'selmodel-row-numberer-cell';
    }
});


Ext.define('Ext.grid.plugin.ColumnResizing', {
    extend: Ext.Component,
    alias: [
        'plugin.columnresizing',
        'plugin.gridcolumnresizing'
    ],
    config: {
        grid: null,
        
        realtime: false
    },
    hasResizingCls: Ext.baseCSSPrefix + 'has-columnresizing',
    resizingCls: Ext.baseCSSPrefix + 'resizing',
    columnSelector: '.' + Ext.baseCSSPrefix + 'gridcolumn',
    resizerSelector: '.' + Ext.baseCSSPrefix + 'gridcolumn .' + Ext.baseCSSPrefix + 'resizer-el',
    init: function(grid) {
        this.setGrid(grid);
        grid.getHeaderContainer().setTouchAction({
            panX: false
        });
    },
    updateGrid: function(grid, oldGrid) {
        var me = this,
            cls = me.hasResizingCls,
            headerContainer, resizeMarker;
        if (oldGrid) {
            headerContainer = oldGrid.getHeaderContainer();
            headerContainer.renderElement.un({
                touchstart: 'onContainerTouchStart',
                scope: me,
                priority: 100
            });
            oldGrid.removeCls(cls);
        }
        if (grid) {
            me._resizeMarker = resizeMarker = grid.resizeMarkerElement;
            me._resizeMarkerParent = resizeMarker.parent();
            headerContainer = grid.getHeaderContainer();
            headerContainer.renderElement.on({
                touchstart: 'onContainerTouchStart',
                scope: me
            });
            grid.addCls(cls);
        }
    },
    onContainerTouchStart: function(e) {
        var me = this,
            target = e.getTarget(me.columnSelector),
            resizer = e.getTarget(me.resizerSelector),
            column;
        if (resizer && !e.multitouch && target && !me._resizeColumn) {
            column = Ext.Component.from(target);
            if (column && column.getResizable()) {
                me._startColumnWidth = column.getComputedWidth();
                me._minColumnWidth = column.getMinWidth();
                me._maxColumnWidth = column.getMaxWidth();
                me._resizeColumn = column;
                me._startX = e.getX();
                column.addCls(me.resizingCls);
                
                e.claimGesture();
                if (!this.getRealtime()) {
                    me._resizeMarker.show();
                    me._resizeMarker.setLeft(column.el.getOffsetsTo(me._resizeMarkerParent)[0] + me._startColumnWidth);
                } else {
                    column.setWidth(me._startColumnWidth);
                }
                me.touchListeners = Ext.getBody().on({
                    touchEnd: 'onTouchEnd',
                    touchMove: 'onTouchMove',
                    scope: me,
                    destroyable: true
                });
            }
        } else if (e.multitouch && me._resizeColumn) {
            me.endResize();
        }
    },
    onTouchMove: function(e) {
        
        if (e.isMultitouch) {
            this.endResize();
            return;
        }
        if (this._resizeColumn) {
            var column = this._resizeColumn,
                resizeAmount = e.getX() - this._startX;
            if (column) {
                this.currentColumnWidth = Math.max(Math.ceil(this._startColumnWidth + resizeAmount), this._minColumnWidth);
                if (this._maxColumnWidth) {
                    this.currentColumnWidth = Math.min(this.currentColumnWidth, this._maxColumnWidth);
                }
                if (this.getRealtime()) {
                    column.setWidth(this.currentColumnWidth);
                    column.renderElement.setWidth(this.currentColumnWidth);
                } else {
                    this._resizeMarker.setLeft(column.el.getOffsetsTo(this._resizeMarkerParent)[0] + this.currentColumnWidth);
                }
                e.claimGesture();
            }
        }
    },
    onTouchEnd: function(e) {
        var column = this._resizeColumn,
            hasResized = e.getX() !== this._startX;
        Ext.destroy(this.touchListeners);
        if (column) {
            this.endResize();
            
            if (!hasResized) {
                column.onResizerTap(e);
            }
        }
    },
    endResize: function() {
        var me = this,
            column = me._resizeColumn,
            grid = me.getGrid();
        if (column) {
            if (!me.getRealtime()) {
                grid.resizeMarkerElement.hide();
            }
            if (me.currentColumnWidth) {
                column.setFlex(null);
                column.setWidth(me.currentColumnWidth);
            }
            column.removeCls(me.resizingCls);
            me._resizeColumn = null;
        }
    }
});


Ext.define('Ext.grid.RowHeader', {
    extend: Ext.dataview.ItemHeader,
    xtype: 'rowheader',
    classCls: Ext.baseCSSPrefix + 'rowheader',
    isRowHeader: true,
    toolDefaults: {
        ui: 'itemheader rowheader'
    },
    privates: {
        augmentToolHandler: function(tool, args) {
            
            this.callParent([
                tool,
                args
            ]);
            var info = args[1];
            info.grid = info.list;
        },
        getGroupHeaderTplData: function() {
            var data = this.callParent([
                    
                    true
                ]),
                grid = this.parent,
                column = data && grid.getColumnForField(data.groupField);
            if (column) {
                data.columnName = column.getText();
                if (column.printValue) {
                    data.html = column.printValue(data.value);
                }
            } else if (data) {
                data.html = Ext.htmlEncode(data.name);
            }
            return data;
        }
    }
});


Ext.define('Ext.grid.Grid', {
    extend: Ext.dataview.List,
    xtype: 'grid',
    isGrid: true,
    mixins: [
        Ext.mixin.ConfigProxy
    ],
    storeEventListeners: {
        sort: 'onStoreSort'
    },
    config: {
        
        columns: null,
        
        columnMenu: {
            xtype: 'menu',
            weighted: true,
            align: 'tl-bl?',
            hideOnParentHide: false,
            
            items: {
                sortAsc: {
                    xtype: 'gridsortascmenuitem',
                    group: 'sortDir',
                    weight: -100
                },
                
                sortDesc: {
                    xtype: 'gridsortdescmenuitem',
                    group: 'sortDir',
                    weight: -90
                },
                
                
                
                
                groupByThis: {
                    xtype: 'gridgroupbythismenuitem',
                    handler: 'column.onGroupByThis',
                    separator: true,
                    weight: -70
                },
                showInGroups: {
                    xtype: 'gridshowingroupsmenuitem',
                    handler: 'column.onToggleShowInGroups',
                    weight: -60
                }
            }
        },
        
        columnResize: true,
        headerContainer: {
            xtype: 'headercontainer'
        },
        
        hideHeaders: false,
        
        itemsFocusable: false,
        
        title: '',
        titleBar: {
            xtype: 'titlebar',
            docked: 'top'
        },
        
        sortable: true,
        
        multiColumnSort: false,
        
        columnsMenuItem: {
            lazy: true,
            $value: {
                xtype: 'gridcolumnsmenu',
                weight: -80,
                separator: true
            }
        },
        
        columnLines: null,
        
        rowNumbers: null
    },
    
    itemConfig: {
        xtype: 'gridrow'
    },
    
    groupHeader: {
        xtype: 'rowheader'
    },
    
    infinite: true,
    
    navigationModel: 'grid',
    
    pinnedHeader: {
        xtype: 'rowheader'
    },
    
    scrollable: true,
    
    scrollToTopOnRefresh: false,
    
    striped: true,
    
    proxyConfig: {
        headerContainer: [
            
            'reserveScrollbar'
        ]
    },
    
    
    
    
    
    
    
    
    
    
    
    selectionModel: 'grid',
    
    classCls: Ext.baseCSSPrefix + 'grid',
    columnLinesCls: Ext.baseCSSPrefix + 'column-lines',
    getTemplate: function() {
        var template = this.callParent();
        template.push({
            reference: 'resizeMarkerElement',
            className: Ext.baseCSSPrefix + 'resize-marker-el',
            hidden: true
        });
        return template;
    },
    beforeInitialize: function() {
        
        
        this.ownerGrid = this;
        this.callParent();
    },
    initialize: function() {
        var me = this,
            titleBar = me.getTitleBar(),
            headerContainer = me.getHeaderContainer(),
            scroller = me.getScrollable(),
            selectable = me.getSelectable();
        me.callParent();
        if (scroller) {
            headerContainer.getScrollable().addPartner(scroller, 'x');
        }
        if (titleBar) {
            me.insert(0, titleBar);
        }
        me.add(headerContainer);
        if (selectable) {
            selectable.onViewCreated(me);
        }
    },
    addColumn: function(column) {
        return this.getHeaderContainer().add([
            column
        ])[0];
    },
    beforeShowColumnMenu: function(column, menu) {
        return this.fireEvent('beforeshowcolumnmenu', this, column, menu);
    },
    doDestroy: function() {
        this.destroyMembers('columnsMenu', 'columnsMenuItem', 'rowNumbererColumn');
        this.callParent();
    },
    getColumnForField: function(fieldName) {
        return this.getHeaderContainer().getColumnForField(fieldName);
    },
    
    getColumns: function(selector) {
        return this.getHeaderContainer().getColumns(selector);
    },
    getVisibleColumns: function() {
        return this.getHeaderContainer().getVisibleColumns();
    },
    insertColumn: function(index, column) {
        return this.getHeaderContainer().insert(index, column);
    },
    
    mapToCell: function(value, column) {
        var me = this,
            ret;
        if (value) {
            if (value.isGridCell && value.row.getGrid() === me) {
                ret = value;
            } else {
                if (value.isEntity) {
                    value = me.mapToItem(value);
                }
                if (value) {
                    if (value.isGridRow) {
                        column = column || me.getFirstVisibleColumn();
                        if (column) {
                            ret = value.getCellByColumn(column);
                        }
                    } else {
                        ret = Ext.Component.from(value, me.innerCt, 'gridcellbase');
                    }
                }
            }
        }
        return ret || null;
    },
    mapToItem: function(value, as) {
        if (value && value.isGridCell) {
            value = value.row;
        }
        return this.callParent([
            value,
            as
        ]);
    },
    
    mapToRowBody: function(value) {
        if (value) {
            if (!value.isGridRow) {
                value = this.mapToItem(value);
            }
            if (value && value.isGridRow) {
                value = value.getBody();
            }
        }
        return value || null;
    },
    removeColumn: function(column) {
        return this.getHeaderContainer().remove(column);
    },
    
    registerActionable: function(actionable) {
        this.getNavigationModel().registerActionable(actionable);
    },
    
    unregisterActionable: function(actionable) {
        this.getNavigationModel().unregisterActionable(actionable);
    },
    
    
    onColumnAdd: function(container, column, columnIndex) {
        var me = this,
            items, ln, i, row;
        if (!me.initializingColumns && !me.destroying) {
            items = me.items.items;
            ln = items.length;
            for (i = 0; i < ln; i++) {
                row = items[i];
                if (row.isGridRow) {
                    row.insertColumn(columnIndex, column);
                }
            }
            me.onColumnChange('columnadd', [
                me,
                column,
                columnIndex
            ]);
        }
    },
    onColumnHide: function(container, column) {
        var me = this,
            items, ln, i, row;
        if (me.initialized && !me.destroying) {
            items = me.items.items;
            ln = items.length;
            for (i = 0; i < ln; i++) {
                row = items[i];
                if (row.isGridRow) {
                    row.hideColumn(column);
                }
            }
            me.onColumnChange('columnhide', [
                me,
                column
            ]);
        }
    },
    onColumnMove: function(container, columns, group, fromIdx) {
        var me = this,
            before = null,
            colLen = columns.length,
            items, ln, i, j, row, column, index, leaves;
        if (me.initialized && !me.destroying) {
            items = me.items.items;
            ln = items.length;
            
            
            
            leaves = me.getHeaderContainer().getLeaves();
            index = leaves.indexOf(columns[colLen - 1]);
            before = leaves[index + 1] || null;
            for (i = colLen - 1; i >= 0; --i) {
                column = columns[i];
                for (j = 0; j < ln; j++) {
                    row = items[j];
                    if (row.isGridRow) {
                        row.insertColumnBefore(column, before);
                    }
                }
                me.onColumnChange('columnmove', [
                    me,
                    column,
                    fromIdx + i,
                    leaves.indexOf(column)
                ]);
                before = column;
            }
        }
    },
    onColumnRemove: function(container, column) {
        var me = this,
            items, ln, i, row;
        if (me.initialized && !me.destroying) {
            if (column === me.sortedColumn) {
                me.sortedColumn = null;
            }
            items = me.items.items;
            ln = items.length;
            for (i = 0; i < ln; i++) {
                row = items[i];
                if (row.isGridRow) {
                    row.removeColumn(column);
                }
            }
            me.onColumnChange('columnremove', [
                me,
                column
            ]);
        }
    },
    onColumnResize: function(container, column, width, oldWidth) {
        if (!this.destroying) {
            
            if (oldWidth && !column.getHidden()) {
                this.fireEvent('columnresize', this, column, width);
            }
        }
    },
    onColumnShow: function(container, column) {
        var me = this,
            items, ln, i, row;
        if (me.initialized && !me.destroying) {
            items = me.items.items;
            ln = items.length;
            for (i = 0; i < ln; i++) {
                row = items[i];
                if (row.isGridRow) {
                    row.showColumn(column);
                }
            }
            me.onColumnChange('columnshow', [
                me,
                column
            ]);
        }
    },
    onColumnSort: function(container, column, direction) {
        this.fireEvent('columnsort', this, column, direction);
    },
    onRender: function() {
        var hideHeaders = this._hideHeaders;
        this.callParent();
        
        if (hideHeaders) {
            this.updateHideHeaders(hideHeaders);
        }
    },
    privates: {
        dataItemMap: {
            header: 1,
            footer: 1
        },
        handleStoreSort: function() {
            if (this.rendered) {
                this.getHeaderContainer().setSortState();
            }
        },
        onStoreGroupChange: function(store, grouper) {
            this.callParent([
                store,
                grouper
            ]);
            this.handleStoreSort();
        },
        onStoreSort: function() {
            this.handleStoreSort();
        },
        registerColumn: function(column) {
            var me = this,
                columns = me.registeredColumns,
                headerCt = me.getHeaderContainer();
            if (!column.isGridColumn) {
                column = Ext.create(column);
            }
            if (!columns) {
                me.registeredColumns = columns = [];
            }
            columns.push(column);
            
            
            if (!me.isConfiguring || (headerCt && headerCt.items.getCount())) {
                headerCt.add(column);
            }
            return column;
        },
        unregisterColumn: function(column, destroy) {
            var columns = this.registeredColumns,
                headerCt = this.getHeaderContainer();
            if (!this.destroying) {
                if (columns) {
                    Ext.Array.remove(columns, column);
                }
                if (headerCt) {
                    headerCt.remove(column, destroy === true);
                }
            }
            return column;
        },
        
        generateSelectorFunctions: function() {
            var me = this;
            me.callParent();
            
            
            
            me.eventDelegate = function(candidate) {
                var comp = Ext.Component.from(candidate),
                    ret = true,
                    row;
                
                if (!comp || comp === me) {
                    return false;
                }
                
                if (comp.getRefOwner() === me) {
                    ret = comp.isGridRow || me.dataItemMap[comp.$dataItem];
                } else {
                    
                    
                    
                    
                    
                    row = comp.row;
                    
                    
                    ret = row && row.isGridRow && (row.grid || row.list) === me;
                }
                return ret;
            };
        },
        getFirstVisibleColumn: function() {
            var columns = this.getVisibleColumns();
            return columns.length ? columns[0] : null;
        },
        getLastVisibleColumn: function() {
            var columns = this.getVisibleColumns(),
                len = columns.length;
            return len ? columns[len - 1] : null;
        },
        isFirstVisibleColumn: function(column) {
            return this.getFirstVisibleColumn() === column;
        },
        isLastVisibleColumn: function(column) {
            return this.getLastVisibleColumn() === column;
        },
        createDataItem: function(cfg) {
            var item = this.callParent([
                    cfg
                ]);
            item.grid = this;
            return item;
        },
        
        
        onColumnChange: function(changeEvent, eventArgs) {
            var me = this;
            
            
            if (changeEvent !== 'columnmove' && changeEvent !== 'columnadd' && changeEvent !== 'columnremove') {
                me.refreshInnerWidth();
            }
            if (!me.isConfiguring) {
                me.fireEventArgs(changeEvent, eventArgs);
            }
            me.clearItemCaches();
        },
        
        
        
        
        refreshInnerWidth: function() {
            var headerCtBody = this.getHeaderContainer().bodyElement.dom,
                scrollWidth;
            
            scrollWidth = headerCtBody.scrollWidth;
            this.setInnerWidth(scrollWidth > headerCtBody.clientWidth ? scrollWidth : null);
        },
        onColumnComputedWidthChange: function(changedColumns, totalColumnWidth) {
            var me = this,
                groupingInfo = me.groupingInfo;
            if (!me.destroying) {
                
                me.setInnerWidth(totalColumnWidth);
                me.setCellSizes(changedColumns, me.items.items);
                me.setCellSizes(changedColumns, me.itemCache);
                if (me.isGrouping()) {
                    me.setCellSizes(changedColumns, groupingInfo.headers.unused);
                    me.setCellSizes(changedColumns, groupingInfo.footers.unused);
                }
                
                me.fireEvent('columnlayout', me, changedColumns, totalColumnWidth);
            }
        },
        onCellSelect: function(location) {
            var cell = location.getCell();
            if (cell) {
                cell.addCls(this.selectedCls);
            }
        },
        onCellDeselect: function(location) {
            var cell = location.getCell();
            if (cell) {
                cell.removeCls(this.selectedCls);
            }
        },
        setCellSizes: function(changedColumns, items) {
            var len = items.length,
                changedColCount = changedColumns.length,
                row, i, j;
            
            for (i = 0; i < len; i++) {
                row = items[i];
                if (row.isGridRow) {
                    for (j = 0; j < changedColCount; j++) {
                        row.setColumnWidth(changedColumns[j]);
                    }
                }
            }
        },
        
        
        
        updateColumnLines: function(columnLines) {
            this.el.toggleCls(this.columnLinesCls, columnLines);
        },
        
        updateColumnResize: function(enabled) {
            var me = this,
                plugin = me.findPlugin('columnresizing');
            if (!plugin) {
                if (enabled) {
                    me.addPlugin('columnresizing');
                }
            } else {
                plugin.setGrid(enabled ? me : null);
            }
        },
        
        updateColumns: function(columns) {
            var me = this,
                header = me.getHeaderContainer(),
                count = columns && columns.length,
                persist = me.registeredColumns;
            
            
            if (header) {
                
                
                me.rowHeight = null;
                if (header) {
                    header.beginColumnUpdate();
                    if (header.getItems().getCount()) {
                        
                        if (persist) {
                            header.remove(persist, false);
                        }
                        
                        if (count) {
                            header.remove(columns.filter(function(col) {
                                return col.isInstance;
                            }), 
                            false);
                        }
                        header.removeAll(
                        true, 
                        true);
                    }
                    if (count) {
                        me.initializingColumns = me.isConfiguring;
                        header.setColumns(columns);
                        
                        if (persist) {
                            header.add(persist);
                        }
                        delete me.initializingColumns;
                    }
                    
                    
                    
                    
                    header.endColumnUpdate();
                }
            }
        },
        applyRowNumbers: function(rowNumbers) {
            var me = this;
            if (rowNumbers) {
                rowNumbers = me.rowNumbererColumn = Ext.create(Ext.apply({
                    xtype: 'rownumberer',
                    weight: -1000,
                    editRenderer: me.renderEmpty
                }, rowNumbers));
            }
            return rowNumbers;
        },
        updateRowNumbers: function(rowNumbers, oldRowNumbers) {
            if (oldRowNumbers) {
                this.unregisterColumn(oldRowNumbers, true);
            }
            if (rowNumbers) {
                this.registerColumn(rowNumbers);
            }
        },
        renderEmpty: function() {
            return '\xa0';
        },
        
        applyColumnsMenuItem: function(config, existing) {
            return Ext.updateWidget(existing, config, this, 'createColumnsMenuItem');
        },
        createColumnsMenuItem: function(config) {
            return Ext.apply({
                grid: this
            }, config);
        },
        
        applyHeaderContainer: function(config, existing) {
            return Ext.updateWidget(existing, config, this, 'createHeaderContainer');
        },
        
        
        
        
        
        
        
        
        
        createHeaderContainer: function(config) {
            config = this.mergeProxiedConfigs('headerContainer', config, 
            true);
            config.sortable = this.getSortable();
            config.grid = this;
            return config;
        },
        updateHeaderContainer: function(headerContainer) {
            if (headerContainer) {
                
                
                headerContainer.on({
                    columnresize: 'onColumnResize',
                    columnshow: 'onColumnShow',
                    columnhide: 'onColumnHide',
                    columnadd: 'onColumnAdd',
                    columnmove: 'onColumnMove',
                    columnremove: 'onColumnRemove',
                    columnsort: 'onColumnSort',
                    scope: this
                });
            }
        },
        
        updateHideHeaders: function(hideHeaders) {
            if (this.isRendered) {
                var headerContainer = this.getHeaderContainer();
                
                if (hideHeaders) {
                    headerContainer.el.setStyle({
                        marginBottom: '-' + headerContainer.el.measure('h') + 'px'
                    });
                } else {
                    headerContainer.el.setStyle({
                        marginBottom: ''
                    });
                }
            }
        },
        
        updateTitle: function(title) {
            var titleBar = this.getTitleBar();
            if (titleBar) {
                if (title) {
                    titleBar.setTitle(title);
                    if (titleBar.isHidden()) {
                        titleBar.show();
                    }
                } else {
                    titleBar.hide();
                }
            }
        },
        
        applyTitleBar: function(config, existing) {
            return Ext.updateWidget(existing, config);
        },
        updateTitleBar: function(titleBar) {
            if (titleBar && !titleBar.getTitle()) {
                titleBar.setTitle(this.getTitle());
            }
        },
        
        applyTotalColumnWidth: function(totalColumnWidth) {
            var rows = this.dataItems;
            
            return rows.length === 0 ? undefined : totalColumnWidth;
        },
        
        updateVerticalOverflow: function(value, was) {
            var headerContainer = this.getHeaderContainer(),
                verticalScrollbarWidth = Ext.getScrollbarSize().width;
            this.callParent([
                value,
                was
            ]);
            headerContainer.setVerticalOverflow(verticalScrollbarWidth > 0 && value);
        }
    }
}, 
function(Grid) {
    Grid.prototype.indexModifiedFields = Ext.Array.toMap;
});


Ext.define('Ext.grid.PagingToolbar', {
    extend: Ext.Toolbar,
    xtype: 'pagingtoolbar',
    classCls: Ext.baseCSSPrefix + 'pagingtoolbar',
    config: {
        
        prevButton: {
            xtype: 'button',
            iconCls: Ext.baseCSSPrefix + 'pagingtoolbar-prev'
        },
        
        nextButton: {
            xtype: 'button',
            iconCls: Ext.baseCSSPrefix + 'pagingtoolbar-next'
        },
        
        sliderField: {
            xtype: 'singlesliderfield',
            liveUpdate: true,
            value: 1,
            flex: 1,
            minValue: 1
        },
        
        
        
        
        
        summaryComponent: {
            xtype: 'component',
            cls: Ext.baseCSSPrefix + 'pagingtoolbar-summary'
        }
    },
    inheritUi: true,
    initialize: function() {
        var me = this;
        me.callParent();
        me.add([
            me.getPrevButton(),
            me.getSummaryComponent(),
            me.getSliderField(),
            me.getNextButton()
        ]);
    },
    applyPrevButton: function(prevButton, oldPrevButton) {
        return Ext.factory(prevButton, Ext.Button, oldPrevButton);
    },
    applyNextButton: function(nextButton, oldNextButton) {
        return Ext.factory(nextButton, Ext.Button, oldNextButton);
    },
    applySliderField: function(sliderField, oldSliderField) {
        return Ext.factory(sliderField, Ext.field.SingleSlider, oldSliderField);
    },
    applySummaryComponent: function(summaryComponent, oldSummaryComponent) {
        return Ext.factory(summaryComponent, Ext.Component, oldSummaryComponent);
    }
});


Ext.define('Ext.grid.SummaryRow', {
    extend: Ext.grid.Row,
    xtype: 'gridsummaryrow',
    isSummaryRow: true,
    config: {
        
        
        
        group: null
    },
    defaultCellUI: 'summary',
    classCls: Ext.baseCSSPrefix + 'summaryrow',
    updateGroup: function() {
        this.syncSummary();
    },
    privates: {
        beginRefresh: function(context) {
            var me = this;
            context = me.callParent([
                context
            ]);
            context.group = me.getGroup();
            context.records = (context.group || context.store.data).items;
            context.summary = true;
            return context;
        },
        syncSummary: function() {
            var me = this,
                owner = me.getGroup() || me.parent.store,
                record = owner.getSummaryRecord(),
                viewModel = me.getViewModel();
            if (record === me.getRecord()) {
                me.refresh();
            } else {
                me.setRecord(record);
                if (viewModel) {
                    viewModel.setData({
                        record: record
                    });
                }
            }
        }
    }
});



Ext.define('Ext.grid.cell.Boolean', {
    extend: Ext.grid.cell.Text,
    xtype: 'booleancell',
    isBooleanCell: true,
    config: {
        
        falseText: 'False',
        
        trueText: 'True',
        
        undefinedText: '\xa0'
    },
    updateColumn: function(column, oldColumn) {
        this.callParent([
            column,
            oldColumn
        ]);
        if (column && column.isBooleanColumn) {
            var text = column.getFalseText();
            if (text !== null) {
                this.setFalseText(text);
            }
            text = column.getTrueText();
            if (text !== null) {
                this.setTrueText(text);
            }
            text = column.getUndefinedText();
            if (text !== null) {
                this.setUndefinedText(text);
            }
        }
    },
    updateFalseText: function() {
        if (!this.isConfiguring) {
            this.writeValue();
        }
    },
    updateTrueText: function() {
        if (!this.isConfiguring) {
            this.writeValue();
        }
    },
    updateUndefinedText: function() {
        if (!this.isConfiguring) {
            this.writeValue();
        }
    },
    formatValue: function(value) {
        var me = this;
        if (value === undefined) {
            value = me.getUndefinedText();
        } else if (!value || value === 'false') {
            value = me.getFalseText();
        } else {
            value = me.getTrueText();
        }
        return value;
    }
});

Ext.define('Ext.grid.cell.Expander', {
    extend: Ext.grid.cell.Base,
    xtype: 'expandercell',
    isExpanderCell: true,
    config: {
        collapsed: true
    },
    align: 'center',
    classCls: Ext.baseCSSPrefix + 'expandercell',
    expandedCls: Ext.baseCSSPrefix + 'expanded',
    innerTemplate: [
        {
            reference: 'iconElement',
            cls: Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon'
        }
    ],
    updateCollapsed: function(collapsed) {
        this.element.toggleCls(this.expandedCls, !collapsed);
    }
});


Ext.define('Ext.grid.cell.Widget', {
    extend: Ext.grid.cell.Base,
    xtype: 'widgetcell',
    isWidgetCell: true,
    config: {
        
        forceWidth: false,
        
        widget: null
    },
    
    align: 'center',
    
    classCls: Ext.baseCSSPrefix + 'widgetcell',
    
    selectable: false,
    getRefItems: function(deep) {
        var result = [],
            widget = this.getWidget();
        if (widget) {
            result.push(widget);
            if (deep && widget.getRefItems) {
                result.push.apply(result, widget.getRefItems(deep));
            }
        }
        return result;
    },
    setValue: function(value) {
        
        
        
        if (value && typeof value === 'object') {
            
            this._value = value;
            this.updateValue(value);
        } else {
            if (value === undefined) {
                
                value = null;
            }
            this.callParent([
                value
            ]);
        }
        return this;
    },
    updateValue: function(value) {
        var me = this,
            widget = me.getWidget(),
            
            defaultBindCfg = me.defaultBindCfg;
        if (defaultBindCfg && widget) {
            widget[defaultBindCfg.names.set](value);
        }
    },
    applyWidget: function(widget) {
        var me = this;
        if (widget) {
            widget = Ext.apply({
                ownerCmp: me
            }, widget);
            widget = Ext.create(widget);
        }
        return widget;
    },
    updateWidget: function(widget, oldWidget) {
        var me = this,
            defaultBindCfg;
        if (oldWidget) {
            me.widgetChangeListener = Ext.destroy(me.widgetChangeListener);
            oldWidget.measurer = null;
            oldWidget.destroy();
        }
        if (widget) {
            
            
            me.bodyElement.setHtml('');
            me.bodyElement.appendChild(widget.element);
            if (me.getForceWidth()) {
                me.setWidgetWidth(me.getWidth());
            }
            defaultBindCfg = widget.defaultBindProperty;
            defaultBindCfg = widget.self.getConfigurator().configs[defaultBindCfg];
            me.defaultBindCfg = defaultBindCfg || null;
            
            if (!defaultBindCfg || !widget[defaultBindCfg.names.get] || !widget[defaultBindCfg.names.set]) {
                Ext.raise('Invalid config "' + widget.defaultBindProperty + '" for ' + widget.$className);
            }
            
            if (me.dataIndex) {
                me.widgetChangeListener = widget.on({
                    change: 'onWidgetChange',
                    scope: me
                });
            }
        }
    },
    onWidgetChange: function(widget) {
        if (!this.refreshContext) {
            var me = this,
                record = me.getRecord(),
                defaultBindCfg = me.defaultBindCfg,
                dataIndex = me.dataIndex,
                value;
            if (defaultBindCfg) {
                value = widget[defaultBindCfg.names.get]();
                this.setValue(value);
                if (record && !record.isSummaryRecord && dataIndex) {
                    record.set(dataIndex, value);
                }
            }
        }
    },
    updateWidth: function(width, oldWidth) {
        this.callParent([
            width,
            oldWidth
        ]);
        if (this.getForceWidth()) {
            this.setWidgetWidth(width);
        }
    },
    onRender: function() {
        var me = this;
        if (me.getForceWidth()) {
            me.setWidgetWidth(me.getWidth());
        }
    },
    doDestroy: function() {
        this.setWidget(null);
        this.callParent();
    },
    privates: {
        setWidgetWidth: function(width) {
            var me = this,
                el = me.bodyElement,
                widget, column, leftPad, rightPad;
            if (!me.rendered) {
                return;
            }
            widget = me.getWidget();
            if (widget) {
                column = me.getColumn();
                leftPad = parseInt(column.getCachedStyle(el, 'padding-left'), 10) || 0;
                rightPad = parseInt(column.getCachedStyle(el, 'padding-right'), 10) || 0;
                
                widget.measurer = column;
                widget.setWidth(width - leftPad - rightPad);
            }
        }
    }
});


Ext.define('Ext.grid.column.Boolean', {
    extend: Ext.grid.column.Column,
    xtype: 'booleancolumn',
    isBooleanColumn: true,
    config: {
        
        trueText: null,
        
        falseText: null,
        
        undefinedText: null,
        defaultEditor: {
            xtype: 'checkboxfield'
        },
        cell: {
            xtype: 'booleancell'
        }
    }
});


Ext.define('Ext.grid.column.Text', {
    extend: Ext.grid.column.Column,
    xtype: 'textcolumn',
    cell: {
        xtype: 'textcell'
    }
});


Ext.define('Ext.grid.plugin.CellEditing', {
    extend: Ext.plugin.Abstract,
    alias: [
        'plugin.gridcellediting',
        'plugin.cellediting'
    ],
    config: {
        
        grid: null,
        
        triggerEvent: 'doubletap',
        
        selectOnEdit: null
    },
    init: function(grid) {
        this.setGrid(grid);
        grid.setTouchAction({
            doubleTapZoom: false
        });
        grid.$cellEditing = true;
    },
    getEditor: function(location) {
        var column = location.column,
            fieldName = column.getDataIndex(),
            record = location.record,
            editable = column.getEditable(),
            editor, field;
        if (!(editor = editable !== false && column.getEditor(location.record)) && editable) {
            editor = Ext.create(column.getDefaultEditor());
        }
        if (editor) {
            if (!editor.isCellEditor) {
                editor = Ext.create({
                    xtype: 'celleditor',
                    field: editor
                });
            }
            column.setEditor(editor);
            editor.editingPlugin = this;
            field = editor.getField();
            field.addUi('celleditor');
            
            field.setValidationField(record.getField(fieldName), record);
        }
        return editor;
    },
    getActiveEditor: function() {
        return this.activeEditor;
    },
    updateGrid: function(grid, oldGrid) {
        if (oldGrid) {
            oldGrid.unregisterActionable(this);
        }
        if (grid) {
            grid.registerActionable(this);
        }
    },
    
    activateCell: function(location) {
        var me = this,
            activeEditor = me.activeEditor,
            previousEditor = me.$previousEditor,
            editor, selModel, result;
        
        if (!location) {
            Ext.raise('A grid Location must be passed into CellEditing#activateCell');
        }
        
        
        
        
        
        if (activeEditor && activeEditor.$activeLocation.cell === location.cell) {
            return activeEditor.$activeLocation;
        } else {
            editor = me.getEditor(location);
            if (editor) {
                if (previousEditor) {
                    if (previousEditor.isCancelling) {
                        previousEditor.cancelEdit();
                    } else {
                        previousEditor.completeEdit();
                    }
                }
                result = editor.startEdit(location);
                if (editor.editing) {
                    
                    if (me.getSelectOnEdit()) {
                        selModel = me.getGrid().getSelectable();
                        if (selModel.getCells()) {
                            selModel.selectCells(location, location);
                        } else if (selModel.getRows()) {
                            selModel.select(location.record);
                        }
                    }
                    me.$previousEditor = editor;
                    return result;
                }
            }
        }
    },
    
    startEdit: function(record, column) {
        this.activateCell(new Ext.grid.Location(this.getGrid(), {
            record: record,
            column: column
        }));
    },
    destroy: function() {
        var grid = this.getGrid();
        if (grid) {
            grid.$cellEditing = false;
        }
        this.$previousEditor = null;
        this.callParent();
    }
});


Ext.define('Ext.grid.plugin.Clipboard', {
    extend: Ext.plugin.AbstractClipboard,
    alias: 'plugin.clipboard',
    formats: {
        cell: {
            get: 'getCells'
        },
        html: {
            get: 'getCellData'
        },
        raw: {
            get: 'getCellData',
            put: 'putCellData'
        }
    },
    gridListeners: {
        initialize: 'onCmpReady'
    },
    getCellData: function(format, erase) {
        var cmp = this.getCmp(),
            selectable = cmp.getSelectable(),
            selection = selectable && selectable.getSelection(),
            ret = [],
            isRaw = format === 'raw',
            isText = format === 'text',
            data, dataIndex, lastRecord, column, record, row;
        if (selection) {
            selection.eachCell(function(location, colIdx, rowIdx) {
                column = location.column;
                record = location.record;
                
                if (column.getIgnoreExport()) {
                    return;
                }
                if (lastRecord !== record) {
                    lastRecord = record;
                    ret.push(row = []);
                }
                dataIndex = column.getDataIndex();
                data = record.data[dataIndex];
                if (!isRaw) {
                    
                    data = column.printValue(data);
                    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(),
            selectable = cmp.getSelectable(),
            selection = selectable && selectable.getSelection(),
            ret = [],
            dataIndex, lastRecord, record, row;
        if (selection) {
            selection.eachCell(function(location) {
                record = location.record;
                if (lastRecord !== record) {
                    lastRecord = record;
                    ret.push(row = {
                        model: record.self,
                        fields: []
                    });
                }
                dataIndex = location.column.getDataIndex();
                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 cmp = this.getCmp(),
            
            
            
            
            values = Ext.util.TSV.decode(data, undefined, null),
            recCount = values.length,
            colCount = recCount ? values[0].length : 0,
            columns = cmp.getHeaderContainer().getVisibleColumns(),
            store = cmp.getStore(),
            maxRowIdx = store ? store.getCount() - 1 : 0,
            maxColIdx = columns.length - 1,
            selectable = cmp.getSelectable(),
            selection = selectable && selectable.getSelection(),
            row, sourceRowIdx, sourceColIdx, column, record, columnIndex, recordIndex, dataObject, destination, dataIndex, startColumnIndex, startRecordIndex;
        if (maxRowIdx <= 0 || maxColIdx <= 0) {
            return;
        }
        if (selection) {
            selection.eachCell(function(c) {
                destination = c;
                return false;
            });
        }
        startColumnIndex = destination ? destination.columnIndex : 0;
        startRecordIndex = destination ? destination.recordIndex : 0;
        for (sourceRowIdx = 0; sourceRowIdx < recCount; sourceRowIdx++) {
            row = values[sourceRowIdx];
            recordIndex = startRecordIndex + sourceRowIdx;
            
            if (recordIndex > maxRowIdx) {
                break;
            }
            record = store.getAt(recordIndex);
            dataObject = {};
            columnIndex = startColumnIndex;
            sourceColIdx = 0;
            
            while (sourceColIdx < colCount && columnIndex <= maxColIdx) {
                column = columns[columnIndex];
                dataIndex = column.getDataIndex();
                
                if (!column.getIgnoreExport()) {
                    
                    if (dataIndex && (format === 'raw' || format === 'text')) {
                        dataObject[dataIndex] = row[sourceColIdx];
                    }
                    sourceColIdx++;
                }
                columnIndex++;
            }
            
            record.set(dataObject);
        }
    },
    putTextData: function(data, format) {
        this.putCellData(data, format);
    },
    getTarget: function(comp) {
        return comp.element;
    },
    privates: {
        validateAction: function(event) {
            var cmp = this.getCmp(),
                viewLocation = cmp.getNavigationModel().getLocation(),
                selectable = cmp.getSelectable(),
                checkColumn = selectable && selectable.getCheckbox();
            
            if (viewLocation && viewLocation.actionable && checkColumn !== viewLocation.column) {
                return false;
            }
        }
    }
});


Ext.define('Ext.grid.plugin.Editable', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.grideditable',
    config: {
        
        grid: null,
        
        triggerEvent: 'childdoubletap',
        
        formConfig: null,
        
        defaultFormConfig: {
            xtype: 'formpanel',
            scrollable: true,
            items: [
                {
                    xtype: 'fieldset'
                }
            ]
        },
        
        toolbarConfig: {
            xtype: 'titlebar',
            docked: 'top',
            items: [
                {
                    xtype: 'button',
                    ui: 'alt',
                    text: 'Cancel',
                    align: 'left',
                    action: 'cancel'
                },
                {
                    xtype: 'button',
                    ui: 'alt',
                    text: 'Submit',
                    align: 'right',
                    action: 'submit'
                }
            ]
        },
        
        enableDeleteButton: true
    },
    init: function(grid) {
        this.setGrid(grid);
        grid.setTouchAction({
            doubleTapZoom: false
        });
    },
    destroy: function() {
        this.cleanup();
        this.callParent();
    },
    updateGrid: function(grid, oldGrid) {
        var triggerEvent = this.getTriggerEvent();
        if (oldGrid) {
            oldGrid.un(triggerEvent, 'onTrigger', this);
        }
        if (grid) {
            grid.on(triggerEvent, 'onTrigger', this);
        }
    },
    onCancelTap: function() {
        this.sheet.hide();
    },
    onSubmitTap: function() {
        this.form.getRecord().set(this.form.getValues());
        this.sheet.hide();
    },
    onSheetHide: function() {
        this.cleanup();
    },
    getEditorFields: function(columns) {
        var fields = [],
            ln = columns.length,
            
            map = {},
            
            i, column, editor, editable, cfg;
        for (i = 0; i < ln; i++) {
            column = columns[i];
            editable = column.getEditable();
            editor = editable !== false && column.getEditor();
            if (!editor && editable) {
                cfg = column.getDefaultEditor();
                editor = Ext.create(cfg);
                column.setEditor(editor);
            }
            if (editor) {
                
                if (map[column.getDataIndex()]) {
                    Ext.raise('An editable column with the same dataIndex "' + column.getDataIndex() + '" already exists.');
                }
                map[column.getDataIndex()] = true;
                
                if (editor.isEditor) {
                    editor = editor.getField();
                }
                editor.setLabel(column.getText());
                editor.setName(column.getDataIndex());
                fields.push(editor);
            }
        }
        return fields;
    },
    onTrigger: function(grid, location) {
        var me = this,
            record = location.record,
            formConfig = me.getFormConfig(),
            toolbarConfig = me.getToolbarConfig(),
            fields, form, sheet, toolbar;
        
        if (!record || !location.row) {
            return;
        }
        if (formConfig) {
            me.form = form = Ext.factory(formConfig, Ext.form.Panel);
        } else {
            me.form = form = Ext.factory(me.getDefaultFormConfig());
            fields = me.getEditorFields(grid.getColumns());
            form.down('fieldset').setItems(fields);
            form.clearFields = true;
        }
        toolbar = Ext.factory(toolbarConfig, Ext.form.TitleBar);
        me.submitButton = toolbar.down('button[action=submit]');
        toolbar.down('button[action=cancel]').on('tap', 'onCancelTap', me);
        me.submitButton.on('tap', 'onSubmitTap', me);
        
        form.on({
            change: 'onFieldChange',
            delegate: 'field',
            scope: me
        });
        form.setRecord(record);
        me.sheet = sheet = grid.add({
            xtype: 'sheet',
            items: [
                toolbar,
                form
            ],
            hideOnMaskTap: true,
            enter: 'right',
            exit: 'right',
            right: 0,
            width: 320,
            layout: 'fit',
            stretchY: true,
            hidden: true
        });
        if (me.getEnableDeleteButton()) {
            form.add({
                xtype: 'button',
                text: 'Delete',
                ui: 'decline',
                margin: 10,
                handler: function() {
                    grid.getStore().remove(record);
                    sheet.hide();
                }
            });
        }
        sheet.on('hide', 'onSheetHide', me);
        sheet.show();
    },
    privates: {
        onFieldChange: function() {
            this.submitButton.setDisabled(!this.form.isValid());
        },
        cleanup: function() {
            var me = this,
                form = me.form;
            if (form && !form.destroyed && form.clearFields) {
                form.removeAll(false);
            }
            me.form = me.sheet = Ext.destroy(me.sheet);
        }
    }
});


Ext.define('Ext.grid.plugin.PagingToolbar', {
    extend: Ext.plugin.Abstract,
    alias: [
        'plugin.pagingtoolbar',
        'plugin.gridpagingtoolbar'
    ],
    mixins: [
        Ext.mixin.Hookable
    ],
    config: {
        
        grid: null,
        currentPage: 1,
        
        pageSize: 0,
        totalCount: 0,
        totalPages: 0,
        loadPages: null,
        
        buffer: 250,
        toolbar: {
            xtype: 'pagingtoolbar',
            docked: 'bottom'
        }
    },
    init: function(grid) {
        this.setGrid(grid);
        grid.add(this.getToolbar());
    },
    destroy: function() {
        this.setBuffer(null);
        this.setGrid(null);
        this.callParent();
    },
    updateGrid: function(grid, oldGrid) {
        var me = this;
        me.gridListeners = me.storeListeners = me.scrollListeners = Ext.destroy(me.gridListeners, me.storeListeners, me.scrollListeners);
        if (grid) {
            me.gridListeners = grid.on({
                updatevisiblecount: 'onUpdateVisibleCount',
                storechange: 'onStoreChanged',
                destroyable: true,
                scope: me
            });
            me.scrollListeners = grid.getScrollable().on({
                scrollend: 'checkPageChange',
                buffer: 100,
                scope: me
            });
            me.bindStore(grid.getStore());
        }
    },
    bindStore: function(store) {
        var me = this;
        Ext.destroy(me.storeListeners);
        me.getToolbar().setDisabled(!!store);
        if (!store) {
            return;
        }
        me.storeListeners = store.on({
            add: 'onTotalCountChange',
            remove: 'onTotalCountChange',
            refresh: 'onTotalCountChange',
            clear: 'onTotalCountChange',
            destroyable: true,
            scope: me
        });
        
        me.setLoadPages(store.pageSize > 0);
        me.cancelBufferTask();
        if (store.isLoaded()) {
            me.onTotalCountChange(store);
        }
    },
    onStoreChanged: function(grid, store) {
        this.bindStore(store);
    },
    
    getPageData: function() {
        var grid = this.getGrid(),
            store = grid.getStore(),
            totalCount = store.getTotalCount() || store.getCount(),
            pageSize = this.getLoadPages() ? store.pageSize : grid.visibleCount,
            pageCount = Math.ceil(totalCount / pageSize);
        return {
            totalCount: totalCount,
            totalPages: Ext.Number.isFinite(pageCount) ? pageCount : 1,
            currentPage: store.currentPage,
            pageSize: pageSize
        };
    },
    checkPageChange: function() {
        var me = this,
            grid = me.getGrid(),
            pageSize = me.getPageSize(),
            currentPage = me.getCurrentPage(),
            topVisibleIndex = grid.topVisibleIndex,
            newPage = Math.ceil((topVisibleIndex + pageSize) / pageSize);
        
        if (grid.getStore() && !me.getLoadPages() && newPage > 0 && newPage !== currentPage) {
            me.preventGridScroll = true;
            me.setCurrentPage(newPage);
            me.preventGridScroll = false;
        }
    },
    updateBuffer: function(buffer) {
        var me = this,
            bufferTask = me.bufferTask;
        if (Ext.isNumber(buffer)) {
            me.bufferTask = bufferTask || new Ext.util.DelayedTask(me.bufferTaskRun, me);
            me.cancelBufferTask();
        } else if (bufferTask) {
            bufferTask.cancel();
            me.bufferTask = null;
        }
    },
    cancelBufferTask: function() {
        if (this.bufferTask) {
            this.bufferTask.cancel();
        }
    },
    loadCurrentPage: function() {
        this.getGrid().getStore().loadPage(this.getCurrentPage());
    },
    bufferTaskRun: function() {
        this.loadCurrentPage();
    },
    applyToolbar: function(toolbar, oldToolbar) {
        return Ext.factory(toolbar, Ext.Toolbar, oldToolbar);
    },
    updateToolbar: function(toolbar) {
        var me = this;
        if (toolbar) {
            toolbar.getSliderField().on({
                change: 'onPageChange',
                dragstart: 'onPageSliderDrag',
                drag: 'onPageSliderDrag',
                dragend: 'onPageSliderDragEnd',
                scope: me
            });
            toolbar.getNextButton().on({
                tap: 'onNextPageTap',
                scope: me
            });
            toolbar.getPrevButton().on({
                tap: 'onPreviousPageTap',
                scope: me
            });
        }
    },
    onPageChange: function(field, value) {
        this.setCurrentPage(value);
    },
    onPageSliderDrag: function(field, slider, value) {
        this.isDragging = true;
        this.setCurrentPage(Ext.isArray(value) ? value[0] : value);
    },
    onPageSliderDragEnd: function() {
        var me = this;
        me.isDragging = false;
        if (me.getBuffer() === 'dragend' || me.bufferTask.Id) {
            me.cancelBufferTask();
            me.loadCurrentPage();
        }
    },
    onNextPageTap: function() {
        var nextPage = this.getCurrentPage() + 1;
        if (nextPage <= this.getTotalPages()) {
            this.setCurrentPage(nextPage);
        }
    },
    onPreviousPageTap: function() {
        var previousPage = this.getCurrentPage() - 1;
        if (previousPage > 0) {
            this.setCurrentPage(previousPage);
        }
    },
    onTotalCountChange: function(store) {
        var me = this,
            data = me.getPageData();
        me.bulkConfigs = true;
        me.setConfig(data);
        me.bulkConfigs = false;
        me.syncSummary();
    },
    onUpdateVisibleCount: function(grid, visibleCount) {
        var store = grid.getStore(),
            totalCount;
        if (store && !this.getLoadPages()) {
            visibleCount -= 1;
            this.setPageSize(visibleCount);
            totalCount = store.getTotalCount() || store.getCount();
            this.setTotalPages(Math.ceil(totalCount / visibleCount));
        }
    },
    updateTotalPages: function() {
        if (!this.isConfiguring) {
            this.syncSummary();
        }
    },
    updateCurrentPage: function(page) {
        var me = this,
            isDragging = me.isDragging,
            bufferTask = me.bufferTask,
            buffer = me.getBuffer();
        if (!me.isConfiguring) {
            if (me.getLoadPages()) {
                if (bufferTask && Ext.isNumber(buffer) && isDragging) {
                    bufferTask.delay(buffer);
                } else if (buffer !== 'dragend' || !isDragging) {
                    me.getGrid().getStore().loadPage(page);
                }
            } else {
                me.syncSummary();
            }
        }
    },
    updateTotalCount: function(totalCount) {
        if (!this.isConfiguring) {
            this.syncSummary();
        }
    },
    getPageTopRecord: function(page) {
        var grid = this.getGrid(),
            store = grid && grid.getStore(),
            pageSize = this.getPageSize(),
            pageTopRecordIndex = (page - 1) * pageSize;
        return store && store.getAt(pageTopRecordIndex);
    },
    privates: {
        syncSummary: function() {
            var me = this,
                grid = me.getGrid(),
                toolbar = me.getToolbar(),
                sliderField = toolbar.getSliderField(),
                currentPage = me.getCurrentPage(),
                totalPages = me.getTotalPages(),
                pageTopRecord;
            if (me.bulkConfigs) {
                return;
            }
            
            
            toolbar.getSummaryComponent().element.dom.innerHTML = currentPage + ' / ' + totalPages;
            sliderField.setMaxValue(totalPages || 1);
            sliderField.setValue(currentPage);
            sliderField.setDisabled(totalPages <= 1);
            pageTopRecord = me.getPageTopRecord(currentPage);
            if (grid && !me.preventGridScroll && pageTopRecord) {
                grid.scrollToRecord(pageTopRecord);
            }
            toolbar.getNextButton().setDisabled(currentPage === totalPages);
            toolbar.getPrevButton().setDisabled(currentPage === 1);
        }
    }
});


Ext.define('Ext.grid.plugin.RowExpander', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.rowexpander',
    config: {
        grid: null,
        column: {
            weight: -1100,
            xtype: 'gridcolumn',
            align: 'center',
            text: '',
            width: 50,
            resizable: false,
            hideable: false,
            sortable: false,
            editable: false,
            ignore: true,
            ignoreExport: true,
            cell: {
                xtype: 'expandercell'
            },
            menuDisabled: true
        }
    },
    expanderSelector: '.' + Ext.baseCSSPrefix + 'expandercell .' + Ext.baseCSSPrefix + 'icon-el',
    init: function(grid) {
        grid.setVariableHeights(true);
        this.setGrid(grid);
    },
    destroy: function() {
        var grid = this.getGrid(),
            col = this.colInstance;
        if (col && !grid.destroying) {
            grid.unregisterColumn(col, true);
        }
        this.callParent();
    },
    applyColumn: function(column, oldColumn) {
        return Ext.factory(Ext.apply({}, column), null, oldColumn);
    },
    updateGrid: function(grid) {
        var me = this;
        if (grid) {
            grid.hasRowExpander = true;
            grid.addCls(Ext.baseCSSPrefix + 'has-rowexpander');
            me.colInstance = grid.registerColumn(me.getColumn());
            grid.refreshScrollerSize();
            grid.element.on({
                tap: 'onGridTap',
                delegate: me.expanderSelector,
                scope: me
            });
        }
    },
    onGridTap: function(e) {
        var cell = Ext.Component.from(e),
            row = cell.row;
        
        if (row.getGrid() === this.getGrid()) {
            row.toggleCollapsed();
        }
    }
});


Ext.define('Ext.grid.plugin.RowOperations', {
    extend: Ext.plugin.Abstract,
    alias: [
        'plugin.rowoperations',
        
        'plugin.multiselection',
        'plugin.gridmultiselection'
    ],
    alternateClassName: 'Ext.grid.plugin.MultiSelection',
    config: {
        
        operation: {
            lazy: true,
            $value: {
                xtype: 'button',
                ui: 'alt decline',
                align: 'right',
                handler: 'me.onOperationTap',
                margin: '0 0 0 10'
            }
        },
        
        selectButton: {
            lazy: true,
            $value: {
                xtype: 'button',
                ui: 'alt',
                align: 'right',
                margin: '0 0 0 10'
            }
        },
        
        selectionColumn: {
            sortable: false
        },
        
        useTriggerButton: true,
        
        triggerText: 'Select',
        
        cancelText: 'Done',
        
        deleteText: 'Delete',
        
        disableSelection: true,
        
        selecting: null
    },
    init: function(grid) {
        if (!this.useSelectButton()) {
            this.setSelecting(true);
        }
        if (this.getDisableSelection()) {
            grid.setDisableSelection(true);
        }
    },
    destroy: function() {
        this.setOperation(null);
        this.setSelectButton(null);
        this.callParent();
    },
    getRecords: function() {
        var grid = this.cmp;
        return grid.getSelections();
    },
    onOperationTap: function() {
        this.deleteSelectedRecords();
    },
    onTriggerTap: function() {
        this.setSelecting(!this.getSelecting());
    },
    
    applyOperation: function(config, button) {
        return Ext.updateWidget(button, config, this, 'createOperation');
    },
    createOperation: function(config) {
        var me = this,
            ret = Ext.apply({
                text: me.getDeleteText()
            }, config);
        ret.plugin = me;
        if (ret.handler === 'me.onOperationTap') {
            ret.handler = 'onOperationTap';
            ret.scope = me;
        }
        return ret;
    },
    updateOperation: function(operation) {
        if (operation) {
            var selectButton = this.useSelectButton(),
                titleBar = this.cmp.getTitleBar(),
                container;
            if (titleBar) {
                if (selectButton) {
                    container = selectButton.getParent();
                    titleBar.insert(container.indexOf(selectButton), operation);
                } else {
                    titleBar.add(operation);
                }
            }
        }
    },
    
    applySelectButton: function(config, button) {
        return Ext.updateWidget(button, config, this, 'createSelectButton');
    },
    createSelectButton: function(config) {
        var me = this,
            ret = Ext.apply({
                text: me.getTriggerText()
            }, config);
        ret.handler = 'onTriggerTap';
        ret.scope = me;
        return ret;
    },
    updateSelectButton: function(selectButton) {
        if (selectButton) {
            this.cmp.getTitleBar().add(selectButton);
        }
    },
    updateSelecting: function(selecting) {
        var me = this,
            grid = me.cmp,
            disableSelection = me.getDisableSelection(),
            operation = me.getOperation(),
            selectButton = me.useSelectButton(),
            selectionModel = grid.getSelectable();
        if (operation) {
            operation.setHidden(!selecting);
        }
        if (selectButton) {
            selectButton.setText(selecting ? me.getCancelText() : me.getTriggerText());
        }
        if (disableSelection) {
            grid.setDisableSelection(!selecting);
        }
        selectionModel.setCheckbox(selecting && me.getSelectionColumn());
        selectionModel.setMode(selecting ? 'multi' : 'single');
        if (disableSelection || !selecting) {
            selectionModel.deselectAll();
        }
    },
    privates: {
        deleteSelectedRecords: function() {
            var records = this.getRecords(),
                store = this.cmp.getStore();
            store.remove(records);
        },
        useSelectButton: function() {
            var me = this,
                titleBar = me.cmp.getTitleBar();
            return me.getUseTriggerButton() && titleBar && titleBar.getTitle() && me.getSelectButton();
        }
    }
});


Ext.define('Ext.grid.plugin.Summary', {
    extend: Ext.plugin.Abstract,
    alias: [
        'plugin.gridsummary',
        'plugin.summaryrow',
        'plugin.gridsummaryrow'
    ],
    alternateClassName: 'Ext.grid.plugin.SummaryRow',
    mixins: [
        Ext.mixin.Bufferable,
        Ext.mixin.StoreWatcher
    ],
    config: {
        
        row: {
            lazy: true,
            $value: {
                xtype: 'gridsummaryrow',
                docked: 'bottom'
            }
        }
    },
    inheritUi: true,
    storeListeners: {
        add: 'syncSummary',
        clear: 'syncSummary',
        remove: 'syncSummary',
        refresh: 'syncSummary',
        update: 'syncSummary'
    },
    bufferableMethods: {
        
        syncSummary: 5
    },
    init: function(grid) {
        var scrollable = grid.getScrollable(),
            row, rowScroller;
        this.setOwner(grid);
        row = this.getRow();
        grid.addCls(Ext.baseCSSPrefix + 'grid-has-summaryrow');
        if (scrollable) {
            rowScroller = row.getScrollable();
            if (!rowScroller) {
                row.setScrollable({
                    x: false,
                    y: false
                });
                rowScroller = row.getScrollable();
            }
            rowScroller.addPartner(scrollable, 'x');
        }
    },
    destroy: function() {
        this.setOwner(null);
        this.callParent();
    },
    createRow: function(config) {
        return Ext.apply({
            viewModel: this.getOwner().getItemConfig().viewModel
        }, config);
    },
    applyRow: function(row) {
        if (row) {
            row = this.createRow(row);
            row = this.cmp.add(row);
        }
        return row;
    },
    updateStore: function(store, oldStore) {
        this.mixins.storewatcher.updateStore.call(this, store, oldStore);
        if (store && store.isLoaded()) {
            
            this.syncSummary();
        }
    },
    privates: {
        doSyncSummary: function() {
            var row = this.getRow();
            if (row) {
                row.syncSummary();
            }
        },
        onContainerScroll: function(scr, x) {
            var item = this.getRow(),
                scroller;
            if (!(scroller = item.getScrollable())) {
                item.setScrollable({
                    x: false,
                    y: false
                });
                scroller = item.getScrollable();
            }
            scroller.scrollTo(x, null);
        }
    }
});

Ext.define('Ext.grid.plugin.ViewOptionsListItem', {
    extend: Ext.dataview.SimpleListItem,
    xtype: 'viewoptionslistitem',
    toolDefaults: {
        
        hideMode: 'visibility'
    },
    tools: {
        handle: {
            zone: 'start',
            cls: Ext.baseCSSPrefix + 'no-ripple',
            iconCls: Ext.baseCSSPrefix + 'column-options-sortablehandle'
        },
        icon: {
            zone: 'start',
            
            iconCls: ''
        },
        group: {
            zone: 'end',
            iconCls: Ext.baseCSSPrefix + 'column-options-groupindicator',
            
            
            
            stopEvent: false
        },
        hide: {
            zone: 'end',
            iconCls: Ext.baseCSSPrefix + 'column-options-visibleindicator',
            
            
            
            stopEvent: false
        }
    },
    cls: Ext.baseCSSPrefix + 'column-options-item',
    updateRecord: function(record, oldRecord) {
        if (!record) {
            return;
        }
        var me = this,
            tool;
        me.callParent([
            record,
            oldRecord
        ]);
        me.toggleCls(me.hiddenColumnCls, record.get('hidden'));
        me.toggleCls(me.groupedColumnCls, record.get('grouped'));
        tool = me.lookupTool('icon');
        tool.setIconCls(record.get('header') ? me.headerCls : me.leafCls);
        me.lookupTool('group').setHidden(!record.get('groupable'));
        me.lookupTool('hide').setHidden(!record.get('hideable'));
    },
    privates: {
        groupedColumnCls: Ext.baseCSSPrefix + 'column-options-grouped',
        headerCls: Ext.baseCSSPrefix + 'column-options-folder',
        hiddenColumnCls: Ext.baseCSSPrefix + 'column-options-hidden',
        leafCls: Ext.baseCSSPrefix + 'column-options-leaf'
    }
});


Ext.define('Ext.layout.Fit', {
    extend: Ext.layout.Auto,
    alias: 'layout.fit',
    isFit: true,
    cls: Ext.baseCSSPrefix + 'layout-fit',
    itemCls: Ext.baseCSSPrefix + 'layout-fit-item'
});


Ext.define('Ext.grid.plugin.ViewOptions', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.gridviewoptions',
    config: {
        
        grid: null,
        
        sheetWidth: 320,
        
        sheet: {
            lazy: true,
            $value: {
                xtype: 'sheet',
                cls: Ext.baseCSSPrefix + 'gridviewoptions',
                items: [
                    {
                        docked: 'top',
                        xtype: 'titlebar',
                        title: 'Customize',
                        items: [
                            {
                                xtype: 'button',
                                text: 'Done',
                                ui: 'action',
                                align: 'right',
                                role: 'donebutton'
                            }
                        ]
                    }
                ],
                hidden: true,
                hideOnMaskTap: true,
                enter: 'right',
                exit: 'right',
                modal: true,
                right: 0,
                layout: 'fit',
                stretchY: true
            }
        },
        
        columnList: {
            lazy: true,
            $value: {
                xtype: 'nestedlist',
                title: 'Columns',
                clearSelectionOnListChange: false,
                listConfig: {
                    triggerEvent: null,
                    infinite: true,
                    mode: 'MULTI',
                    variableHeights: true,
                    plugins: {
                        sortablelist: {
                            source: {
                                handle: '.' + Ext.baseCSSPrefix + 'column-options-sortablehandle'
                            }
                        }
                    },
                    itemConfig: {
                        xtype: 'viewoptionslistitem'
                    },
                    itemTpl: '{text}'
                },
                store: {
                    type: 'tree',
                    fields: [
                        'id',
                        'text',
                        'dataIndex',
                        'header',
                        'hidden',
                        'hideable',
                        'grouped',
                        'groupable'
                    ],
                    root: {
                        text: 'Columns'
                    }
                }
            }
        },
        
        visibleIndicatorSelector: '.' + Ext.baseCSSPrefix + 'column-options-visibleindicator',
        
        groupIndicatorSelector: '.' + Ext.baseCSSPrefix + 'column-options-groupindicator'
    },
    init: function(grid) {
        this.setGrid(grid);
    },
    destroy: function() {
        this.destroyMembers('sheet', 'columnList');
        this.callParent();
    },
    updateGrid: function(grid, oldGrid) {
        if (oldGrid) {
            oldGrid.getHeaderContainer().renderElement.un({
                contextmenu: 'onHeaderContextMenu',
                longpress: 'onHeaderLongPress',
                scope: this
            });
            oldGrid.un({
                columnadd: 'onColumnAdd',
                columnmove: 'onColumnMove',
                columnremove: 'onColumnRemove',
                columnhide: 'onColumnHide',
                columnshow: 'onColumnShow',
                scope: this
            });
        }
        if (grid) {
            grid.getHeaderContainer().renderElement.on({
                contextmenu: 'onHeaderContextMenu',
                longpress: 'onHeaderLongPress',
                scope: this
            });
        }
    },
    applySheet: function(sheet) {
        if (sheet && !sheet.isComponent) {
            sheet = Ext.factory(sheet, Ext.Sheet);
        }
        return sheet;
    },
    applyColumnList: function(list) {
        if (list && !list.isComponent) {
            list = Ext.factory(list, Ext.Container);
        }
        return list;
    },
    updateColumnList: function(list) {
        if (list) {
            list.on({
                listchange: 'onListChange',
                scope: this
            });
            
            list.on('dragsort', 'onColumnDrag', this, {
                delegate: '> list'
            });
            this.attachTapListeners();
        }
    },
    updateSheet: function(sheet) {
        sheet.setWidth(this.getSheetWidth());
        sheet.add(this.getColumnList());
        sheet.on('hide', 'onSheetHide', this);
    },
    onDoneButtonTap: function() {
        this.getSheet().hide();
    },
    onColumnDrag: function(list, row, newIndex) {
        var column = Ext.getCmp(row.getRecord().get('id')),
            parent = column.getParent(),
            siblings = parent.getInnerItems(),
            i, ln, sibling;
        for (i = 0 , ln = newIndex; i < ln; i++) {
            sibling = siblings[i];
            if (!sibling.isHeaderGroup && sibling.getIgnore()) {
                newIndex += 1;
            }
        }
        this.isMoving = true;
        parent.insert(newIndex, column);
        this.isMoving = false;
    },
    attachTapListeners: function() {
        var activeList = this.getColumnList().getActiveItem();
        if (!activeList.hasAttachedTapListeners) {
            activeList.onBefore({
                childtap: 'onListChildTap',
                scope: this
            });
            activeList.hasAttachedTapListeners = true;
        }
    },
    onListChange: function(nestedList, list) {
        var store = list.getStore(),
            activeNode = store.getNode(),
            records = activeNode.childNodes,
            ln = records.length,
            i, column, record;
        for (i = 0; i < ln; i++) {
            record = records[i];
            column = Ext.getCmp(record.getId());
            record.set('hidden', column.isHidden());
        }
        this.attachTapListeners();
    },
    onListChildTap: function(list, location) {
        var me = this,
            handled = false,
            e = location.event;
        if (Ext.fly(e.target).is(me.getVisibleIndicatorSelector())) {
            me.onVisibleIndicatorTap(location.row, location.record);
            handled = true;
        } else if (Ext.fly(e.target).is(me.getGroupIndicatorSelector())) {
            me.onGroupIndicatorTap(location.row, location.record);
            handled = true;
        }
        return !handled;
    },
    onVisibleIndicatorTap: function(row, record) {
        var hidden = !record.get('hidden'),
            column = Ext.getCmp(record.get('id'));
        column.setHidden(hidden);
        record.set('hidden', hidden);
    },
    onGroupIndicatorTap: function(row, record) {
        var me = this,
            grouped = !record.get('grouped'),
            store = me.getGrid().getStore();
        
        this.getListRoot().cascade(function(node) {
            node.set('grouped', false);
        });
        if (grouped) {
            store.setGrouper({
                property: record.get('dataIndex')
            });
            record.set('grouped', true);
        } else {
            store.setGrouper(null);
        }
    },
    onColumnHide: function(headerContainer, column) {
        var nestedList = this.getColumnList(),
            activeList = nestedList.getActiveItem(),
            store = activeList.getStore(),
            record = store.getById(column.getId());
        if (record) {
            record.set('hidden', true);
        }
    },
    onColumnShow: function(headerContainer, column) {
        var nestedList = this.getColumnList(),
            activeList = nestedList.getActiveItem(),
            store = activeList.getStore(),
            record = store.getById(column.getId());
        if (record) {
            record.set('hidden', false);
        }
    },
    onColumnAdd: function(grid, column) {
        if (column.getIgnore() || this.isMoving) {
            return;
        }
        var me = this,
            nestedList = me.getColumnList(),
            mainHeaderCt = grid.getHeaderContainer(),
            header = column.getParent(),
            store = nestedList.getStore(),
            parentNode = store.getRoot(),
            isGridGrouped = grid.getGrouped(),
            grouper = grid.getStore().getGrouper(),
            dataIndex = column.getDataIndex(),
            data = {
                id: column.getId(),
                text: column.getText() || '\xa0',
                groupable: isGridGrouped && column.canGroup(),
                hidden: column.isHidden(),
                hideable: column.getHideable(),
                grouped: !!(isGridGrouped && grouper && grouper.getProperty() === dataIndex),
                dataIndex: column.getDataIndex(),
                leaf: true
            },
            idx, headerNode;
        if (header !== mainHeaderCt) {
            headerNode = parentNode.findChild('id', header.getId());
            if (!headerNode) {
                idx = header.getParent().indexOf(header);
                headerNode = parentNode.insertChild(idx, {
                    groupable: false,
                    header: true,
                    hidden: header.isHidden(),
                    id: header.getId(),
                    text: header.getText()
                });
            }
            idx = header.indexOf(column);
            parentNode = headerNode;
        } else {
            idx = mainHeaderCt.indexOf(column);
        }
        parentNode.insertChild(idx, data);
    },
    onColumnMove: function(headerContainer, column, header) {
        this.onColumnRemove(headerContainer, column);
        this.onColumnAdd(headerContainer, column, header);
    },
    onColumnRemove: function(headerContainer, column) {
        if (column.getIgnore() || this.isMoving) {
            return;
        }
        var root = this.getListRoot(),
            record = root.findChild('id', column.getId(), true);
        if (record) {
            record.parentNode.removeChild(record, true);
        }
    },
    onHeaderContextMenu: function(e) {
        
        e.preventDefault();
    },
    onHeaderLongPress: function(e) {
        if (!this.getSheet().isVisible()) {
            this.showViewOptions();
        }
    },
    hideViewOptions: function() {
        var me = this,
            sheet = me.getSheet();
        me.getGrid().getHeaderContainer().setSortable(me.cachedSortable);
        delete me.cachedSortable;
        sheet.hide();
    },
    onSheetHide: function() {
        this.hideViewOptions();
    },
    showViewOptions: function() {
        var me = this,
            sheet = me.getSheet(),
            header;
        me.setup();
        if (!sheet.isVisible()) {
            
            
            
            header = me.getGrid().getHeaderContainer();
            me.cachedSortable = header.getSortable();
            header.setSortable(false);
            me.updateListInfo();
            sheet.show();
        }
    },
    privates: {
        getListRoot: function() {
            return this.getColumnList().getStore().getRoot();
        },
        setup: function() {
            var me = this,
                grid = me.getGrid(),
                sheet, root;
            if (me.doneSetup) {
                return;
            }
            me.doneSetup = true;
            root = this.getListRoot();
            root.removeAll();
            grid.getColumns().forEach(function(leaf) {
                me.onColumnAdd(grid, leaf);
            });
            
            
            grid.on({
                columnadd: 'onColumnAdd',
                columnmove: 'onColumnMove',
                columnremove: 'onColumnRemove',
                columnhide: 'onColumnHide',
                columnshow: 'onColumnShow',
                scope: me
            });
            sheet = me.getSheet();
            sheet.down('button[role=donebutton]').on({
                tap: 'onDoneButtonTap',
                scope: me
            });
        },
        updateListInfo: function() {
            var grid = this.getGrid(),
                store = grid.getStore(),
                grouper = store.getGrouper(),
                isGridGrouped = grid.getGrouped(),
                grouperProp = grouper && grouper.getProperty();
            this.getColumnList().getStore().getRoot().cascade(function(node) {
                var grouped = false,
                    dataIndex;
                if (isGridGrouped) {
                    dataIndex = node.get('dataIndex');
                    grouped = dataIndex && dataIndex === grouperProp;
                }
                node.set('grouped', dataIndex && grouped);
            });
        }
    }
});


Ext.define('Ext.layout.Center', {
    extend: Ext.layout.Auto,
    alias: 'layout.center',
    cls: Ext.baseCSSPrefix + 'layout-center',
    itemCls: Ext.baseCSSPrefix + 'layout-center-item'
});


Ext.define('Ext.layout.Float', {
    extend: Ext.layout.Auto,
    alias: 'layout.float',
    config: {
        direction: 'left'
    },
    cls: Ext.baseCSSPrefix + 'layout-float',
    itemCls: Ext.baseCSSPrefix + 'layout-float-item',
    updateDirection: function(direction, oldDirection) {
        var prefix = 'direction-';
        this.getContainer().getRenderTarget().swapCls(prefix + direction, prefix + oldDirection);
    }
});


Ext.define('Ext.layout.Form', {
    extend: Ext.layout.Auto,
    alias: 'layout.form',
    isFormLayout: true,
    cls: Ext.baseCSSPrefix + 'layout-form',
    itemCls: Ext.baseCSSPrefix + 'layout-form-item',
    config: {
        
        itemSpacing: null,
        
        labelWidth: null
    },
    hasLabelWidthCls: Ext.baseCSSPrefix + 'has-label-width',
    
    onItemAdd: function(item, index) {
        if (item.isInner && !item.isFormField) {
            Ext.raise("Cannot add item to container.  Only Ext.field.Field instances are allowed as inner items in a form layout.");
        }
        this.callParent([
            item,
            index
        ]);
    },
    
    updateContainer: function(container, oldContainer) {
        var colGroup;
        colGroup = this.columnGroupElement = container.getRenderTarget().appendChild({
            cls: Ext.baseCSSPrefix + 'colgroup-el',
            cn: [
                {
                    cls: Ext.baseCSSPrefix + 'label-column-el'
                },
                {
                    cls: Ext.baseCSSPrefix + 'body-column-el'
                }
            ]
        });
        this.labelColumnElement = colGroup.first();
        this.callParent([
            container,
            oldContainer
        ]);
    },
    updateItemSpacing: function(itemSpacing) {
        this.getContainer().getRenderTarget().setStyle('border-spacing', Ext.Element.addUnits(itemSpacing));
    },
    updateLabelWidth: function(labelWidth) {
        this.labelColumnElement.setWidth(labelWidth);
        this.getContainer().getRenderTarget().toggleCls(this.hasLabelWidthCls, labelWidth != null && labelWidth !== 'auto');
    }
});


Ext.define('Ext.layout.overflow.Scroller', {
    alias: 'layout.overflow.scroller',
    mixins: [
        Ext.mixin.Factoryable
    ],
    config: {
        
        arrows: Ext.os.is.Desktop,
        
        backwardTool: {
            xtype: 'tool',
            ui: 'boxscroller',
            focusable: false
        },
        
        animation: true,
        
        forwardTool: {
            xtype: 'tool',
            ui: 'boxscroller',
            focusable: false
        },
        
        toolAlign: 'stretch',
        
        increment: 'item',
        
        repeatInterval: 500,
        
        mouseWheel: true,
        
        wheelIncrement: 10,
        
        vertical: null,
        
        owner: null,
        
        container: null,
        
        wrap: true
    },
    cls: Ext.baseCSSPrefix + 'boxscroller',
    bodyCls: Ext.baseCSSPrefix + 'boxscroller-body',
    toolAlignCls: {
        start: Ext.baseCSSPrefix + 'tool-align-start',
        end: Ext.baseCSSPrefix + 'tool-align-end',
        center: Ext.baseCSSPrefix + 'tool-align-center',
        stretch: Ext.baseCSSPrefix + 'tool-align-stretch'
    },
    orientMap: {
        "false": {
            cls: Ext.baseCSSPrefix + 'horizontal',
            crossSize: 'height',
            getSize: 'getWidth',
            setCrossSize: 'setHeight',
            measureCross: 'h',
            scrollSize: 'scrollWidth',
            scrollbarMargin: 'margin-bottom',
            forwardTool: 'scroll-right',
            backwardTool: 'scroll-left',
            startPad: 'l',
            endPad: 'r',
            posProp: 'x'
        },
        "true": {
            cls: Ext.baseCSSPrefix + 'vertical',
            crossSize: 'width',
            getSize: 'getHeight',
            setCrossSize: 'setWidth',
            measureCross: 'w',
            scrollSize: 'scrollHeight',
            scrollbarMargin: 'margin-right',
            forwardTool: 'scroll-down',
            backwardTool: 'scroll-up',
            startPad: 't',
            endPad: 'b',
            posProp: 'y'
        }
    },
    constructor: function(config) {
        var me = this;
        me.repeaters = [];
        me.initConfig(config);
        me.syncMouseWheel();
        
        if (me.getOwner().getPack() !== 'start') {
            Ext.raise('Overflow scroller requires box layout to be packed \'start\'');
        }
        if (me.getContainer().getAutoSize() === false) {
            Ext.raise('Overflow scroller is not compatible with autoSize: false');
        }
        
        me.getContainer().$onScrollerContainerVisible = me.sync.bind(me);
    },
    applyWrap: function() {
        var me = this,
            container = me.getContainer(),
            containerBody = me.getContainer().bodyElement,
            wrap = Ext.Element.create({
                cls: me.cls,
                children: [
                    {
                        cls: me.bodyCls
                    }
                ]
            }),
            uiCls = 'boxscroller',
            bodyEl = wrap.first();
        
        
        container.boxScrollerElement = wrap;
        
        
        container.uiReferences.boxScrollerElement = uiCls;
        container.initUiReference('boxScrollerElement', uiCls);
        if (!container.isConfiguring) {
            
            
            
            container.syncUiCls({
                boxScrollerElement: 'boxscroller'
            });
        }
        this._body = bodyEl;
        wrap.insertBefore(containerBody);
        bodyEl.appendChild(containerBody);
        return wrap;
    },
    getBody: function() {
        var body = this._body;
        if (!body) {
            this.getWrap();
        }
        return this._body;
    },
    onActiveItemChange: function(render, item) {
        var me = this;
        if (me.getContainer().rendered) {
            me.ensureVisible(item);
        }
    },
    onActiveTabChange: function(render, tab) {
        var me = this;
        if (me.getContainer().rendered) {
            me.ensureVisible(tab);
        }
    },
    ensureVisible: function(item, animation) {
        var me = this;
        if (animation === undefined) {
            animation = me.getAnimation();
        }
        me.getOwner().ensureVisible(item, {
            animation: animation
        });
    },
    createForwardTool: function(config) {
        var me = this;
        return Ext.apply({
            $initParent: me.getContainer(),
            hidden: true,
            preventRefocus: true
        }, config);
    },
    createBackwardTool: function(config) {
        var me = this;
        return Ext.apply({
            $initParent: me.getContainer(),
            hidden: true,
            preventRefocus: true
        }, config);
    },
    scrollToItemOffset: function(offset, page) {
        var animate = this.getAnimation();
        this.getOwner().ensureVisible({
            offset: offset,
            scroll: page ? 'max' : 'min',
            animation: animate
        });
    },
    applyVertical: function(vertical) {
        return !!vertical;
    },
    updateVertical: function(vertical) {
        var me = this,
            orientMap = me.orientMap[vertical];
        me.getWrap().replaceCls(me.orientMap[!vertical].cls, orientMap.cls);
        if (me.getArrows()) {
            me.getForwardTool().setType(orientMap.forwardTool);
            me.getBackwardTool().setType(orientMap.backwardTool);
        }
        if (!me.isConfiguring) {
            me.syncContainerScrollable();
            me.syncMouseWheel();
        }
    },
    applyForwardTool: function(tool, oldTool) {
        var ct = this.getContainer();
        tool = Ext.updateWidget(oldTool, tool, this, 'createForwardTool');
        delete tool.$initParent;
        tool.ownerCmp = ct;
        tool.doInheritUi();
        tool.addUi('boxscroller-' + ct.xtype);
        return tool;
    },
    applyBackwardTool: function(tool, oldTool) {
        var ct = this.getContainer();
        tool = Ext.updateWidget(oldTool, tool, this, 'createBackwardTool');
        delete tool.$initParent;
        tool.ownerCmp = ct;
        tool.doInheritUi();
        tool.addUi('boxscroller-' + ct.xtype);
        return tool;
    },
    updateArrows: function(arrows) {
        var me = this,
            container = me.getContainer(),
            el = me.getWrap(),
            forward = me.getForwardTool(),
            backward = me.getBackwardTool();
        el.insertFirst(backward.el);
        el.append(forward.el);
        me.addClickListener(forward, me.onForwardClick);
        me.addClickListener(backward, me.onBackwardClick);
        if (container.rendered) {
            me.sync();
        } else {
            container.whenVisible('$onScrollerContainerVisible');
            container.on({
                painted: 'sync',
                scope: me,
                single: true
            });
        }
    },
    updateMouseWheel: function() {
        if (!this.isConfiguring) {
            this.syncMouseWheel();
        }
    },
    updateOwner: function(owner) {
        var me = this,
            container = owner.getContainer();
        container.on({
            activeitemchange: 'onActiveItemChange',
            activetabchange: 'onActiveTabChange',
            
            scope: me
        });
        Ext.override(container, {
            getRefItems: function(deep) {
                var refItems = this.callParent([
                        deep
                    ]),
                    
                    forward = me.getForwardTool(),
                    
                    backward = me.getBackwardTool();
                
                refItems.push(forward, backward);
                return refItems;
            }
        });
        me.syncContainerScrollable();
        container.getScrollable().on('scrollend', 'sync', me);
        container.on('resize', 'sync', me);
        container.bodyElement.on('resize', 'sync', me);
    },
    updateToolAlign: function(toolAlign, oldToolAlign) {
        var map = this.toolAlignCls;
        this.getWrap().replaceCls(map[oldToolAlign], map[toolAlign]);
    },
    getContainer: function() {
        return this.getOwner().getContainer();
    },
    destroy: function() {
        var me = this;
        Ext.destroy(me.repeaters);
        me.getForwardTool().destroy();
        me.getBackwardTool().destroy();
        me.callParent();
    },
    privates: {
        addClickListener: function(tool, clickFn) {
            var me = this,
                repeat = me.getRepeatInterval(),
                repeater;
            if (repeat) {
                repeater = new Ext.util.ClickRepeater(Ext.apply({
                    target: tool,
                    preventDefault: true,
                    listeners: {
                        click: clickFn,
                        scope: me
                    }
                }, repeat));
                me.repeaters.push(repeater);
            } else {
                tool.on({
                    click: clickFn,
                    scope: me
                });
            }
        },
        doMoveFromClick: function(offset) {
            var me = this,
                scrollable = me.getContainer().getScrollable(),
                animate = me.getAnimation(),
                isVertical = me.getVertical(),
                increment = me.getIncrement(),
                dx, dy;
            if (isNaN(increment)) {
                me.scrollToItemOffset(offset, increment === 'page');
            } else {
                dx = !isVertical ? increment : 0;
                dy = isVertical ? increment : 0;
                scrollable.scrollBy(dx * offset, dy * offset, animate);
            }
        },
        onBackwardClick: function() {
            this.doMoveFromClick(-1);
        },
        onForwardClick: function() {
            this.doMoveFromClick(1);
        },
        onMouseWheel: function(e) {
            var me = this,
                scrollable = me.getContainer().getScrollable(),
                isVertical = me.getVertical(),
                delta = e.getWheelDelta() * me.getWheelIncrement() * -1,
                dx = !isVertical ? delta : 0,
                dy = isVertical ? delta : 0;
            scrollable.scrollBy(dx, dy);
        },
        syncMouseWheel: function() {
            var me = this,
                target = me.getContainer().getScrollerTarget(),
                fn = me.getMouseWheel() && !me.getVertical() ? 'on' : 'un';
            target[fn]('wheel', 'onMouseWheel', me);
        },
        syncContainerScrollable: function() {
            var me = this,
                isVertical = me.getVertical(),
                container = me.getContainer();
            container.setScrollable({
                element: me.getBody(),
                x: !isVertical,
                y: isVertical
            });
        },
        sync: function() {
            var me = this,
                container = me.getContainer(),
                target = container.getScrollerTarget(),
                orientMap = me.orientMap[me.getVertical()],
                elementSize = container.el[orientMap.getSize](),
                targetSize = target.dom[orientMap.scrollSize],
                forwardTool = me.getForwardTool(),
                backwardTool = me.getBackwardTool(),
                scrollbarSize = Ext.getScrollbarSize()[orientMap.crossSize],
                hasOverflow = targetSize > elementSize,
                posProp = orientMap.posProp,
                scrollable, maxPos, pos;
            if (me.getArrows() && hasOverflow) {
                forwardTool.show();
                backwardTool.show();
                scrollable = container.getScrollable();
                maxPos = scrollable.getMaxPosition()[posProp];
                pos = scrollable.getPosition()[posProp];
                forwardTool.setDisabled(pos + target.getPadding(orientMap.endPad) >= maxPos);
                backwardTool.setDisabled(pos <= target.getPadding(orientMap.startPad));
            } else {
                forwardTool.hide();
                backwardTool.hide();
            }
            
            
            
            
            
            container.bodyElement.setStyle(orientMap.scrollbarMargin, hasOverflow ? (-scrollbarSize + 'px') : 0);
            
            
            me.getBody()[orientMap.setCrossSize](container.bodyElement.measure(orientMap.measureCross));
        }
    }
});


Ext.define('Ext.list.Location', {
    extend: Ext.dataview.Location,
    
    isListLocation: true,
    
    dataItem: false,
    
    footer: false,
    
    group: null,
    
    header: false,
    attach: function(source) {
        var me = this,
            item;
        me.callParent([
            source
        ]);
        item = me.item;
        if (item) {
            me.header = item.$dataItem === 'header';
            me.footer = item.$dataItem === 'footer';
            if (me.header || me.footer) {
                me.group = me.getGroup();
            } else {
                me.dataItem = true;
            }
        }
    },
    clone: function() {
        var me = this,
            ret = me.callParent();
        ret.dataItem = me.dataItem;
        ret.footer = me.footer;
        ret.group = me.group;
        ret.header = me.header;
        return ret;
    },
    equals: function(other) {
        var me = this;
        if (other && other.isListLocation && other.view === me.view) {
            if (me.sourceElement) {
                return other.sourceElement === me.sourceElement;
            }
            
            
            
            
            return me.recordIndex !== other.recordIndex;
        }
        return false;
    }
});


Ext.define('Ext.menu.Separator', {
    extend: Ext.Component,
    alias: 'widget.menuseparator',
    isMenuSeparator: true,
    focusable: false,
    classCls: Ext.baseCSSPrefix + 'menuseparator',
    ariaRole: 'separator'
});


Ext.define('Ext.mixin.Progressable', {
    extend: Ext.Mixin,
    isProgressable: true,
    mixinConfig: {
        id: 'progressable'
    },
    config: {
        
        minProgressInput: 0,
        
        maxProgressInput: 1,
        
        minProgressOutput: 0,
        
        maxProgressOutput: 100,
        
        dynamic: true,
        
        state: null
    },
    
    _progressActive: false,
    _progress: 0,
    _rawProgress: 0,
    onStartProgress: Ext.emptyFn,
    onUpdateProgress: Ext.emptyFn,
    onEndProgress: Ext.emptyFn,
    startProgress: function() {
        if (!this._progressActive) {
            this._progressActive = true;
            this.onStartProgress();
            this.updateProgress(this.getMinProgressInput());
        }
    },
    updateProgress: function(value, state) {
        if (state && state != this.getState())  {
            this.setState(state);
        }
        
        if (value > this.getMaxProgressInput())  {
            value = this.getMaxProgressInput();
        }
        
        if (value < this.getMinProgressInput())  {
            value = this.getMinProgressInput();
        }
        
        var mappedValue = this.mapValues(value, this.getMinProgressInput(), this.getMaxProgressInput(), this.getMinProgressOutput(), this.getMaxProgressOutput());
        this._progress = mappedValue;
        this._rawProgress = value;
        if (this.getDynamic()) {
            this.onUpdateProgress(mappedValue);
        }
    },
    endProgress: function() {
        if (this._progressActive) {
            this._progressActive = false;
            this.updateProgress(this.getMaxProgressInput());
            this.onEndProgress();
        }
    },
    mapValues: function(value, inputMin, inputMax, outputMin, outputMax) {
        return (value - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin;
    },
    setProgress: function(value) {
        this.updateProgress(value);
    },
    getProgress: function() {
        return this._progress;
    },
    getRawProgress: function() {
        return this._rawProgress;
    }
});


Ext.define('Ext.navigation.Bar', {
    extend: Ext.TitleBar,
    
    isToolbar: true,
    config: {
        
        cls: Ext.baseCSSPrefix + 'navigation-bar',
        
        ui: 'dark',
        
        title: null,
        
        defaultType: 'button',
        
        layout: {
            type: 'hbox'
        },
        
        
        defaultBackButtonText: 'Back',
        
        animation: {
            duration: 300
        },
        
        useTitleForBackButtonText: null,
        
        view: null,
        
        android2Transforms: false,
        
        backButton: {
            align: 'left',
            ui: 'back',
            hidden: true
        }
    },
    
    baseCls: Ext.baseCSSPrefix + 'toolbar',
    
    constructor: function(config) {
        config = config || {};
        if (!config.items) {
            config.items = [];
        }
        this.backButtonStack = [];
        this.activeAnimations = [];
        this.callParent([
            config
        ]);
    },
    
    applyBackButton: function(config) {
        return Ext.factory(config, Ext.Button, this.getBackButton());
    },
    
    updateBackButton: function(newBackButton, oldBackButton) {
        if (oldBackButton) {
            this.remove(oldBackButton);
        }
        if (newBackButton) {
            this.add(newBackButton);
            newBackButton.on({
                scope: this,
                tap: this.onBackButtonTap
            });
        }
    },
    onBackButtonTap: function() {
        this.fireEvent('back', this);
    },
    
    updateView: function(newView) {
        var me = this,
            backButton, innerItems, i, backButtonText, item, title, titleText;
        
        me.getItems();
        backButton = me.getBackButton();
        if (newView) {
            
            innerItems = newView.getInnerItems();
            for (i = 0; i < innerItems.length; i++) {
                item = innerItems[i];
                title = (item.getTitle) ? item.getTitle() : item.config.title;
                me.backButtonStack.push(title || '&nbsp;');
            }
            titleText = me.getTitleText();
            if (titleText === undefined) {
                titleText = '';
            }
            me.setTitle(titleText);
            backButtonText = me.getBackButtonText();
            if (backButtonText) {
                backButton.setText(backButtonText);
                backButton.show();
            }
        }
    },
    
    onViewAdd: function(view, item) {
        var me = this,
            backButtonStack = me.backButtonStack,
            hasPrevious, title;
        me.endAnimation();
        title = (item.getTitle) ? item.getTitle() : item.config.title;
        backButtonStack.push(title || '&nbsp;');
        hasPrevious = backButtonStack.length > 1;
        me.doChangeView(view, hasPrevious, false);
    },
    
    onViewRemove: function(view) {
        var me = this,
            backButtonStack = me.backButtonStack,
            hasPrevious;
        me.endAnimation();
        backButtonStack.pop();
        hasPrevious = backButtonStack.length > 1;
        me.doChangeView(view, hasPrevious, true);
    },
    
    doChangeView: function(view, hasPrevious, reverse) {
        var me = this,
            leftBox = me.leftBox,
            leftBoxElement = leftBox.element,
            titleComponent = me.titleComponent,
            titleElement = titleComponent.element,
            backButton = me.getBackButton(),
            titleText = me.getTitleText(),
            backButtonText = me.getBackButtonText(),
            animation = me.getAnimation() && view.getLayout().getAnimation(),
            animated = animation && animation.isAnimation && view.isPainted(),
            properties, leftGhost, titleGhost, leftProps, titleProps;
        if (animated) {
            leftGhost = me.createProxy(leftBox.element);
            leftBoxElement.setStyle('opacity', '0');
            backButton.setText(backButtonText);
            backButton[hasPrevious ? 'show' : 'hide']();
            titleGhost = me.createProxy(titleComponent.element.getParent());
            titleElement.setStyle('opacity', '0');
            me.setTitle(titleText);
            properties = me.measureView(leftGhost, titleGhost, reverse);
            leftProps = properties.left;
            titleProps = properties.title;
            me.isAnimating = true;
            me.animate(leftBoxElement, leftProps.element);
            me.animate(titleElement, titleProps.element, function() {
                titleElement.setLeft(properties.titleLeft);
                me.isAnimating = false;
                me.refreshTitlePosition();
            });
            me.animate(leftGhost.ghost, leftProps.ghost);
            me.animate(titleGhost.ghost, titleProps.ghost, function() {
                leftGhost.ghost.destroy();
                titleGhost.ghost.destroy();
            });
        } else {
            if (hasPrevious) {
                backButton.setText(backButtonText);
                backButton.show();
            } else {
                backButton.hide();
            }
            me.setTitle(titleText);
        }
    },
    
    measureView: function(oldLeft, oldTitle, reverse) {
        var me = this,
            barElement = me.element,
            newLeftElement = me.leftBox.element,
            titleElement = me.titleComponent.element,
            minOffset = Math.min(barElement.getWidth() / 3, 200),
            newLeftWidth = newLeftElement.getWidth(),
            barX = barElement.getX(),
            barWidth = barElement.getWidth(),
            titleX = titleElement.getX(),
            titleLeft = titleElement.getLeft(true),
            titleWidth = titleElement.getWidth(),
            oldLeftX = oldLeft.x,
            oldLeftWidth = oldLeft.width,
            oldLeftLeft = oldLeft.left,
            newOffset, oldOffset, leftAnims, titleAnims, omega, theta;
        theta = barX - oldLeftX - oldLeftWidth;
        if (reverse) {
            newOffset = theta;
            oldOffset = Math.min(titleX - oldLeftWidth, minOffset);
        } else {
            oldOffset = theta;
            newOffset = Math.min(titleX - barX, minOffset);
        }
        leftAnims = {
            element: {
                from: {
                    transform: {
                        translateX: newOffset
                    },
                    opacity: 0
                },
                to: {
                    transform: {
                        translateX: 0
                    },
                    opacity: 1
                }
            },
            ghost: {
                to: {
                    transform: {
                        translateX: oldOffset
                    },
                    opacity: 0
                }
            }
        };
        theta = barX - titleX + newLeftWidth;
        if ((oldLeftLeft + titleWidth) > titleX) {
            omega = barX - titleX - titleWidth;
        }
        if (reverse) {
            titleElement.setLeft(0);
            oldOffset = barX + barWidth - titleX - titleWidth;
            if (omega !== undefined) {
                newOffset = omega;
            } else {
                newOffset = theta;
            }
        } else {
            newOffset = barX + barWidth - titleX - titleWidth;
            if (omega !== undefined) {
                oldOffset = omega;
            } else {
                oldOffset = theta;
            }
            newOffset = Math.max(titleLeft, newOffset);
        }
        titleAnims = {
            element: {
                from: {
                    transform: {
                        translateX: newOffset
                    },
                    opacity: 0
                },
                to: {
                    transform: {
                        translateX: titleLeft
                    },
                    opacity: 1
                }
            },
            ghost: {
                to: {
                    transform: {
                        translateX: oldOffset
                    },
                    opacity: 0
                }
            }
        };
        return {
            left: leftAnims,
            title: titleAnims,
            titleLeft: titleLeft
        };
    },
    
    animate: function(element, config, callback) {
        var me = this,
            animation;
        
        element.setLeft(0);
        config = Ext.apply(config, {
            element: element,
            easing: 'ease-in-out',
            duration: me.getAnimation().duration || 250,
            preserveEndState: true
        });
        animation = new Ext.fx.Animation(config);
        animation.on('animationend', function() {
            if (callback) {
                callback.call(me);
            }
        }, me);
        Ext.Animator.run(animation);
        me.activeAnimations.push(animation);
    },
    endAnimation: function() {
        var activeAnimations = this.activeAnimations,
            animation, i, ln;
        if (activeAnimations) {
            ln = activeAnimations.length;
            for (i = 0; i < ln; i++) {
                animation = activeAnimations[i];
                if (animation.isAnimating) {
                    animation.stopAnimation();
                } else {
                    animation.destroy();
                }
            }
            this.activeAnimations = [];
        }
    },
    refreshTitlePosition: function() {
        if (!this.isAnimating) {
            this.callParent();
        }
    },
    
    getBackButtonText: function() {
        var text = this.backButtonStack[this.backButtonStack.length - 2],
            useTitleForBackButtonText = this.getUseTitleForBackButtonText();
        if (!useTitleForBackButtonText) {
            if (text) {
                text = this.getDefaultBackButtonText();
            }
        }
        return text;
    },
    
    getTitleText: function() {
        return this.backButtonStack[this.backButtonStack.length - 1];
    },
    
    beforePop: function(count) {
        count--;
        for (var i = 0; i < count; i++) {
            this.backButtonStack.pop();
        }
    },
    
    updateHidden: function(hidden) {
        if (!hidden) {
            this.element.setStyle({
                position: 'relative',
                top: 'auto',
                left: 'auto',
                width: 'auto'
            });
        } else {
            this.element.setStyle({
                position: 'absolute',
                top: '-1000px',
                left: '-1000px',
                width: this.element.getWidth() + 'px'
            });
        }
    },
    
    createProxy: function(element) {
        var ghost, x, y, left, width;
        ghost = element.dom.cloneNode(true);
        ghost.id = element.id + '-proxy';
        
        element.getParent().dom.appendChild(ghost);
        
        ghost = Ext.get(ghost);
        x = element.getX();
        y = element.getY();
        left = element.getLeft(true);
        width = element.getWidth();
        ghost.setStyle('position', 'absolute');
        ghost.setX(x);
        ghost.setY(y);
        ghost.setHeight(element.getHeight());
        ghost.setWidth(width);
        return {
            x: x,
            y: y,
            left: left,
            width: width,
            ghost: ghost
        };
    }
});


Ext.define('Ext.navigation.View', {
    extend: Ext.Container,
    alternateClassName: 'Ext.NavigationView',
    xtype: 'navigationview',
    config: {
        
        navigationBar: {
            docked: 'top'
        },
        
        defaultBackButtonText: 'Back',
        
        useTitleForBackButtonText: false,
        
        
        layout: {
            type: 'card',
            animation: {
                duration: 300,
                easing: 'ease-out',
                type: 'slide',
                direction: 'left'
            }
        }
    },
    baseCls: Ext.baseCSSPrefix + 'navigationview',
    
    
    
    
    initialize: function() {
        var me = this,
            navBar = me.getNavigationBar();
        me.callParent();
        
        if (navBar) {
            navBar.on({
                back: me.onBackButtonTap,
                scope: me
            });
            me.relayEvents(navBar, 'rightbuttontap');
            me.relayEvents(me, {
                add: 'push',
                remove: 'pop'
            });
        }
        
        var layout = me.getLayout();
        if (layout && !layout.isCard) {
            Ext.Logger.error('The base layout for a NavigationView must always be a Card Layout');
        }
    },
    
    
    onBackButtonTap: function() {
        this.pop();
        this.fireEvent('back', this);
    },
    
    push: function(view) {
        return this.add(view);
    },
    
    pop: function(count) {
        if (this.beforePop(count)) {
            return this.doPop();
        }
    },
    
    beforePop: function(count) {
        var me = this,
            innerItems = me.getInnerItems(),
            last, i, ln, toRemove;
        if (Ext.isString(count) || Ext.isObject(count)) {
            last = innerItems.length - 1;
            for (i = last; i >= 0; i--) {
                if ((Ext.isString(count) && Ext.ComponentQuery.is(innerItems[i], count)) || (Ext.isObject(count) && count == innerItems[i])) {
                    count = last - i;
                    break;
                }
            }
            if (!Ext.isNumber(count)) {
                return false;
            }
        }
        ln = innerItems.length;
        
        if (!Ext.isNumber(count) || count < 1) {
            count = 1;
        }
        
        count = Math.min(count, ln - 1);
        if (count) {
            
            me.getNavigationBar().beforePop(count);
            
            toRemove = innerItems.splice(-count, count - 1);
            for (i = 0; i < toRemove.length; i++) {
                this.remove(toRemove[i]);
            }
            return true;
        }
        return false;
    },
    
    doPop: function() {
        var me = this,
            innerItems = this.getInnerItems();
        
        me.remove(innerItems[innerItems.length - 1]);
        
        if (innerItems.length < 3 && this.$backButton) {
            this.$backButton.hide();
        }
        
        if (this.$titleContainer) {
            
            if (!this.$titleContainer.setTitle) {
                Ext.Logger.error([
                    'You have selected to display a title in a component that does not ',
                    'support titles in NavigationView. Please remove the `title` configuration from your ',
                    'NavigationView item, or change it to a component that has a `setTitle` method.'
                ].join(''));
            }
            
            var item = innerItems[innerItems.length - 2];
            this.$titleContainer.setTitle((item.getTitle) ? item.getTitle() : item.config.title);
        }
        return this.getActiveItem();
    },
    
    getPreviousItem: function() {
        var innerItems = this.getInnerItems();
        return innerItems[innerItems.length - 2];
    },
    
    updateUseTitleForBackButtonText: function(useTitleForBackButtonText) {
        var navigationBar = this.getNavigationBar();
        if (navigationBar) {
            navigationBar.setUseTitleForBackButtonText(useTitleForBackButtonText);
        }
    },
    
    updateDefaultBackButtonText: function(defaultBackButtonText) {
        var navigationBar = this.getNavigationBar();
        if (navigationBar) {
            navigationBar.setDefaultBackButtonText(defaultBackButtonText);
        }
    },
    
    onBackButtonContainerAdd: function(toolbar, item) {
        item.on({
            scope: this,
            show: this.refreshBackButtonContainer,
            hide: this.refreshBackButtonContainer
        });
        this.refreshBackButtonContainer();
    },
    
    onBackButtonContainerRemove: function(toolbar, item) {
        item.un({
            scope: this,
            show: this.refreshBackButtonContainer,
            hide: this.refreshBackButtonContainer
        });
        this.refreshBackButtonContainer();
    },
    
    refreshBackButtonContainer: function() {
        if (!this.$backButtonContainer) {
            return;
        }
        var i = 0,
            backButtonContainer = this.$backButtonContainer,
            items = backButtonContainer.items,
            item;
        for (; i < items.length; i++) {
            item = items.get(i);
            if (!item.isHidden()) {
                this.$backButtonContainer.show();
                return;
            }
        }
        this.$backButtonContainer.hide();
    },
    
    applyNavigationBar: function(config) {
        var me = this;
        if (!config) {
            config = {
                hidden: true,
                docked: 'top'
            };
        }
        
        
        me.getItems();
        if (config.title) {
            delete config.title;
            
            Ext.Logger.warn("Ext.navigation.View: The 'navigationBar' configuration does not accept a 'title' property. You " + "set the title of the navigationBar by giving this navigation view's children a 'title' property.");
        }
        
        config.view = me;
        config.useTitleForBackButtonText = me.getUseTitleForBackButtonText();
        
        if (config.splitNavigation) {
            me.$titleContainer = me.add({
                docked: 'top',
                xtype: 'titlebar',
                ui: 'light',
                title: me.$currentTitle || ''
            });
            var containerConfig = (config.splitNavigation === true) ? {} : config.splitNavigation;
            me.$backButtonContainer = me.add({
                xtype: 'toolbar',
                docked: 'bottom',
                hidden: true
            });
            
            
            me.$backButtonContainer.on({
                scope: me,
                add: me.onBackButtonContainerAdd,
                remove: me.onBackButtonContainerRemove
            });
            me.$backButton = me.$backButtonContainer.add({
                xtype: 'button',
                text: 'Back',
                hidden: true,
                ui: 'back'
            });
            
            if (config.items) {
                me.$backButtonContainer.add(config.items);
            }
            
            if (containerConfig.items) {
                me.$titleContainer.add(containerConfig.items);
            }
            me.$backButton.on({
                scope: me,
                tap: me.onBackButtonTap
            });
            config = {
                hidden: true,
                docked: 'top'
            };
        }
        return Ext.factory(config, Ext.navigation.Bar, this.getNavigationBar());
    },
    
    updateNavigationBar: function(newNavigationBar, oldNavigationBar) {
        if (oldNavigationBar) {
            this.remove(oldNavigationBar, true);
        }
        if (newNavigationBar) {
            this.add(newNavigationBar);
        }
    },
    
    applyActiveItem: function(activeItem, currentActiveItem) {
        var me = this,
            innerItems = me.getInnerItems();
        
        me.getItems();
        
        if (!me.initialized) {
            activeItem = innerItems.length - 1;
        }
        return this.callParent([
            activeItem,
            currentActiveItem
        ]);
    },
    doResetActiveItem: function(innerIndex) {
        var me = this,
            innerItems = me.getInnerItems(),
            animation = me.getLayout().getAnimation();
        if (innerIndex > 0) {
            if (animation && animation.isAnimation) {
                animation.setReverse(true);
            }
            me.setActiveItem(innerIndex - 1);
            me.getNavigationBar().onViewRemove(me, innerItems[innerIndex], innerIndex);
        }
    },
    
    doRemove: function() {
        var animation = this.getLayout().getAnimation();
        if (animation && animation.isAnimation) {
            animation.setReverse(false);
        }
        this.callParent(arguments);
    },
    
    onItemAdd: function(item, index) {
        var me = this,
            initialized = me.initialized,
            navigationBar;
        
        if (item && item.getDocked() && item.config.title === true) {
            me.$titleContainer = item;
        }
        me.doItemLayoutAdd(item, index);
        if (initialized && item.isInnerItem()) {
            me.setActiveItem(item);
            navigationBar = this.getNavigationBar();
            if (navigationBar) {
                this.getNavigationBar().onViewAdd(me, item, index);
            }
            
            if (me.$backButtonContainer) {
                me.$backButton.show();
            }
        }
        if (item && item.isInnerItem()) {
            
            me.updateTitleContainerTitle((item.getTitle) ? item.getTitle() : item.config.title);
        }
        if (initialized) {
            me.fireEvent('add', me, item, index);
        }
    },
    
    updateTitleContainerTitle: function(title) {
        if (this.$titleContainer) {
            
            if (!this.$titleContainer.setTitle) {
                Ext.Logger.error([
                    'You have selected to display a title in a component that does not ',
                    'support titles in NavigationView. Please remove the `title` configuration from your ',
                    'NavigationView item, or change it to a component that has a `setTitle` method.'
                ].join(''));
            }
            
            this.$titleContainer.setTitle(title);
        } else {
            this.$currentTitle = title;
        }
    },
    
    reset: function() {
        return this.pop(this.getInnerItems().length);
    }
});


Ext.define('Ext.panel.Collapser', {
    config: {
        
        animation: {
            duration: 250
        },
        
        collapseAnimation: null,
        
        collapsed: false,
        
        collapseToolText: 'Collapse panel',
        
        direction: 'top',
        
        drawer: {
            xtype: 'panel',
            border: true,
            top: 0,
            left: 0,
            cls: Ext.baseCSSPrefix + 'drawer'
        },
        
        drawerHideDelay: 500,
        
        dynamic: null,
        
        expandAnimation: null,
        
        expandToolText: 'Expand panel',
        
        target: null,
        
        tool: {
            xtype: 'tool',
            weight: 900
        },
        
        useDrawer: true
    },
    constructor: function(config) {
        this.initConfig(config);
    },
    initialize: function() {
        var me = this;
        me.rendered = true;
        me.ensureCollapseTool();
        me.initialized = true;
        if (me.getCollapsed()) {
            me.doExpandCollapse(true, true);
        }
        me.setupDrawerListeners();
    },
    destroy: function() {
        var me = this,
            active = me.activeOperation,
            task = me.drawerTask;
        me.destroying = true;
        if (task) {
            task.cancel();
        }
        if (active) {
            
            active.anim.end();
        }
        if (!me.getTarget().destroying) {
            me.reattachBodyWrap();
        }
        Ext.destroy(me.drawerHeaderListener, me.drawerListeners, me.drawer, me.collapsibleTool);
        me.destroying = false;
        me.callParent();
    },
    
    collapse: function(animation) {
        return this.toggleCollapsed(true, animation);
    },
    
    expand: function(animation) {
        return this.toggleCollapsed(false, animation);
    },
    
    hideDrawer: function(animation) {
        var me = this,
            drawer = me.drawer,
            ret;
        if (me.isSliding || !me.getCollapsed() || !drawer || !me.drawerVisible) {
            return;
        }
        
        animation = me.parseAnimation(false, animation);
        if (animation) {
            me.getTarget().element.addCls(me.slidingCls);
            ret = me.doAnimation(false, me.getSlideOutCfg(me.getDirection(), me.afterDrawerHide, animation));
            me.isSliding = true;
        } else {
            me.afterDrawerHide();
            ret = Ext.Promise.resolve();
        }
        return ret;
    },
    
    isDynamic: function() {
        var dynamic = this.getDynamic(),
            target;
        if (dynamic === null) {
            target = this.getTarget();
            dynamic = target.getFloated() || !target.getRefOwner();
        }
        return dynamic;
    },
    
    showDrawer: function(animation) {
        var me = this,
            savedProps = me.savedProps,
            direction = me.getDirection(),
            target = me.getTarget(),
            headerSize = me.getHeaderSize(),
            endDirection = me.endDirection,
            vertical = me.verticalMap[direction],
            drawer, w, h, ret, header;
        if (me.isSliding || !me.getCollapsed() || !me.getDrawer()) {
            return Ext.Promise.resolve();
        }
        drawer = me.createDrawer();
        if (vertical) {
            h = '100%';
            w = savedProps.measuredWidth || me.defaultSize;
            drawer.setTop(0);
            drawer.setBottom(0);
            if (endDirection[direction]) {
                drawer.setLeft(null);
                drawer.setRight(headerSize);
            } else {
                drawer.setRight(null);
                drawer.setLeft(headerSize);
            }
        } else {
            w = '100%';
            h = savedProps.measuredHeight || me.defaultSize;
            drawer.setRight(0);
            drawer.setLeft(0);
            if (endDirection[direction]) {
                drawer.setTop(null);
                drawer.setBottom(headerSize);
            } else {
                drawer.setBottom(null);
                drawer.setTop(headerSize);
            }
            if (target.getHeader() && target.getHeaderPosition() === direction) {
                header = drawer.ensureHeader();
                if (header) {
                    header.hide();
                }
            }
        }
        me.configureDrawer(drawer, w, h);
        drawer.show();
        animation = me.parseAnimation(false, animation);
        me.isSliding = true;
        if (animation) {
            animation = me.getSlideInCfg(direction, me.afterDrawerShow, animation);
            animation.preserveEndState = true;
            me.getTarget().element.addCls(me.slidingCls);
            ret = me.doAnimation(false, animation);
        } else {
            me.afterDrawerShow();
            ret = Ext.Promise.resolve();
        }
        return ret;
    },
    
    toggleCollapsed: function(collapsed, animation) {
        var me = this,
            target = me.getTarget(),
            current = me.getCollapsed(),
            event, ret;
        if (collapsed === current) {
            return Ext.Promise.resolve();
        }
        event = 'before' + (collapsed ? 'collapse' : 'expand');
        if (me.initialized && target.hasListeners[event] && target.fireEvent(event, target) === false) {
            return Ext.Promise.resolve();
        }
        if (me.rendered) {
            animation = me.parseAnimation(collapsed, animation);
        } else {
            animation = null;
        }
        me.hideDrawer(false);
        if (animation) {
            ret = me.doExpandCollapseAnimated(collapsed, animation);
        } else {
            ret = me.doExpandCollapse(collapsed);
        }
        return ret;
    },
    applyAnimation: function(config) {
        if (config === true) {
            config = {};
        }
        return config;
    },
    updateCollapsed: function(collapsed) {
        var me = this;
        if (me.rendered && !me.preventUpdate) {
            
            
            
            me._collapsed = !collapsed;
            me.toggleCollapsed(collapsed);
        }
    },
    updateCollapseToolText: function(text) {
        this.setToolTextIf(text, this.getCollapsed());
    },
    updateDirection: function(direction, oldDirection) {
        var me = this;
        if (!me.isConfiguring) {
            if (me.getCollapsed()) {
                me.getTarget().moveHeaderPosition(direction, oldDirection);
            }
            me.ensureCollapseTool();
        }
    },
    updateDynamic: function(dynamic) {
        var me = this,
            drawer = me.drawer;
        if (dynamic && drawer) {
            if (me.hasDetachedBody) {
                me.reattachBodyWrap();
            }
            me.drawer = Ext.destroy(drawer);
        }
    },
    updateExpandToolText: function(text) {
        this.setToolTextIf(text, !this.getCollapsed());
    },
    updateUseDrawer: function() {
        if (this.rendered) {
            this.setupDrawerListeners();
        }
    },
    privates: {
        animateEndCls: Ext.baseCSSPrefix + 'placeholder-animate-end',
        collapsingDirections: {
            top: [
                'up',
                'down'
            ],
            left: [
                'left',
                'right'
            ],
            bottom: [
                'down',
                'up'
            ],
            right: [
                'right',
                'left'
            ]
        },
        defaultSize: 300,
        endDirection: {
            right: 1,
            bottom: 1
        },
        headerChangePosition: {
            top: [
                'top',
                'bottom'
            ],
            left: [
                'left',
                'right'
            ],
            bottom: [
                'bottom',
                'top'
            ],
            right: [
                'right',
                'left'
            ]
        },
        hasDetachedBody: false,
        rendered: false,
        slidingCls: Ext.baseCSSPrefix + 'sliding',
        verticalMap: {
            right: 1,
            left: 1
        },
        afterAnimation: function(active) {
            active.deferred.resolve();
            this.activeOperation = null;
        },
        afterDrawerHide: function() {
            var me = this,
                target = me.getTarget(),
                active = me.activeOperation,
                drawer = me.drawer,
                header;
            target.element.removeCls(me.slidingCls);
            me.drawerVisible = me.isSliding = false;
            if (!me.destroying) {
                me.drawerListeners = Ext.destroy(me.drawerListeners);
                drawer.hide();
                header = drawer.getHeader();
                if (header) {
                    header.show();
                }
                target.fireEvent('drawerhide', target);
            }
            if (active) {
                me.afterAnimation(active);
            }
        },
        afterDrawerShow: function() {
            var me = this,
                active = me.activeOperation,
                drawerListeners, listenerCfg,
                target = me.getTarget(),
                header;
            me.isSliding = false;
            me.drawerVisible = true;
            if (!me.destroying) {
                target.element.removeCls(me.slidingCls);
                listenerCfg = {
                    mouseleave: 'handleElMouseLeave',
                    mouseenter: 'handleElMouseEnter',
                    scope: me,
                    destroyable: true
                };
                drawerListeners = [
                    Ext.on('mousedown', 'handleGlobalDrawerEvent', me, {
                        destroyable: true
                    }),
                    Ext.getDoc().on('mousemove', 'handleGlobalDrawerEvent', me, {
                        destroyable: true
                    }),
                    me.drawer.element.on(listenerCfg)
                ];
                header = target.getHeader();
                if (header) {
                    drawerListeners.push(header.element.on(listenerCfg));
                }
                me.drawerListeners = drawerListeners;
                target.fireEvent('drawershow', target);
            }
            if (active) {
                me.afterAnimation(active);
            }
        },
        afterExpandCollapseAnimation: function() {
            var me = this,
                active = me.activeOperation,
                target = me.getTarget(),
                cls = active.animCls,
                header, bodyWrap;
            if (!me.destroying) {
                header = target.getHeader();
                if (active.placeHolder) {
                    me.drawer.hide();
                }
                if (active.reattach) {
                    me.reattachBodyWrap();
                }
                if (header && active.restoreHeaderVis) {
                    header.element.setVisibility(true);
                }
                if (cls) {
                    target.element.removeCls(cls);
                }
                if (active.restore) {
                    me.restoreProps();
                    bodyWrap = target.bodyWrapElement;
                    bodyWrap.setWidth(null).setHeight(null);
                    header = header && header.element;
                    if (header) {
                        header.setWidth(null).setHeight(null);
                    }
                }
                me.afterExpandCollapse(active.collapsed, true);
            }
            me.afterAnimation(active);
        },
        afterExpandCollapse: function(collapsed) {
            var me = this,
                target = me.getTarget(),
                types = me.headerChangePosition,
                direction = me.getDirection(),
                headerPosition = target.getHeaderPosition(),
                event = collapsed ? 'collapse' : 'expand';
            target.bodyWrapElement.setVisible(!collapsed);
            if (types[headerPosition].indexOf(direction) < 0) {
                target.moveHeaderPosition(collapsed ? direction : headerPosition, !collapsed ? direction : headerPosition);
            }
            me.preventUpdate = true;
            me.setCollapsed(collapsed);
            me.preventUpdate = false;
            me.ensureCollapseTool();
            if (me.initialized && target.hasListeners[event]) {
                target.fireEvent(event, target);
            }
        },
        createDrawer: function() {
            var me = this,
                p = me.drawer;
            if (!p) {
                me.drawer = p = Ext.create(me.getDrawer());
                p.bodyWrapElement.hide();
            }
            return p;
        },
        configureDrawer: function(drawer, width, height, resetPos) {
            var me = this,
                target = me.getTarget(),
                bodyWrap = target.bodyWrapElement;
            drawer.setTitle(target.getTitle());
            drawer.setWidth(width);
            drawer.setHeight(height);
            if (resetPos) {
                drawer.setTop(0);
                drawer.setRight(null);
                drawer.setBottom(null);
                drawer.setLeft(0);
            }
            drawer.element.append(bodyWrap);
            me.getContainerTarget().appendChild(drawer.element);
            bodyWrap.show();
            drawer.show();
            me.hasDetachedBody = true;
        },
        doAnimation: function(collapsed, animation, activeOperation) {
            activeOperation = activeOperation || {};
            var deferred = activeOperation.deferred || new Ext.Deferred(),
                anim = new Ext.fx.Animation(animation);
            activeOperation.anim = anim;
            activeOperation.deferred = deferred;
            activeOperation.collapsed = collapsed;
            this.activeOperation = activeOperation;
            Ext.Animator.run(anim);
            return deferred.promise;
        },
        doExpandCollapse: function(collapsed, initial) {
            var me = this,
                target = me.getTarget(),
                direction;
            if (me.rendered) {
                if (collapsed) {
                    me.saveProps();
                    target.setFlex(null);
                    direction = me.getDirection();
                    if (direction === 'top' || direction === 'bottom') {
                        target.setHeight(null);
                    } else {
                        target.setWidth(null);
                    }
                } else {
                    me.reattachBodyWrap();
                    me.restoreProps();
                }
                me.afterExpandCollapse(collapsed);
            }
            return initial ? null : Ext.Promise.resolve();
        },
        doExpandCollapseAnimated: function(collapsed, animation) {
            if (this.isDynamic()) {
                return this.doExpandCollapseDynamic(collapsed, animation);
            }
            return this.doExpandCollapsePlaceholder(collapsed, animation);
        },
        doExpandCollapseDynamic: function(collapsed, animation) {
            var me = this,
                target = me.getTarget(),
                headerPosition = target.getHeaderPosition(),
                direction = me.getDirection(),
                targetEl = target.element,
                bodyWrap = target.bodyWrapElement,
                header = target.getHeader(),
                headerEl = header && header.element,
                from = {},
                to = {},
                directionVertical = direction === 'top' || direction === 'bottom',
                headerVertical = headerPosition === 'top' || headerPosition === 'bottom',
                headerSize = me.getHeaderSize(),
                headerDifferent = headerPosition !== direction,
                height, width, savedProps, size;
            if (collapsed) {
                savedProps = me.saveProps();
                height = savedProps.measuredHeight;
                width = savedProps.measuredWidth;
                if (directionVertical) {
                    me.measureAndSet(bodyWrap, 'Height');
                    if (headerDifferent) {
                        me.measureAndSet(headerEl, 'Height');
                    }
                    from.height = height;
                    to.height = headerVertical ? headerSize : 0;
                    target.setHeight(null);
                    target.setMinHeight(null);
                } else {
                    me.measureAndSet(bodyWrap, 'Width');
                    if (headerDifferent) {
                        me.measureAndSet(headerEl, 'Width');
                    }
                    from.width = width;
                    to.width = headerVertical ? headerSize : 0;
                    target.setWidth(null);
                    target.setMinWidth(null);
                }
                target.setFlex(null);
            } else {
                headerDifferent = headerPosition !== direction;
                me.reattachBodyWrap();
                
                
                if (directionVertical) {
                    targetEl.setHeight(null);
                } else {
                    targetEl.setWidth(null);
                }
                me.restoreProps(true);
                if (headerDifferent) {
                    target.moveHeaderPosition(collapsed ? direction : headerPosition, !collapsed ? direction : headerPosition);
                }
                bodyWrap.show();
                if (headerEl) {
                    headerEl.setWidth(null).setHeight(null);
                }
                me.measureAndSet(bodyWrap, directionVertical ? 'Height' : 'Width', true);
                size = targetEl.measure();
                height = size.height;
                width = size.width;
                target.setFlex(null);
                me.measureAndSet(headerEl, directionVertical ? 'Height' : 'Width');
                if (directionVertical) {
                    from.height = headerSize;
                    to.height = height;
                    target.setHeight(null);
                } else {
                    from.width = headerSize;
                    to.width = width;
                    target.setWidth(null);
                }
            }
            return me.doAnimation(collapsed, Ext.apply({
                scope: me,
                callback: me.afterExpandCollapseAnimation,
                element: targetEl,
                preserveEndState: true,
                from: from,
                to: to
            }, animation), {
                restore: !collapsed
            });
        },
        doExpandCollapsePlaceholder: function(collapsed, animation) {
            var me = this,
                types = me.collapsingDirections,
                target = me.getTarget(),
                targetEl = target.element,
                headerPosition = target.getHeaderPosition(),
                direction = me.getDirection(),
                directionVertical = direction === 'top' || direction === 'bottom',
                headerVertical = headerPosition === 'top' || headerPosition === 'bottom',
                header = target.getHeader(),
                headerDifferent = directionVertical !== headerVertical,
                containerBox = me.getContainerTarget().getBox(),
                height, width, drawer, anim, animCls, restoreHeaderVis, savedProps, size;
            drawer = me.createDrawer();
            if (collapsed) {
                savedProps = me.saveProps();
                height = savedProps.measuredHeight;
                width = savedProps.measuredWidth;
            } else {
                me.reattachBodyWrap();
                me.restoreProps(true);
                size = targetEl.measure();
                height = size.height;
                width = size.width;
            }
            me.configureDrawer(drawer, width, height, true);
            drawer.setLeft(targetEl.getLeft() - containerBox.left);
            drawer.setTop(targetEl.getTop() - containerBox.top);
            if (directionVertical) {
                target.setHeight(null);
                target.setMinHeight(null);
            } else {
                target.setWidth(null);
                target.setMinWidth(null);
            }
            target.setFlex(null);
            if (collapsed) {
                if (headerDifferent && types[headerPosition].indexOf(direction) < 0) {
                    target.moveHeaderPosition(collapsed ? direction : headerPosition, !collapsed ? direction : headerPosition);
                }
                if (header) {
                    
                    header.element.setVisibility(false);
                }
                anim = me.getSlideOutCfg(direction, function() {
                    if (me.destroying) {
                        return;
                    }
                    var active = me.activeOperation;
                    drawer.hide();
                    if (header) {
                        header.element.setVisibility(true);
                        me.doAnimation(collapsed, {
                            type: 'slideIn',
                            element: header.element,
                            reverse: true,
                            direction: direction,
                            isElementBoxFit: false,
                            scope: me,
                            callback: me.afterExpandCollapseAnimation
                        }, active);
                    } else {
                        me.afterExpandCollapseAnimation();
                    }
                }, animation);
            } else {
                anim = me.getSlideInCfg(direction, me.afterExpandCollapseAnimation, animation);
                if (me.endDirection[direction]) {
                    animCls = me.animateEndCls;
                    targetEl.addCls(animCls);
                }
                if (!headerDifferent) {
                    header.element.setVisibility(false);
                    restoreHeaderVis = true;
                }
            }
            return me.doAnimation(collapsed, anim, {
                placeHolder: true,
                restore: !collapsed,
                reattach: true,
                animCls: animCls,
                restoreHeaderVis: restoreHeaderVis
            });
        },
        ensureCollapseTool: function() {
            var me = this,
                target = me.getTarget(),
                header = target.ensureHeader(),
                pos = me.getDirection(),
                collapsed = me.getCollapsed(),
                types = me.collapsingDirections,
                tool = me.collapsibleTool,
                cfg = me.getTool();
            if (header && cfg) {
                if (!tool) {
                    me.collapsibleTool = tool = target.addTool(Ext.apply({
                        handler: me.onToggleToolTap,
                        scope: me,
                        $internal: true
                    }, cfg))[0];
                }
                tool.setType(collapsed ? types[pos][1] : types[pos][0]);
                tool.setTooltip(collapsed ? me.getExpandToolText() : me.getCollapseToolText());
            } else {
                me.collapsibleTool = Ext.destroy(tool);
            }
        },
        getAnimationFor: function(collapsed) {
            var anim = collapsed ? this.getCollapseAnimation() : this.getExpandAnimation();
            return anim || this.getAnimation();
        },
        getContainerTarget: function() {
            return this.getTarget().element.parent();
        },
        getDrawerTask: function() {
            var me = this,
                task = me.drawerTask;
            if (!task) {
                me.drawerTask = task = new Ext.util.DelayedTask(me.hideDrawer, me);
            }
            return task;
        },
        getHeaderSize: function() {
            var header = this.getTarget().ensureHeader(),
                headerEl = header && header.element;
            return headerEl ? Math.min(headerEl.measure('h'), headerEl.measure('w')) : 0;
        },
        getSlideInCfg: function(direction, callback, animation) {
            return Ext.apply({
                type: 'slideIn',
                direction: direction,
                reverse: true,
                element: this.drawer.element,
                isElementBoxFit: false,
                scope: this,
                callback: callback
            }, animation);
        },
        getSlideOutCfg: function(direction, callback, animation) {
            return Ext.apply({
                type: 'slideOut',
                direction: direction,
                element: this.drawer.element,
                isElementBoxFit: false,
                scope: this,
                callback: callback
            }, animation);
        },
        handleElMouseEnter: function() {
            this.getDrawerTask().cancel();
        },
        handleElMouseLeave: function(e) {
            var me = this,
                toElement = e.getRelatedTarget(),
                target = me.getTarget();
            
            if (toElement && (target.owns(toElement) || me.drawer.owns(toElement))) {
                return;
            }
            me.getDrawerTask().delay(me.getDrawerHideDelay());
        },
        handleGlobalDrawerEvent: function(e) {
            var me = this,
                drawer = me.drawer,
                target = me.getTarget(),
                task;
            task = me.getDrawerTask();
            if (target.owns(e) || drawer.owns(e)) {
                task.cancel();
            } else {
                task.delay(me.getDrawerHideDelay());
            }
        },
        measureAndSet: function(el, dimension, clear) {
            if (!el) {
                return;
            }
            var setter = 'set' + dimension,
                getter = 'get' + dimension;
            if (clear) {
                el[setter](null);
            }
            el[setter](el[getter](false, true));
        },
        onHeaderTap: function(e) {
            var me = this,
                tool = me.collapsibleTool;
            if (me.getCollapsed() && !me.isDynamic() && !(tool && tool.owns(e))) {
                if (me.drawerVisible) {
                    me.hideDrawer();
                } else {
                    me.showDrawer();
                }
            }
        },
        onToggleToolTap: function() {
            this.toggleCollapsed(!this.getCollapsed());
        },
        parseAnimation: function(collapsed, animation) {
            if (animation === undefined) {
                animation = this.getAnimationFor(collapsed);
            } else if (animation) {
                if (typeof animation === 'boolean') {
                    animation = {};
                }
                animation = Ext.apply({}, animation, this.getAnimationFor(collapsed));
            }
            return animation;
        },
        reattachBodyWrap: function() {
            if (this.hasDetachedBody) {
                this.getTarget().reattachBodyWrap();
                this.hasDetachedBody = false;
            }
        },
        restoreProps: function(keep) {
            var target = this.getTarget(),
                savedProps = this.savedProps,
                prop;
            if (savedProps) {
                prop = savedProps.flex;
                if (prop) {
                    target.setFlex(prop);
                }
                target.setMinHeight(savedProps.minHeight);
                target.setMinWidth(savedProps.minWidth);
                target.setHeight(savedProps.height);
                target.setWidth(savedProps.width);
            }
            if (!keep) {
                this.savedProps = null;
            }
        },
        saveProps: function() {
            var me = this,
                target = me.getTarget(),
                size = target.element.measure();
            me.savedProps = {
                flex: target.getFlex(),
                minHeight: target.getMinHeight(),
                minWidth: target.getMinWidth(),
                height: target.getHeight(),
                width: target.getWidth(),
                measuredWidth: size.width,
                measuredHeight: size.height
            };
            return me.savedProps;
        },
        setToolTextIf: function(text, doSet) {
            var tool = this.collapsibleTool;
            if (text && tool && doSet) {
                tool.setTooltip(text);
            }
        },
        setupDrawerListeners: function() {
            var me = this,
                header = me.getTarget().getHeader();
            me.drawerHeaderListener = Ext.destroy(me.drawerHeaderListener);
            if (header && me.getUseDrawer()) {
                me.drawerHeaderListener = header.element.on({
                    destroyable: true,
                    scope: me,
                    tap: 'onHeaderTap'
                });
            }
        }
    }
});


Ext.define('Ext.panel.Header', {
    extend: Ext.Container,
    xtype: 'panelheader',
    
    isPanelHeader: true,
    config: {
        
        icon: null,
        
        iconAlign: null,
        
        iconCls: null,
        
        titleRotation: 'auto',
        
        title: null,
        
        titleAlign: null,
        layout: {
            type: 'box',
            vertical: false,
            align: 'center'
        },
        
        position: null
    },
    autoSize: null,
    classCls: Ext.baseCSSPrefix + 'panelheader',
    verticalCls: Ext.baseCSSPrefix + 'vertical',
    horizontalCls: Ext.baseCSSPrefix + 'horizontal',
    toolEndCls: Ext.baseCSSPrefix + 'end',
    toolStartCls: Ext.baseCSSPrefix + 'start',
    rotationMap: {
        top: '0',
        right: '90',
        bottom: '0',
        left: '270'
    },
    dockCls: {
        top: Ext.baseCSSPrefix + 'docked-top',
        right: Ext.baseCSSPrefix + 'docked-right',
        bottom: Ext.baseCSSPrefix + 'docked-bottom',
        left: Ext.baseCSSPrefix + 'docked-left'
    },
    weighted: true,
    vertical: false,
    inheritUi: true,
    addTools: function(tools) {
        var items = Ext.Array.from(tools);
        if (items && items.length) {
            items = this.add(items);
        }
        return items;
    },
    applyTitle: function(newTitle, oldTitle) {
        var title = oldTitle;
        if (title) {
            if (!newTitle || typeof newTitle === 'string') {
                title.setText(newTitle || '');
            } else if (newTitle) {
                title.setConfig(newTitle);
            }
        } else {
            title = Ext.create(this.createTitle(newTitle));
        }
        return title;
    },
    createTitle: function(config) {
        var panel = this.getRefOwner();
        if (config && typeof config === 'string') {
            config = {
                text: config
            };
        }
        return Ext.merge({
            xtype: 'paneltitle',
            instanceCls: (panel && panel.titleCls) || null,
            flex: '1 1 auto'
        }, config);
    },
    onItemAdd: function(item, index) {
        var me = this,
            title = me.getTitle(),
            titleWeight = (title && title.weight) || -10,
            itemWeight = item.weight || 0;
        me.callParent([
            item,
            index
        ]);
        if (item.isTool) {
            item.addCls((itemWeight < titleWeight) ? me.toolStartCls : me.toolEndCls);
        }
    },
    onItemRemove: function(item, index, destroying) {
        this.callParent([
            item,
            index,
            destroying
        ]);
        if (item.isTool) {
            item.removeCls([
                this.toolStartCls,
                this.toolEndCls
            ]);
        }
    },
    updateIcon: function(icon) {
        this.ensureTitle().setIcon(icon);
    },
    updateIconAlign: function(align) {
        this.ensureTitle().setIconAlign(align);
    },
    updateIconCls: function(cls) {
        this.ensureTitle().setIconCls(cls);
    },
    updateTitle: function(title, oldTitle) {
        if (oldTitle) {
            oldTitle.setConfig(title);
        } else {
            this.add(title);
        }
    },
    updateTitleAlign: function(align) {
        this.ensureTitle().setTextAlign(align);
    },
    updateTitleRotation: function(rotation) {
        var me = this,
            owner;
        if (rotation === 'auto') {
            
            
            
            if (me.isConfiguring) {
                return;
            }
            owner = me.getRefOwner();
            
            if (!owner) {
                Ext.raise('Cannot use rotation auto without an owning panel.');
            }
            
            if (owner) {
                rotation = me.rotationMap[owner.getHeaderPosition()];
            }
        }
        me.rotateTitle(rotation);
    },
    updatePosition: function(position, oldPosition) {
        var me = this,
            layout = me.getLayout(),
            isLeft = (position === 'left'),
            isRight = (position === 'right'),
            vertical = me.vertical = (isLeft || isRight),
            verticalCls = me.verticalCls,
            horizontalCls = me.horizontalCls,
            dockCls = me.dockCls;
        layout.setVertical(vertical);
        layout.setReverse(isLeft);
        
        
        if (oldPosition) {
            me.removeCls(dockCls[oldPosition]);
        }
        if (position) {
            me.addCls(dockCls[position]);
        }
        if (vertical) {
            me.replaceCls(horizontalCls, verticalCls);
        } else {
            me.replaceCls(verticalCls, horizontalCls);
        }
        if (me.getTitleRotation() === 'auto') {
            me.rotateTitle(me.rotationMap[position]);
        }
    },
    privates: {
        clearTools: function() {
            var items = this.getItems().items,
                c, i;
            for (i = items.length; i-- > 0; ) {
                c = items[i];
                if (c.isTool && !c.$internal) {
                    this.remove(c);
                }
            }
        },
        ensureTitle: function() {
            var me = this,
                title = me.getTitle();
            if (!title) {
                me.setTitle('');
                title = me.getTitle();
            }
            return title;
        },
        isVertical: function() {
            return this.vertical;
        },
        rotateTitle: function(rotation) {
            this.ensureTitle().setRotation(rotation);
        },
        sortByWeight: function(item1, item2) {
            return (item1.weight || 0) - (item2.weight || 0);
        }
    },
    deprecated: {
        '6.5': {
            configs: {
                
                glyph: null
            }
        }
    }
});


Ext.define('Ext.panel.Resizer', {
    config: {
        
        constrainToParent: true,
        
        dynamic: false,
        
        edges: null,
        
        maxSize: null,
        
        minSize: null,
        
        preserveRatio: false,
        
        snap: null,
        
        split: false,
        
        target: null,
        
        ui: null
    },
    constructor: function(config) {
        this.edgeMap = {};
        this.initConfig(config);
    },
    applyEdges: function(edges, oldEdges) {
        var positionMap = this.positionMap,
            len, i;
        if (edges) {
            if (edges === 'all') {
                edges = Ext.Array.clone(this.allEdges);
            } else if (typeof edges === 'string') {
                edges = edges.trim();
                edges = edges.split(this.edgeDelimiterRe);
            } else {
                edges = Ext.Array.clone(edges);
            }
            for (i = 0 , len = edges.length; i < len; ++i) {
                
                if (!positionMap[edges[i]]) {
                    Ext.raise('Invalid edge specified: ' + edges[i]);
                }
                
                edges[i] = positionMap[edges[i]];
            }
            edges.sort();
        }
        
        if (edges && oldEdges && Ext.Array.equals(edges, oldEdges)) {
            edges = undefined;
        }
        return edges;
    },
    updateEdges: function(edges, oldEdges) {
        var me = this,
            map = me.edgeMap,
            newMap = {},
            split = me.getSplit(),
            baseCls = me.baseCls,
            infoMap = me.edgeInfoMap,
            ui = me.getUi(),
            splitEdges = me.splitEdges,
            disabled = me.disabled,
            firstCorner = me.firstCorner,
            el = me.getTarget().element,
            splitPrefix = me.splitPrefix,
            edgeEl, key, len, i, edge;
        if (edges) {
            for (i = 0 , len = edges.length; i < len; ++i) {
                newMap[edges[i]] = true;
            }
        }
        if (oldEdges) {
            for (key in map) {
                if (!newMap[key]) {
                    edgeEl = map[key];
                    if (infoMap[key].corner && edgeEl === firstCorner) {
                        firstCorner = null;
                    }
                    edgeEl.destroy();
                    if (split && key in splitEdges) {
                        el.removeCls(splitPrefix + key);
                    }
                }
            }
            
            if (me.firstCorner && !firstCorner) {
                me.firstCorner = el.child('.' + this.baseCls + '[data-corner]');
            }
        }
        if (edges) {
            for (key in newMap) {
                newMap[key] = edge = map[key] || me.createEdge(el, key);
                if (split) {
                    edge.addCls(me.splitterCls);
                    if (key in splitEdges) {
                        el.addCls(splitPrefix + key);
                    }
                } else {
                    edge.addCls(me.handleCls);
                }
                if (disabled) {
                    edge.addCls(me.disabledCls);
                }
                if (ui) {
                    edge.addCls(ui, baseCls);
                }
            }
        }
        this.edgeMap = newMap;
    },
    applyMaxSize: function(maxSize) {
        return this.applyConstraintValue(maxSize);
    },
    applyMinSize: function(minSize) {
        return this.applyConstraintValue(minSize);
    },
    applySnap: function(snap) {
        return this.applyConstraintValue(snap);
    },
    updateSplit: function(split) {
        var me = this,
            map = me.edgeMap,
            splitEdges = me.splitEdges,
            splitPrefix = me.splitPrefix,
            key, el, i, len, target;
        if (me.isConfiguring) {
            
            
            return;
        }
        target = me.getTarget().element;
        for (key in map) {
            el = map[key];
            if (el.isElement) {
                el.toggleCls(me.splitterCls, split);
                el.toggleCls(me.handleCls, !split);
                if (key in splitEdges) {
                    target.toggleCls(splitPrefix + key, split);
                }
            }
        }
    },
    updateTarget: function(target) {
        var me = this;
        me.targetListeners = me.dragListeners = Ext.destroy(me.targetListeners, me.dragListeners);
        if (target) {
            me.setupDragListeners();
            me.targetListeners = target.on({
                expand: 'onTargetExpand',
                collapse: 'onTargetCollapse',
                scope: me,
                destroyable: true
            });
            if (target.hasCollapsible && target.getCollapsed()) {
                me.onTargetCollapse(target);
            }
        }
    },
    updateUi: function(ui, oldUi) {
        var edgeMap = this.edgeMap,
            baseCls = this.baseCls,
            key, edge;
        if (this.isConfiguring) {
            return;
        }
        for (key in edgeMap) {
            edge = edgeMap[key];
            if (oldUi) {
                edge.removeCls(oldUi, baseCls);
            }
            if (ui) {
                edge.addCls(ui, baseCls);
            }
        }
    },
    destroy: function() {
        var me = this,
            target = me.getTarget();
        if (me.dragStarted) {
            me.cleanup();
        }
        
        if (target && !target.destroying) {
            me.setEdges(null);
        }
        me.setTarget(null);
        me.callParent();
    },
    privates: {
        baseCls: Ext.baseCSSPrefix + 'panelresizer',
        proxyCls: Ext.baseCSSPrefix + 'panelresizer-proxy',
        disabledCls: Ext.baseCSSPrefix + 'disabled',
        handleCls: Ext.baseCSSPrefix + 'handle',
        splitterCls: Ext.baseCSSPrefix + 'splitter',
        horizontalCls: Ext.baseCSSPrefix + 'horizontal',
        verticalCls: Ext.baseCSSPrefix + 'vertical',
        splitPrefix: Ext.baseCSSPrefix + 'split-',
        edgeDelegateSelector: '> .' + Ext.baseCSSPrefix + 'panelresizer',
        
        allEdges: [
            'north',
            'northeast',
            'east',
            'southeast',
            'south',
            'southwest',
            'west',
            'northwest'
        ],
        
        defaultMaxSize: 100000,
        
        defaultMinSize: 50,
        
        edgeDelimiterRe: /(?:\s*,\s*)|\s+/,
        
        emptyConstrain: [
            null,
            null
        ],
        
        edgeInfoMap: {
            north: {
                vert: true,
                constrainProp: {
                    vert: 'bottom'
                },
                adjustHeightOffset: -1,
                splitPosSetter: 'setY',
                oppSplitPosSetter: 'setX',
                sizeProp: 'height',
                startEdge: 'top',
                touchAction: {
                    panY: false
                }
            },
            northeast: {
                horz: true,
                vert: true,
                corner: true,
                constrainProp: {
                    horz: 'left',
                    vert: 'bottom'
                },
                adjustHeightOffset: -1,
                adjustWidthOffset: 1,
                touchAction: {
                    panX: false,
                    panY: false
                }
            },
            east: {
                horz: true,
                constrainProp: {
                    horz: 'left'
                },
                adjustWidthOffset: 1,
                splitPosSetter: 'setX',
                oppSplitPosSetter: 'setY',
                sizeProp: 'width',
                startEdge: 'right',
                touchAction: {
                    panX: false
                }
            },
            southeast: {
                horz: true,
                vert: true,
                corner: true,
                constrainProp: {
                    horz: 'left',
                    vert: 'top'
                },
                adjustHeightOffset: 1,
                adjustWidthOffset: 1,
                touchAction: {
                    panX: false,
                    panY: false
                }
            },
            south: {
                vert: true,
                constrainProp: {
                    vert: 'top'
                },
                adjustHeightOffset: 1,
                splitPosSetter: 'setY',
                oppSplitPosSetter: 'setX',
                sizeProp: 'height',
                startEdge: 'bottom',
                touchAction: {
                    panY: false
                }
            },
            southwest: {
                horz: true,
                vert: true,
                corner: true,
                constrainProp: {
                    horz: 'right',
                    vert: 'top'
                },
                adjustHeightOffset: 1,
                adjustWidthOffset: -1,
                touchAction: {
                    panX: false,
                    panY: false
                }
            },
            west: {
                horz: true,
                constrainProp: {
                    horz: 'right'
                },
                adjustWidthOffset: -1,
                splitPosSetter: 'setX',
                oppSplitPosSetter: 'setY',
                sizeProp: 'width',
                startEdge: 'left',
                touchAction: {
                    panX: false
                }
            },
            northwest: {
                horz: true,
                vert: true,
                corner: true,
                constrainProp: {
                    horz: 'right',
                    vert: 'bottom'
                },
                adjustHeightOffset: -1,
                adjustWidthOffset: -1,
                touchAction: {
                    panX: false,
                    panY: false
                }
            }
        },
        
        positionMap: {
            n: 'north',
            north: 'north',
            ne: 'northeast',
            northeast: 'northeast',
            e: 'east',
            east: 'east',
            se: 'southeast',
            southeast: 'southeast',
            s: 'south',
            south: 'south',
            sw: 'southwest',
            southwest: 'southwest',
            w: 'west',
            west: 'west',
            nw: 'northwest',
            northwest: 'northwest'
        },
        sideInvertMap: {
            top: 'bottom',
            right: 'left',
            bottom: 'top',
            left: 'right'
        },
        splitEdges: {
            north: true,
            east: true,
            south: true,
            west: true
        },
        applyConstraintValue: function(v) {
            if (!Ext.isArray(v)) {
                v = [
                    v,
                    v
                ];
            }
            return v;
        },
        calculateConstrain: function(targetVal, localVal, defaultValue) {
            var v = targetVal;
            if (v === null) {
                v = localVal;
            }
            if (v === null) {
                v = defaultValue;
            }
            return v;
        },
        createEdge: function(targetEl, pos) {
            var me = this,
                info = me.edgeInfoMap[pos],
                corner = info.corner,
                firstCorner = me.firstCorner,
                
                el = targetEl.createChild({
                    cls: me.baseCls + ' ' + Ext.baseCSSPrefix + pos,
                    'data-edge': pos
                }, corner ? null : firstCorner);
            el.setTouchAction(info.touchAction);
            if (corner) {
                el.dom.setAttribute('data-corner', 'true');
            }
            if (corner && !firstCorner) {
                me.firstCorner = el;
            }
            return el;
        },
        createProxy: function(edge, asFloat) {
            var me = this,
                proxyCls = me.proxyCls,
                orientationCls = edge.horz ? me.horizontalCls : me.verticalCls,
                modeCls = asFloat ? me.handleCls : me.splitterCls;
            return Ext.getBody().createChild({
                cls: proxyCls + ' ' + modeCls + ' ' + orientationCls
            });
        },
        cleanup: function() {
            var me = this,
                info = me.info,
                proxy = info && info.proxy;
            if (me.dragStarted) {
                if (proxy) {
                    proxy.destroy();
                }
                me.dragStarted = false;
                me.info = null;
            }
        },
        getBoxLayout: function() {
            var parent = this.getTarget().getParent(),
                layout = parent && parent.getLayout();
            return layout && layout.isBox ? layout : null;
        },
        getEdge: function(position) {
            position = this.positionMap[position];
            return this.edgeMap[position] || null;
        },
        getProxy: function() {
            var info = this.info;
            return info && info.proxy;
        },
        handleDrag: function(e) {
            if (!this.dragStarted) {
                return;
            }
            var info = this.info,
                target = info.target,
                edge = info.edge,
                asFloat = info.asFloat,
                box = info.startBox,
                horz = edge.horz,
                vert = edge.vert,
                offsetWidth = 0,
                offsetHeight = 0,
                adjustWidthOffset = edge.adjustWidthOffset,
                adjustHeightOffset = edge.adjustHeightOffset,
                modifiesX = asFloat && edge.adjustWidthOffset < 0,
                modifiesY = asFloat && edge.adjustHeightOffset < 0,
                minHeight = info.minHeight,
                minWidth = info.minWidth,
                maxHeight = info.maxHeight,
                maxWidth = info.maxWidth,
                snappedWidth, snappedHeight, w, h, ratio, dragRatio, oppX, oppY, newBox;
            if (adjustWidthOffset) {
                offsetWidth = adjustWidthOffset * e.deltaX;
            }
            if (adjustHeightOffset) {
                offsetHeight = adjustHeightOffset * e.deltaY;
            }
            newBox = {
                width: box.width + offsetWidth,
                height: box.height + offsetHeight,
                x: box.x + (modifiesX ? -offsetWidth : 0),
                y: box.y + (modifiesY ? -offsetHeight : 0)
            };
            w = newBox.width;
            h = newBox.height;
            snappedWidth = horz ? this.snap(w, info.snapWidth, offsetWidth > 0) : w;
            snappedHeight = vert ? this.snap(h, info.snapHeight, offsetHeight > 0) : h;
            if (w !== snappedWidth || h !== snappedHeight) {
                if (modifiesX) {
                    newBox.x -= snappedWidth - w;
                }
                if (modifiesY) {
                    newBox.y -= snappedHeight - h;
                }
                newBox.width = w = snappedWidth;
                newBox.height = h = snappedHeight;
            }
            if (horz && (w < minWidth || w > maxWidth)) {
                newBox.width = w = Ext.Number.constrain(w, minWidth, maxWidth);
                if (modifiesX) {
                    newBox.x = box.x + (box.width - w);
                }
            }
            if (vert && (h < minHeight || h > maxHeight)) {
                newBox.height = h = Ext.Number.constrain(h, minHeight, maxHeight);
                if (modifiesY) {
                    newBox.y = box.y + (box.height - h);
                }
            }
            if (asFloat && (info.preserveRatio || e.shiftKey)) {
                ratio = info.ratio;
                h = Math.min(Math.max(minHeight, w / ratio), maxHeight);
                
                w = Math.min(Math.max(minWidth, newBox.height * ratio), maxWidth);
                if (horz && vert) {
                    
                    oppX = box.x + (modifiesX ? box.width : 0);
                    oppY = box.y + (modifiesY ? box.height : 0);
                    dragRatio = Math.abs(oppX - e.pageX) / Math.abs(oppY - e.pageY);
                    if (dragRatio > ratio) {
                        newBox.height = h;
                    } else {
                        newBox.width = w;
                    }
                    if (modifiesX) {
                        newBox.x = box.x - (newBox.width - box.width);
                    }
                    if (modifiesY) {
                        newBox.y = box.y - (newBox.height - box.height);
                    }
                } else if (horz) {
                    
                    newBox.height = h;
                } else {
                    
                    newBox.width = w;
                }
            }
            if (target.hasListeners.resizedrag) {
                target.fireEvent('resizedrag', target, {
                    edge: info.edgeName,
                    event: e,
                    width: newBox.width,
                    height: newBox.height
                });
            }
            this.resize(newBox, e.type === 'dragend', e);
        },
        handleDragCancel: function(e) {
            var info = this.info,
                target = info.target;
            this.cleanup();
            if (target.hasListeners.resizedragcancel) {
                target.fireEvent('resizedragcancel', target, {
                    edge: info.edgeName,
                    event: e
                });
            }
        },
        handleDragEnd: function(e) {
            this.handleDrag(e);
            this.cleanup();
        },
        handleDragStart: function(e) {
            var me = this,
                emptyConstrain = me.emptyConstrain,
                target = me.getTarget(),
                hasListeners = target.hasListeners,
                dynamic = me.getDynamic(),
                edgeName = e.target.getAttribute('data-edge'),
                edge = me.edgeInfoMap[edgeName],
                horz = edge.horz,
                vert = edge.vert,
                region = target.element.getRegion(),
                snap = me.getSnap() || emptyConstrain,
                minSize = me.getMinSize() || emptyConstrain,
                maxSize = me.getMaxSize() || emptyConstrain,
                defaultMinSize = me.defaultMinSize,
                defaultMaxSize = me.defaultMaxSize,
                info, proxy, context, asFloat, layout, layoutVert, clearFlex;
            if (hasListeners.beforeresizedragstart) {
                context = {
                    edge: edgeName,
                    event: e
                };
                if (target.fireEvent('beforeresizedragstart', target, context) === false) {
                    return;
                }
            }
            asFloat = target.getFloated() || target.isPositioned();
            if (target.getFlex()) {
                layout = me.getBoxLayout();
                if (layout) {
                    layoutVert = layout.getVertical();
                    clearFlex = (horz && !layoutVert) || (vert && layoutVert);
                }
            }
            me.info = info = {
                target: target,
                edgeName: edgeName,
                dynamic: dynamic,
                startBox: region,
                snapHeight: snap[1],
                snapWidth: snap[0],
                clearFlex: clearFlex,
                minHeight: me.calculateConstrain(target.getMinHeight(), minSize[1], defaultMinSize),
                minWidth: me.calculateConstrain(target.getMinWidth(), minSize[0], defaultMinSize),
                maxHeight: me.calculateConstrain(target.getMaxHeight(), maxSize[1], defaultMaxSize),
                maxWidth: me.calculateConstrain(target.getMaxWidth(), maxSize[0], defaultMaxSize),
                edge: edge,
                asFloat: asFloat,
                preserveRatio: asFloat ? me.getPreserveRatio() : false,
                ratio: asFloat ? region.width / region.height : 0,
                start: region[edge.startEdge],
                floated: target.getFloated()
            };
            if (!dynamic) {
                info.proxy = proxy = me.createProxy(edge, asFloat);
                if (asFloat) {
                    proxy.setBox(region);
                } else {
                    proxy[edge.splitPosSetter](info.start);
                    proxy[edge.oppSplitPosSetter](horz ? region.top : region.left);
                    proxy.setSize(horz ? undefined : region.width, vert ? undefined : region.height);
                }
            }
            me.setupDragConstraints(info);
            me.dragStarted = true;
            if (hasListeners.resizedragstart) {
                target.fireEvent('resizedragstart', target, context || {
                    edge: edgeName,
                    event: e
                });
            }
            e.stopPropagation();
            
            return false;
        },
        handleTouchStart: function(e) {
            
            e.preventDefault();
        },
        onTargetCollapse: function() {
            var me = this,
                map = me.edgeMap,
                key;
            me.disabled = true;
            me.dragListeners = Ext.destroy(me.dragListeners);
            for (key in map) {
                map[key].addCls(me.disabledCls);
            }
        },
        onTargetExpand: function() {
            var me = this,
                map = me.edgeMap,
                key;
            me.disabled = false;
            me.setupDragListeners();
            for (key in map) {
                map[key].removeCls(me.disabledCls);
            }
        },
        resize: function(newBox, atEnd, e) {
            var me = this,
                info = me.info,
                target = info.target,
                box = info.startBox,
                asFloat = info.asFloat,
                edge = info.edge,
                x = newBox.x,
                y = newBox.y,
                posChanged = asFloat && (box.x !== x || box.y !== y),
                horz = edge.horz,
                vert = edge.vert,
                floated = info.floated,
                onTarget = info.dynamic || atEnd,
                resizeTarget, isProxy, prop, diff, offset, targetParent, parentXY, positionEnd;
            if (onTarget) {
                resizeTarget = me.getTarget();
            } else {
                resizeTarget = info.proxy;
                isProxy = true;
            }
            if (!asFloat && isProxy) {
                prop = edge.sizeProp;
                offset = horz ? edge.adjustWidthOffset : edge.adjustHeightOffset;
                diff = (newBox[prop] - box[prop]) * offset;
                resizeTarget[edge.splitPosSetter](info.start + diff);
            } else {
                resizeTarget.setSize(horz ? newBox.width : undefined, vert ? newBox.height : undefined);
                if (!isProxy && info.clearFlex) {
                    resizeTarget.setFlex(null);
                }
                if (posChanged) {
                    positionEnd = !floated && onTarget;
                    if (positionEnd) {
                        targetParent = target.element.dom.parentNode;
                        parentXY = Ext.fly(targetParent).getXY();
                    }
                    if (horz) {
                        if (positionEnd) {
                            resizeTarget.setLeft(x - parentXY[0]);
                        } else {
                            resizeTarget.setX(x);
                        }
                    }
                    if (vert) {
                        if (positionEnd) {
                            resizeTarget.setTop(y - parentXY[1]);
                        } else {
                            resizeTarget.setY(y);
                        }
                    }
                }
            }
            if (atEnd) {
                if (target.hasListeners.resizedragend) {
                    target.fireEvent('resizedragend', target, {
                        edge: info.edgeName,
                        event: e,
                        width: newBox.width,
                        height: newBox.height
                    });
                }
            }
        },
        setupDragConstraints: function(info) {
            var me = this,
                dom = me.getTarget().element.dom,
                parent = dom.parentNode,
                clone = dom.cloneNode(false),
                fly = Ext.fly(clone),
                maxSize = me.defaultMaxSize,
                box, parentBox, edge, prop, invertMap;
            clone.style.position = 'absolute';
            fly.setMinHeight(info.minHeight);
            fly.setMinWidth(info.minWidth);
            fly.setMaxHeight(info.maxHeight);
            fly.setMaxWidth(info.maxWidth);
            
            fly.setHeight(1);
            fly.setWidth(1);
            parent.appendChild(clone);
            info.minHeight = fly.getHeight();
            info.minWidth = fly.getWidth();
            
            fly.setHeight(maxSize);
            fly.setWidth(maxSize);
            info.maxHeight = fly.getHeight();
            info.maxWidth = fly.getWidth();
            if (me.getConstrainToParent()) {
                box = info.startBox;
                parentBox = Ext.fly(parent).getRegion();
                edge = info.edge;
                invertMap = me.sideInvertMap;
                if (edge.horz) {
                    prop = edge.constrainProp.horz;
                    info.maxWidth = Math.min(info.maxWidth, Math.abs(box[prop] - parentBox[invertMap[prop]]));
                }
                if (edge.vert) {
                    prop = edge.constrainProp.vert;
                    info.maxHeight = Math.min(info.maxHeight, Math.abs(box[prop] - parentBox[invertMap[prop]]));
                }
            }
            parent.removeChild(clone);
        },
        setupDragListeners: function() {
            var me = this,
                delegate = me.edgeDelegateSelector;
            me.dragListeners = me.getTarget().element.on({
                scope: me,
                destroyable: true,
                delegate: delegate,
                dragstart: {
                    
                    priority: 1000,
                    delegate: delegate,
                    fn: 'handleDragStart'
                },
                drag: 'handleDrag',
                dragend: 'handleDragEnd',
                dragcancel: 'handleDragCancel',
                touchstart: 'handleTouchStart'
            });
        },
        snap: function(value, increment, roundUp) {
            var m, m2;
            if (increment) {
                m = value % increment;
                if (m !== 0) {
                    m2 = m * 2;
                    value -= m;
                    if (roundUp && m2 >= increment) {
                        value += increment;
                    } else if (!roundUp && m2 > increment) {
                        value += increment;
                    } else if (m2 < -increment) {
                        value -= increment;
                    }
                }
            }
            return value;
        }
    }
});


Ext.define('Ext.plugin.Responsive', {
    extend: Ext.mixin.Responsive,
    alias: 'plugin.responsive',
    id: 'responsive',
    isPlugin: true,
    weight: -1000,
    $configStrict: false,
    constructor: function(config) {
        
        if (!config || !config.cmp) {
            Ext.raise('Responsive plugin must be constructed by Component');
        }
        
        var me = this,
            cmp = config.cmp,
            cmpConfig = cmp.initialConfig,
            c = {
                responsiveConfig: cmpConfig.responsiveConfig,
                responsiveFormulas: cmpConfig.responsiveFormulas
            },
            transformed;
        delete c.cmp;
        delete c.type;
        me.cmp = cmp;
        me.initConfig(c);
        me.setConfig(config);
        transformed = me.transformed;
        
        if (transformed) {
            me.transformed = null;
            if (cmp.initConfig.$nullFn) {
                
                
                cmp.setConfig(transformed);
            } else {
                cmp.initialConfig = Ext.merge(Ext.merge({}, cmpConfig), transformed);
            }
        }
    },
    init: Ext.emptyFn,
    privates: {
        transformInstanceConfig: function(config) {
            
            
            var transformed = this.callParent([
                    config
                ]),
                ret;
            this.transformed = transformed;
            ret = Ext.apply({}, config);
            delete ret.responsiveConfig;
            
            delete ret.responsiveFormulas;
            return ret;
        },
        updateResponsiveState: function() {
            var config = this.getResponsiveState();
            
            this.cmp.setConfig(config);
        }
    }
});


Ext.define('Ext.plugin.TabGuard', {
    extend: Ext.plugin.Abstract,
    alias: 'plugin.tabguard',
    
    tabGuard: true,
    
    
    
    tabGuardTemplate: [
        {
            
            
            
            tag: 'span',
            'aria-hidden': 'true',
            cls: Ext.baseCSSPrefix + 'tab-guard-el'
        }
    ],
    
    tabGuardElements: {
        before: 'tabGuardBeforeEl',
        after: 'tabGuardAfterEl'
    },
    init: function(cmp) {
        var me = this;
        me.decorateComponent(cmp);
        if (cmp.addTool) {
            cmp.addTool = Ext.Function.createSequence(cmp.addTool, me.maybeInitTabGuards, me);
        }
        if (cmp.add) {
            cmp.add = Ext.Function.createSequence(cmp.add, me.maybeInitTabGuards, me);
        }
        if (cmp.remove) {
            cmp.remove = Ext.Function.createSequence(cmp.remove, me.maybeInitTabGuards, me);
        }
        cmp.getTabGuard = me.getTabGuard.bind(me);
        cmp.on('show', me.initTabGuards, me);
    },
    destroy: function() {
        var cmp = this.getCmp();
        if (cmp) {
            delete cmp.addTool;
            delete cmp.add;
            delete cmp.remove;
        }
        this.callParent();
    },
    privates: {
        decorateComponent: function(cmp) {
            var tpl = this.tabGuardTemplate;
            cmp = cmp || this.getCmp();
            cmp[this.tabGuardElements.before] = cmp.el.insertFirst(tpl);
            cmp[this.tabGuardElements.after] = cmp.el.createChild(tpl);
        },
        getTabGuard: function(position) {
            var cmp = this.getCmp(),
                prop = this.tabGuardElements[position];
            return cmp[prop];
        },
        maybeInitTabGuards: function() {
            var cmp = this.getCmp();
            if (cmp.rendered && cmp.initialized && cmp.tabGuard) {
                this.initTabGuards();
            }
        },
        initTabGuards: function() {
            var me = this,
                cmp = me.getCmp(),
                minTabIndex = me.tabGuardBeforeIndex || 0,
                maxTabIndex = me.tabGuardAfterIndex || 0,
                beforeGuard = me.getTabGuard('before'),
                afterGuard = me.getTabGuard('after'),
                i, tabIndex, nodes;
            if (!cmp.rendered || !cmp.tabGuard) {
                return;
            }
            nodes = cmp.el.findTabbableElements({
                skipSelf: true
            });
            
            if (nodes[0] === beforeGuard.dom) {
                nodes.shift();
            }
            if (nodes[nodes.length - 1] === afterGuard.dom) {
                nodes.pop();
            }
            if (nodes && nodes.length) {
                
                
                
                
                
                if (minTabIndex == null || maxTabIndex == null) {
                    for (i = 0; i < nodes.length; i++) {
                        
                        tabIndex = +nodes[i].getAttribute('tabIndex');
                        if (tabIndex > 0) {
                            minTabIndex = Math.min(minTabIndex, tabIndex);
                            maxTabIndex = Math.max(maxTabIndex, tabIndex);
                        }
                    }
                }
                beforeGuard.dom.setAttribute('tabIndex', minTabIndex);
                afterGuard.dom.setAttribute('tabIndex', maxTabIndex);
            } else {
                
                
                beforeGuard.dom.removeAttribute('tabIndex');
                afterGuard.dom.removeAttribute('tabIndex');
            }
            if (!beforeGuard.hasListeners.focusenter) {
                beforeGuard.on('focusenter', me.onTabGuardFocusEnter, cmp);
            }
            if (!afterGuard.hasListeners.focusenter) {
                afterGuard.on('focusenter', me.onTabGuardFocusEnter, cmp);
            }
        },
        onTabGuardFocusEnter: function(e, target) {
            var cmp = this,
                el = cmp.el,
                beforeGuard = cmp.getTabGuard('before'),
                afterGuard = cmp.getTabGuard('after'),
                from = e.relatedTarget,
                nodes, forward, nextFocus;
            nodes = el.findTabbableElements({
                skipSelf: true
            });
            
            if (nodes[0] === beforeGuard.dom) {
                nodes.shift();
            }
            if (nodes[nodes.length - 1] === afterGuard.dom) {
                nodes.pop();
            }
            
            
            
            if (nodes.length === 0) {
                nextFocus = el;
            }
            
            
            else if (from === el.dom) {
                forward = target === beforeGuard.dom;
            }
            
            
            
            else if (el.contains(from)) {
                forward = !!e.forwardTab;
            } else 
            
            
            
            {
                forward = target === beforeGuard.dom;
            }
            nextFocus = nextFocus || (forward ? nodes[0] : nodes[nodes.length - 1]);
            if (nextFocus) {
                
                
                
                
                
                Ext.fly(nextFocus).focus(nodes.length === 1 ? 1 : 0);
            }
        }
    }
});


Ext.define('Ext.tab.Tab', {
    extend: Ext.Button,
    xtype: 'tab',
    alternateClassName: 'Ext.Tab',
    
    isTab: true,
    config: {
        
        active: null,
        
        title: null,
        
        closable: null
    },
    pressedDelay: true,
    classCls: Ext.baseCSSPrefix + 'tab',
    activeCls: Ext.baseCSSPrefix + 'active',
    closableCls: Ext.baseCSSPrefix + 'closable',
    getTemplate: function() {
        var template = this.callParent();
        template.push({
            reference: 'activeIndicatorElement',
            cls: Ext.baseCSSPrefix + 'active-indicator-el'
        }, {
            reference: 'closeIconElement',
            cls: Ext.baseCSSPrefix + 'close-icon-el ' + Ext.baseCSSPrefix + 'font-icon ' + Ext.baseCSSPrefix + 'no-ripple',
            onclick: 'return Ext.doEv(this, event);'
        });
        return template;
    },
    shouldRipple: function() {
        return this.getRipple();
    },
    
    
    onClick: function(e) {
        var me = this,
            tabBar = me.tabBar;
        if (e.currentTarget === me.closeIconElement.dom) {
            if (tabBar && !me.getDisabled()) {
                tabBar.closeTab(me);
            }
            e.stopPropagation();
        } else {
            return me.callParent([
                e
            ]);
        }
    },
    updateTitle: function(title) {
        this.setText(title);
    },
    updateActive: function(active, oldActive) {
        var me = this,
            el = me.el,
            activeCls = me.activeCls;
        if (active && !oldActive) {
            el.addCls(activeCls);
            me.fireEvent('activate', me);
        } else if (oldActive) {
            el.removeCls(activeCls);
            me.fireEvent('deactivate', me);
        }
    },
    updateClosable: function(closable) {
        this.toggleCls(this.closableCls, !!closable);
    },
    onAdded: function(parent, instanced) {
        this.callParent([
            parent,
            instanced
        ]);
        this.tabBar = parent.isTabBar ? parent : null;
    },
    onRemoved: function(destroying) {
        this.callParent([
            destroying
        ]);
        this.tabBar = null;
    }
}, function() {
    this.override({
        activate: function() {
            this.setActive(true);
        },
        deactivate: function() {
            this.setActive(false);
        }
    });
});


Ext.define('Ext.tab.Bar', {
    extend: Ext.Toolbar,
    alternateClassName: 'Ext.TabBar',
    xtype: 'tabbar',
    isTabBar: true,
    config: {
        
        defaultTabUI: null,
        
        animateIndicator: false
    },
    
    defaultType: 'tab',
    
    layout: {
        type: 'hbox',
        align: 'stretch'
    },
    eventedConfig: {
        
        activeTab: null
    },
    
    baseCls: Ext.baseCSSPrefix + 'tabbar',
    
    indicatorAnimationSpeed: 150,
    
    initialize: function() {
        var me = this;
        me.callParent();
        me.on({
            tap: 'onTabTap',
            delegate: '> tab',
            scope: me
        });
    },
    getTemplate: function() {
        var template = this.callParent();
        template.push({
            reference: 'stripElement',
            cls: Ext.baseCSSPrefix + 'strip-el'
        });
        return template;
    },
    
    onTabTap: function(tab) {
        this.setActiveTab(tab);
    },
    
    applyActiveTab: function(newActiveTab, oldActiveTab) {
        if (!newActiveTab && newActiveTab !== 0) {
            return;
        }
        var newTabInstance = this.parseActiveTab(newActiveTab);
        if (!newTabInstance) {
            
            if (oldActiveTab) {
                Ext.Logger.warn('Trying to set a non-existent activeTab');
            }
            
            return;
        }
        return newTabInstance;
    },
    
    updateDocked: function(newDocked) {
        var layout = this.getLayout(),
            initialConfig = this.getInitialConfig(),
            pack;
        if (!initialConfig.layout || !initialConfig.layout.pack) {
            pack = (newDocked == 'bottom') ? 'center' : 'left';
            
            if (layout.isLayout) {
                layout.setPack(pack);
            } else {
                layout.pack = (layout && layout.pack) ? layout.pack : pack;
            }
        }
        this.callParent(arguments);
    },
    
    updateActiveTab: function(newTab, oldTab) {
        var me = this,
            animateIndicator = this.getAnimateIndicator();
        if (animateIndicator && newTab && oldTab && oldTab.parent) {
            me.animateTabIndicator(newTab, oldTab);
        } else {
            if (newTab) {
                newTab.setActive(true);
            }
            
            if (oldTab && oldTab.parent) {
                oldTab.setActive(false);
                this.previousTab = oldTab;
            }
        }
    },
    updateAnimateIndicator: function() {
        var me = this;
        if (me.$animateIndicatorElement) {
            me.$animateIndicatorElement.destroy();
        }
        if (me.$indicatorAnimationListeners) {
            me.$indicatorAnimationListeners.destroy();
        }
        me.$indicatorAnimationListeners = me.$animateIndicatorElement = null;
    },
    animateTabIndicator: function(newTab, oldTab) {
        var me = this,
            newTabElement = newTab.element,
            oldTabElement = oldTab.element,
            oldIndicator = oldTab.activeIndicatorElement,
            newIndicator = newTab.activeIndicatorElement,
            tabbarElement = me.element,
            oldIndicatorProps, newIndicatorProps, animateIndicatorElement;
        newTab.setActive(true);
        oldIndicatorProps = {
            transform: {
                translateX: oldTabElement.getX() - tabbarElement.getX()
            },
            width: oldTabElement.getWidth(),
            height: oldIndicator.getHeight(),
            'background-color': oldIndicator.getStyle('background-color')
        };
        newIndicatorProps = {
            transform: {
                translateX: newTabElement.getX() - tabbarElement.getX()
            },
            width: newTabElement.getWidth(),
            height: newIndicator.getHeight(),
            'background-color': newIndicator.getStyle('background-color')
        };
        oldTab.setActive(false);
        newIndicator.hide();
        if (oldIndicatorProps.height || newIndicatorProps.height) {
            animateIndicatorElement = me.$animateIndicatorElement;
            if (!animateIndicatorElement) {
                animateIndicatorElement = me.$animateIndicatorElement = me.element.insertFirst({
                    cls: Ext.baseCSSPrefix + 'active-indicator-el'
                });
            }
            animateIndicatorElement.show();
            if (me.$indicatorAnimationListeners) {
                me.$indicatorAnimationListeners.destroy();
                me.$indicatorAnimationListeners = null;
            }
            me.$indicatorAnimation = animateIndicatorElement.animate({
                duration: me.indicatorAnimationSpeed,
                from: oldIndicatorProps,
                to: newIndicatorProps
            });
            me.$indicatorAnimationListeners = me.$indicatorAnimation.on({
                destroyable: true,
                animationend: {
                    fn: function() {
                        newIndicator.show();
                        animateIndicatorElement.hide();
                        me.$indicatorAnimationListeners.destroy();
                        me.$indicatorAnimation = me.$indicatorAnimationListeners = null;
                    },
                    single: true
                }
            });
        }
    },
    
    parseActiveTab: function(tab) {
        
        if (typeof tab == 'number') {
            return this.getItems().items[tab];
        } else if (typeof tab == 'string') {
            tab = this.getComponent(tab) || Ext.getCmp(tab);
        }
        return tab;
    },
    onItemAdd: function(item, index) {
        var defaultTabUI = this.getDefaultTabUI();
        if (defaultTabUI && item.isTab && (item.getUi() == null)) {
            item.setUi(defaultTabUI);
        }
        this.callParent([
            item,
            index
        ]);
    },
    privates: {
        
        findNextActivatableTab: function(tabToClose) {
            var me = this,
                previousTab = me.previousTab,
                nextTab;
            if (tabToClose.getActive() && me.getItems().getCount() > 1) {
                if (previousTab && previousTab !== tabToClose && !previousTab.getDisabled()) {
                    nextTab = previousTab;
                } else {
                    nextTab = tabToClose.next('tab:not([disabled=true])') || tabToClose.prev('tab:not([disabled=true])');
                }
            }
            
            
            
            return nextTab || me.getActiveTab();
        },
        
        closeTab: function(tab) {
            var me = this,
                nextActivatableTab = me.findNextActivatableTab(tab),
                parent = me.parent;
            if (parent && parent.isTabPanel) {
                
                if (nextActivatableTab) {
                    parent.setActiveItem(nextActivatableTab.card);
                }
                
                parent.remove(tab.card);
            } else {
                if (nextActivatableTab) {
                    me.setActiveTab(nextActivatableTab);
                }
                me.remove(tab);
            }
        }
    }
});


Ext.define('Ext.tab.Panel', {
    extend: Ext.Container,
    xtype: 'tabpanel',
    alternateClassName: 'Ext.TabPanel',
    isTabPanel: true,
    config: {
        
        tabBar: true,
        
        tabBarPosition: 'top',
        
        layout: {
            type: 'card',
            animation: {
                type: 'slide'
            }
        },
        
        cls: Ext.baseCSSPrefix + 'tabpanel'
    },
    
    
    defaults: {
        allowHeader: false
    },
    initialize: function() {
        var me = this;
        me.callParent();
        me.on({
            beforeactivetabchange: 'doTabChange',
            delegate: '> tabbar',
            scope: me
        });
        me.on({
            disabledchange: 'onItemDisabledChange',
            delegate: '> component',
            scope: me
        });
    },
    
    applyScrollable: function() {
        return false;
    },
    
    updateUi: function(ui, oldUi) {
        var bar;
        this.callParent([
            ui,
            oldUi
        ]);
        bar = this.getTabBar();
        if (this.initialized && bar) {
            bar.setUi(ui);
        }
    },
    
    updateActiveItem: function(newActiveItem, oldActiveItem) {
        if (newActiveItem) {
            var items = this.getInnerItems(),
                oldIndex = items.indexOf(oldActiveItem),
                newIndex = items.indexOf(newActiveItem),
                tabBar = this.getTabBar(),
                oldTab = tabBar.parseActiveTab(oldIndex),
                newTab = tabBar.parseActiveTab(newIndex);
            this.callParent(arguments);
            if (newIndex != -1) {
                this.forcedChange = true;
                tabBar.setActiveTab(newIndex);
                this.forcedChange = false;
                if (oldTab) {
                    oldTab.setActive(false);
                }
                if (newTab) {
                    newTab.setActive(true);
                }
            }
        }
    },
    
    doTabChange: function(tabBar, newTab) {
        var oldActiveItem = this.getActiveItem(),
            newActiveItem;
        this.setActiveItem(tabBar.indexOf(newTab));
        newActiveItem = this.getActiveItem();
        return this.forcedChange || oldActiveItem !== newActiveItem;
    },
    
    applyTabBar: function(config) {
        var innerItems, activeItem;
        if (this.isConfiguring) {
            activeItem = this.initialConfig.activeItem || 0;
        } else {
            innerItems = this.getInnerItems();
            activeItem = innerItems.indexOf(this._activeItem);
        }
        if (config === true) {
            config = {};
        }
        if (config) {
            Ext.applyIf(config, {
                ui: this.getUi(),
                docked: this.getTabBarPosition(),
                activeItem: activeItem
            });
            return Ext.factory(config, Ext.tab.Bar, this.getTabBar());
        }
        return null;
    },
    
    updateTabBar: function(tabBar, oldTabBar) {
        var me = this;
        if (oldTabBar && me.removingTabBar === undefined) {
            me.remove(oldTabBar, true);
        }
        if (tabBar) {
            me.add(tabBar);
            me.setTabBarPosition(tabBar.getDocked());
        }
    },
    
    updateTabBarPosition: function(position) {
        var tabBar = this.getTabBar();
        if (tabBar) {
            tabBar.setDocked(position);
        }
    },
    onItemAdd: function(card) {
        var me = this;
        if (!card.isInnerItem()) {
            return me.callParent(arguments);
        }
        var tabBar = me.getTabBar(),
            initialConfig = card.getInitialConfig(),
            tabConfig = initialConfig.tab || {},
            tabTitle = (card.getTitle) ? card.getTitle() : initialConfig.title,
            tabClosable = (card.getClosable) ? card.getClosable() : initialConfig.closable,
            tabIconAlign = (card.getIconAlign) ? card.getIconAlign() : initialConfig.iconAlign,
            tabIconCls = (card.getIconCls) ? card.getIconCls() : initialConfig.iconCls,
            tabIcon = (card.getIcon) ? card.getIcon() : initialConfig.icon,
            tabHidden = (card.getHidden) ? card.getHidden() : initialConfig.hidden,
            tabDisabled = (card.getDisabled) ? card.getDisabled() : initialConfig.disabled,
            tabBadgeText = (card.getBadgeText) ? card.getBadgeText() : initialConfig.badgeText,
            innerItems = me.getInnerItems(),
            index = innerItems.indexOf(card),
            tabs = tabBar.getItems(),
            activeTab = tabBar.getActiveTab(),
            currentTabInstance = (tabs.length >= innerItems.length) && tabs.getAt(index),
            header = card.getConfig('header', false, true),
            tabInstance;
        if (tabTitle && !tabConfig.title) {
            tabConfig.title = tabTitle;
        }
        if (tabClosable && !tabConfig.closable) {
            tabConfig.closable = tabClosable;
        }
        if (tabIconAlign && !tabConfig.iconAlign) {
            tabConfig.iconAlign = tabIconAlign;
        }
        if (tabIconCls && !tabConfig.iconCls) {
            tabConfig.iconCls = tabIconCls;
        }
        if (tabIcon && !tabConfig.icon) {
            tabConfig.icon = tabIcon;
        }
        if (tabHidden && !tabConfig.hidden) {
            tabConfig.hidden = tabHidden;
        }
        if (tabDisabled && !tabConfig.disabled) {
            tabConfig.disabled = tabDisabled;
        }
        if (tabBadgeText && !tabConfig.badgeText) {
            tabConfig.badgeText = tabBadgeText;
        }
        
        if (!currentTabInstance && !tabConfig.title && !tabConfig.iconCls) {
            if (!tabConfig.title && !tabConfig.iconCls) {
                Ext.Logger.error('Adding a card to a tab container without specifying any tab configuration');
            }
        }
        
        tabInstance = Ext.factory(tabConfig, Ext.tab.Tab, currentTabInstance);
        if (!currentTabInstance) {
            tabBar.insert(index, tabInstance);
        }
        card.tab = tabInstance;
        tabInstance.card = card;
        
        
        if (header) {
            header.setHidden(true);
        }
        me.callParent(arguments);
        if (!activeTab && activeTab !== 0) {
            tabBar.setActiveTab(tabBar.getActiveItem());
        }
    },
    
    onItemDisabledChange: function(item, newDisabled) {
        if (item && item.tab) {
            item.tab.setDisabled(newDisabled);
        }
    },
    
    onItemRemove: function(item, index, destroying) {
        var me = this,
            meDestroying = me.meDestroying,
            clearBar, tabBar;
        if (!meDestroying) {
            tabBar = me.getTabBar();
            if (item === tabBar) {
                clearBar = me.removingTabBar === undefined;
            } else if (tabBar) {
                tabBar.remove(item.tab, true);
            }
        }
        me.callParent([
            item,
            index,
            destroying
        ]);
        if (clearBar) {
            
            
            me.removingTabBar = destroying;
            me.setTabBar(null);
            delete me.removingTabBar;
        }
    }
});


Ext.define('Ext.tip.Manager', {
    config: {
        tooltip: {
            xtype: 'tooltip',
            
            align: '',
            anchorToTarget: false,
            anchor: false,
            closeAction: 'hide',
            quickShowInterval: 0,
            maxWidth: '80vw'
        },
        
        overflowTip: {
            align: 'l-r?',
            anchor: true,
            showOnTap: true
        }
    },
    
    interceptTitles: false,
    constructor: function(config) {
        var me = this,
            tip;
        me.initConfig(config);
        me._fly = new Ext.dom.Fly();
        me.tip = tip = Ext.create(me.createTooltip());
        
        
        tip.allowRealign = false;
        tip.on({
            beforeshow: 'onBeforeShow',
            hovertarget: 'onHoverTarget',
            scope: me
        });
        me.globalListeners = Ext.on({
            scope: me,
            destroyable: true,
            dragstart: 'dragDisable',
            dragend: 'dragEnable',
            dragcancel: 'dragEnable'
        });
        if (!me.self.instance) {
            me.self.instance = me;
        }
    },
    
    disable: function() {
        var n = ++this.disabled;
        if (n === 1) {
            this.getTooltip().disable();
        }
    },
    
    enable: function() {
        var n = --this.disabled;
        if (n === 0) {
            this.getTooltip().enable();
        } else if (n < 0) {
            this.disabled = 0;
        }
    },
    destroy: function() {
        var me = this;
        if (me.self.instance === me) {
            me.self.instance = null;
        }
        me._fly.detach();
        
        me.globalListeners = me.tip = Ext.destroy(me.tip, me.globalListeners);
        me.callParent();
    },
    createTooltip: function() {
        var me = this,
            config = me.getTooltip();
        return Ext.apply({
            id: 'ext-global-tooltip',
            delegate: me.delegateQuickTip.bind(me),
            target: Ext.getBody()
        }, config);
    },
    hide: function() {
        if (this.tip) {
            this.tip.hide();
        }
    },
    privates: {
        disabled: 0,
        
        _propertyMap: (function() {
            var numFn = function(v) {
                    return parseInt(v, 10);
                },
                boolFn = function(v) {
                    return !!v;
                },
                fn = Ext.identityFn;
            return {
                ui: {
                    prop: 'data-qui',
                    parse: fn
                },
                html: {
                    prop: 'data-qtip',
                    parse: fn
                },
                width: {
                    prop: 'data-qwidth',
                    parse: numFn
                },
                minWidth: {
                    prop: 'data-qminWidth',
                    parse: fn
                },
                maxWidth: {
                    prop: 'data-qmaxWidth',
                    parse: fn
                },
                title: {
                    prop: 'data-qtitle',
                    parse: fn
                },
                autoHide: {
                    prop: 'data-qautoHide',
                    parse: boolFn
                },
                cls: {
                    prop: 'data-qcls',
                    parse: fn
                },
                axisLock: {
                    prop: 'data-axislock',
                    parse: fn
                },
                align: {
                    prop: 'data-qalign',
                    parse: fn
                },
                alignDelegate: {
                    prop: 'data-qaligndelegate',
                    parse: fn
                },
                anchor: {
                    prop: 'data-qanchor',
                    parse: fn
                },
                showDelay: {
                    prop: 'data-qshowDelay',
                    parse: numFn
                },
                hideDelay: {
                    prop: 'data-qhideDelay',
                    parse: numFn
                },
                dismissDelay: {
                    prop: 'data-qdismissDelay',
                    parse: numFn
                },
                trackMouse: {
                    prop: 'data-qtrackMouse',
                    parse: boolFn
                },
                anchorToTarget: {
                    prop: 'data-qanchorToTarget',
                    parse: boolFn
                },
                allowOver: {
                    prop: 'data-qallowover',
                    parse: boolFn
                },
                closable: true
            };
        })(),
        applyOverflowTip: function(config) {
            var phone = Ext.platformTags.phone;
            return Ext.apply({
                
                
                axisLock: !phone,
                
                maxWidth: phone ? '80vw' : 400
            }, config);
        },
        delegateQuickTip: function(dom) {
            var qtip = this.getTipConfig(dom, 'html');
            return !!qtip;
        },
        dragDisable: function() {
            if (!this.disabled) {
                this.tip.disable();
            }
        },
        dragEnable: function() {
            if (!this.disabled) {
                this.tip.enable();
            }
        },
        getTipConfig: function(dom, property) {
            var me = this,
                propertyMap = me._propertyMap,
                tipDefaults = me._tipDefaults,
                fly = me._fly,
                data = fly.attach(dom).getData().qtip,
                tip = me.tip,
                textAttr = propertyMap.html.prop,
                name, text, ret, value, item;
            
            
            
            if (!tipDefaults && property !== 'html') {
                me._tipDefaults = tipDefaults = {};
                for (name in propertyMap) {
                    tipDefaults[name] = tip.getConfig(name);
                }
            }
            if (data) {
                if (property) {
                    ret = data[property];
                } else {
                    ret = Ext.apply({}, tipDefaults);
                    Ext.apply(ret, data);
                }
            } else {
                if (dom.hasAttribute(textAttr)) {
                    text = dom.getAttribute(textAttr);
                    if (!text) {
                        text = me.interceptTitles && dom.title;
                        if (text) {
                            dom.setAttribute(textAttr, text);
                            dom.removeAttribute('title');
                        }
                    }
                } else if (dom.hasAttribute('data-qoverflow')) {
                    text = fly.dom.innerHTML;
                    
                    
                    if (property !== 'html') {
                        tipDefaults = Ext.apply({}, me.getOverflowTip(), tipDefaults);
                    }
                    
                    if (!property) {
                        
                        
                        if (!me.hasTextOverflow(dom)) {
                            return false;
                        }
                    }
                }
                if (text) {
                    
                    if (property === 'html') {
                        ret = text;
                    } else if (property) {
                        item = propertyMap[property];
                        if (item.prop) {
                            if (dom.hasAttribute(item.prop)) {
                                ret = item.parse(dom.getAttribute(item.prop));
                            }
                        }
                    } else {
                        ret = data = {
                            html: text
                        };
                        for (name in propertyMap) {
                            if (name !== 'html') {
                                item = propertyMap[name];
                                value = null;
                                if (item.prop) {
                                    if (dom.hasAttribute(item.prop)) {
                                        value = item.parse(dom.getAttribute(item.prop));
                                    }
                                }
                                if (value === null) {
                                    value = tipDefaults[name];
                                }
                                data[name] = value;
                            }
                        }
                    }
                }
            }
            
            fly.detach();
            if (property && ret == null && property !== 'html') {
                ret = tipDefaults[property];
            }
            return ret;
        },
        onBeforeShow: function(tip) {
            var me = this,
                dom = tip.currentTarget.dom,
                data, header;
            if (dom) {
                data = me.getTipConfig(dom);
                
                
                
                if (data === false) {
                    return false;
                }
                
                if (!data) {
                    return;
                }
                data.anchorToTarget = !!(data.align || data.anchor);
                tip.setConfig(data);
                header = tip.getHeader();
                if (header) {
                    header.setHidden(!data.title && !data.closable);
                }
            }
        },
        priorityConfigs: [
            'showDelay',
            'anchor',
            'anchorToTarget',
            'align',
            'trackMouse'
        ],
        onHoverTarget: function(tip, currentTarget) {
            var dom = currentTarget.dom,
                cfg;
            if (dom) {
                cfg = {};
                this.priorityConfigs.forEach(function(name) {
                    cfg[name] = this.getTipConfig(dom, name);
                }, this);
                cfg.anchorToTarget = !!(cfg.align || cfg.anchor);
                tip.setConfig(cfg);
            }
        },
        hasTextOverflow: function(candidate) {
            
            if (Ext.fly(candidate).isStyle('text-overflow', 'ellipsis')) {
                var textSize = Ext.util.TextMetrics.measure(candidate, candidate.innerHTML);
                return (textSize.width > Ext.fly(candidate).getViewRegion().width);
            }
        }
    }
});


Ext.define('Ext.util.Audio', {
    singleton: true,
    ctx: null,
    beep: function(callback) {
        this.oscillate(200, 1, callback);
    },
    oscillate: function(duration, type, callback) {
        if (!this.ctx) {
            this.ctx = new (window.audioContext || window.webkitAudioContext)();
        }
        if (!this.ctx) {
            console.log("BEEP");
            return;
        }
        type = (type % 5) || 0;
        try {
            var osc = this.ctx.createOscillator();
            osc.type = type;
            osc.connect(this.ctx.destination);
            osc.noteOn(0);
            Ext.defer(function() {
                osc.noteOff(0);
                if (callback)  {
                    callback();
                }
                
            }, duration);
        } catch (e) {
            throw new Error("[Ext.util.Audio.oscillate] Error with Oscillator playback");
        }
    }
});


Ext.define('Ext.util.BufferedCollection', {
    extend: Ext.util.Collection,
    mixins: [
        Ext.mixin.Observable
    ],
    config: {
        totalCount: 0,
        autoSort: false,
        autoFilter: false,
        pageSize: 0
    },
    updateTotalCount: function(totalCount) {
        this.length = totalCount;
        this.all = this.items = Array.apply(null, new Array(totalCount));
    },
    addPage: function(page, records) {
        var pageSize = this.getPageSize(),
            start = (page - 1) * pageSize,
            limit = pageSize;
        this.all.splice.apply(this.all, [
            start,
            limit
        ].concat(records));
        this.fireEvent('pageadded', page, records, this.items);
    },
    hasRange: function(start, end) {
        var items = this.items,
            i;
        for (i = start; i <= end; i++) {
            if (!items[i]) {
                return false;
            }
        }
        return true;
    }
});


Ext.define('Ext.util.Draggable', {
    isDraggable: true,
    mixins: [
        Ext.mixin.Observable
    ],
    
    
    
    config: {
        cls: Ext.baseCSSPrefix + 'draggable',
        draggingCls: Ext.baseCSSPrefix + 'dragging',
        element: null,
        constraint: 'container',
        disabled: null,
        
        direction: 'both',
        
        initialOffset: {
            x: 0,
            y: 0
        },
        translatable: {},
        
        component: null
    },
    DIRECTION_BOTH: 'both',
    DIRECTION_VERTICAL: 'vertical',
    DIRECTION_HORIZONTAL: 'horizontal',
    defaultConstraint: {
        min: {
            x: -Infinity,
            y: -Infinity
        },
        max: {
            x: Infinity,
            y: Infinity
        }
    },
    containerWidth: 0,
    containerHeight: 0,
    width: 0,
    height: 0,
    
    constructor: function(config) {
        var element;
        this.extraConstraint = {};
        this.initialConfig = config;
        this.offset = {
            x: 0,
            y: 0
        };
        this.elementListeners = {
            dragstart: 'onDragStart',
            drag: 'onDrag',
            dragend: 'onDragEnd',
            resize: 'onElementResize',
            touchstart: 'onPress',
            touchend: 'onRelease',
            
            
            priority: 2000,
            scope: this
        };
        if (config && config.element) {
            element = config.element;
            delete config.element;
            this.setElement(element);
        }
        return this;
    },
    applyElement: function(element) {
        if (!element) {
            return;
        }
        return Ext.get(element);
    },
    updateElement: function(element) {
        element.on(this.elementListeners);
        element.setTouchAction({
            panX: false,
            panY: false
        });
        this.mixins.observable.constructor.call(this, this.initialConfig);
    },
    updateInitialOffset: function(initialOffset) {
        if (typeof initialOffset === 'number') {
            initialOffset = {
                x: initialOffset,
                y: initialOffset
            };
        } else if (!initialOffset) {
            return;
        }
        var offset = this.offset,
            x, y;
        offset.x = x = initialOffset.x;
        offset.y = y = initialOffset.y;
        this.getTranslatable().translate(x, y);
    },
    updateCls: function(cls) {
        this.getElement().addCls(cls);
    },
    applyTranslatable: function(translatable, currentInstance) {
        translatable = Ext.factory(translatable, Ext.util.translatable.CssTransform, currentInstance, 'translatable');
        if (translatable) {
            translatable.setElement(this.getElement());
        }
        return translatable;
    },
    setExtraConstraint: function(constraint) {
        this.extraConstraint = constraint || {};
        this.refreshConstraint();
        return this;
    },
    addExtraConstraint: function(constraint) {
        Ext.merge(this.extraConstraint, constraint);
        this.refreshConstraint();
        return this;
    },
    applyConstraint: function(newConstraint) {
        this.currentConstraint = newConstraint;
        if (!newConstraint) {
            newConstraint = this.defaultConstraint;
        }
        if (newConstraint === 'container') {
            return Ext.merge(this.getContainerConstraint(), this.extraConstraint);
        }
        return Ext.merge({}, this.extraConstraint, newConstraint);
    },
    updateConstraint: function() {
        this.refreshOffset();
    },
    getContainerConstraint: function() {
        var container = this.getContainer(),
            element = this.getElement(),
            borders;
        if (!container || !element.dom) {
            return this.defaultConstraint;
        }
        borders = container.getBorders();
        return {
            min: {
                x: 0,
                y: 0
            },
            max: {
                x: this.containerWidth - this.width - borders.beforeX - borders.afterX,
                y: this.containerHeight - this.height - borders.beforeY - borders.afterY
            }
        };
    },
    getContainer: function() {
        var container = this.container;
        if (!container) {
            container = this.getElement().getParent();
            if (container) {
                this.container = container;
                container.on({
                    resize: 'onContainerResize',
                    destroy: 'onContainerDestroy',
                    scope: this,
                    
                    
                    
                    
                    
                    priority: 2000
                });
            }
        }
        return container;
    },
    onElementResize: function(element, info) {
        this.width = info.width;
        this.height = info.height;
        this.refreshContainerSize();
    },
    onContainerResize: function(container, info) {
        this.containerWidth = info.contentWidth;
        this.containerHeight = info.contentHeight;
        this.refresh();
    },
    refreshContainerSize: function() {
        
        
        
        var me = this,
            container = me.getContainer();
        me.containerWidth = container.getWidth();
        me.containerHeight = container.getHeight();
        this.refresh();
        return me;
    },
    onContainerDestroy: function() {
        delete this.container;
        delete this.containerSizeMonitor;
    },
    detachListeners: function() {
        this.getElement().un(this.elementListeners);
    },
    isAxisEnabled: function(axis) {
        var direction = this.getDirection();
        if (axis === 'x') {
            return (direction === this.DIRECTION_BOTH || direction === this.DIRECTION_HORIZONTAL);
        }
        return (direction === this.DIRECTION_BOTH || direction === this.DIRECTION_VERTICAL);
    },
    onPress: function(e) {
        this.fireEvent('touchstart', this, e);
    },
    onRelease: function(e) {
        this.fireEvent('touchend', this, e);
    },
    onDragStart: function(e) {
        var me = this,
            offset = me.offset;
        if (me.getDisabled()) {
            return false;
        }
        me.fireEventedAction('dragstart', [
            me,
            e,
            offset.x,
            offset.y
        ], me.initDragStart, me);
    },
    initDragStart: function(me, e, offsetX, offsetY) {
        this.dragStartOffset = {
            x: offsetX,
            y: offsetY
        };
        this.isDragging = true;
        this.getElement().addCls(this.getDraggingCls());
    },
    onDrag: function(e) {
        if (!this.isDragging) {
            return;
        }
        var startOffset = this.dragStartOffset;
        this.fireAction('drag', [
            this,
            e,
            startOffset.x + e.deltaX,
            startOffset.y + e.deltaY
        ], this.doDrag);
    },
    doDrag: function(me, e, offsetX, offsetY) {
        me.setOffset(offsetX, offsetY);
    },
    onDragEnd: function(e) {
        if (!this.isDragging) {
            return;
        }
        this.onDrag(e);
        this.isDragging = false;
        this.getElement().removeCls(this.getDraggingCls());
        this.fireEvent('dragend', this, e, this.offset.x, this.offset.y);
    },
    setOffset: function(x, y, animation) {
        var currentOffset = this.offset,
            constraint = this.getConstraint(),
            minOffset = constraint.min,
            maxOffset = constraint.max,
            min = Math.min,
            max = Math.max;
        if (this.isAxisEnabled('x') && typeof x === 'number') {
            x = min(max(x, minOffset.x), maxOffset.x);
        } else {
            x = currentOffset.x;
        }
        if (this.isAxisEnabled('y') && typeof y === 'number') {
            y = min(max(y, minOffset.y), maxOffset.y);
        } else {
            y = currentOffset.y;
        }
        currentOffset.x = x;
        currentOffset.y = y;
        this.getTranslatable().translate(x, y, animation);
    },
    getOffset: function() {
        return this.offset;
    },
    refreshConstraint: function() {
        this.setOffset.apply(this, this.getTranslatable().syncPosition());
        this.setConstraint(this.currentConstraint);
    },
    refreshOffset: function() {
        var offset = this.offset;
        this.setOffset(offset.x, offset.y);
    },
    refresh: function() {
        this.refreshConstraint();
        this.getTranslatable().refresh();
        this.refreshOffset();
    },
    
    enable: function() {
        return this.setDisabled(false);
    },
    
    disable: function() {
        return this.setDisabled(true);
    },
    destroy: function() {
        var me = this,
            translatable = me.getTranslatable();
        var element = me.getElement();
        if (element && !element.destroyed) {
            element.removeCls(me.getCls());
        }
        me.setComponent(null);
        me.detachListeners();
        if (translatable) {
            translatable.destroy();
        }
        me.callParent();
    }
});


Ext.define('Ext.util.Geolocation', {
    extend: Ext.Evented,
    alternateClassName: [
        'Ext.util.GeoLocation'
    ],
    config: {
        
        
        
        autoUpdate: true,
        
        frequency: 10000,
        
        latitude: null,
        
        longitude: null,
        
        accuracy: null,
        
        altitude: null,
        
        altitudeAccuracy: null,
        
        heading: null,
        
        speed: null,
        
        timestamp: null,
        
        
        allowHighAccuracy: false,
        
        timeout: Infinity,
        
        maximumAge: 0,
        
        provider: undefined
    },
    updateMaximumAge: function() {
        if (this.watchOperation) {
            this.updateWatchOperation();
        }
    },
    updateTimeout: function() {
        if (this.watchOperation) {
            this.updateWatchOperation();
        }
    },
    updateAllowHighAccuracy: function() {
        if (this.watchOperation) {
            this.updateWatchOperation();
        }
    },
    applyProvider: function(config) {
        if (Ext.feature.has.Geolocation) {
            if (!config) {
                if (navigator && navigator.geolocation) {
                    config = navigator.geolocation;
                } else if (window.google) {
                    config = google.gears.factory.create('beta.geolocation');
                }
            }
        } else {
            this.fireEvent('locationerror', this, false, false, true, 'This device does not support Geolocation.');
        }
        return config;
    },
    updateAutoUpdate: function(newAutoUpdate, oldAutoUpdate) {
        var me = this,
            provider = me.getProvider();
        if (oldAutoUpdate && provider) {
            Ext.uninterval(me.watchOperationId);
            me.watchOperationId = null;
        }
        if (newAutoUpdate) {
            if (!provider) {
                me.fireEvent('locationerror', me, false, false, true, null);
                return;
            }
            try {
                me.updateWatchOperation();
            } catch (e) {
                me.fireEvent('locationerror', me, false, false, true, e.message);
            }
        }
    },
    
    updateWatchOperation: function() {
        var me = this,
            provider = me.getProvider();
        
        if (me.watchOperationId) {
            Ext.uninterval(me.watchOperationId);
        }
        function pollPosition() {
            provider.getCurrentPosition(Ext.bind(me.fireUpdate, me), Ext.bind(me.fireError, me), me.parseOptions());
        }
        pollPosition();
        me.watchOperationId = Ext.interval(pollPosition, this.getFrequency());
    },
    
    updateLocation: function(callback, scope, positionOptions) {
        var me = this,
            provider = me.getProvider();
        var failFunction = function(message, error) {
                if (error) {
                    me.fireError(error);
                } else {
                    me.fireEvent('locationerror', me, false, false, true, message);
                }
                if (callback) {
                    callback.call(scope || me, null, me);
                }
            };
        
        if (!provider) {
            failFunction(null);
            return;
        }
        try {
            provider.getCurrentPosition(
            function(position) {
                me.fireUpdate(position);
                if (callback) {
                    callback.call(scope || me, me, me);
                }
            }, 
            
            function(error) {
                failFunction(null, error);
            }, positionOptions || me.parseOptions());
        } catch (e) {
            failFunction(e.message);
        }
    },
    
    fireUpdate: function(position) {
        var me = this,
            coords = position.coords;
        this.position = position;
        me.setConfig({
            timestamp: position.timestamp,
            latitude: coords.latitude,
            longitude: coords.longitude,
            accuracy: coords.accuracy,
            altitude: coords.altitude,
            altitudeAccuracy: coords.altitudeAccuracy,
            heading: coords.heading,
            speed: coords.speed
        });
        me.fireEvent('locationupdate', me);
    },
    
    fireError: function(error) {
        var errorCode = error.code;
        this.fireEvent('locationerror', this, errorCode == error.TIMEOUT, errorCode == error.PERMISSION_DENIED, errorCode == error.POSITION_UNAVAILABLE, error.message == undefined ? null : error.message);
    },
    
    parseOptions: function() {
        var timeout = this.getTimeout(),
            ret = {
                maximumAge: this.getMaximumAge(),
                enableHighAccuracy: this.getAllowHighAccuracy()
            };
        
        if (timeout !== Infinity) {
            ret.timeout = timeout;
        }
        return ret;
    },
    destroy: function() {
        this.setAutoUpdate(false);
        this.callParent();
    }
});


Ext.define('Ext.util.LineSegment', {
    
    constructor: function(point1, point2) {
        var Point = Ext.util.Point;
        this.point1 = Point.from(point1);
        this.point2 = Point.from(point2);
    },
    
    intersects: function(lineSegment) {
        var point1 = this.point1,
            point2 = this.point2,
            point3 = lineSegment.point1,
            point4 = lineSegment.point2,
            x1 = point1.x,
            x2 = point2.x,
            x3 = point3.x,
            x4 = point4.x,
            y1 = point1.y,
            y2 = point2.y,
            y3 = point3.y,
            y4 = point4.y,
            d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4),
            xi, yi;
        if (d == 0) {
            return null;
        }
        xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
        yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
        if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2) || xi < Math.min(x3, x4) || xi > Math.max(x3, x4) || yi < Math.min(y1, y2) || yi > Math.max(y1, y2) || yi < Math.min(y3, y4) || yi > Math.max(y3, y4)) {
            return null;
        }
        return new Ext.util.Point(xi, yi);
    },
    getLength: function() {
        return Math.abs(this.point1.getDistanceTo(this.point2));
    },
    getAngleToX: function() {
        var point1 = this.point1,
            point2 = this.point2,
            deltaY = point2.y - point1.y,
            deltaX = point2.x - point1.x;
        return Math.atan2(deltaY, deltaX);
    },
    getInBetweenPoint: function(distance) {
        var point1 = this.point1,
            angle = this.getAngleToX(),
            x = point1.x + Math.cos(angle) * distance,
            y = point1.y + Math.sin(angle) * distance;
        return new Ext.util.Point(x, y);
    },
    
    toString: function() {
        return this.point1.toString() + " " + this.point2.toString();
    }
});


Ext.define('Ext.util.PositionMap', {
    config: {
        minimumHeight: null
    },
    constructor: function(config) {
        var me = this;
        me.map = [];
        me.adjustments = {};
        me.offset = 0;
        me.initConfig(config);
    },
    populate: function(count, offset) {
        var me = this,
            map = me.map = me.map || [],
            minimumHeight = me.getMinimumHeight(),
            i, previousIndex, ln;
        me.adjustments = {
            indices: [],
            heights: {}
        };
        if (minimumHeight === null) {
            return;
        }
        offset = offset || 0;
        
        count++;
        map.length = count;
        map[0] = 0;
        for (i = offset + 1 , ln = count - 1; i <= ln; i++) {
            previousIndex = i - 1;
            map[i] = map[previousIndex] + minimumHeight;
        }
        me.offset = 0;
        for (i = 1 , ln = count - 1; i <= ln; i++) {
            previousIndex = i - 1;
            me.offset += map[i] - map[previousIndex] - minimumHeight;
        }
    },
    setItemHeight: function(index, height) {
        height = Math.max(height, this.getMinimumHeight());
        if (height !== this.getItemHeight(index)) {
            var adjustments = this.adjustments;
            adjustments.indices.push(parseInt(index, 10));
            adjustments.heights[index] = height;
        }
    },
    update: function() {
        var me = this,
            adjustments = me.adjustments,
            indices = adjustments.indices,
            heights = adjustments.heights,
            map = me.map,
            ln = indices && indices.length,
            minimumHeight = me.getMinimumHeight(),
            difference = 0,
            i, j, height, index, nextIndex, currentHeight;
        if (!ln) {
            return false;
        }
        Ext.Array.sort(indices, function(a, b) {
            return a - b;
        });
        for (i = 0; i < ln; i++) {
            index = indices[i];
            nextIndex = indices[i + 1] || map.length - 1;
            currentHeight = (map[index + 1] !== undefined) ? (map[index + 1] - map[index] + difference) : minimumHeight;
            height = heights[index];
            difference += height - currentHeight;
            for (j = index + 1; j <= nextIndex; j++) {
                map[j] += difference;
            }
        }
        me.offset += difference;
        me.adjustments = {
            indices: [],
            heights: {}
        };
        return true;
    },
    getItemHeight: function(index) {
        return this.map[index + 1] - this.map[index];
    },
    getTotalHeight: function() {
        return ((this.map.length - 1) * this.getMinimumHeight()) + this.offset;
    },
    findIndex: function(pos) {
        return this.map.length ? this.binarySearch(this.map, pos) : 0;
    },
    binarySearch: function(sorted, value) {
        var start = 0,
            end = sorted.length;
        if (value < sorted[0]) {
            return 0;
        }
        if (value > sorted[end - 1]) {
            return end - 1;
        }
        while (start + 1 < end) {
            var mid = (start + end) >> 1,
                val = sorted[mid];
            if (val == value) {
                return mid;
            } else if (val < value) {
                start = mid;
            } else {
                end = mid;
            }
        }
        return start;
    }
});


Ext.define('Ext.util.TranslatableList', {
    extend: Ext.util.translatable.Abstract,
    config: {
        items: []
    },
    applyItems: function(items) {
        return Ext.Array.from(items);
    },
    doTranslate: function(x, y) {
        var items = this.getItems(),
            offset = 0,
            i, ln, item, translateY;
        for (i = 0 , ln = items.length; i < ln; i++) {
            item = items[i];
            if (item && !item._list_hidden) {
                translateY = y + offset;
                offset += item.$height;
                item.translate(0, translateY);
            }
        }
    }
});


Ext.define('Ext.util.translatable.Component', {
    extend: Ext.util.translatable.CssTransform,
    alias: 'translatable.component',
    
    config: {
        component: null
    },
    doTranslate: function(x, y) {
        var component = this.getComponent();
        if (component.getFloated()) {
            component.setX(x);
            component.setY(y);
        } else if (component.isPositioned()) {
            component.setLeft(x);
            component.setTop(y);
        } else {
            this.callParent([
                x,
                y
            ]);
        }
    },
    syncPosition: function() {
        var component = this.getComponent(),
            result;
        if (component.getFloated()) {
            result = [
                component.getX(),
                component.getY()
            ];
        } else if (component.isPositioned()) {
            result = [
                component.getLeft(),
                component.getTop()
            ];
        } else {
            result = this.callParent();
        }
        this.x = result[0];
        this.y = result[1];
        return result;
    }
});


Ext.define('Ext.grid.Tree', {
    extend: Ext.grid.Grid,
    xtype: 'tree',
    alternateClassName: 'Ext.tree.Tree',
    classCls: Ext.baseCSSPrefix + 'tree',
    expanderLastCls: Ext.baseCSSPrefix + 'expander-last',
    expanderFirstCls: Ext.baseCSSPrefix + 'expander-first',
    expanderOnlyCls: Ext.baseCSSPrefix + 'expander-only',
    cellExpanderCls: Ext.baseCSSPrefix + 'cell-expander',
    
    
    
    
    cachedConfig: {
        
        expanderFirst: true,
        
        expanderOnly: true
    },
    config: {
        root: {},
        
        selectOnExpander: false,
        
        singleExpand: false,
        rootVisible: true,
        displayField: 'text',
        columns: false,
        
        rowLines: false,
        
        folderSort: false
    },
    eventsSelector: '.' + Ext.baseCSSPrefix + 'grid-cell',
    applyColumns: function(columns) {
        if (!columns) {
            this.setHideHeaders(true);
            columns = [
                {
                    xtype: 'treecolumn',
                    text: 'Name',
                    dataIndex: this.getDisplayField(),
                    minWidth: 100,
                    flex: 1
                }
            ];
        }
        return columns;
    },
    onRootChange: function(newRoot, oldRoot) {
        var me = this,
            fireEventArgs;
        if (oldRoot) {
            delete oldRoot.fireEventArgs;
        }
        
        
        if (newRoot) {
            fireEventArgs = newRoot.fireEventArgs;
            newRoot.fireEventArgs = function(eventName) {
                
                var ret = fireEventArgs.apply(newRoot, arguments);
                
                if (ret !== false) {
                    arguments[0] = me.rootEventsMap[eventName] || ('item' + eventName);
                    ret = me.fireEventArgs.apply(me, arguments);
                }
                return ret;
            };
        }
    },
    updateExpanderFirst: function(expanderFirst) {
        var el = this.element;
        el.toggleCls(this.expanderFirstCls, expanderFirst);
        el.toggleCls(this.expanderLastCls, !expanderFirst);
    },
    updateExpanderOnly: function(expanderOnly) {
        var el = this.element;
        el.toggleCls(this.expanderOnlyCls, expanderOnly);
        el.toggleCls(this.cellExpanderCls, !expanderOnly);
    },
    
    setRootNode: function(root) {
        var store = this.getStore();
        root = store.setRoot(root);
        return root;
    },
    
    getRootNode: function() {
        var store = this.getStore();
        return store ? store.getRoot() : null;
    },
    
    expandNode: function(record, deep, callback, scope) {
        return record.expand(deep, callback, scope || this);
    },
    
    collapseNode: function(record, deep, callback, scope) {
        return record.collapse(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();
        if (root) {
            Ext.suspendLayouts();
            scope = scope || me;
            if (me.getStore().rootVisible) {
                root.collapse(true, callback, scope);
            } else {
                root.collapseChildren(true, callback, scope);
            }
            Ext.resumeLayouts(true);
        }
    },
    privates: {
        rootEventsMap: {
            beforeappend: 'beforeitemappend',
            beforeremove: 'beforeritememove',
            beforemove: 'beforeitemmove',
            beforeinsert: 'beforeiteminsert',
            beforeexpand: 'beforeitemexpand',
            beforecollapse: 'beforeitemcollapse'
        },
        doChildTouchStart: function(location) {
            var cell = location.cell;
            if (cell && (!cell.isTreeCell || this.getSelectOnExpander() || location.event.target !== cell.expanderElement.dom)) {
                this.callParent([
                    location
                ]);
            }
        },
        updateStore: function(newStore, oldStore) {
            var me = this,
                newRoot;
            
            if (oldStore) {
                Ext.destroy(me.storeListeners, me.storeRelayers);
            }
            if (newStore) {
                me.store = newStore;
                
                
                if (newRoot = newStore.getRoot()) {
                    me.onRootChange(newRoot);
                } else {
                    newStore.setRoot(me.getRoot());
                    newRoot = newStore.getRoot();
                }
                
                if (!('rootVisible' in newStore.initialConfig)) {
                    newStore.setRootVisible(me.getRootVisible());
                }
                
                
                
                newStore.ownerTree = me;
                me.callParent([
                    newStore,
                    oldStore
                ]);
                newStore.folderSort = me.getFolderSort();
                
                me.storeListeners = me.mon(newStore, {
                    destroyable: true,
                    rootchange: me.onRootChange,
                    scope: me
                });
                
                me.storeRelayers = me.relayEvents(newStore, [
                    
                    'beforeload',
                    
                    'load'
                ]);
                
                
                
                
                if (!newStore.rootVisible && !newStore.autoLoad && !(newRoot.isExpanded() || newRoot.isLoading())) {
                    
                    
                    if (newRoot.isLoaded()) {
                        newRoot.data.expanded = true;
                        newStore.onNodeExpand(newRoot, newRoot.childNodes);
                    }
                    
                    
                    
                    
                    
                    else if (newStore.autoLoad !== false && !newStore.hasPendingLoad()) {
                        newRoot.data.expanded = false;
                        newRoot.expand();
                    }
                }
            }
        }
    }
});


Ext.define('Ext.grid.cell.Tree', {
    extend: Ext.grid.cell.Cell,
    xtype: 'treecell',
    isTreeCell: true,
    
    classCls: Ext.baseCSSPrefix + 'treecell',
    collapsedCls: Ext.baseCSSPrefix + 'collapsed',
    expandedCls: Ext.baseCSSPrefix + 'expanded',
    leafCls: Ext.baseCSSPrefix + 'leaf',
    expandableCls: Ext.baseCSSPrefix + 'expandable',
    withIconCls: Ext.baseCSSPrefix + 'with-icon',
    withoutIconCls: Ext.baseCSSPrefix + 'no-icon',
    loadingCls: Ext.baseCSSPrefix + 'loading',
    selectedCls: Ext.baseCSSPrefix + 'selected',
    config: {
        
        iconClsProperty: 'iconCls',
        
        iconCls: null,
        indent: null,
        
        text: {
            lazy: true,
            $value: ''
        }
    },
    
    
    
    
    element: {
        reference: 'element',
        children: [
            {
                reference: 'innerElement',
                cls: Ext.baseCSSPrefix + 'inner-el',
                children: [
                    {
                        reference: 'indentElement',
                        cls: Ext.baseCSSPrefix + 'indent-el'
                    },
                    {
                        reference: 'expanderElement',
                        cls: Ext.baseCSSPrefix + 'expander-el ' + Ext.baseCSSPrefix + 'font-icon'
                    },
                    {
                        reference: 'iconElement',
                        cls: Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon'
                    },
                    {
                        reference: 'bodyElement',
                        cls: Ext.baseCSSPrefix + 'body-el',
                        uiCls: 'body-el'
                    }
                ]
            }
        ]
    },
    
    toolDefaults: {
        zone: 'tail'
    },
    constructor: function(config) {
        this.callParent([
            config
        ]);
        this.element.on({
            scope: this,
            tap: 'maybeToggle'
        });
    },
    
    toggle: function() {
        var me = this,
            record = me.getRecord();
        if (record.isExpanded()) {
            me.collapse();
        } else if (record.isExpandable()) {
            me.expand();
        }
    },
    
    collapse: function() {
        var me = this,
            record = me.getRecord();
        me.getGrid().fireEventedAction('nodecollapse', [
            me.parent,
            record,
            'collapse'
        ], 'doToggle', this);
    },
    
    expand: function() {
        var me = this,
            record = me.getRecord(),
            tree = me.getGrid(),
            siblings, i, len, sibling;
        tree.fireEventedAction('nodeexpand', [
            me.parent,
            record,
            'expand'
        ], 'doToggle', me);
        
        if (record.isExpanded && !record.isRoot() && tree.getSingleExpand()) {
            siblings = record.parentNode.childNodes;
            for (i = 0 , len = siblings.length; i < len; ++i) {
                sibling = siblings[i];
                if (sibling !== record) {
                    sibling.collapse();
                }
            }
        }
    },
    refresh: function(ctx) {
        this.callParent([
            ctx
        ]);
        var record = this.getRecord();
        if (record) {
            this.doNodeUpdate(record);
        }
    },
    updateIconCls: function(iconCls, oldIconCls) {
        var me = this,
            el = me.element,
            noIcon = !iconCls;
        me.iconElement.replaceCls(oldIconCls, iconCls);
        el.toggleCls(me.withIconCls, !noIcon);
        el.toggleCls(me.withoutIconCls, noIcon);
    },
    updateUi: function(ui, oldUi) {
        this.callParent([
            ui,
            oldUi
        ]);
        
        this._indent = null;
        this.syncIndent();
    },
    privates: {
        
        doNodeUpdate: function(record) {
            var me = this,
                iconClsProperty = me.getIconClsProperty(),
                el = me.element;
            if (iconClsProperty) {
                me.setIconCls(record.data[iconClsProperty]);
            }
            el.toggleCls(me.loadingCls, record.data.loading);
            el.toggleCls(me.leafCls, record.isLeaf());
            me.syncExpandCls();
            me.syncIndent();
        },
        getGrid: function() {
            return this.row.grid;
        },
        syncExpandCls: function() {
            if (!this.updatingExpandCls) {
                var me = this,
                    record = me.getRecord(),
                    expandable = record.isExpandable(),
                    element = me.element,
                    expanded = record.isExpanded(),
                    expandedCls = me.expandedCls,
                    collapsedCls = me.collapsedCls;
                me.updatingExpandCls = true;
                element.toggleCls(me.expandableCls, expandable);
                if (expandable) {
                    element.toggleCls(expandedCls, expanded);
                    element.toggleCls(collapsedCls, !expanded);
                } else {
                    element.removeCls([
                        expandedCls,
                        collapsedCls
                    ]);
                }
                me.updatingExpandCls = false;
            }
        },
        syncIndent: function() {
            var me = this,
                column = me.getColumn(),
                indentSize, record, depth;
            if (column) {
                indentSize = column._indentSize;
                record = me.getRecord();
                if (!indentSize) {
                    column._indentSize = indentSize = parseInt(me.el.getStyle('background-position'), 10);
                }
                if (record) {
                    depth = record.getTreeStore().rootVisible ? record.data.depth : record.data.depth - 1;
                    me.indentElement.dom.style.width = (depth * indentSize) + 'px';
                }
            }
        },
        
        maybeToggle: function(e) {
            var me = this,
                record = me.getRecord(),
                wasExpanded = record.isExpanded();
            if (!record.isLeaf() && (!me.getGrid().getExpanderOnly() || e.target === me.expanderElement.dom)) {
                me.toggle();
            }
            
            
            if (record.isExpanded() !== wasExpanded) {
                e.nodeToggled = true;
                e.stopEvent();
            }
        },
        doToggle: function(row, record, fn) {
            record[fn]();
        }
    }
});


Ext.define('Ext.grid.column.Tree', {
    extend: Ext.grid.column.Column,
    xtype: 'treecolumn',
    config: {
        cell: {
            xtype: 'treecell'
        }
    },
    isTreeColumn: true
});

})(this.Ext6 || (this.Ext6 = {}));
